{"id":418841,"date":"2026-11-15T11:22:29","date_gmt":"2026-11-15T10:22:29","guid":{"rendered":"https:\/\/www.tiendabernad.com\/birrail-overhead-rail-designer\/"},"modified":"2026-11-15T11:23:46","modified_gmt":"2026-11-15T10:23:46","slug":"birrail-overhead-rail-designer","status":"publish","type":"page","link":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/","title":{"rendered":"BIRRAIL overhead rail designer"},"content":{"rendered":"<style id=\"bxr-lcss\">:is(#bxr-intro,#bxr-guia,#bxr-mod){--navy:#08184A;--blue:#1238D6;--orange:#FF5700;--amber:#FFC629;--ink:#26303F;--muted:#5D6A7E;--line:#D8DFE9;--panel:#F3F6FA;--ok:#17794A;--font:Lato,\"Helvetica Neue\",Arial,sans-serif;font-family:var(--font);font-size:15px;line-height:1.5;color:var(--ink);text-align:left;-webkit-font-smoothing:antialiased;clear:both}\r\n:is(#bxr-intro,#bxr-guia,#bxr-mod) *{box-sizing:border-box}\r\n:is(#bxr-intro,#bxr-guia,#bxr-mod) :is(p,ul,ol,li,h2,h3,table,thead,tbody,tr,th,td,details,summary,form,fieldset,legend,label,strong,em,span){margin:0;padding:0;border:0;float:none;width:auto;max-width:none;background:none;font-family:var(--font);font-size:inherit;line-height:inherit;letter-spacing:0;text-transform:none;list-style:none;color:inherit;font-style:normal}\r\n:is(#bxr-intro,#bxr-guia,#bxr-mod) li:before{content:none}\r\n#bxr-intro{display:flex;gap:14px 36px;flex-wrap:wrap;align-items:center;justify-content:space-between;margin:0 0 18px}\r\n#bxr-intro p{max-width:74ch;font-size:16.5px;color:var(--ink)}\r\n#bxr-intro ul{display:flex;gap:8px;flex-wrap:wrap}\r\n#bxr-intro li{border:1px solid var(--line);border-radius:8px;padding:7px 12px;min-width:112px;background:#fff}\r\n#bxr-intro li strong{display:block;font-size:18px;font-weight:900;color:var(--navy);line-height:1.2}\r\n#bxr-intro li span{font-size:12px;color:var(--muted)}\r\n#bxr-guia{display:flex;flex-direction:column;gap:34px;max-width:1080px;margin:0 auto;padding:6px 0 10px}\r\n#bxr-guia section{display:flex;flex-direction:column;gap:13px}\r\n#bxr-guia h2{font-size:25px;font-weight:900;color:var(--navy);line-height:1.15;text-wrap:balance}\r\n#bxr-guia p{max-width:70ch}\r\n#bxr-guia a{color:var(--blue);text-decoration:underline}\r\n#bxr-guia .bxr-tw{overflow-x:auto}\r\n#bxr-guia table{width:100%;min-width:560px;border-collapse:collapse;font-size:14.5px}\r\n#bxr-guia th{text-align:left;font-size:12px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:8px 10px;border-bottom:2px solid var(--ink);font-weight:700}\r\n#bxr-guia td{padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}\r\n#bxr-guia td+td{font-variant-numeric:tabular-nums;font-weight:700;color:var(--navy)}\r\n#bxr-guia .bxr-comp{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px}\r\n#bxr-guia .bxr-comp li{border:1px solid var(--line);border-radius:10px;padding:14px;display:flex;flex-direction:column;gap:6px;background:#fff}\r\n#bxr-guia .bxr-comp svg{width:100%;height:60px;display:block}\r\n#bxr-guia .bxr-comp a{color:var(--navy);font-weight:900;text-decoration:none}\r\n#bxr-guia .bxr-comp a:hover{text-decoration:underline}\r\n#bxr-guia .bxr-comp span{font-size:13.5px;color:var(--muted);line-height:1.4}\r\n#bxr-guia .bxr-comp em{font-weight:900;margin-top:auto}\r\n#bxr-guia details{border-bottom:1px solid var(--line);padding:12px 0}\r\n#bxr-guia summary{font-weight:900;color:var(--navy);cursor:pointer;font-size:16px;display:list-item;list-style:revert}\r\n#bxr-guia details p{margin-top:8px}\r\n#bxr-mod{background:var(--navy);border-radius:12px;padding:18px 22px;display:flex;gap:14px 28px;flex-wrap:wrap;align-items:center;justify-content:space-between;margin:0 0 22px}\r\n#bxr-mod .bxr-mt{display:flex;flex-direction:column;gap:3px;flex:1 1 300px;min-width:0}\r\n#bxr-mod .bxr-mt small{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--amber);font-weight:900;font-family:var(--font)}\r\n#bxr-mod .bxr-mt strong{font-size:21px;font-weight:900;color:#fff;line-height:1.2}\r\n#bxr-mod .bxr-mt span{color:#C9D3E8;font-size:14.5px}\r\n#bxr-mod form{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end}\r\n#bxr-mod label{display:flex;flex-direction:column;gap:3px;font-size:12px;font-weight:700;color:#C9D3E8}\r\n#bxr-mod input[type=number]{width:86px;height:42px;border:1px solid #8FA0C8;border-radius:8px;padding:0 8px;background:#fff;color:var(--ink);font:700 15px var(--font);margin:0;box-shadow:none}\r\n#bxr-mod .bxr-sp{display:flex;border:1px solid #8FA0C8;border-radius:8px;overflow:hidden;height:42px}\r\n#bxr-mod .bxr-sp label{flex-direction:row;align-items:center;padding:0 11px;font-size:13.5px;color:#fff;cursor:pointer;border-left:1px solid #8FA0C8;position:relative}\r\n#bxr-mod .bxr-sp label:first-child{border-left:0}\r\n#bxr-mod .bxr-sp input{position:absolute;opacity:0;width:1px;height:1px;margin:0}\r\n#bxr-mod .bxr-sp label:has(input:checked){background:#fff;color:var(--navy)}\r\n#bxr-mod .bxr-sp label:has(input:focus-visible){outline:2px solid var(--amber);outline-offset:-2px}\r\n#bxr-mod button{height:42px;border:0;border-radius:8px;background:var(--amber);color:#1c1500;font:900 15px var(--font);padding:0 18px;cursor:pointer;margin:0;text-transform:none;letter-spacing:0;box-shadow:none;line-height:1}\r\n#bxr-mod button:hover{background:#FFD24F;color:#1c1500}\r\n.bxr-cta-w{margin:14px 0 6px!important;clear:both}\r\na.bxr-cta{display:inline-flex;align-items:center;gap:9px;border:2px solid #08184A;border-radius:8px;padding:9px 14px;font:900 14.5px Lato,Arial,sans-serif;color:#08184A;text-decoration:none;background:#fff;line-height:1.2}\r\na.bxr-cta:hover{background:#08184A;color:#fff}\r\na.bxr-cta svg{width:26px;height:16px;flex:none}\r\n@media(max-width:760px){.bx-s-birrail{order:5}#bxr-mod{padding:16px}#bxr-mod form{width:100%}#bxr-mod input[type=number]{width:100%}#bxr-mod form>label{flex:1 1 90px}#bxr-mod button{flex:1 1 100%}#bxr-intro p{font-size:15.5px}#bxr-intro li{flex:1 1 96px;min-width:0}#bxr-guia h2{font-size:22px}}\r\nbody.bxr-page #main{padding-top:26px}\r\nbody.bxr-page .post-content>p:empty{display:none}<\/style><div id=\"bxr-intro\"><p>Enter your cold room dimensions, choose the species and the designer lays out the rail following Bernad\u2019s mounting criteria: dimensioned plan, parts list with references and the price of the whole installation.<\/p><ul><li><strong>61,50 \u20ac\/m<\/strong><span>complete twin-track rail, excl. VAT<\/span><\/li><li><strong>R 350 mm<\/strong><span>bends and switches<\/span><\/li><li><strong>No welding<\/strong><span>bolted assembly<\/span><\/li><li><strong>Since 1987<\/strong><span>own manufacture<\/span><\/li><\/ul><\/div><div id=\"bxr\"><\/div><template id=\"bxr-tpl\"><style>\/* Dise\u00f1ador BIRRAIL \u2014 identidad de tiendabernad.com. Idea de p\u00e1gina: mesa de trabajo en tres columnas\r\n   (datos \u00b7 plano \u00b7 despiece) sobre fondo gris panel; debajo, la gu\u00eda indexable en una columna de lectura. *\/\r\n:host{display:block;margin:0 0 34px}\r\n:host,.app{\r\n  --navy:#08184A; --blue:#1238D6; --orange:#FF5700; --amber:#FFC629;\r\n  --ink:#26303F; --muted:#5D6A7E; --line:#D8DFE9; --panel:#F3F6FA; --white:#FFFFFF;\r\n  --ok:#17794A; --warn:#9A5B00; --err:#C81E25; --teal:#0B7C73;\r\n  --ok-bg:#E7F4EC; --warn-bg:#FFF3D6; --err-bg:#FDE8E8;\r\n  --font:Lato,\"Helvetica Neue\",Arial,sans-serif;\r\n  --r:10px;\r\n  color-scheme:light;\r\n}\r\n*{box-sizing:border-box}\r\n.app{background:var(--panel);border-radius:14px;padding:2px 16px 0;color:var(--ink);font-family:var(--font);font-size:15px;line-height:1.5;letter-spacing:0;text-transform:none;text-align:left;-webkit-font-smoothing:antialiased}\r\nh1,h2,h3,p,ul,ol,dl,dd,figure,table{margin:0;padding:0}\r\nh1,h2,h3{text-wrap:balance;color:var(--navy);line-height:1.15}\r\nul,ol{list-style:none}\r\na{color:var(--blue)}\r\nbutton,input,select{font:inherit;color:inherit}\r\nbutton{cursor:pointer}\r\n:focus-visible{outline:3px solid var(--blue);outline-offset:2px;border-radius:4px}\r\n.num{font-variant-numeric:tabular-nums}\r\n\r\n\r\n\r\n\/* c\u00e1maras *\/\r\n.rooms{display:flex;gap:6px;flex-wrap:wrap;align-items:center;padding:16px 0 12px}\r\n.rooms button{border:1px solid var(--line);background:#fff;border-radius:999px;padding:7px 14px;font-weight:700;font-size:14px;color:var(--muted);display:flex;gap:8px;align-items:center}\r\n.rooms button[aria-selected=\"true\"]{background:var(--navy);border-color:var(--navy);color:#fff}\r\n.rooms button .x{opacity:.7;font-weight:400;padding:0 2px;border-radius:50%}\r\n.rooms button .x:hover{opacity:1}\r\n.rooms .add{border-style:dashed;color:var(--blue);border-color:#A9B7E8}\r\n.rooms .eg{margin-left:auto;display:flex;gap:8px;align-items:center;font-size:13px;color:var(--muted)}\r\n.rooms .eg{min-width:0;max-width:100%}\r\n.rooms select{border:1px solid var(--line);border-radius:8px;padding:6px 8px;background:#fff;font-size:13px;min-width:0;max-width:100%;flex:0 1 auto}\r\n\r\n\/* mesa de trabajo *\/\r\n.grid{display:grid;grid-template-columns:284px minmax(0,1fr) 364px;gap:16px;align-items:start;padding-bottom:16px}\r\n.card{background:#fff;border:1px solid var(--line);border-radius:var(--r)}\r\n.in{padding:4px 16px 16px}\r\n.grp{padding:14px 0;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:10px}\r\n.grp:last-child{border-bottom:0;padding-bottom:0}\r\n.grp>h2{font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:900;display:flex;gap:8px;align-items:center}\r\n.grp>h2 i{font-style:normal;background:var(--navy);color:#fff;border-radius:50%;width:19px;height:19px;display:inline-grid;place-items:center;font-size:11px;letter-spacing:0}\r\n.f{display:flex;flex-direction:column;gap:4px}\r\n.f label,.f .lb{font-size:13px;font-weight:700;color:var(--ink)}\r\n.f small{font-size:12px;color:var(--muted);line-height:1.35}\r\n.row{display:grid;grid-template-columns:1fr 1fr;gap:10px}\r\n.row3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px}\r\n.ipt{position:relative;display:flex}\r\n.ipt input,.f select,.f input[type=text]{width:100%;border:1px solid #B9C3D3;border-radius:8px;padding:8px 10px;background:#fff;min-width:0}\r\n.ipt input{padding-right:34px;font-variant-numeric:tabular-nums;font-weight:700}\r\n.ipt u{position:absolute;right:10px;top:50%;transform:translateY(-50%);text-decoration:none;font-size:12px;color:var(--muted);pointer-events:none}\r\n.row3 .ipt input{padding:8px 22px 8px 8px}\r\n.row3 .ipt u{right:7px}\r\n#doors{display:flex;flex-direction:column;gap:8px}\r\n#doors:empty{display:none}\r\n.door{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr) minmax(0,1fr);gap:4px 8px;align-items:end;padding:8px 10px 10px;border:1px solid var(--line);border-radius:10px;background:#FAFBFD}\r\n.door .dh{grid-column:1\/-1;display:flex;justify-content:space-between;align-items:baseline;font-size:13.5px}\r\n.door .ipt input{padding:8px 2px 8px 7px;font-size:14px}\r\n.door select{padding:8px 2px 8px 6px;min-width:0;max-width:100%}\r\n\r\n.ipt input:focus,.f select:focus{border-color:var(--blue)}\r\n.seg{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;border:1px solid #B9C3D3;border-radius:8px;overflow:hidden}\r\n.seg button{border:0;background:#fff;padding:8px 4px;font-size:13.5px;font-weight:700;color:var(--muted);border-left:1px solid var(--line)}\r\n.seg button:first-child{border-left:0}\r\n.seg button[aria-pressed=\"true\"]{background:var(--navy);color:#fff}\r\ninput[type=range]{width:100%;accent-color:var(--blue);margin:2px 0 0}\r\n.lnk{border:0;background:none;color:var(--blue);padding:0;font-size:12.5px;font-weight:700;text-decoration:underline}\r\n.tpls{display:grid;grid-template-columns:1fr 1fr;gap:8px}\r\n.tpl{border:1px solid #B9C3D3;background:#fff;border-radius:8px;padding:8px 8px 7px;text-align:left;display:flex;flex-direction:column;gap:3px;position:relative;min-height:96px}\r\n.tpl svg{width:100%;height:38px;display:block}\r\n.tpl b{font-size:13.5px;color:var(--navy)}\r\n.tpl span{font-size:11.5px;color:var(--muted);line-height:1.25}\r\n.tpl[aria-pressed=\"true\"]{border-color:var(--blue);box-shadow:0 0 0 2px var(--blue) inset;background:#F5F7FD}\r\n.tpl[disabled]{opacity:.5;cursor:not-allowed}\r\n.tpl em{position:absolute;top:-8px;right:6px;background:var(--ok);color:#fff;font-style:normal;font-size:10px;font-weight:900;letter-spacing:.06em;text-transform:uppercase;border-radius:999px;padding:2px 7px}\r\n.tpl .tk{stroke:var(--navy);stroke-width:2.2;fill:none;stroke-linecap:round;stroke-linejoin:round}\r\n.tpl .rm{stroke:#B9C3D3;stroke-width:1.2;fill:none}\r\n.step{display:flex;align-items:center;gap:8px}\r\n.step button{width:34px;height:34px;border-radius:8px;border:1px solid #B9C3D3;background:#fff;font-size:18px;font-weight:700;line-height:1}\r\n.step button:disabled{opacity:.35;cursor:default}\r\n.step output{min-width:30px;text-align:center;font-weight:900;font-size:17px}\r\n\r\n\/* plano *\/\r\n.stage{display:flex;flex-direction:column;min-width:0}\r\n.sth{display:flex;gap:8px 12px;flex-wrap:wrap;align-items:center;justify-content:space-between;padding:10px 12px;border-bottom:1px solid var(--line)}\r\n.views{display:flex;gap:2px;background:var(--panel);border-radius:8px;padding:3px}\r\n.views button{border:0;background:none;border-radius:6px;padding:6px 13px;font-weight:700;font-size:13.5px;color:var(--muted)}\r\n.views button[aria-selected=\"true\"]{background:#fff;color:var(--navy);box-shadow:0 1px 2px rgba(8,24,74,.18)}\r\n.tools{display:flex;gap:6px;flex-wrap:wrap;align-items:center}\r\n.tools button{border:1px solid #B9C3D3;background:#fff;border-radius:8px;padding:6px 11px;font-size:13px;font-weight:700}\r\n.tools button[aria-pressed=\"true\"]{background:var(--blue);border-color:var(--blue);color:#fff}\r\n.tools button:disabled{opacity:.4;cursor:default}\r\n.cv{position:relative;padding:8px;min-height:340px;display:grid;place-items:center;overflow:hidden;background:\r\n  linear-gradient(#fff,#fff)}\r\n.cv svg{display:block;max-width:100%;height:auto;touch-action:pan-y}\r\n.cv.edit svg{touch-action:none;cursor:crosshair}\r\n.cv.erase svg{cursor:pointer}\r\n.hint{font-size:12.5px;color:var(--muted);padding:0 14px 10px;min-height:1.4em}\r\n.legend{display:flex;gap:6px 16px;flex-wrap:wrap;padding:10px 14px;border-top:1px solid var(--line);font-size:12.5px;color:var(--muted)}\r\n.legend span{display:inline-flex;gap:6px;align-items:center}\r\n.legend i{width:18px;height:5px;border-radius:3px;display:inline-block}\r\n.checks{margin-top:16px;padding:14px 16px}\r\n.checks h2{font-size:15px;font-weight:900;display:flex;gap:10px;align-items:center;flex-wrap:wrap}\r\n.pill{display:inline-flex;align-items:center;gap:5px;border-radius:999px;padding:2px 10px;font-size:12px;font-weight:900}\r\n.pill.ok{background:var(--ok-bg);color:var(--ok)} .pill.warn{background:var(--warn-bg);color:var(--warn)} .pill.err{background:var(--err-bg);color:var(--err)}\r\n.checks ul{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:6px 18px;margin-top:10px}\r\n.checks li{display:grid;grid-template-columns:20px 1fr;gap:8px;font-size:13.5px;line-height:1.35;padding:4px 0}\r\n.checks li b{font-weight:700}\r\n.checks li small{display:block;color:var(--muted);font-size:12.5px}\r\n.ic{width:18px;height:18px;border-radius:50%;display:grid;place-items:center;font-size:11px;font-weight:900;color:#fff;margin-top:1px}\r\n.ic.ok{background:var(--ok)} .ic.warn{background:#E0A100;color:#3a2c00} .ic.err{background:var(--err)}\r\n\r\n\/* trazos del plano *\/\r\n.p-floor{fill:#fff} .p-grid{stroke:#E6EBF3;fill:none} .p-wall{fill:#DDE3EC;stroke:var(--ink)}\r\n.p-door{stroke:var(--ink);fill:none} .p-axis{stroke:var(--muted);fill:none}\r\n.t-out{stroke:var(--navy);fill:none;stroke-linecap:butt;stroke-linejoin:round}\r\n.t-in{stroke:#fff;fill:none;stroke-linecap:butt;stroke-linejoin:round}\r\n.t-curva{stroke:var(--blue)} .t-desvio,.t-desvio45{stroke:var(--orange)} .t-triple{stroke:var(--teal)} .t-bad{stroke:var(--err)}\r\n.t-tope{stroke:var(--err);stroke-linecap:round}\r\n.t-sil{stroke:var(--navy);opacity:.55}\r\n.d-l{stroke:var(--muted);fill:none} .d-t{fill:var(--ink);font-family:var(--font);font-variant-numeric:tabular-nums;text-anchor:middle}\r\n.d-t.b{font-weight:900;fill:var(--navy)}\r\n.lbl{fill:var(--muted);font-family:var(--font)}\r\n.handle{cursor:ew-resize}\r\n.handle rect{fill:var(--blue)} .handle path{stroke:#fff;fill:none}\r\n.hit{stroke:transparent;fill:none;pointer-events:stroke}\r\n.cv.erase .hit:hover{stroke:rgba(200,30,37,.35)}\r\n.prev{stroke:var(--blue);stroke-dasharray:6 5;fill:none}\r\n.i-floor{fill:#EEF2F8;stroke:#9AA6B6} .i-wall{fill:#F7F9FC;stroke:#9AA6B6} .i-near{fill:none;stroke:#9AA6B6}\r\n.i-sh{stroke:#C9D3E2;fill:none} .i-rail{stroke:var(--navy);fill:none;stroke-linecap:round;stroke-linejoin:round}\r\n.i-hook{stroke:#55626F} .i-meat{stroke:#E4795A;opacity:.42;stroke-linecap:round;fill:none}\r\n.a-fill{fill:#E3E9F1;stroke:var(--ink)} .a-rail{fill:var(--navy)} .a-ln{stroke:var(--ink);fill:none}\r\n\r\n\/* hoja de plano *\/\r\n.sheetw{overflow-x:auto;width:100%}\r\n.sheet{min-width:600px;border:2px solid var(--ink);margin:6px;background:#fff;display:grid;grid-template-columns:minmax(0,1fr) 222px;font-size:11.5px;line-height:1.35}\r\n.sheet .dr{padding:6px;display:grid;place-items:center;border-right:1px solid var(--ink)}\r\n.sheet .side{display:flex;flex-direction:column}\r\n.sheet h3{font-size:11px;letter-spacing:.08em;text-transform:uppercase;padding:6px 8px;border-bottom:1px solid var(--ink);background:var(--panel)}\r\n.sheet table{width:100%;border-collapse:collapse}\r\n.sheet td{padding:3px 8px;border-bottom:1px solid var(--line);vertical-align:top}\r\n.sheet td:last-child{text-align:right;font-variant-numeric:tabular-nums;white-space:nowrap;font-weight:700}\r\n.caj{margin-top:auto;border-top:2px solid var(--ink);display:grid;grid-template-columns:1fr 1fr}\r\n.caj div{padding:4px 8px;border-bottom:1px solid var(--ink);min-height:38px}\r\n.caj div:nth-child(odd){border-right:1px solid var(--ink)}\r\n.caj .w{grid-column:1\/-1;border-right:0!important}\r\n.caj small{display:block;font-size:9px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted)}\r\n.caj input{border:0;border-bottom:1px dashed #9AA6B6;width:100%;padding:1px 0;font-size:12px;font-weight:700;background:none}\r\n.caj b{font-size:12px}\r\n.caj .logo{font-weight:900;color:var(--navy);font-size:15px;letter-spacing:.02em}\r\n\r\n\/* despiece *\/\r\n.out{padding:16px;display:flex;flex-direction:column;gap:14px;position:sticky;top:96px}\r\n.out .cols,.out .col{display:flex;flex-direction:column;gap:14px}\r\n.tot{background:var(--navy);color:#fff;border-radius:8px;padding:14px 16px}\r\n.tot small{font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:#B8C4DE;font-weight:900}\r\n.tot .big{font-size:34px;font-weight:900;line-height:1.1;margin-top:2px}\r\n.tot .big span{font-size:14px;font-weight:700;color:#C9D3E8}\r\n.tot p{font-size:13px;color:#C9D3E8;margin-top:4px}\r\n.kpi{display:grid;grid-template-columns:1fr 1fr;gap:8px}\r\n.kpi li{border:1px solid var(--line);border-radius:8px;padding:8px 10px}\r\n.kpi b{display:block;font-size:18px;font-weight:900;color:var(--navy);line-height:1.2}\r\n.kpi span{font-size:12px;color:var(--muted);line-height:1.3;display:block}\r\n.bom{width:100%;border-collapse:collapse;font-size:13.5px}\r\n.bom caption{text-align:left;font-size:12px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);font-weight:900;padding-bottom:6px}\r\n.bom td{padding:8px 0;border-top:1px solid var(--line);vertical-align:top}\r\n.bom td:first-child{width:46px;font-weight:900;color:var(--navy);white-space:nowrap;padding-right:8px}\r\n.bom td:last-child{text-align:right;white-space:nowrap;font-weight:700;padding-left:8px}\r\n.bom a{color:var(--ink);font-weight:700;text-decoration-color:#B9C3D3}\r\n.bom small{display:block;color:var(--muted);font-size:12px}\r\n.bom tfoot td{font-weight:400;color:var(--muted);padding:5px 0;border-top:0}\r\n.bom tfoot tr:first-child td{border-top:2px solid var(--ink);padding-top:9px}\r\n.bom tfoot tr.g td{color:var(--navy);font-weight:900;font-size:16px}\r\n.cta{border:0;border-radius:8px;background:var(--amber);color:#1c1500;font-weight:900;font-size:16px;padding:13px 16px;width:100%}\r\n.cta:hover{background:#FFD24F}\r\n.cta[disabled]{background:#E3E9F1;color:var(--muted);cursor:not-allowed}\r\n.acts{display:grid;grid-template-columns:1fr 1fr;gap:8px}\r\n.acts button,.acts a{border:1px solid #B9C3D3;background:#fff;border-radius:8px;padding:9px 8px;font-size:13px;font-weight:700;text-align:center;text-decoration:none;color:var(--ink)}\r\n.acts .wa,.acts .w2{grid-column:1\/-1}\r\n.acts .wa{border-color:var(--ok);color:var(--ok)}\r\n.acts button:hover,.acts a:hover{border-color:var(--navy)}\r\n.cmsg{font-size:13.5px;font-weight:700;color:var(--ok);background:var(--ok-bg);border-radius:8px;padding:9px 12px}\r\n.cmsg[data-err]{color:var(--err);background:var(--err-bg)}\r\n.cmsg a{color:inherit}\r\n.cmsg[hidden]{display:none}\r\n.note{font-size:12.5px;color:var(--muted);line-height:1.4}\r\ndetails.tec{border-top:1px solid var(--line);padding-top:10px}\r\ndetails.tec summary{font-weight:900;color:var(--navy);cursor:pointer;font-size:14px}\r\ndetails.tec dl{display:grid;grid-template-columns:1fr auto;gap:4px 12px;font-size:13px;margin-top:8px}\r\ndetails.tec dt{color:var(--muted)} details.tec dd{font-weight:700;text-align:right;font-variant-numeric:tabular-nums}\r\n\r\n\r\n\/* barra inferior en m\u00f3vil y avisos *\/\r\n.bar{display:none}\r\n.toast{position:fixed;left:50%;bottom:22px;transform:translate(-50%,20px);background:var(--ink);color:#fff;padding:11px 16px;border-radius:8px;font-size:14px;max-width:min(92vw,520px);opacity:0;pointer-events:none;transition:opacity .2s,transform .2s;z-index:99999;text-align:center}\r\n.toast.on{opacity:1;transform:translate(-50%,0)}\r\n@media (prefers-reduced-motion:reduce){.toast{transition:none}}\r\n\r\n@media (max-width:1180px){\r\n  .grid{grid-template-columns:284px minmax(0,1fr)}\r\n  .out{grid-column:1\/-1;position:static}\r\n  .out .cols{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}\r\n}\r\n@media (max-width:820px){\r\n  .grid{grid-template-columns:minmax(0,1fr)}\r\n  .stage{order:-1}\r\n  .out .cols{display:flex;flex-direction:column}\r\n  .rooms .eg{margin-left:0;width:100%}\r\n  .cv{min-height:260px;padding:4px}\r\n  .views button{padding:6px 10px}\r\n  .app{padding:2px 10px 0}\r\n  .bar{display:flex;position:sticky;bottom:var(--bxr-bot,0px);margin:0 -10px;border-radius:0 0 14px 14px;background:#fff;border-top:1px solid var(--line);padding:8px 14px;gap:12px;align-items:center;justify-content:space-between;z-index:20;box-shadow:0 -4px 14px rgba(8,24,74,.1)}\r\n  .bar b{font-size:19px;font-weight:900;color:var(--navy);display:block;line-height:1.1}\r\n  .bar small{font-size:11.5px;color:var(--muted)}\r\n  .bar button{border:0;border-radius:8px;background:var(--amber);font-weight:900;padding:10px 14px;font-size:14px;color:#1c1500}\r\n  .toast{bottom:calc(74px + var(--bxr-bot,0px))}\r\n}<\/style><div class=\"app\">\r\n<div class=\"main\">\r\n  <div class=\"rooms\" id=\"rooms\" role=\"tablist\" aria-label=\"Project rooms\"><\/div>\r\n\r\n  <div class=\"grid\">\r\n    <!-- 1 \u00b7 datos -->\r\n    <aside class=\"card in\" aria-label=\"Room data\">\r\n      <div class=\"grp\">\r\n        <h2><i>1<\/i>Room<\/h2>\r\n        <div class=\"f\"><label for=\"in-name\">Name<\/label><input type=\"text\" id=\"in-name\" maxlength=\"28\" autocomplete=\"off\"><\/div>\r\n        <div class=\"f\"><span class=\"lb\" id=\"lb-sp\">What will hang<\/span>\r\n          <div class=\"seg\" id=\"in-sp\" role=\"group\" aria-labelledby=\"lb-sp\">\r\n            <button type=\"button\" data-v=\"vacuno\">Beef<\/button><button type=\"button\" data-v=\"porcino\">Pork<\/button><button type=\"button\" data-v=\"ovino\">Lamb<\/button>\r\n          <\/div>\r\n          <small id=\"sp-info\"><\/small>\r\n        <\/div>\r\n        <div class=\"row3\">\r\n          <div class=\"f\"><label for=\"in-w\">Clear width<\/label><div class=\"ipt\"><input id=\"in-w\" type=\"number\" step=\"0.05\" min=\"1.2\" max=\"40\" inputmode=\"decimal\"><u>m<\/u><\/div><\/div>\r\n          <div class=\"f\"><label for=\"in-d\">Clear depth<\/label><div class=\"ipt\"><input id=\"in-d\" type=\"number\" step=\"0.05\" min=\"1.5\" max=\"60\" inputmode=\"decimal\"><u>m<\/u><\/div><\/div>\r\n          <div class=\"f\"><label for=\"in-h\">Clear height<\/label><div class=\"ipt\"><input id=\"in-h\" type=\"number\" step=\"0.05\" min=\"2\" max=\"8\" inputmode=\"decimal\"><u>m<\/u><\/div><\/div>\r\n        <\/div>\r\n        <small class=\"note\">The width is the wall with the entry door.<\/small>\r\n      <\/div>\r\n\r\n      <div class=\"grp\">\r\n        <h2><i>2<\/i>Entry door<\/h2>\r\n        <div class=\"f\"><label for=\"in-xd\">Door axis, from the left wall<\/label>\r\n          <div class=\"ipt\"><input id=\"in-xd\" type=\"number\" step=\"0.05\" min=\"0.3\" inputmode=\"decimal\"><u>m<\/u><\/div>\r\n          <input type=\"range\" id=\"in-xdr\" min=\"0\" max=\"100\" step=\"1\" aria-label=\"Door position\">\r\n          <button type=\"button\" class=\"lnk\" id=\"bt-center\" style=\"align-self:flex-start\">Centre the door<\/button>\r\n        <\/div>\r\n        <div class=\"row\">\r\n          <div class=\"f\"><label for=\"in-dw\">Door width<\/label><div class=\"ipt\"><input id=\"in-dw\" type=\"number\" step=\"0.05\" min=\"0.6\" max=\"4\" inputmode=\"decimal\"><u>m<\/u><\/div><\/div>\r\n          <div class=\"f\"><label for=\"in-ext\">Rail going out<\/label><div class=\"ipt\"><input id=\"in-ext\" type=\"number\" step=\"0.1\" min=\"0\" max=\"30\" inputmode=\"decimal\"><u>m<\/u><\/div><\/div>\r\n        <\/div>\r\n        <small class=\"note\">\u201cRail going out\u201d is the section that continues along the corridor or the dock, past the door.<\/small>\r\n        <div class=\"f\" id=\"f-doors\"><span class=\"lb\">Other doors<\/span>\r\n          <div id=\"doors\"><\/div>\r\n          <button type=\"button\" class=\"lnk\" id=\"bt-door\" style=\"align-self:flex-start\">+ Add another door<\/button>\r\n          <small class=\"note\" id=\"doors-note\" hidden>The rail reaches each door automatically. Axis and width in metres: the axis is measured from the left wall or, on the side walls, from the entry wall. You can also drag its handle on the plan.<\/small>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"grp\">\r\n        <h2><i>3<\/i>Layout<\/h2>\r\n        <div class=\"tpls\" id=\"tpls\"><\/div>\r\n        <div class=\"f\" id=\"f-cierre\"><span class=\"lb\" id=\"lb-ci\">End of the lines<\/span>\r\n          <div class=\"seg\" id=\"in-ci\" role=\"group\" aria-labelledby=\"lb-ci\"><button type=\"button\" data-v=\"1\">Closed circuit<\/button><button type=\"button\" data-v=\"0\">End stops<\/button><\/div>\r\n          <small id=\"ci-info\"><\/small>\r\n        <\/div>\r\n        <div class=\"f\" id=\"f-tri\"><span class=\"lb\" id=\"lb-tri\">Switches closing the circuit<\/span>\r\n          <div class=\"seg\" id=\"in-tri\" role=\"group\" aria-labelledby=\"lb-tri\"><button type=\"button\" data-v=\"0\">Single<\/button><button type=\"button\" data-v=\"1\">Triple<\/button><\/div>\r\n          <small id=\"tri-info\"><\/small>\r\n        <\/div>\r\n        <div class=\"f\" id=\"f-lines\"><span class=\"lb\">Storage lines<\/span>\r\n          <div class=\"step\"><button type=\"button\" id=\"bt-lm\" aria-label=\"One line fewer\">\u2212<\/button><output id=\"out-lines\">4<\/output><button type=\"button\" id=\"bt-lp\" aria-label=\"One more line\">+<\/button><small id=\"lines-max\"><\/small><\/div>\r\n        <\/div>\r\n      <\/div>\r\n\r\n      <div class=\"grp\">\r\n        <h2><i>4<\/i>Mounting and trolleys<\/h2>\r\n        <div class=\"f\"><label for=\"in-nr\">Running level<\/label>\r\n          <div class=\"ipt\"><input id=\"in-nr\" type=\"number\" step=\"10\" min=\"1800\" max=\"6000\" inputmode=\"numeric\"><u>mm<\/u><\/div>\r\n          <small id=\"nr-info\"><\/small>\r\n        <\/div>\r\n        <div class=\"f\"><span class=\"lb\" id=\"lb-sil\">Bracket spacing (whole project)<\/span>\r\n          <div class=\"seg\" id=\"in-sil\" role=\"group\" aria-labelledby=\"lb-sil\"><button type=\"button\" data-v=\"66\">Every 66 cm<\/button><button type=\"button\" data-v=\"50\">Every 50 cm<\/button><\/div>\r\n          <small id=\"sil-info\"><\/small>\r\n        <\/div>\r\n        <div class=\"f\"><label for=\"in-car\">Trolleys<\/label>\r\n          <select id=\"in-car\"><\/select>\r\n        <\/div>\r\n        <div class=\"f\"><label for=\"in-ncar\">Number of trolleys<\/label>\r\n          <div class=\"ipt\"><input id=\"in-ncar\" type=\"number\" step=\"1\" min=\"0\" max=\"5000\" inputmode=\"numeric\"><u>pcs<\/u><\/div>\r\n          <small id=\"car-info\"><\/small>\r\n        <\/div>\r\n      <\/div>\r\n    <\/aside>\r\n\r\n    <!-- 2 \u00b7 plano -->\r\n    <section class=\"stage\" aria-label=\"Drawing\">\r\n      <div class=\"card\">\r\n        <div class=\"sth\">\r\n          <div class=\"views\" role=\"tablist\" aria-label=\"View\" id=\"views\">\r\n            <button type=\"button\" role=\"tab\" data-v=\"planta\">Plan<\/button><button type=\"button\" role=\"tab\" data-v=\"alzado\">Elevation<\/button><button type=\"button\" role=\"tab\" data-v=\"iso\">3D<\/button><button type=\"button\" role=\"tab\" data-v=\"plano\">Drawing sheet<\/button>\r\n          <\/div>\r\n          <div class=\"tools\" id=\"tools\"><\/div>\r\n        <\/div>\r\n        <div class=\"cv\" id=\"cv\"><\/div>\r\n        <p class=\"hint\" id=\"hint\"><\/p>\r\n        <div class=\"legend\" id=\"legend\"><\/div>\r\n      <\/div>\r\n      <div class=\"card checks\">\r\n        <h2>Design checks <span id=\"chk-pill\"><\/span><\/h2>\r\n        <ul id=\"checks\"><\/ul>\r\n      <\/div>\r\n    <\/section>\r\n\r\n    <!-- 3 \u00b7 despiece -->\r\n    <aside class=\"card out\" aria-label=\"Parts list and price\">\r\n      <div class=\"tot\"><small id=\"tot-lb\">Project total<\/small><div class=\"big num\" id=\"tot-big\"><\/div><p class=\"num\" id=\"tot-sub\"><\/p><\/div>\r\n      <div class=\"cols\">\r\n        <ul class=\"kpi\" id=\"kpi\"><\/ul>\r\n        <div class=\"col\">\r\n          <table class=\"bom\"><caption>Parts list<\/caption><tbody id=\"bom\"><\/tbody><tfoot id=\"bom-f\"><\/tfoot><\/table>\r\n          <button type=\"button\" class=\"cta\" id=\"bt-cart\">Add everything to the cart<\/button>\r\n          <p class=\"cmsg\" id=\"cart-msg\" role=\"status\" aria-live=\"polite\" hidden><\/p>\r\n          <div class=\"acts\">\r\n            <button type=\"button\" id=\"bt-pdf2\">Download drawing as PDF<\/button>\r\n            <button type=\"button\" id=\"bt-link\">Copy design link<\/button>\r\n            <button type=\"button\" id=\"bt-copy\" class=\"w2\">Copy parts list<\/button>\r\n            <a class=\"wa\" id=\"bt-wa\" href=\"#\" target=\"_blank\" rel=\"noopener\">Send to our technical office on WhatsApp \u00b7 +34 670 863 794<\/a>\r\n          <\/div>\r\n          <p class=\"note\">Shipping is calculated in the cart. Straight rail is supplied in 4 m bars.<\/p>\r\n        <\/div>\r\n      <\/div>\r\n      <details class=\"tec\" id=\"tec\"><summary>Mounting data<\/summary><dl id=\"tec-dl\"><\/dl><p class=\"note\" style=\"margin-top:8px\">The support structure (galvanised columns and beams) is sized and quoted separately from this drawing.<\/p><\/details>\r\n    <\/aside>\r\n  <\/div>\r\n<\/div>\r\n<div class=\"bar\"><div><b class=\"num\" id=\"bar-tot\"><\/b><small id=\"bar-sub\"><\/small><\/div><button type=\"button\" id=\"bt-cart2\">Add to cart<\/button><\/div>\r\n<div class=\"toast\" id=\"toast\" role=\"status\" aria-live=\"polite\"><\/div>\r\n<\/div><\/template><noscript><p>The designer needs JavaScript. If you prefer, send us your cold room dimensions on WhatsApp at +34 670 863 794 and we will prepare the layout.<\/p><\/noscript><div id=\"bxr-guia\"><section><h2>BIRRAIL overhead rail: what it is and what it is for<\/h2><p>BIRRAIL is the twin-track overhead rail that Jos\u00e9 Bernad, S.L. manufactures in Albacete, Spain, for moving meat overhead in abattoirs, cutting rooms, cold rooms and sausage factories. Two extruded aluminium profiles form the rail; they hang from the structure on brackets with stainless steel bolts, with no welding and no drilling.<\/p><p>Being modular, the layout can be extended or changed while reusing every part. Trolleys run lightly and quietly, and the twin track keeps them from derailing.<\/p><\/section><section><h2>Design criteria for an overhead rail layout<\/h2><p>These are the dimensions the designer applies. All of them refer to the rail axis.<\/p><div class=\"bxr-tw\"><table><thead><tr><th>Criterion<\/th><th>Beef<\/th><th>Pork and lamb<\/th><\/tr><\/thead><tbody><tr><td>Minimum spacing between storage lines<\/td><td>900 mm<\/td><td>800 mm<\/td><\/tr><tr><td>Recommended running level<\/td><td>3,000 mm<\/td><td>2,400 to 2,600 mm<\/td><\/tr><tr><td>Design load per metre of rail<\/td><td>450 kp\/m<\/td><td>350 kp\/m<\/td><\/tr><tr><td>Storage density<\/td><td>3 half carcasses per metre<\/td><td>3 to 5 half carcasses per metre \u00b7 10 lambs per hanging frame<\/td><\/tr><tr><td>Rail axis to wall<\/td><td colspan=\"2\">at least 500 mm<\/td><\/tr><tr><td>Rail end to wall<\/td><td colspan=\"2\">at least 300 mm<\/td><\/tr><tr><td>Radius of bends and switches<\/td><td colspan=\"2\">350 mm (400 \u00d7 400 mm footprint; triple switch, 800 \u00d7 400 mm)<\/td><\/tr><tr><td>Underside of the support beam<\/td><td colspan=\"2\">Running level + 110 mm<\/td><\/tr><tr><td>Maximum beam span<\/td><td colspan=\"2\">4,000 mm<\/td><\/tr><\/tbody><\/table><\/div><p>The entry rail always runs through the door axis, and hanging carcasses must not touch the walls or the floor.<\/p><\/section><section><h2>System parts and prices<\/h2><ul class=\"bxr-comp\"><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M14 32H186\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M14 32H186\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/complete-dual-carriageway-network\/\">Complete twin-track rail<\/a><span>Aluminium rail, brackets and stainless steel bolts. Supplied in 4 m bars.<\/span><em>61,50 \u20ac\/m + VAT<\/em><\/li><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M60 58V40A30 30 0 0 1 90 10H140\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M60 58V40A30 30 0 0 1 90 10H140\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/full-90-horizontal-curve\/\">90\u00b0 horizontal bend<\/a><span>350 mm radius. Also available at 45\u00b0.<\/span><em>95,00 \u20ac + VAT<\/em><\/li><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M70 58V8M70 58V42A30 30 0 0 1 100 12H140\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M70 58V8M70 58V42A30 30 0 0 1 100 12H140\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/simple-deviation-to-90-full\/\">90\u00b0 single switch<\/a><span>Diverts trolleys to a perpendicular line. Also available at 45\u00b0.<\/span><em>130,00 \u20ac + VAT<\/em><\/li><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M40 12H160M100 58V42A30 30 0 0 0 70 12M100 58V42A30 30 0 0 1 130 12\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M40 12H160M100 58V42A30 30 0 0 0 70 12M100 58V42A30 30 0 0 1 130 12\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/full-90-triple-bypass\/\">90\u00b0 triple switch<\/a><span>Splits left and right from the entry rail.<\/span><em>290,00 \u20ac + VAT<\/em><\/li><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M30 32H150\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M30 32H150\"\/><path d=\"M158 18V46\" stroke=\"#C81E25\" stroke-width=\"6\" stroke-linecap=\"round\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/stainless-steel-end-stop-set\/\">Stainless steel end stop<\/a><span>Closes every line that ends inside the room.<\/span><em>16,00 \u20ac + VAT<\/em><\/li><li><svg viewBox=\"0 0 200 64\" aria-hidden=\"true\"><path stroke=\"#08184A\" stroke-width=\"5\" fill=\"none\" stroke-linecap=\"round\" d=\"M40 12H160\"\/><path stroke=\"#fff\" stroke-width=\"1.8\" fill=\"none\" d=\"M40 12H160\"\/><path d=\"M100 14V34a10 10 0 1 0 10 10\" stroke=\"#55626F\" stroke-width=\"3.5\" fill=\"none\" stroke-linecap=\"round\"\/><\/svg><a href=\"https:\/\/www.tiendabernad.com\/en\/producto\/trolley-with-15-mm-stainless-steel-hook\/\">Trolleys with stainless steel hook<\/a><span>15 mm hook for pork and 18 mm for beef.<\/span><em>from 29,45 \u20ac + VAT<\/em><\/li><\/ul><\/section><section><h2>Frequently asked questions<\/h2><div><details open><summary>How much does BIRRAIL overhead rail cost per metre?<\/summary><p>Complete twin-track rail costs 61,50 \u20ac per metre with brackets every 66 cm and 69,00 \u20ac per metre with brackets every 50 cm, VAT not included. Add to that the bends (95,00 \u20ac), the switches (130,00 \u20ac single and 290,00 \u20ac triple), the end stops (16,00 \u20ac) and the trolleys.<\/p><\/details><details><summary>How far apart should the lines be?<\/summary><p>800 mm between axes for pork and lamb, and 900 mm for beef. The axis of each line stays 500 mm or more from the walls.<\/p><\/details><details><summary>At what height is the rail mounted?<\/summary><p>The recommended running level is 3,000 mm for beef and 2,400 to 2,600 mm for pork and lamb. The underside of the beam sits 110 mm above the running level.<\/p><\/details><details><summary>How many carcasses fit per metre of rail?<\/summary><p>Under normal conditions, 3 beef half carcasses per metre, 3 to 5 pork half carcasses per metre and 10 lambs per hanging frame and metre.<\/p><\/details><details><summary>Does it need welding or drilling?<\/summary><p>No. The brackets are bolted to the structure with stainless steel bolts, and the rail is held to the brackets by its slots.<\/p><\/details><details><summary>What structure does it need?<\/summary><p>A galvanised structure independent of the cold room insulation, resting on the floor: columns of 80.80.4 structural tube with anchor plates and IPN beams from series 120 to 280, with spans of up to 4,000 mm. It is sized from the layout drawing.<\/p><\/details><details><summary>Can I save or send my design?<\/summary><p>Yes. The page address changes with every design: copy it with \u201cCopy design link\u201d and open it whenever you like. You can also download the drawing as a PDF or send it to our technical office on WhatsApp, at +34 670 863 794.<\/p><\/details><\/div><\/section><\/div><script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebApplication\",\"@id\":\"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/#app\",\"name\":\"BIRRAIL overhead rail designer\",\"url\":\"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/\",\"description\":\"Design your BIRRAIL overhead meat rail online for cold rooms and abattoirs: dimensioned plan, parts list and the price of the whole installation.\",\"applicationCategory\":\"DesignApplication\",\"operatingSystem\":\"Web\",\"inLanguage\":\"en\",\"isAccessibleForFree\":true,\"offers\":{\"@type\":\"Offer\",\"price\":\"0\",\"priceCurrency\":\"EUR\"},\"provider\":{\"@type\":\"Organization\",\"name\":\"Jos\u00e9 Bernad, S.L.\",\"url\":\"https:\/\/www.josebernad.com\/\"}},{\"@type\":\"FAQPage\",\"@id\":\"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/#faq\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"How much does BIRRAIL overhead rail cost per metre?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Complete twin-track rail costs 61,50 \u20ac per metre with brackets every 66 cm and 69,00 \u20ac per metre with brackets every 50 cm, VAT not included. Add to that the bends (95,00 \u20ac), the switches (130,00 \u20ac single and 290,00 \u20ac triple), the end stops (16,00 \u20ac) and the trolleys.\"}},{\"@type\":\"Question\",\"name\":\"How far apart should the lines be?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"800 mm between axes for pork and lamb, and 900 mm for beef. The axis of each line stays 500 mm or more from the walls.\"}},{\"@type\":\"Question\",\"name\":\"At what height is the rail mounted?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"The recommended running level is 3,000 mm for beef and 2,400 to 2,600 mm for pork and lamb. The underside of the beam sits 110 mm above the running level.\"}},{\"@type\":\"Question\",\"name\":\"How many carcasses fit per metre of rail?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Under normal conditions, 3 beef half carcasses per metre, 3 to 5 pork half carcasses per metre and 10 lambs per hanging frame and metre.\"}},{\"@type\":\"Question\",\"name\":\"Does it need welding or drilling?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. The brackets are bolted to the structure with stainless steel bolts, and the rail is held to the brackets by its slots.\"}},{\"@type\":\"Question\",\"name\":\"What structure does it need?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"A galvanised structure independent of the cold room insulation, resting on the floor: columns of 80.80.4 structural tube with anchor plates and IPN beams from series 120 to 280, with spans of up to 4,000 mm. It is sized from the layout drawing.\"}},{\"@type\":\"Question\",\"name\":\"Can I save or send my design?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Yes. The page address changes with every design: copy it with \u201cCopy design link\u201d and open it whenever you like. You can also download the drawing as a PDF or send it to our technical office on WhatsApp, at +34 670 863 794.\"}}]}]}<\/script><script>window.BXR={\"p\":{\"via\":{\"66\":61.5,\"50\":69},\"curva\":95,\"desvio\":130,\"desvio45\":130,\"triple\":290,\"tope\":16,\"c18\":37.5,\"c15\":32.5,\"c12\":29.45,\"cvp\":61.5,\"cvc\":55.5,\"c4\":30.4},\"u\":{\"via\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/complete-dual-carriageway-network\\\/\",\"curva\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/full-90-horizontal-curve\\\/\",\"desvio\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/simple-deviation-to-90-full\\\/\",\"desvio45\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/simple-deviation-at-45o-complete\\\/\",\"triple\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/full-90-triple-bypass\\\/\",\"tope\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/stainless-steel-end-stop-set\\\/\",\"c18\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/trolley-with-18-mm-stainless-steel-hook\\\/\",\"c15\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/trolley-with-15-mm-stainless-steel-hook\\\/\",\"c12\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/trolley-with-12-mm-stainless-steel-hook\\\/\",\"cvp\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/trolley-with-stainless-steel-pig-bed-v\\\/\",\"cvc\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/trolley-with-stainless-steel-lamb-trolley\\\/\",\"c4\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/producto\\\/4-wheeled-aerial-trolley\\\/\"},\"ok\":{\"via\":true,\"curva\":true,\"desvio\":true,\"desvio45\":true,\"triple\":true,\"tope\":true,\"c18\":true,\"c15\":true,\"c12\":true,\"cvp\":true,\"cvc\":true,\"c4\":true},\"ajax\":\"\\\/en\\\/?wc-ajax=bxr_add\",\"cart\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/cart\\\/\",\"wa\":\"34670863794\",\"lang\":\"en\",\"dec\":\",\",\"tho\":\".\"};<\/script><script>\/* Motor de c\u00e1lculo del dise\u00f1ador BIRRAIL \u2014 sin dependencias, vale en navegador y en Node.\r\n   Datos: manual \u00abRed viaria exclusiva Jos\u00e9 Bernad, S.L.\u00bb y tarifa red viaria feb-2026 (sin IVA). Cotas en mm. *\/\r\n(function (root) {\r\n  'use strict';\r\n\r\n  var R = 350;      \/\/ radio de curvas y desv\u00edos\r\n  var P = 400;      \/\/ huella de curva\/desv\u00edo sobre cada eje (figuras del manual: 400 \u00d7 400; triple 800 \u00d7 400)\r\n  \/\/ Paso entre dos l\u00edneas paralelas pr\u00f3ximas: dos desv\u00edos a 45\u00b0 y un tramo recto en diagonal.\r\n  \/\/ Geometr\u00eda supuesta, a falta del plano del desv\u00edo a 45\u00b0: mismo radio de 350 mm, arco de 45\u00b0 y 300 mm ocupados en la v\u00eda pasante.\r\n  var S45 = Math.SQRT1_2, A45 = R * S45, H45 = R * (1 - S45), P45 = 300;\r\n  var RULES = {\r\n    pared: 500,     \/\/ eje de v\u00eda a pared, m\u00ednimo\r\n    fin: 300,       \/\/ final de v\u00eda a pared, m\u00ednimo\r\n    viga: 110,      \/\/ nivel de rodadura \u2192 cara inferior de viga\r\n    vigaMin: 120,   \/\/ canto m\u00ednimo de viga (IPN 120)\r\n    vigaMax: 280,   \/\/ canto m\u00e1ximo de viga (IPN 280)\r\n    luzViga: 4000,  \/\/ luz m\u00e1xima de viga\r\n    paso45: 1200,   \/\/ hasta esta separaci\u00f3n entre ejes, el paso entre dos l\u00edneas paralelas se monta con desv\u00edos a 45\u00b0\r\n    carril: 50,     \/\/ alto del perfil de aluminio\r\n    barra: 4,       \/\/ la v\u00eda recta se sirve en barras de 4 m\r\n    puertaHolgura: 50,\r\n    iva: 0.21\r\n  };\r\n\r\n  var SP = {\r\n    vacuno:  { n: 'Beef',  sep: 900, nrMin: 3000, nrMax: 3000, kp: 450, dens: 3,  densTxt: '3 half carcasses per metre', ud: 'half carcasses', carro: 'c18' },\r\n    porcino: { n: 'Pork', sep: 800, nrMin: 2400, nrMax: 2600, kp: 350, dens: 4,  densTxt: '3 to 5 half carcasses per metre', ud: 'half carcasses', carro: 'c15' },\r\n    ovino:   { n: 'Lamb',   sep: 800, nrMin: 2400, nrMax: 2600, kp: 350, dens: 10, densTxt: '10 carcasses per hanging frame and metre', ud: 'carcasses', carro: '' }\r\n  };\r\n\r\n  \/\/ Tarifa red viaria feb-2026, PVP sin IVA. En la tienda estos precios se leen en vivo de WooCommerce.\r\n  var CAT = {\r\n    via:    { sku: '245000050', id: 229659, slug: 'red-viaria-doble-via-completa', n: 'Complete twin-track rail', u: 'm', p: { '66': 61.5, '50': 69 } },\r\n    curva:  { sku: '245000090', id: 229667, slug: 'curva-horizontal-90-completa', n: 'Complete 90\u00b0 horizontal bend', u: 'pcs', p: 95 },\r\n    desvio: { sku: '245000130', id: 229675, slug: 'desvio-simple-a-90-completo', n: 'Complete 90\u00b0 single switch', u: 'pcs', p: 130 },\r\n    desvio45: { sku: '245000125', id: 229671, slug: 'desvio-simple-a-45-completo', n: 'Complete 45\u00b0 single switch', u: 'pcs', p: 130 },\r\n    triple: { sku: '245000210', id: 229679, slug: 'desvio-triple-a-90-completo', n: 'Complete 90\u00b0 triple switch', u: 'pcs', p: 290 },\r\n    tope:   { sku: '245000600', id: 231757, slug: 'juego-tope-final-via-inox', n: 'Stainless steel end stop set', u: 'pcs', p: 16 },\r\n    c18:    { sku: '245120160', id: 229313, slug: 'carro-con-gancho-inox-de-18-mm', n: 'Trolley with 18 mm stainless steel hook', u: 'pcs', p: 37.5, gancho: 384 },\r\n    c15:    { sku: '245120150', id: 229309, slug: 'carro-con-gancho-inox-de-15-mm', n: 'Trolley with 15 mm stainless steel hook', u: 'pcs', p: 32.5, gancho: 314 },\r\n    c12:    { sku: '245120170', id: 359030, slug: 'carro-con-gancho-inox-de-12-mm', n: 'Trolley with 12 mm stainless steel hook', u: 'pcs', p: 29.45 },\r\n    cvp:    { sku: '245120140', id: 231778, slug: 'carro-con-camal-v-porcino-inox', n: 'Trolley with stainless steel V gambrel for pork', u: 'pcs', p: 61.5 },\r\n    cvc:    { sku: '245120189', id: 231782, slug: 'carro-con-camal-v-cordero-inox', n: 'Trolley with stainless steel V gambrel for lamb', u: 'pcs', p: 55.5 },\r\n    c4:     { sku: '245000310', id: 229684, slug: 'carro-tteaereo-de-4-ruedas', n: '4-wheel overhead trolley', u: 'pcs', p: 30.4 }\r\n  };\r\n  var CARROS = ['c18', 'c15', 'c12', 'cvp', 'cvc', 'c4'];\r\n  \/\/ Tornillos M8 de fijaci\u00f3n a la estructura por elemento (manual, apdo. 3.3.4)\r\n  var M8 = { simple: 2, doble: 4, triple: 10, desvio: 5, desvio45: 5, curva: 4, tope: 4 };\r\n\r\n  function r10(v) { return Math.round(v \/ 10) * 10; }\r\n  function key(x, y) { return x + ',' + y; }\r\n\r\n  \/* ---------- puertas ---------- *\/\r\n  \/\/ La principal est\u00e1 en la pared de entrada (y = 0). Las adicionales: w = pared (S entrada, N fondo, W izquierda, E derecha),\r\n  \/\/ p = eje medido a lo largo de la pared (desde la izquierda en S y N; desde la pared de entrada en W y E), dw = ancho.\r\n  function doorsOf(r) {\r\n    return [{ w: 'S', p: r.xd, dw: r.dw, main: true }].concat((r.doors || []).map(function (d) { return { w: d.w, p: d.p, dw: d.dw }; }));\r\n  }\r\n  function enHueco(r, w, c) {\r\n    return doorsOf(r).some(function (d) { return d.w === w && Math.abs(c - d.p) <= d.dw \/ 2; });\r\n  }\r\n  \/\/ En los trazados autom\u00e1ticos, cada puerta adicional recibe un tramo desde su eje hasta la primera v\u00eda que tiene enfrente.\r\n  function conPuertas(r, g) {\r\n    (r.doors || []).forEach(function (d) {\r\n      var v = d.w === 'N' || d.w === 'S', from = d.w === 'N' ? r.D : d.w === 'E' ? r.W : 0, sg = d.w === 'N' || d.w === 'E' ? -1 : 1, best = null;\r\n      g.forEach(function (s) {\r\n        var t, a = v ? 0 : 1, b = v ? 1 : 0; \/\/ a: coordenada a lo largo de la pared; b: coordenada de avance\r\n        if (s[b] === s[b + 2]) { if (Math.min(s[a], s[a + 2]) > d.p || Math.max(s[a], s[a + 2]) < d.p) return; t = s[b]; }\r\n        else if (s[a] === d.p) t = sg > 0 ? Math.min(s[b], s[b + 2]) : Math.max(s[b], s[b + 2]);\r\n        else return;\r\n        if ((t - from) * sg < 0) return;\r\n        if (best === null || (t - best) * sg < 0) best = t;\r\n      });\r\n      if (best !== null && best !== from) g.push(v ? [d.p, from, d.p, best] : [from, d.p, best, d.p]);\r\n    });\r\n    return g;\r\n  }\r\n\r\n  \/* ---------- plantillas de trazado ---------- *\/\r\n  function genLinea(r) {\r\n    return [[r.xd, -r.ext, r.xd, r.D - RULES.fin]];\r\n  }\r\n\r\n  function genPeine(r) {\r\n    var s = SP[r.sp].sep, yc = RULES.pared, yEnd = r.D - RULES.fin, k, x;\r\n    if (yEnd - yc < 2 * P) return null;\r\n    \/\/ lo normal es cerrar el circuito al fondo con un colector; si no, cada l\u00ednea acaba en un tope.\r\n    \/\/ En recintos muy cortos no se cierra: las l\u00edneas quedar\u00edan como simples pasos entre los dos colectores.\r\n    var cier = !!r.cierre && r.D - 2 * RULES.pared > RULES.paso45;\r\n    var left = [], right = [];\r\n    for (k = 1; (x = r.xd - k * s) >= RULES.pared; k++) left.push(x);\r\n    for (k = 1; (x = r.xd + k * s) <= r.W - RULES.pared; k++) right.push(x);\r\n    var X = left.concat(right);\r\n    if (!X.length) return null;\r\n    if ((!left.length || !right.length) && r.xd >= RULES.pared && r.xd <= r.W - RULES.pared) X.push(r.xd);\r\n    X.sort(function (a, b) { return Math.abs(a - r.xd) - Math.abs(b - r.xd); });\r\n    var max = X.length, n = r.nLines > 0 ? Math.min(r.nLines, max) : max;\r\n    X = X.slice(0, n);\r\n    var lo = Math.min.apply(null, X.concat([r.xd])), hi = Math.max.apply(null, X.concat([r.xd]));\r\n    var segs = [[r.xd, -r.ext, r.xd, yc]];\r\n    if (hi > lo) segs.push([lo, yc, hi, yc]);\r\n    cier = cier && n >= 2;\r\n    if (cier) { yEnd = r.D - RULES.pared; segs.push([Math.min.apply(null, X), yEnd, Math.max.apply(null, X), yEnd]); }\r\n    X.forEach(function (xx) { segs.push([xx, yc, xx, yEnd]); });\r\n    segs.max = max; segs.n = n; segs.cierre = cier;\r\n    return segs;\r\n  }\r\n\r\n  function genMalla(r) {\r\n    var s = SP[r.sp].sep, xl = RULES.pared, xr = r.W - RULES.pared;\r\n    if (r.xd - xl < 2 * P || xr - r.xd < 2 * P) return null;\r\n    var max = Math.floor((r.D - 2 * RULES.pared) \/ s) + 1;\r\n    if (max < 2) return null;\r\n    var n = r.nLines > 0 ? Math.max(2, Math.min(r.nLines, max)) : max;\r\n    var y0 = r10((r.D - (n - 1) * s) \/ 2), i, y;\r\n    var segs = [[r.xd, -r.ext, r.xd, y0]];\r\n    for (i = 0; i < n; i++) { y = y0 + i * s; segs.push([xl, y, xr, y]); }\r\n    segs.push([xl, y0, xl, y0 + (n - 1) * s]);\r\n    segs.push([xr, y0, xr, y0 + (n - 1) * s]);\r\n    segs.max = max; segs.n = n;\r\n    return segs;\r\n  }\r\n\r\n  function generate(r) {\r\n    if (r.tpl === 'libre') return r.segs || [];\r\n    var g = r.tpl === 'malla' ? genMalla(r) : r.tpl === 'peine' ? genPeine(r) : null;\r\n    if (!g) { g = genLinea(r); g.max = 1; g.n = 1; g.fallback = r.tpl !== 'linea'; }\r\n    return conPuertas(r, g);\r\n  }\r\n\r\n  \/* ---------- an\u00e1lisis del trazado ---------- *\/\r\n  function mergeSegs(segs) {\r\n    var H = {}, V = {};\r\n    segs.forEach(function (s) {\r\n      var x1 = s[0], y1 = s[1], x2 = s[2], y2 = s[3];\r\n      if (y1 === y2 && x1 !== x2) (H[y1] = H[y1] || []).push([Math.min(x1, x2), Math.max(x1, x2)]);\r\n      else if (x1 === x2 && y1 !== y2) (V[x1] = V[x1] || []).push([Math.min(y1, y2), Math.max(y1, y2)]);\r\n    });\r\n    function uni(map) {\r\n      var out = [];\r\n      Object.keys(map).forEach(function (c) {\r\n        var iv = map[c].sort(function (a, b) { return a[0] - b[0]; }), cur = iv[0].slice();\r\n        for (var i = 1; i < iv.length; i++) {\r\n          if (iv[i][0] <= cur[1]) cur[1] = Math.max(cur[1], iv[i][1]);\r\n          else { out.push({ c: +c, a: cur[0], b: cur[1] }); cur = iv[i].slice(); }\r\n        }\r\n        out.push({ c: +c, a: cur[0], b: cur[1] });\r\n      });\r\n      return out;\r\n    }\r\n    return { H: uni(H), V: uni(V) };\r\n  }\r\n\r\n  var OPP = { E: 'W', W: 'E', N: 'S', S: 'N' };\r\n  var DIR = { E: [1, 0], W: [-1, 0], N: [0, 1], S: [0, -1] };\r\n\r\n  function analyse(r, segsIn) {\r\n    var segs = segsIn || generate(r);\r\n    var m = mergeSegs(segs), sp = SP[r.sp];\r\n    var nodes = {}, edges = [], issues = [];\r\n\r\n    function node(x, y) { var k = key(x, y); return nodes[k] || (nodes[k] = { x: x, y: y, legs: {}, cons: {} }); }\r\n    function addEdge(x1, y1, x2, y2, d1, d2) {\r\n      var e = { a: node(x1, y1), b: node(x2, y2), len: Math.abs(x2 - x1) + Math.abs(y2 - y1), h: y1 === y2 };\r\n      e.a.legs[d1] = e; e.b.legs[d2] = e; edges.push(e);\r\n    }\r\n    m.H.forEach(function (h) {\r\n      var pts = [h.a, h.b];\r\n      m.V.forEach(function (v) { if (v.c >= h.a && v.c <= h.b && h.c >= v.a && h.c <= v.b) pts.push(v.c); });\r\n      pts = pts.sort(function (a, b) { return a - b; }).filter(function (p, i, arr) { return !i || p !== arr[i - 1]; });\r\n      for (var i = 0; i < pts.length - 1; i++) addEdge(pts[i], h.c, pts[i + 1], h.c, 'E', 'W');\r\n    });\r\n    m.V.forEach(function (v) {\r\n      var pts = [v.a, v.b];\r\n      m.H.forEach(function (h) { if (h.c >= v.a && h.c <= v.b && v.c >= h.a && v.c <= h.b) pts.push(h.c); });\r\n      pts = pts.sort(function (a, b) { return a - b; }).filter(function (p, i, arr) { return !i || p !== arr[i - 1]; });\r\n      for (var i = 0; i < pts.length - 1; i++) addEdge(v.c, pts[i], v.c, pts[i + 1], 'N', 'S');\r\n    });\r\n\r\n    var list = Object.keys(nodes).map(function (k) { return nodes[k]; });\r\n    list.forEach(function (n) { n.deg = Object.keys(n.legs).length; n.dist = Infinity; });\r\n\r\n    \/\/ entrada: extremo libre en el plano de la puerta o fuera del recinto\r\n    var entry = null;\r\n    function enPuerta(n) {\r\n      return (n.y <= 0 && enHueco(r, 'S', n.x)) || (n.y >= r.D && enHueco(r, 'N', n.x)) || (n.x <= 0 && enHueco(r, 'W', n.y)) || (n.x >= r.W && enHueco(r, 'E', n.y));\r\n    }\r\n    list.forEach(function (n) {\r\n      if (n.deg !== 1 || n.y > 0) return;\r\n      n.pri = Math.abs(n.x - r.xd) <= r.dw \/ 2;\r\n      if (!entry || (n.pri && !entry.pri) || (n.pri === entry.pri && n.y < entry.y)) entry = n;\r\n    });\r\n    if (!entry) list.forEach(function (n) { if (!entry || n.y < entry.y || (n.y === entry.y && Math.abs(n.x - r.xd) < Math.abs(entry.x - r.xd))) entry = n; });\r\n\r\n    \/\/ distancias desde la entrada (Dijkstra sobre una red peque\u00f1a)\r\n    if (entry) {\r\n      entry.dist = 0;\r\n      var open = [entry];\r\n      while (open.length) {\r\n        open.sort(function (a, b) { return a.dist - b.dist; });\r\n        var cur = open.shift();\r\n        Object.keys(cur.legs).forEach(function (d) {\r\n          var e = cur.legs[d], o = e.a === cur ? e.b : e.a;\r\n          if (cur.dist + e.len < o.dist) { o.dist = cur.dist + e.len; open.push(o); }\r\n        });\r\n      }\r\n    }\r\n    function other(n, d) { var e = n.legs[d]; return e.a === n ? e.b : e.a; }\r\n\r\n    \/\/ \u00bfse llega a los dos lados de la v\u00eda pasante de un nudo sin pasar por \u00e9l? Entonces el nudo cierra un circuito.\r\n    function enBucle(n, thr) {\r\n      if (!entry || entry === n) return false;\r\n      var mark = {}, q = [entry], c;\r\n      entry.vm = mark;\r\n      while ((c = q.pop())) Object.keys(c.legs).forEach(function (d) { var o = other(c, d); if (o !== n && o.vm !== mark) { o.vm = mark; q.push(o); } });\r\n      return other(n, thr[0]).vm === mark && other(n, thr[1]).vm === mark;\r\n    }\r\n    var count = { curva: 0, desvio: 0, desvio45: 0, triple: 0, tope: 0, cruce: 0 }, cierres = 0;\r\n    list.forEach(function (n) {\r\n      var ds = Object.keys(n.legs);\r\n      n.type = '';\r\n      if (n.deg === 1) {\r\n        n.type = (n === entry || enPuerta(n)) ? 'entrada' : 'tope';\r\n      } else if (n.deg === 2) {\r\n        if (OPP[ds[0]] !== ds[1]) { n.type = 'curva'; n.cons[ds[0]] = P; n.cons[ds[1]] = P; }\r\n      } else if (n.deg === 3) {\r\n        var stem = ds.filter(function (d) { return !n.legs[OPP[d]]; })[0];\r\n        var thr = ds.filter(function (d) { return d !== stem; });\r\n        n.stem = stem;\r\n        \/\/ Cuando se llega por el tronco hace falta repartir a los dos lados: desv\u00edo triple. Es as\u00ed en la entrada y en el ramal de cada puerta.\r\n        \/\/ Donde el nudo cierra un circuito (colector del fondo) lo normal es un desv\u00edo simple; el triple queda como opci\u00f3n (r.tri).\r\n        var os = other(n, stem), aPuerta = os.deg === 1 && enPuerta(os), tronco = os.dist < n.dist || aPuerta;\r\n        var cierra = tronco && !aPuerta && enBucle(n, thr);\r\n        if (cierra) cierres++;\r\n        if (tronco && !(cierra && !r.tri)) {\r\n          n.type = 'triple'; ds.forEach(function (d) { n.cons[d] = P; });\r\n        } else {\r\n          n.type = 'desvio';\r\n          \/\/ el arco mira hacia el lado por el que antes se llega desde la entrada; a igualdad, hacia el m\u00e1s cercano a ella\r\n          var o0 = other(n, thr[0]), o1 = other(n, thr[1]);\r\n          var cerca = function (o) { return entry ? Math.abs(o.x - entry.x) + Math.abs(o.y - entry.y) : 0; };\r\n          n.arc = o0.dist < o1.dist || (o0.dist === o1.dist && cerca(o0) <= cerca(o1)) ? thr[0] : thr[1];\r\n          n.cons[stem] = P; n.cons[n.arc] = P;\r\n        }\r\n      } else if (n.deg === 4) {\r\n        n.type = 'cruce';\r\n      }\r\n      if (count[n.type] !== undefined) count[n.type]++;\r\n    });\r\n\r\n    \/\/ pasos entre l\u00edneas paralelas pr\u00f3ximas: el tramo corto que las une se monta con dos desv\u00edos a 45\u00b0 y una diagonal\r\n    var sinSitio = 0;\r\n    edges.forEach(function (e) {\r\n      var a = e.a, b = e.b, da = e.h ? 'E' : 'N', db = e.h ? 'W' : 'S';\r\n      if (e.len > RULES.paso45 || e.len < 2 * H45 + 100 || a.deg !== 3 || b.deg !== 3 || a.stem !== da || b.stem !== db) return;\r\n      if (a.type === 'desvio45' || b.type === 'desvio45') return;\r\n      var src = a.dist <= b.dist ? a : b, dst = src === a ? b : a;       \/\/ origen: el lado m\u00e1s cercano a la entrada\r\n      var thr = e.h ? ['N', 'S'] : ['E', 'W'];\r\n      var back = other(src, thr[0]).dist <= other(src, thr[1]).dist ? thr[0] : thr[1], fwd = OPP[back];\r\n      var e1 = src.legs[back], e2 = dst.legs[fwd], n1 = other(src, back), n2 = other(dst, fwd);\r\n      var S = e.len + 2 * (A45 - H45);                                   \/\/ avance total del paso a lo largo de las l\u00edneas\r\n      var roomS = e1.len - (n1.cons[fwd] || 0) - (e1.mid || 0), roomD = e2.len - (n2.cons[back] || 0) - (e2.mid || 0);\r\n      var lo = Math.max(S \/ 2 + 50 - roomS, -(S \/ 2 - A45)), hi = Math.min(roomD - S \/ 2 - 50, S \/ 2 - A45);\r\n      if (lo > hi) { sinSitio++; return; }\r\n      var dl = Math.max(lo, Math.min(hi, 0));                            \/\/ centrado en el punto dibujado, si cabe\r\n      [src, dst].forEach(function (n) { count[n.type]--; n.type = 'desvio45'; n.cons = {}; n.arc = null; });\r\n      count.desvio45 += 2;\r\n      var u = DIR[fwd], v = DIR[src === a ? da : db];                    \/\/ u: sentido de avance; v: de la l\u00ednea de origen a la de destino\r\n      var T = [src.x + (-S \/ 2 + dl) * u[0], src.y + (-S \/ 2 + dl) * u[1]];\r\n      src.g45 = { T: T, u: u, v: v, s: 1 };\r\n      dst.g45 = { T: [T[0] + S * u[0] + e.len * v[0], T[1] + S * u[1] + e.len * v[1]], u: u, v: v, s: -1 };\r\n      e.diag = [[T[0] + A45 * u[0] + H45 * v[0], T[1] + A45 * u[1] + H45 * v[1]], [T[0] + (S - A45) * u[0] + (e.len - H45) * v[0], T[1] + (S - A45) * u[1] + (e.len - H45) * v[1]]];\r\n      e.ld = (e.len - 2 * H45) \/ S45;\r\n      e1.mid = (e1.mid || 0) + P45; e2.mid = (e2.mid || 0) + P45;\r\n    });\r\n\r\n    \/\/ tramos rectos netos\r\n    var straight = 0, inside = 0, solape = 0;\r\n    edges.forEach(function (e) {\r\n      var da = e.h ? 'E' : 'N', db = e.h ? 'W' : 'S';\r\n      e.ca = e.a.cons[da] || 0; e.cb = e.b.cons[db] || 0;\r\n      e.net = e.diag ? e.ld : e.len - e.ca - e.cb - (e.mid || 0);\r\n      if (e.net < 0) { solape++; e.bad = true; e.net = 0; }\r\n      straight += e.net;\r\n      \/\/ parte neta dentro del recinto (y \u2265 0)\r\n      var lo, hi;\r\n      if (e.h || e.diag) { if (e.a.y >= 0) inside += e.net; }\r\n      else { lo = Math.max(e.a.y + e.ca, 0); hi = e.b.y - e.cb; if (hi > lo) inside += Math.max(hi - lo - (e.mid || 0), 0); }\r\n    });\r\n\r\n    \/\/ tramos continuos (para la lista de cortes): une aristas alineadas sin pieza entre ellas\r\n    var par = edges.map(function (_, i) { return i; });\r\n    function find(i) { while (par[i] !== i) { par[i] = par[par[i]]; i = par[i]; } return i; }\r\n    edges.forEach(function (e, i) { e.i = i; });\r\n    list.forEach(function (n) {\r\n      [['E', 'W'], ['N', 'S']].forEach(function (p) {\r\n        var e1 = n.legs[p[0]], e2 = n.legs[p[1]];\r\n        if (e1 && e2 && !(n.cons[p[0]] || 0) && !(n.cons[p[1]] || 0) && n.type !== 'cruce') par[find(e1.i)] = find(e2.i);\r\n      });\r\n    });\r\n    var runMap = {};\r\n    edges.forEach(function (e) { var k = find(e.i); runMap[k] = (runMap[k] || 0) + e.net; });\r\n    var runs = Object.keys(runMap).map(function (k) { return runMap[k]; }).filter(function (v) { return v > 0; }).sort(function (a, b) { return b - a; });\r\n\r\n    \/* ---------- comprobaciones de dise\u00f1o ---------- *\/\r\n    function add(id, lvl, t, d) { issues.push({ id: id, lvl: lvl, t: t, d: d || '' }); }\r\n    var W = r.W, D = r.D, sep = sp.sep;\r\n\r\n    \/\/ 1. dentro del recinto\r\n    var fuera = 0;\r\n    \/\/ la v\u00eda solo puede atravesar una pared por el hueco de una puerta\r\n    m.H.forEach(function (h) {\r\n      if (h.c > D || h.c < 0) fuera++;\r\n      else if ((h.a < 0 && !enHueco(r, 'W', h.c)) || (h.b > W && !enHueco(r, 'E', h.c))) fuera++;\r\n    });\r\n    m.V.forEach(function (v) {\r\n      if (v.c < 0 || v.c > W) fuera++;\r\n      else if ((v.a < 0 && !enHueco(r, 'S', v.c)) || (v.b > D && !enHueco(r, 'N', v.c))) fuera++;\r\n    });\r\n    var puertas = doorsOf(r), noCabe = 0, pisan = 0;\r\n    puertas.forEach(function (d, i) {\r\n      var L = d.w === 'N' || d.w === 'S' ? W : D;\r\n      if (d.p - d.dw \/ 2 < 0 || d.p + d.dw \/ 2 > L) noCabe++;\r\n      for (var j = 0; j < i; j++) if (puertas[j].w === d.w && Math.abs(puertas[j].p - d.p) < (puertas[j].dw + d.dw) \/ 2) pisan++;\r\n    });\r\n    if (noCabe) add('puerta', 'err', 'The door does not fit in the wall', 'Move the door axis or reduce its width.');\r\n    if (pisan) add('pisan', 'err', 'Two doors overlap', 'Move apart the doors that share a wall.');\r\n    if (fuera) add('recinto', 'err', 'Some rail is outside the room', fuera + (fuera === 1 ? ' section goes' : ' sections go') + ' beyond the walls.');\r\n    else add('recinto', 'ok', 'The whole layout is inside the room');\r\n\r\n    \/\/ 2. eje por el eje de la puerta\r\n    var cruza = m.V.filter(function (v) { return v.a <= 0 && v.b > 0 && Math.abs(v.c - r.xd) <= r.dw \/ 2; });\r\n    if (!cruza.length) add('eje', 'warn', 'The layout does not reach the door', 'The entry rail must run through the door axis.');\r\n    else if (cruza.some(function (v) { return Math.abs(v.c - r.xd) > 1; })) add('eje', 'warn', 'The entry rail is off the door axis', 'The rail axis must match the door axis.');\r\n    else add('eje', 'ok', 'The entry rail runs through the door axis');\r\n\r\n    \/\/ 2 bis. puertas adicionales: cada una con su v\u00eda, por el eje\r\n    var sinVia = 0, desEje = 0, extra = puertas.slice(1);\r\n    extra.forEach(function (d) {\r\n      var lejos = d.w === 'N' || d.w === 'E', lim = d.w === 'N' ? D : d.w === 'E' ? W : 0;\r\n      var hit = (d.w === 'N' || d.w === 'S' ? m.V : m.H).filter(function (s) {\r\n        return Math.abs(s.c - d.p) <= d.dw \/ 2 && (lejos ? s.b >= lim && s.a < lim : s.a <= lim && s.b > lim);\r\n      });\r\n      if (!hit.length) sinVia++;\r\n      else if (hit.some(function (s) { return Math.abs(s.c - d.p) > 1; })) desEje++;\r\n    });\r\n    if (sinVia) add('puertas', 'warn', sinVia === 1 ? 'One door has no rail' : sinVia + ' doors have no rail', 'Move it until it faces a line or a collector rail, or draw the section by hand.');\r\n    else if (desEje) add('puertas', 'warn', 'The rail is off the axis of a door', 'The rail axis must match the axis of each door.');\r\n    else if (extra.length) add('puertas', 'ok', extra.length === 1 ? 'The second door has rail on its axis' : 'The other ' + extra.length + ' doors have rail on their axis');\r\n\r\n    \/\/ 3. distancia a paredes\r\n    var cerca = 0;\r\n    m.V.forEach(function (v) { if (v.b > 0 && (v.c < RULES.pared || v.c > W - RULES.pared)) cerca++; });\r\n    m.H.forEach(function (h) { if (h.c >= 0 && (h.c < RULES.pared || h.c > D - RULES.pared)) cerca++; });\r\n    if (cerca) add('pared', 'warn', 'Rail axis less than 500 mm from a wall', cerca + (cerca === 1 ? ' section' : ' sections') + ' too close: carcasses must not touch the walls.');\r\n    else add('pared', 'ok', 'Rail axes 500 mm or more from the walls');\r\n\r\n    \/\/ 4. finales de v\u00eda\r\n    var finMal = 0;\r\n    list.forEach(function (n) {\r\n      if (n.type !== 'tope') return;\r\n      var d = Object.keys(n.legs)[0];\r\n      var libre = d === 'S' ? D - n.y : d === 'N' ? n.y : d === 'W' ? W - n.x : n.x;\r\n      if (libre < RULES.fin) finMal++;\r\n    });\r\n    if (finMal) add('fin', 'warn', 'Rail end less than 300 mm from the wall', finMal + (finMal === 1 ? ' rail end' : ' rail ends') + ' too close.');\r\n    else if (count.tope) add('fin', 'ok', 'Rail ends 300 mm or more from the wall');\r\n\r\n    \/\/ 5. separaci\u00f3n entre l\u00edneas\r\n    var juntas = 0;\r\n    function pares(arr) {\r\n      for (var i = 0; i < arr.length; i++) for (var j = i + 1; j < arr.length; j++) {\r\n        var d = Math.abs(arr[i].c - arr[j].c), ov = Math.min(arr[i].b, arr[j].b) - Math.max(arr[i].a, arr[j].a);\r\n        if (d > 0 && d < sep && ov > 0) juntas++;\r\n      }\r\n    }\r\n    pares(m.V); pares(m.H);\r\n    if (juntas) add('sep', 'warn', 'Lines less than ' + sep + ' mm apart', juntas + (juntas === 1 ? ' pair of lines' : ' pairs of lines') + ' below the minimum spacing for ' + sp.n.toLowerCase() + '.');\r\n    else add('sep', 'ok', 'Lines ' + sep + ' mm or more apart (' + sp.n.toLowerCase() + ')');\r\n\r\n    \/\/ 6. piezas\r\n    if (count.cruce) add('cruce', 'err', 'Four-way crossing', 'That part does not exist: move one of the lines, or the door, to form two switches.');\r\n    if (solape) add('solape', 'err', 'Bends or switches too close together', 'Each bend or switch takes 400 mm along the axis: keep junctions at least 800 mm apart.');\r\n    if (!count.cruce && !solape) add('piezas', 'ok', 'Bends and switches fit (350 mm radius)');\r\n    if (count.desvio45) add('paso45', 'ok', 'Crossover between close lines with two 45\u00b0 switches');\r\n    if (sinSitio) add('paso45n', 'warn', 'No room for a 45\u00b0 crossover between lines', 'It has been built with 90\u00b0 parts. For a 45\u00b0 crossover, leave about 600 mm of straight rail on each side.');\r\n\r\n    \/\/ 7. conexi\u00f3n\r\n    var sueltos = list.filter(function (n) { return n.dist === Infinity; }).length;\r\n    if (sueltos) add('conex', 'warn', 'Some sections are not connected to the entry');\r\n\r\n    \/\/ 8. alturas\r\n    var nrMaxAlt = Math.floor((r.H - RULES.viga - RULES.vigaMin) \/ 10) * 10;\r\n    var nr = r.nr > 0 ? r.nr : Math.min(sp.nrMax, nrMaxAlt);\r\n    var hNec = nr + RULES.viga + RULES.vigaMin;\r\n    if (hNec > r.H) add('alt', 'err', 'Not enough clear height', 'With the running level at ' + nr + ' mm you need ' + hNec + ' mm clear (110 mm bracket plus a beam of at least 120 mm).');\r\n    else if (nr < sp.nrMin) add('alt', 'warn', 'Running level low for ' + sp.n.toLowerCase(), 'Recommended: ' + (sp.nrMin === sp.nrMax ? sp.nrMin : sp.nrMin + ' to ' + sp.nrMax) + ' mm. With ' + r.H + ' mm of clear height, ' + nr + ' mm fits.');\r\n    else add('alt', 'ok', 'Running level ' + nr + ' mm, within the recommended range');\r\n\r\n    var tot = straight;\r\n    var silletas = Math.ceil(tot \/ 1000 \/ (r.sil === '50' ? 0.5 : 0.66));\r\n    var dobles = Math.ceil(tot \/ 1000 \/ 4);\r\n    var m8 = Math.max(silletas - dobles, 0) * M8.simple + dobles * M8.doble + count.triple * M8.triple + (count.desvio + count.desvio45) * M8.desvio + count.curva * M8.curva + count.tope * M8.tope;\r\n    var carroKey = r.carro === 'auto' ? sp.carro : r.carro;\r\n    var cap = Math.round(inside \/ 1000 * sp.dens);\r\n    var nCar = !carroKey ? 0 : (r.nCar === null || r.nCar === undefined ? (r.sp === 'ovino' ? Math.round(inside \/ 1000) : cap) : r.nCar);\r\n\r\n    return {\r\n      segs: segs, merged: m, nodes: list, edges: edges, entry: entry, count: count, runs: runs,\r\n      straight: straight, inside: inside, issues: issues, nr: nr, hNec: hNec,\r\n      errs: issues.filter(function (i) { return i.lvl === 'err'; }).length,\r\n      warns: issues.filter(function (i) { return i.lvl === 'warn'; }).length,\r\n      cap: cap, carga: Math.round(tot \/ 1000 * sp.kp), silletas: silletas, dobles: dobles, m8: m8,\r\n      carroKey: carroKey, nCar: nCar, max: segs.max || 0, n: segs.n || 0, fallback: !!segs.fallback, cierre: !!segs.cierre, cierres: cierres,\r\n      pilar: nr + RULES.viga\r\n    };\r\n  }\r\n\r\n  \/* ---------- despiece y precio del proyecto ---------- *\/\r\n  function bom(rooms, ans, sil) {\r\n    var mm = 0, q = { curva: 0, desvio: 0, desvio45: 0, triple: 0, tope: 0 }, car = {};\r\n    ans.forEach(function (a) {\r\n      mm += a.straight;\r\n      Object.keys(q).forEach(function (k) { q[k] += a.count[k]; });\r\n      if (a.carroKey && a.nCar > 0) car[a.carroKey] = (car[a.carroKey] || 0) + a.nCar;\r\n    });\r\n    var neto = mm \/ 1000, metros = Math.ceil(neto \/ RULES.barra - 1e-9) * RULES.barra, lines = [];\r\n    function push(k, qty, price, name) {\r\n      if (qty <= 0) return;\r\n      var c = CAT[k];\r\n      lines.push({ k: k, sku: c.sku, id: c.id, slug: c.slug, n: name || c.n, u: c.u, q: qty, p: price, t: Math.round(qty * price * 100) \/ 100 });\r\n    }\r\n    push('via', metros, CAT.via.p[sil], CAT.via.n + ' \u00b7 brackets every ' + sil + ' cm');\r\n    push('curva', q.curva, CAT.curva.p);\r\n    push('desvio', q.desvio, CAT.desvio.p);\r\n    push('desvio45', q.desvio45, CAT.desvio45.p);\r\n    push('triple', q.triple, CAT.triple.p);\r\n    push('tope', q.tope, CAT.tope.p);\r\n    CARROS.forEach(function (k) { if (car[k]) push(k, car[k], CAT[k].p); });\r\n    var sub = Math.round(lines.reduce(function (s, l) { return s + l.t; }, 0) * 100) \/ 100;\r\n    var iva = Math.round(sub * RULES.iva * 100) \/ 100;\r\n    return { lines: lines, neto: neto, metros: metros, barras: metros \/ RULES.barra, sobrante: metros - neto, sub: sub, iva: iva, total: Math.round((sub + iva) * 100) \/ 100 };\r\n  }\r\n\r\n  \/* ---------- geometr\u00eda de piezas (polil\u00edneas en mm, para planta y 3D) ---------- *\/\r\n  function arc(n, ua, ub) {\r\n    var pts = [], cx = n.x + R * (ua[0] + ub[0]), cy = n.y + R * (ua[1] + ub[1]);\r\n    for (var i = 0; i <= 10; i++) {\r\n      var t = i \/ 10 * Math.PI \/ 2, c = Math.cos(t), s = Math.sin(t);\r\n      pts.push([cx - R * (ub[0] * c + ua[0] * s), cy - R * (ub[1] * c + ua[1] * s)]);\r\n    }\r\n    return [[n.x + P * ua[0], n.y + P * ua[1]]].concat(pts, [[n.x + P * ub[0], n.y + P * ub[1]]]);\r\n  }\r\n  function pieceGeom(n) {\r\n    var ds = Object.keys(n.legs), out = [];\r\n    if (n.type === 'curva') out.push(arc(n, DIR[ds[0]], DIR[ds[1]]));\r\n    else if (n.type === 'desvio') {\r\n      out.push([[n.x + P * DIR[n.arc][0], n.y + P * DIR[n.arc][1]], [n.x, n.y]]);\r\n      out.push(arc(n, DIR[n.arc], DIR[n.stem]));\r\n    } else if (n.type === 'triple') {\r\n      var thr = ds.filter(function (d) { return d !== n.stem; });\r\n      out.push([[n.x + P * DIR[thr[0]][0], n.y + P * DIR[thr[0]][1]], [n.x + P * DIR[thr[1]][0], n.y + P * DIR[thr[1]][1]]]);\r\n      out.push(arc(n, DIR[thr[0]], DIR[n.stem]));\r\n      out.push(arc(n, DIR[thr[1]], DIR[n.stem]));\r\n    } else if (n.type === 'desvio45' && n.g45) {\r\n      var g = n.g45, T = g.T, u = g.u, v = g.v, sg = g.s, pts = [], i, t, c, sn;\r\n      out.push([[T[0] - sg * 50 * u[0], T[1] - sg * 50 * u[1]], [T[0] + sg * A45 * u[0], T[1] + sg * A45 * u[1]]]);\r\n      for (i = 0; i <= 6; i++) {\r\n        t = i \/ 6 * Math.PI \/ 4; c = Math.cos(t); sn = Math.sin(t);\r\n        pts.push([T[0] + sg * R * ((1 - c) * v[0] + sn * u[0]), T[1] + sg * R * ((1 - c) * v[1] + sn * u[1])]);\r\n      }\r\n      out.push(pts);\r\n    }\r\n    return out;\r\n  }\r\n  function edgeLine(e) {\r\n    if (e.diag) return e.diag;\r\n    if (e.net <= 0) return null;\r\n    return e.h ? [[e.a.x + e.ca, e.a.y], [e.b.x - e.cb, e.b.y]] : [[e.a.x, e.a.y + e.ca], [e.b.x, e.b.y - e.cb]];\r\n  }\r\n\r\n  \/\/ compara plantillas para un recinto y devuelve la de m\u00e1s metros \u00fatiles\r\n  function compare(r) {\r\n    return ['linea', 'peine', 'malla'].map(function (t) {\r\n      var c = {}; Object.keys(r).forEach(function (k) { c[k] = r[k]; }); c.tpl = t; c.nLines = 0; c.nCar = null;\r\n      var a = analyse(c);\r\n      return { tpl: t, ok: !a.fallback || t === 'linea', inside: a.inside, an: a };\r\n    });\r\n  }\r\n\r\n  var API = { R: R, P: P, RULES: RULES, SP: SP, CAT: CAT, CARROS: CARROS, generate: generate, analyse: analyse, bom: bom, pieceGeom: pieceGeom, edgeLine: edgeLine, compare: compare, DIR: DIR, mergeSegs: mergeSegs, doorsOf: doorsOf };\r\n  if (typeof module !== 'undefined' && module.exports) module.exports = API; else root.BIR = API;\r\n})(typeof window !== 'undefined' ? window : this);<\/script><script>\/* Interfaz del dise\u00f1ador BIRRAIL en tiendabernad.com. El c\u00e1lculo est\u00e1 en el motor (window.BIR); vive en un shadow root para no mezclarse con los estilos del tema. *\/\r\n(function () {\r\n  'use strict';\r\n  var B = window.BIR, X = window.BXR || {}, host = document.getElementById('bxr'), tplEl = document.getElementById('bxr-tpl');\r\n  if (!B || !host || !tplEl || host.shadowRoot || !host.attachShadow) return;\r\n  var root = host.attachShadow({ mode: 'open' });\r\n  root.appendChild(tplEl.content.cloneNode(true));\r\n  var $ = function (id) { return root.getElementById(id); };\r\n  X.p = X.p || {}; X.u = X.u || {}; X.ok = X.ok || {};\r\n  \/\/ precios en vivo de la tienda; los carros que no se pueden comprar salen de la lista\r\n  Object.keys(X.p).forEach(function (k) { if (B.CAT[k]) B.CAT[k].p = X.p[k]; });\r\n  for (var ci = B.CARROS.length - 1; ci >= 0; ci--) if (X.ok[B.CARROS[ci]] === false) B.CARROS.splice(ci, 1);\r\n  Object.keys(B.SP).forEach(function (k) { if (B.SP[k].carro && B.CARROS.indexOf(B.SP[k].carro) < 0) B.SP[k].carro = ''; });\r\n\r\n  \/* ---------- utilidades ---------- *\/\r\n  function esc(s) { return String(s).replace(\/[&<>\"]\/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;' }[c]; }); }\r\n  \/\/ n\u00fameros: medidas con el formato del idioma; precios con el formato de la tienda\r\n  var NS = X.lang === 'en' ? [',', '.'] : ['.', ','], PS = [X.tho === undefined ? '.' : X.tho, X.dec || ','];\r\n  function fmt(v, d, th, dc) {\r\n    var s = Math.abs(v).toFixed(d || 0).split('.');\r\n    s[0] = s[0].replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, th);\r\n    return (v < 0 ? '\u2212' : '') + s[0] + (s[1] ? dc + s[1] : '');\r\n  }\r\n  function nf(v, d) { return fmt(v, d, NS[0], NS[1]); }\r\n  function eur(v) { return fmt(v, 2, PS[0], PS[1]) + ' \u20ac'; }\r\n  function mt(mm, d) { return nf(mm \/ 1000, d === undefined ? 2 : d); }\r\n  function f1(v) { return Math.round(v * 10) \/ 10; }\r\n  function r10(v) { return Math.round(v \/ 10) * 10; }\r\n  function clamp(v, a, b) { return Math.max(a, Math.min(b, v)); }\r\n  function pl(n, uno, varios) { return n + ' ' + (n === 1 ? uno : varios); }\r\n\r\n  \/* ---------- estado ---------- *\/\r\n  function room(o) {\r\n    var r = { name: 'Room 1', W: 5000, D: 6000, H: 3200, sp: 'porcino', tpl: 'peine', xd: 2500, dw: 1000, ext: 0, nLines: 0, nr: 0, carro: 'auto', nCar: null, segs: null, cierre: true, tri: false, doors: [] };\r\n    for (var k in o) r[k] = o[k];\r\n    r.doors = (r.doors || []).map(function (d) { return { w: d.w, p: d.p, dw: d.dw }; });\r\n    return r;\r\n  }\r\n  \/\/ puertas adicionales: w = pared (N fondo, W izquierda, E derecha, S entrada), p = eje a lo largo de la pared, dw = ancho\r\n  var WN = { N: 'Back', W: 'Left', E: 'Right', S: 'Entry' };\r\n  function wallLen(r, d) { return d.w === 'N' || d.w === 'S' ? r.W : r.D; }\r\n  function fixDoors(r) {\r\n    r.doors.forEach(function (d) { var L = wallLen(r, d); d.dw = clamp(d.dw, 600, Math.min(4000, L)); d.p = clamp(d.p, d.dw \/ 2, L - d.dw \/ 2); });\r\n  }\r\n  var S = { rooms: [room()], cur: 0, sil: '66', view: 'planta', tool: 'draw', cliente: '', sitio: '', undo: [] };\r\n  var EJ = {\r\n    a: function () { return [room({ name: 'Pork cold room', W: 2600, D: 3190, H: 3000, xd: 1300, dw: 900 })]; },\r\n    b: function () { return [room({ name: 'Beef cold room', W: 3840, D: 3880, H: 3500, sp: 'vacuno', tpl: 'malla', xd: 1920 })]; },\r\n    c: function () { return [room({ name: 'Chilling room', W: 8000, D: 12000, H: 3400, xd: 4000, dw: 1400, ext: 2 * 1000 })]; },\r\n    d: function () {\r\n      return [room({ name: 'Pork chilling', W: 8000, D: 12000, H: 3400, xd: 4000, dw: 1400, ext: 2000 }),\r\n        room({ name: 'Beef storage', W: 6000, D: 8000, H: 3600, sp: 'vacuno', tpl: 'malla', xd: 3000, dw: 1400 }),\r\n        room({ name: 'Lamb cold room', W: 4000, D: 5000, H: 3200, sp: 'ovino', xd: 2000, carro: 'c12' })];\r\n    }\r\n  };\r\n  function R() { return S.rooms[S.cur]; }\r\n\r\n  \/* ---------- c\u00f3digo del dise\u00f1o (para compartir) ---------- *\/\r\n  \/\/ k = peine con el circuito cerrado al fondo (lo normal), t = peine con topes; las puertas adicionales van como d + pared + eje + a + ancho\r\n  var TL = { linea: 'l', peine: 'k', malla: 'm', libre: 'f' }, LT = { l: 'linea', k: 'peine', t: 'peine', m: 'malla', f: 'libre' };\r\n  var WL = { N: 't', W: 'l', E: 'r', S: 'b' }, LW = { t: 'N', l: 'W', r: 'E', b: 'S' };\r\n  function encode() {\r\n    return 'b1-' + S.sil + '-' + S.rooms.map(function (r) {\r\n      var c = r.carro === 'auto' ? 'a' : r.carro === '' ? 'n' : r.carro;\r\n      var a = [r.sp[0] + r.W \/ 10, r.D \/ 10, r.H \/ 10, r.xd \/ 10, r.dw \/ 10, r.ext \/ 10, r.nr \/ 10, (r.tpl === 'peine' && !r.cierre ? 't' : TL[r.tpl]) + r.nLines, c, r.nCar === null ? 'z' : r.nCar];\r\n      if (r.tri) a.push('y3'); \/\/ desv\u00edos triples en el cierre del circuito\r\n      r.doors.forEach(function (d) { a.push('d' + WL[d.w] + d.p \/ 10 + 'a' + d.dw \/ 10); });\r\n      if (r.tpl === 'libre') a.push('s' + (r.segs || []).map(function (s) { return s.map(function (v) { return v < 0 ? 'n' + (-v \/ 10) : v \/ 10; }).join('x'); }).join('x'));\r\n      return a.join('x');\r\n    }).join('-');\r\n  }\r\n  function decode(h) {\r\n    try {\r\n      var p = String(h || '').replace(\/^#\/, '').split('-');\r\n      if (p[0] !== 'b1' || p.length < 3) return false;\r\n      var rooms = p.slice(2, 10).map(function (t, i) {\r\n        var a = t.split('x'), spk = { v: 'vacuno', p: 'porcino', o: 'ovino' }[a[0][0]];\r\n        var num = function (s) { var v = s[0] === 'n' ? -parseFloat(s.slice(1)) : parseFloat(s); if (!isFinite(v)) throw 0; return Math.round(v * 10); };\r\n        var c = a[8], r = room({\r\n          name: 'Room ' + (i + 1), sp: spk, W: clamp(num(a[0].slice(1)), 1200, 40000), D: clamp(num(a[1]), 1500, 60000), H: clamp(num(a[2]), 2000, 8000),\r\n          xd: num(a[3]), dw: clamp(num(a[4]), 600, 4000), ext: clamp(num(a[5]), 0, 30000), nr: clamp(num(a[6]), 0, 6000),\r\n          tpl: LT[a[7][0]], cierre: a[7][0] !== 't', nLines: parseInt(a[7].slice(1), 10) || 0,\r\n          carro: c === 'a' ? 'auto' : c === 'n' ? '' : (B.CARROS.indexOf(c) >= 0 ? c : 'auto'), nCar: a[9] === 'z' || a[9] === undefined ? null : clamp(parseInt(a[9], 10) || 0, 0, 5000)\r\n        });\r\n        if (!spk || !r.tpl) throw 0;\r\n        r.xd = clamp(r.xd, r.dw \/ 2, r.W - r.dw \/ 2);\r\n        var j0 = 10, dm;\r\n        for (; a[j0] && (a[j0][0] === 'd' || a[j0] === 'y3'); j0++) {\r\n          if (a[j0] === 'y3') { r.tri = true; continue; }\r\n          if (!(dm = \/^d([tlrb])([0-9.]+)a([0-9.]+)$\/.exec(a[j0]))) throw 0;\r\n          if (r.doors.length < 5) r.doors.push({ w: LW[dm[1]], p: num(dm[2]), dw: num(dm[3]) });\r\n        }\r\n        fixDoors(r);\r\n        if (r.tpl === 'libre') {\r\n          var v = a.slice(j0); v[0] = (v[0] || 's').slice(1); v = v.filter(function (s) { return s !== ''; }).map(num);\r\n          r.segs = []; for (var j = 0; j + 3 < v.length; j += 4) r.segs.push(v.slice(j, j + 4));\r\n        }\r\n        return r;\r\n      });\r\n      if (!rooms.length) return false;\r\n      S.rooms = rooms; S.cur = 0; S.sil = p[1] === '50' ? '50' : '66';\r\n      return true;\r\n    } catch (e) { return false; }\r\n  }\r\n\r\n  \/* ---------- dibujo: planta ---------- *\/\r\n  function pathOf(pts, D) { return 'M' + pts.map(function (p) { return f1(p[0]) + ' ' + f1(D - p[1]); }).join('L'); }\r\n\r\n  \/\/ marco de una puerta en el plano: origen en su eje sobre la cara interior, u a lo largo de la pared y v hacia fuera\r\n  function doorFrame(r, d) {\r\n    return d.w === 'S' ? { o: [d.p, r.D], u: [1, 0], v: [0, 1] } : d.w === 'N' ? { o: [d.p, 0], u: [1, 0], v: [0, -1] } : d.w === 'W' ? { o: [0, r.D - d.p], u: [0, 1], v: [-1, 0] } : { o: [r.W, r.D - d.p], u: [0, 1], v: [1, 0] };\r\n  }\r\n\r\n  function planSVG(r, an, cw, o) {\r\n    o = o || {};\r\n    var W = r.W, D = r.D, wl = 100, k = (W + 2 * wl) \/ Math.max(cw - 170, 150), pad, below, vb, i, s = [];\r\n    var swing = Math.min(r.dw, 1300) * 0.85;\r\n    for (i = 0; i < 3; i++) {\r\n      pad = 60 * k; below = Math.max(r.ext, swing) + 46 * k;\r\n      vb = [-wl - pad * 1.3, -wl - pad, W + 2 * wl + pad * 2.6, D + 2 * wl + pad + below];\r\n      k = Math.max(vb[2] \/ cw, vb[3] \/ (o.maxH || 670));\r\n    }\r\n    var tw = Math.max(75, 6.6 * k), fs = 10.5 * k, lw = Math.max(1, k);\r\n    s.push('<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"' + vb.map(f1).join(' ') + '\" width=\"' + Math.round(vb[2] \/ k) + '\" height=\"' + Math.round(vb[3] \/ k) + '\" role=\"img\" aria-label=\"Plan of ' + esc(r.name) + '\">');\r\n    s.push('<rect class=\"p-floor\" x=\"0\" y=\"0\" width=\"' + W + '\" height=\"' + D + '\"\/>');\r\n    \/\/ ret\u00edcula de 1 m\r\n    if (W <= 40000 && 1000 \/ k >= 9) {\r\n      var g = '';\r\n      for (i = 1000; i < W; i += 1000) g += 'M' + i + ' 0V' + D;\r\n      for (i = 1000; i < D; i += 1000) g += 'M0 ' + (D - i) + 'H' + W;\r\n      s.push('<path class=\"p-grid\" stroke-width=\"' + f1(lw) + '\" d=\"' + g + '\"\/>');\r\n    }\r\n    s.push('<path class=\"p-wall\" fill-rule=\"evenodd\" stroke-width=\"' + f1(1.3 * k) + '\" d=\"M' + -wl + ' ' + -wl + 'h' + (W + 2 * wl) + 'v' + (D + 2 * wl) + 'h' + -(W + 2 * wl) + 'zM0 0h' + W + 'v' + D + 'h' + -W + 'z\"\/>');\r\n    \/\/ puerta\r\n    var x1 = r.xd - r.dw \/ 2, x2 = r.xd + r.dw \/ 2;\r\n    s.push('<rect x=\"' + x1 + '\" y=\"' + (D - 2 * k) + '\" width=\"' + r.dw + '\" height=\"' + (wl + 4 * k) + '\" fill=\"#fff\"\/>');\r\n    s.push('<path class=\"p-door\" stroke-width=\"' + f1(1.3 * k) + '\" d=\"M' + x1 + ' ' + D + 'v' + wl + 'M' + x2 + ' ' + D + 'v' + wl + '\"\/>');\r\n    s.push('<path class=\"p-door\" stroke-width=\"' + f1(1.6 * k) + '\" d=\"M' + x1 + ' ' + (D + wl) + 'v' + f1(swing) + '\"\/>');\r\n    s.push('<path class=\"p-door\" stroke-width=\"' + f1(lw) + '\" stroke-dasharray=\"' + f1(4 * k) + ' ' + f1(3 * k) + '\" d=\"M' + x1 + ' ' + f1(D + wl + swing) + 'A' + f1(swing) + ' ' + f1(swing) + ' 0 0 0 ' + f1(x1 + swing) + ' ' + (D + wl) + '\"\/>');\r\n    s.push('<path class=\"p-axis\" stroke-width=\"' + f1(lw) + '\" stroke-dasharray=\"' + f1(9 * k) + ' ' + f1(3 * k) + ' ' + f1(2 * k) + ' ' + f1(3 * k) + '\" d=\"M' + r.xd + ' ' + f1(D + wl + below - 30 * k) + 'V' + f1(D - Math.min(500, D \/ 4)) + '\"\/>');\r\n\r\n    \/\/ puertas adicionales: hueco en su pared y r\u00f3tulo por dentro\r\n    r.doors.forEach(function (d, j) {\r\n      var F = doorFrame(r, d), h = d.dw \/ 2, tx, ty, end = false;\r\n      function pt(a, b) { return f1(F.o[0] + a * F.u[0] + b * F.v[0]) + ' ' + f1(F.o[1] + a * F.u[1] + b * F.v[1]); }\r\n      s.push('<path fill=\"#fff\" d=\"M' + pt(-h, -2 * k) + 'L' + pt(h, -2 * k) + 'L' + pt(h, wl + 2 * k) + 'L' + pt(-h, wl + 2 * k) + 'z\"\/>');\r\n      s.push('<path class=\"p-door\" stroke-width=\"' + f1(1.3 * k) + '\" d=\"M' + pt(-h, 0) + 'L' + pt(-h, wl) + 'M' + pt(h, 0) + 'L' + pt(h, wl) + '\"\/>');\r\n      if (d.w === 'N' || d.w === 'S') { end = F.o[0] + h + 60 * k > W; tx = F.o[0] + (end ? -1 : 1) * (h + 6 * k); ty = d.w === 'N' ? 13 * k : D - 6 * k; }\r\n      else { \/\/ en las paredes laterales el r\u00f3tulo va en vertical, pegado a la pared\r\n        tx = d.w === 'E' ? W - 5 * k : 12 * k; ty = F.o[1] - h - 6 * k; end = ty < 56 * k; if (end) ty = F.o[1] + h + 6 * k;\r\n        s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.95) + '\"' + (end ? ' text-anchor=\"end\"' : '') + ' transform=\"translate(' + f1(tx) + ' ' + f1(ty) + ') rotate(-90)\">Door ' + (j + 2) + '<\/text>');\r\n        return;\r\n      }\r\n      s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.95) + '\"' + (end ? ' text-anchor=\"end\"' : '') + ' x=\"' + f1(tx) + '\" y=\"' + f1(ty) + '\">Door ' + (j + 2) + '<\/text>');\r\n    });\r\n\r\n    \/\/ v\u00eda: primero el cuerpo, despu\u00e9s el alma blanca\r\n    var body = '', soul = '', dS = '', sil = '', hits = '';\r\n    an.edges.forEach(function (e, idx) {\r\n      var ln = B.edgeLine(e);\r\n      if (e.bad) body += '<path class=\"t-out t-bad\" stroke-width=\"' + f1(tw) + '\" d=\"' + pathOf([[e.a.x, e.a.y], [e.b.x, e.b.y]], D) + '\"\/>';\r\n      if (ln) {\r\n        dS += pathOf(ln, D);\r\n        var step = S.sil === '50' ? 500 : 660;\r\n        if (step \/ k >= 5.5 && !e.diag) {\r\n          var len = e.net, n = Math.max(1, Math.round(len \/ step)), st = len \/ n, t, hx = tw * 1.05;\r\n          for (t = st \/ 2; t < len; t += st) {\r\n            if (e.h) sil += 'M' + f1(ln[0][0] + t) + ' ' + f1(D - e.a.y - hx) + 'v' + f1(2 * hx);\r\n            else sil += 'M' + f1(e.a.x - hx) + ' ' + f1(D - ln[0][1] - t) + 'h' + f1(2 * hx);\r\n          }\r\n        }\r\n      }\r\n      if (o.erase) hits += '<path class=\"hit\" data-e=\"' + idx + '\" stroke-width=\"' + f1(Math.max(tw * 2.4, 18 * k)) + '\" d=\"' + pathOf(e.diag || [[e.a.x, e.a.y], [e.b.x, e.b.y]], D) + '\"\/>';\r\n    });\r\n    if (sil) s.push('<path class=\"t-sil\" stroke-width=\"' + f1(Math.max(20, 1.5 * k)) + '\" d=\"' + sil + '\"\/>');\r\n    body += '<path class=\"t-out\" stroke-width=\"' + f1(tw) + '\" d=\"' + dS + '\"\/>';\r\n    soul += '<path class=\"t-in\" stroke-width=\"' + f1(tw * 0.34) + '\" d=\"' + dS + '\"\/>';\r\n    an.nodes.forEach(function (n) {\r\n      var gm = B.pieceGeom(n);\r\n      if (gm.length) {\r\n        var d = gm.map(function (p) { return pathOf(p, D); }).join('');\r\n        var c = B.CAT[n.type];\r\n        body += '<path class=\"t-out t-' + n.type + '\" stroke-width=\"' + f1(tw) + '\" d=\"' + d + '\"><title>' + esc(c.n + ' \u00b7 ' + eur(c.p)) + '<\/title><\/path>';\r\n        soul += '<path class=\"t-in\" stroke-width=\"' + f1(tw * 0.34) + '\" d=\"' + d + '\"\/>';\r\n      } else if (n.type === 'tope') {\r\n        var dir = Object.keys(n.legs)[0], h = tw * 1.5;\r\n        soul += '<path class=\"t-tope\" stroke-width=\"' + f1(tw * 0.6) + '\" d=\"' + (dir === 'N' || dir === 'S' ? 'M' + f1(n.x - h) + ' ' + (D - n.y) + 'h' + f1(2 * h) : 'M' + n.x + ' ' + f1(D - n.y - h) + 'v' + f1(2 * h)) + '\"><title>End stop \u00b7 ' + eur(B.CAT.tope.p) + '<\/title><\/path>';\r\n      } else if (n.type === 'cruce') {\r\n        soul += '<circle cx=\"' + n.x + '\" cy=\"' + (D - n.y) + '\" r=\"' + f1(tw * 1.6) + '\" fill=\"none\" stroke=\"#C81E25\" stroke-width=\"' + f1(2 * k) + '\"\/>';\r\n      }\r\n    });\r\n    s.push(body, soul);\r\n    \/\/ entrada\r\n    if (an.entry && an.entry.deg === 1) {\r\n      var en = an.entry, dd = B.DIR[Object.keys(en.legs)[0]], a = 9 * k, ex = en.x - dd[0] * 6 * k, ey = D - en.y + dd[1] * 6 * k;\r\n      s.push('<path fill=\"var(--ok)\" d=\"M' + f1(ex + dd[0] * a) + ' ' + f1(ey - dd[1] * a) + 'L' + f1(ex - dd[1] * a * 0.7 - dd[0] * a * 0.4) + ' ' + f1(ey - dd[0] * a * 0.7 + dd[1] * a * 0.4) + 'L' + f1(ex + dd[1] * a * 0.7 - dd[0] * a * 0.4) + ' ' + f1(ey + dd[0] * a * 0.7 + dd[1] * a * 0.4) + 'z\"\/>');\r\n      var izq = en.x > W * 0.6;\r\n      s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.95) + '\"' + (izq ? ' text-anchor=\"end\"' : '') + ' x=\"' + f1(en.x + (izq ? -1 : 1) * (o.sheet || en.y < 0 ? 12 : 34) * k) + '\" y=\"' + f1(D - en.y + (en.y < 0 ? 4 : o.sheet ? 16 : 30) * k) + '\">Entry<\/text>');\r\n    }\r\n    \/\/ cotas\r\n    function tick(x, y) { return 'M' + f1(x - 3 * k) + ' ' + f1(y + 3 * k) + 'L' + f1(x + 3 * k) + ' ' + f1(y - 3 * k); }\r\n    function dimH(a, b, y, txt, bold) {\r\n      var g = (b - a) \/ k, out = '<path class=\"d-l\" stroke-width=\"' + f1(lw) + '\" d=\"M' + a + ' ' + f1(y) + 'H' + b + tick(a, y) + tick(b, y) + '\"\/>';\r\n      if (g >= 17) out += '<text class=\"d-t' + (bold ? ' b' : '') + '\" font-size=\"' + f1(g >= 30 || bold ? fs : fs * 0.8) + '\" x=\"' + f1((a + b) \/ 2) + '\" y=\"' + f1(y - 4 * k) + '\">' + txt + '<\/text>';\r\n      return out;\r\n    }\r\n    function dimV(a, b, x, txt, bold) { \/\/ a, b en coordenadas de modelo (y)\r\n      var ya = D - a, yb = D - b, g = (b - a) \/ k, out = '<path class=\"d-l\" stroke-width=\"' + f1(lw) + '\" d=\"M' + f1(x) + ' ' + ya + 'V' + yb + tick(x, ya) + tick(x, yb) + '\"\/>';\r\n      if (g >= 17) out += '<text class=\"d-t' + (bold ? ' b' : '') + '\" font-size=\"' + f1(g >= 30 || bold ? fs : fs * 0.8) + '\" transform=\"translate(' + f1(x - 4 * k) + ' ' + f1((ya + yb) \/ 2) + ') rotate(-90)\">' + txt + '<\/text>';\r\n      return out;\r\n    }\r\n    function uniq(a) { return a.sort(function (x, y) { return x - y; }).filter(function (v, j, arr) { return !j || v !== arr[j - 1]; }); }\r\n    var xs = [0, W], ys = [0, D];\r\n    an.merged.V.forEach(function (v) { if (v.b > 0 && v.c > 0 && v.c < W) xs.push(v.c); if (v.b > 0 && v.b < D) ys.push(v.b); if (v.a > 0) ys.push(v.a); });\r\n    an.merged.H.forEach(function (h) { if (h.c > 0 && h.c < D) ys.push(h.c); });\r\n    xs = uniq(xs); ys = uniq(ys);\r\n    if (xs.length <= 40) for (i = 0; i < xs.length - 1; i++) s.push(dimH(xs[i], xs[i + 1], -wl - 14 * k, nf(xs[i + 1] - xs[i])));\r\n    if (ys.length <= 40) for (i = 0; i < ys.length - 1; i++) s.push(dimV(ys[i], ys[i + 1], W + wl + 22 * k, nf(ys[i + 1] - ys[i])));\r\n    s.push(dimH(0, W, D + wl + below - 8 * k, 'Clear width ' + nf(W), true));\r\n    s.push(dimV(0, D, -wl - 26 * k, 'Clear depth ' + nf(D), true));\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.9) + '\" x=\"' + f1(vb[0] + 4 * k) + '\" y=\"' + f1(vb[1] + 12 * k) + '\">Dimensions in mm, to rail axes<\/text>');\r\n    \/\/ tiradores de las puertas\r\n    if (!o.sheet) {\r\n      var hw = 25 * k, hh = 9 * k;\r\n      var handle = function (cx, cy, rot, key) {\r\n        return '<g class=\"handle\" data-h=\"' + key + '\" transform=\"translate(' + f1(cx) + ' ' + f1(cy) + ')' + (rot ? ' rotate(90)' : '') + '\"><title>Drag to move the door<\/title><rect x=\"' + f1(-hw) + '\" y=\"' + f1(-hh) + '\" width=\"' + f1(2 * hw) + '\" height=\"' + f1(2 * hh) + '\" rx=\"' + f1(hh) + '\"\/><path stroke-width=\"' + f1(1.6 * k) + '\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M' + f1(-hw * 0.25) + ' ' + f1(-hh * 0.45) + 'l' + f1(-hh * 0.5) + ' ' + f1(hh * 0.45) + 'l' + f1(hh * 0.5) + ' ' + f1(hh * 0.45) + 'M' + f1(hw * 0.25) + ' ' + f1(-hh * 0.45) + 'l' + f1(hh * 0.5) + ' ' + f1(hh * 0.45) + 'l' + f1(-hh * 0.5) + ' ' + f1(hh * 0.45) + '\"\/><\/g>';\r\n      };\r\n      s.push(handle(r.xd, D + wl \/ 2, false, 'door'));\r\n      r.doors.forEach(function (d, j) { var F = doorFrame(r, d); s.push(handle(F.o[0] + F.v[0] * wl \/ 2, F.o[1] + F.v[1] * wl \/ 2, d.w === 'W' || d.w === 'E', 'd' + j)); });\r\n    }\r\n    s.push(hits, '<\/svg>');\r\n    return s.join('');\r\n  }\r\n\r\n  \/* ---------- dibujo: alzado ---------- *\/\r\n  function altSVG(r, an, cw) {\r\n    var H = r.H, nr = an.nr, L = 3300, k = 1, pad, vb, i, s = [], car = B.CAT[an.carroKey] || {}, g = car.gancho || 0;\r\n    for (i = 0; i < 3; i++) { pad = 70 * k; vb = [-pad * 1.6, -pad * 0.7, L + pad * 3.6, H + pad * 1.6]; k = Math.max(vb[2] \/ cw, vb[3] \/ 560); }\r\n    var Y = function (z) { return H - z; }, fs = 10.5 * k, lw = Math.max(1, k);\r\n    s.push('<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"' + vb.map(f1).join(' ') + '\" width=\"' + Math.round(vb[2] \/ k) + '\" height=\"' + Math.round(vb[3] \/ k) + '\" role=\"img\" aria-label=\"Elevation of ' + esc(r.name) + '\">');\r\n    s.push('<rect x=\"0\" y=\"' + Y(H) + '\" width=\"' + L + '\" height=\"' + H + '\" fill=\"#fff\"\/>');\r\n    s.push('<rect class=\"a-fill\" stroke-width=\"' + f1(lw) + '\" x=\"' + -20 * k + '\" y=\"' + f1(Y(0)) + '\" width=\"' + f1(L + 40 * k) + '\" height=\"' + f1(9 * k) + '\"\/>');\r\n    s.push('<rect class=\"a-fill\" stroke-width=\"' + f1(lw) + '\" x=\"' + -20 * k + '\" y=\"' + f1(Y(H) - 9 * k) + '\" width=\"' + f1(L + 40 * k) + '\" height=\"' + f1(9 * k) + '\"\/>');\r\n    var bz = nr + B.RULES.viga, ok = an.hNec <= H;\r\n    s.push('<rect class=\"a-fill\" stroke-width=\"' + f1(lw) + '\" x=\"200\" y=\"' + Y(bz + 120) + '\" width=\"' + (L - 400) + '\" height=\"120\"\/>');\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.9) + '\" x=\"230\" y=\"' + f1(Y(bz + 120) - 5 * k) + '\">Structure beam (IPN 120 to 280)<\/text>');\r\n    var step = S.sil === '50' ? 500 : 660;\r\n    for (i = 0; 420 + i * step < L - 380; i++) s.push('<rect x=\"' + (420 + i * step - 14) + '\" y=\"' + Y(bz) + '\" width=\"28\" height=\"' + (B.RULES.viga + 50) + '\" fill=\"#55626F\"\/>');\r\n    s.push('<rect class=\"a-rail\" x=\"320\" y=\"' + Y(nr) + '\" width=\"' + (L - 640) + '\" height=\"50\"\/>');\r\n    [900, 1650, 2400].forEach(function (x) {\r\n      s.push('<circle cx=\"' + x + '\" cy=\"' + Y(nr + 26) + '\" r=\"30\" fill=\"#fff\" stroke=\"#26303F\" stroke-width=\"' + f1(Math.max(8, lw)) + '\"\/>');\r\n      s.push('<path class=\"a-ln\" stroke-width=\"' + f1(Math.max(18, 1.6 * k)) + '\" d=\"M' + x + ' ' + Y(nr - 4) + 'V' + Y(nr - 164) + '\"\/>');\r\n      if (g) s.push('<path class=\"a-ln\" stroke-linecap=\"round\" stroke-width=\"' + f1(Math.max(14, 1.4 * k)) + '\" d=\"M' + x + ' ' + Y(nr - 164) + 'V' + Y(nr - g + 55) + 'a55 55 0 0 0 110 0l-12 -34\"\/>');\r\n    });\r\n    function dv(z1, z2, x, txt, bold) {\r\n      var out = '<path class=\"d-l\" stroke-width=\"' + f1(lw) + '\" d=\"M' + x + ' ' + Y(z1) + 'V' + Y(z2) + 'M' + f1(x - 4 * k) + ' ' + Y(z1) + 'h' + f1(8 * k) + 'M' + f1(x - 4 * k) + ' ' + Y(z2) + 'h' + f1(8 * k) + '\"\/>';\r\n      return out + '<text class=\"d-t' + (bold ? ' b' : '') + '\" font-size=\"' + f1(fs) + '\" transform=\"translate(' + f1(x - 5 * k) + ' ' + f1((Y(z1) + Y(z2)) \/ 2) + ') rotate(-90)\">' + txt + '<\/text>';\r\n    }\r\n    s.push(dv(0, nr, L + 40 * k, 'Running level ' + nf(nr), true));\r\n    s.push(dv(0, H, L + 82 * k, 'Clear height ' + nf(H)));\r\n    s.push(dv(nr, bz, L + 8 * k, '110'));\r\n    if (g) s.push(dv(0, nr - g, 640, 'Hook at ' + nf(nr - g)));\r\n    s.push('<path class=\"d-l\" stroke-dasharray=\"' + f1(8 * k) + ' ' + f1(4 * k) + '\" stroke-width=\"' + f1(lw) + '\" d=\"M0 ' + Y(nr) + 'H' + f1(L + 44 * k) + '\"\/>');\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.9) + '\" x=\"340\" y=\"' + f1(Y(nr - 50) + 13 * k) + '\">BIRRAIL aluminium rail<\/text>');\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(fs * 0.9) + '\" x=\"0\" y=\"' + f1(Y(H) - 14 * k) + '\">Ceiling<\/text><text class=\"lbl\" font-size=\"' + f1(fs * 0.9) + '\" x=\"0\" y=\"' + f1(Y(0) + 22 * k) + '\">Floor<\/text>');\r\n    if (!ok) s.push('<text font-size=\"' + f1(fs * 1.1) + '\" fill=\"#C81E25\" font-weight=\"900\" x=\"230\" y=\"' + f1(Y(0) - 14 * k) + '\">The beam does not fit under the ceiling: lower the running level or check the clear height.<\/text>');\r\n    s.push('<\/svg>');\r\n    return s.join('');\r\n  }\r\n\r\n  \/* ---------- dibujo: 3D ---------- *\/\r\n  function isoSVG(r, an, cw) {\r\n    var W = r.W, D = r.D, H = r.H, nr = an.nr, c = Math.cos(Math.PI \/ 6), sn = 0.5, s = [];\r\n    function P(x, y, z) { var u = x, v = D - y; return [(u - v) * c, (u + v) * sn - z]; }\r\n    function pts(a) { return a.map(function (p) { return f1(p[0]) + ' ' + f1(p[1]); }).join('L'); }\r\n    var yLow = -Math.max(r.ext, 0);\r\n    var cs = [P(0, 0, 0), P(W, 0, 0), P(0, D, 0), P(W, D, 0), P(0, D, H), P(W, D, H), P(0, 0, H), P(W, 0, H), P(r.xd, yLow, 0), P(r.xd, yLow, nr)];\r\n    var minx = Math.min.apply(null, cs.map(function (p) { return p[0]; })), maxx = Math.max.apply(null, cs.map(function (p) { return p[0]; }));\r\n    var miny = Math.min.apply(null, cs.map(function (p) { return p[1]; })), maxy = Math.max.apply(null, cs.map(function (p) { return p[1]; }));\r\n    var k = Math.max((maxx - minx) \/ (cw - 40), (maxy - miny) \/ 540), pad = 20 * k;\r\n    var vb = [minx - pad, miny - pad, maxx - minx + 2 * pad, maxy - miny + 2 * pad + 14 * k], lw = Math.max(1, k);\r\n    s.push('<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" viewBox=\"' + vb.map(f1).join(' ') + '\" width=\"' + Math.round(vb[2] \/ k) + '\" height=\"' + Math.round(vb[3] \/ k) + '\" role=\"img\" aria-label=\"3D view of ' + esc(r.name) + '\">');\r\n    s.push('<path class=\"i-floor\" stroke-width=\"' + f1(lw) + '\" d=\"M' + pts([P(0, 0, 0), P(W, 0, 0), P(W, D, 0), P(0, D, 0)]) + 'z\"\/>');\r\n    s.push('<path class=\"i-wall\" stroke-width=\"' + f1(lw) + '\" d=\"M' + pts([P(0, D, 0), P(W, D, 0), P(W, D, H), P(0, D, H)]) + 'z\"\/>');\r\n    s.push('<path class=\"i-wall\" stroke-width=\"' + f1(lw) + '\" d=\"M' + pts([P(0, 0, 0), P(0, D, 0), P(0, D, H), P(0, 0, H)]) + 'z\"\/>');\r\n    var hd = Math.min(H, 2200);\r\n    function hueco(d) {\r\n      var a = d.p - d.dw \/ 2, b = d.p + d.dw \/ 2, q = d.w === 'N' ? [[a, D], [b, D]] : d.w === 'S' ? [[a, 0], [b, 0]] : d.w === 'W' ? [[0, a], [0, b]] : [[W, a], [W, b]];\r\n      return '<path class=\"i-near\" stroke-width=\"' + f1(1.6 * k) + '\" d=\"M' + pts([P(q[0][0], q[0][1], 0), P(q[0][0], q[0][1], hd), P(q[1][0], q[1][1], hd), P(q[1][0], q[1][1], 0)]) + '\"\/>';\r\n    }\r\n    r.doors.forEach(function (d) { if (d.w === 'N' || d.w === 'W') s.push(hueco(d)); });\r\n    \/\/ polil\u00edneas de v\u00eda\r\n    var lines = [];\r\n    an.edges.forEach(function (e) { var l = B.edgeLine(e); if (l) lines.push({ p: l, t: '', e: e }); });\r\n    an.nodes.forEach(function (n) { B.pieceGeom(n).forEach(function (p) { lines.push({ p: p, t: n.type }); }); });\r\n    var sh = '', rail = { '': '', curva: '', desvio: '', desvio45: '', triple: '' };\r\n    lines.forEach(function (l) {\r\n      sh += 'M' + pts(l.p.map(function (q) { return P(q[0], q[1], 0); }));\r\n      rail[l.t] += 'M' + pts(l.p.map(function (q) { return P(q[0], q[1], nr); }));\r\n    });\r\n    s.push('<path class=\"i-sh\" stroke-width=\"' + f1(Math.max(40, 2 * k)) + '\" d=\"' + sh + '\"\/>');\r\n    \/\/ carga colgada, de fondo a frente\r\n    var n = an.nCar, items = [], tot = an.inside, car = B.CAT[an.carroKey] || {}, g = car.gancho || 314;\r\n    if (n > 0 && tot > 0) {\r\n      var paso = tot \/ Math.min(n, 320), acc = paso \/ 2;\r\n      an.edges.forEach(function (e) {\r\n        var l = B.edgeLine(e); if (!l || e.diag) return;\r\n        var a0 = e.h ? l[0][0] : Math.max(l[0][1], 0), a1 = e.h ? l[1][0] : l[1][1];\r\n        if (e.h && e.a.y < 0) return;\r\n        for (; acc < a1 - a0; acc += paso) items.push(e.h ? [a0 + acc, e.a.y] : [e.a.x, a0 + acc]);\r\n        acc -= Math.max(a1 - a0, 0);\r\n      });\r\n      items.sort(function (a, b) { return (a[0] + D - a[1]) - (b[0] + D - b[1]); });\r\n      var lar = r.sp === 'vacuno' ? 1900 : r.sp === 'porcino' ? 1500 : 1000, hk = '', mw = f1(Math.max(r.sp === 'vacuno' ? 230 : r.sp === 'porcino' ? 170 : 130, 2.4 * k));\r\n      items.forEach(function (q) {\r\n        var top = nr - g, bot = Math.max(top - lar, 250);\r\n        hk += 'M' + pts([P(q[0], q[1], nr), P(q[0], q[1], top)]);\r\n        s.push('<path class=\"i-meat\" stroke-width=\"' + mw + '\" d=\"M' + pts([P(q[0], q[1], top - 60), P(q[0], q[1], bot)]) + '\"\/>');\r\n      });\r\n      s.push('<path class=\"i-hook\" stroke-width=\"' + f1(Math.max(22, k)) + '\" d=\"' + hk + '\"\/>');\r\n    }\r\n    var colr = { '': 'var(--navy)', curva: 'var(--blue)', desvio: 'var(--orange)', desvio45: 'var(--orange)', triple: 'var(--teal)' };\r\n    Object.keys(rail).forEach(function (t) { if (rail[t]) s.push('<path class=\"i-rail\" style=\"stroke:' + colr[t] + '\" stroke-width=\"' + f1(Math.max(80, 3.4 * k)) + '\" d=\"' + rail[t] + '\"\/>'); });\r\n    \/\/ paredes cercanas y hueco de puerta\r\n    var x1 = r.xd - r.dw \/ 2, x2 = r.xd + r.dw \/ 2;\r\n    r.doors.forEach(function (d) { if (d.w === 'S' || d.w === 'E') s.push(hueco(d)); });\r\n    s.push('<path class=\"i-near\" stroke-width=\"' + f1(lw) + '\" stroke-dasharray=\"' + f1(6 * k) + ' ' + f1(4 * k) + '\" d=\"M' + pts([P(0, 0, H), P(W, 0, H), P(W, D, H)]) + 'M' + pts([P(W, 0, 0), P(W, 0, H)]) + '\"\/>');\r\n    s.push('<path class=\"i-near\" stroke-width=\"' + f1(1.6 * k) + '\" d=\"M' + pts([P(x1, 0, 0), P(x1, 0, hd), P(x2, 0, hd), P(x2, 0, 0)]) + '\"\/>');\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(11 * k) + '\" x=\"' + f1(vb[0] + 4 * k) + '\" y=\"' + f1(vb[1] + vb[3] - 6 * k) + '\">' + esc(r.name) + ' \u00b7 ' + mt(W) + ' \u00d7 ' + mt(D) + ' \u00d7 ' + mt(H) + ' m \u00b7 running level at ' + nf(nr) + ' mm<\/text>');\r\n    var t2 = P(r.xd, 0, 0);\r\n    s.push('<text class=\"lbl\" font-size=\"' + f1(11 * k) + '\" x=\"' + f1(t2[0] + 10 * k) + '\" y=\"' + f1(t2[1] + 16 * k) + '\">Door<\/text>');\r\n    s.push('<\/svg>');\r\n    return s.join('');\r\n  }\r\n\r\n  \/* ---------- hoja de plano ---------- *\/\r\n  function sheetHTML(r, an, bm, cw, pr) {\r\n    var hoy = new Date(), f = ('0' + hoy.getDate()).slice(-2) + '\/' + ('0' + (hoy.getMonth() + 1)).slice(-2) + '\/' + hoy.getFullYear();\r\n    var datos = [['Species', B.SP[r.sp].n], ['Width \u00d7 depth', nf(r.W) + ' \u00d7 ' + nf(r.D)], ['Clear height', nf(r.H)], ['Doors', 1 + r.doors.length], ['Running level', nf(an.nr)], ['Beam underside', nf(an.pilar)],\r\n      ['Net straight rail', mt(an.straight) + ' m'], ['90\u00b0 bends', an.count.curva], ['90\u00b0 single switches', an.count.desvio], ['45\u00b0 single switches', an.count.desvio45], ['Triple switches', an.count.triple], ['End stops', an.count.tope],\r\n      ['Brackets every', S.sil + ' cm'], ['Design load', B.SP[r.sp].kp + ' kp\/m']];\r\n    return '<div class=\"sheetw\"><div class=\"sheet\"><div class=\"dr\">' + planSVG(r, an, Math.max(cw - 236, 340), { sheet: true, maxH: pr ? 650 : 560 }) + '<\/div><div class=\"side\">' +\r\n      '<h3>Layout data<\/h3><table>' + datos.map(function (d) { return '<tr><td>' + d[0] + '<\/td><td>' + d[1] + '<\/td><\/tr>'; }).join('') + '<\/table>' +\r\n      '<h3>Project materials<\/h3><table>' + bm.lines.map(function (l) { return '<tr><td>' + esc(l.n) + '<br><span style=\"color:var(--muted)\">Ref. ' + l.sku + '<\/span><\/td><td>' + nf(l.q) + ' ' + l.u + '<\/td><\/tr>'; }).join('') + '<tr><td><b>Project amount, excl. VAT<\/b><\/td><td>' + eur(bm.sub) + '<\/td><\/tr><\/table>' +\r\n      '<div class=\"caj\"><div><small>Client<\/small>' + (pr ? '<b>' + esc(S.cliente) + '<\/b>' : '<input id=\"cj-cli\" aria-label=\"Client\" value=\"' + esc(S.cliente) + '\">') + '<\/div><div><small>Location<\/small>' + (pr ? '<b>' + esc(S.sitio) + '<\/b>' : '<input id=\"cj-sit\" aria-label=\"Location\" value=\"' + esc(S.sitio) + '\">') + '<\/div>' +\r\n      '<div class=\"w\"><small>Project<\/small><b>BIRRAIL overhead rail \u00b7 ' + esc(r.name) + '<\/b><\/div>' +\r\n      '<div><small>Date<\/small><b>' + f + '<\/b><\/div><div><small>Scale<\/small><b>Dimensions in mm<\/b><\/div>' +\r\n      '<div class=\"w\"><small>Made with the BIRRAIL designer by<\/small><span class=\"logo\">BERNAD\u00ae<\/span> \u00b7 +34 967 219 649 \u00b7 tiendabernad.com<\/div><\/div><\/div><\/div><\/div>';\r\n  }\r\n\r\n  \/* ---------- pintar ---------- *\/\r\n  var ICON = {\r\n    linea: '<path class=\"rm\" d=\"M20 4h80v32H20z\"\/><path class=\"tk\" d=\"M60 36V8\"\/>',\r\n    peine: '<path class=\"rm\" d=\"M20 4h80v32H20z\"\/><path class=\"tk\" d=\"M60 36V30M32 8V26a4 4 0 0 0 4 4H84a4 4 0 0 0 4-4V8M46 8V30M74 8V30\"\/>',\r\n    peinec: '<path class=\"rm\" d=\"M20 4h80v32H20z\"\/><path class=\"tk\" d=\"M60 36V30M36 30H84a4 4 0 0 0 4-4V12a4 4 0 0 0-4-4H36a4 4 0 0 0-4 4V26a4 4 0 0 0 4 4M46 8V30M74 8V30\"\/>',\r\n    malla: '<path class=\"rm\" d=\"M20 4h80v32H20z\"\/><path class=\"tk\" d=\"M60 36V30M36 30H84a4 4 0 0 0 4-4V12a4 4 0 0 0-4-4H36a4 4 0 0 0-4 4V26a4 4 0 0 0 4 4M32 19H88\"\/>',\r\n    libre: '<path class=\"rm\" d=\"M20 4h80v32H20z\"\/><path class=\"tk\" d=\"M60 36V24H40V10M60 24H82V12\" stroke-dasharray=\"5 4\"\/>'\r\n  };\r\n  var TN = { linea: ['Single line', 'From the door to the back'], peine: ['Comb', 'Lines running to the back'], malla: ['Grid', 'Lines across, reached from both sides'], libre: ['Freehand', 'Draw your own layout'] };\r\n\r\n  var last = {};\r\n  function setv(el, v) { if (root.activeElement !== el && el.value !== String(v)) el.value = v; }\r\n\r\n  function syncInputs(r, an) {\r\n    setv($('in-name'), r.name); setv($('in-w'), r.W \/ 1000); setv($('in-d'), r.D \/ 1000); setv($('in-h'), r.H \/ 1000);\r\n    setv($('in-xd'), r.xd \/ 1000); setv($('in-dw'), r.dw \/ 1000); setv($('in-ext'), r.ext \/ 1000);\r\n    setv($('in-nr'), an.nr); setv($('in-ncar'), an.nCar);\r\n    r.doors.forEach(function (d, j) { if ($('dp' + j)) { setv($('dp' + j), d.p \/ 1000); setv($('dd' + j), d.dw \/ 1000); $('dw' + j).value = d.w; } });\r\n  }\r\n\r\n  function render() {\r\n    var r = R(), i;\r\n    S.rooms.forEach(function (x) { x.sil = S.sil; });\r\n    var ans = S.rooms.map(function (x) { return B.analyse(x); }), an = ans[S.cur], bm = B.bom(S.rooms, ans, S.sil), sp = B.SP[r.sp];\r\n    last = { ans: ans, an: an, bm: bm };\r\n\r\n    \/\/ c\u00e1maras\r\n    $('rooms').innerHTML = S.rooms.map(function (x, j) {\r\n      return '<button type=\"button\" role=\"tab\" data-r=\"' + j + '\" aria-selected=\"' + (j === S.cur) + '\">' + esc(x.name) + (S.rooms.length > 1 ? ' <span class=\"x\" data-x=\"' + j + '\" title=\"Remove this room\">\u00d7<\/span>' : '') + '<\/button>';\r\n    }).join('') + (S.rooms.length < 8 ? '<button type=\"button\" class=\"add\" id=\"bt-add\">+ Add room<\/button>' : '') +\r\n      '<span class=\"eg\"><label for=\"in-eg\">Examples<\/label><select id=\"in-eg\"><option value=\"\">Load an example\u2026<\/option><option value=\"a\">Small pork cold room, 2.60 \u00d7 3.19 m<\/option><option value=\"b\">Beef cold room, 3.84 \u00d7 3.88 m<\/option><option value=\"c\">Chilling room, 8 \u00d7 12 m<\/option><option value=\"d\">Project with three rooms<\/option><\/select><\/span>';\r\n\r\n    \/\/ puertas adicionales: las filas solo se rehacen cuando cambia su n\u00famero, para no perder el foco al escribir\r\n    var dbox = $('doors'), dsig = S.cur + '\/' + r.doors.length;\r\n    if (dbox.getAttribute('data-s') !== dsig) {\r\n      dbox.setAttribute('data-s', dsig);\r\n      dbox.innerHTML = r.doors.map(function (d, j) {\r\n        return '<div class=\"door\" data-d=\"' + j + '\"><div class=\"dh\"><b>Door ' + (j + 2) + '<\/b><button type=\"button\" class=\"lnk\" data-dx=\"' + j + '\" aria-label=\"Remove door ' + (j + 2) + '\">Remove<\/button><\/div>' +\r\n          '<div class=\"f\"><label for=\"dw' + j + '\">Wall<\/label><select id=\"dw' + j + '\" data-dk=\"w\">' + ['N', 'W', 'E', 'S'].map(function (w) { return '<option value=\"' + w + '\">' + WN[w] + '<\/option>'; }).join('') + '<\/select><\/div>' +\r\n          '<div class=\"f\"><label for=\"dp' + j + '\">Axis<\/label><div class=\"ipt\"><input id=\"dp' + j + '\" data-dk=\"p\" type=\"number\" step=\"0.05\" min=\"0\" inputmode=\"decimal\"><\/div><\/div>' +\r\n          '<div class=\"f\"><label for=\"dd' + j + '\">Width<\/label><div class=\"ipt\"><input id=\"dd' + j + '\" data-dk=\"dw\" type=\"number\" step=\"0.05\" min=\"0.6\" max=\"4\" inputmode=\"decimal\"><\/div><\/div><\/div>';\r\n      }).join('');\r\n    }\r\n    $('bt-door').hidden = r.doors.length >= 5;\r\n    $('doors-note').hidden = !r.doors.length;\r\n\r\n    \/\/ datos\r\n    syncInputs(r, an);\r\n    $('in-xdr').value = Math.round((r.xd - r.dw \/ 2) \/ Math.max(r.W - r.dw, 1) * 100);\r\n    [].forEach.call($('in-sp').children, function (b) { b.setAttribute('aria-pressed', b.dataset.v === r.sp); });\r\n    [].forEach.call($('in-sil').children, function (b) { b.setAttribute('aria-pressed', b.dataset.v === S.sil); });\r\n    $('sil-info').textContent = B.CAT.via.p['50'] > 0 ? 'Every 50 cm for the heaviest loads: ' + eur(B.CAT.via.p['50']) + '\/m instead of ' + eur(B.CAT.via.p['66']) + '\/m.' : '';\r\n    $('sp-info').textContent = 'Lines every ' + sp.sep + ' mm \u00b7 ' + sp.kp + ' kp per metre \u00b7 ' + sp.densTxt + '.';\r\n\r\n    \/\/ plantillas\r\n    var cmp = B.compare(r), best = null;\r\n    cmp.forEach(function (c) { if (c.ok && (!best || c.inside > best.inside)) best = c; });\r\n    $('tpls').innerHTML = cmp.map(function (c) {\r\n      return '<button type=\"button\" class=\"tpl\" data-t=\"' + c.tpl + '\" aria-pressed=\"' + (r.tpl === c.tpl) + '\"' + (c.ok ? '' : ' disabled') + '>' + (c === best && cmp.filter(function (q) { return q.ok; }).length > 1 ? '<em>Most capacity<\/em>' : '') +\r\n        '<svg viewBox=\"0 0 120 40\" aria-hidden=\"true\">' + ICON[c.tpl === 'peine' && r.cierre ? 'peinec' : c.tpl] + '<\/svg><b>' + TN[c.tpl][0] + '<\/b><span>' + (c.ok ? mt(c.inside, 1) + ' m of storage rail' : 'Does not fit these dimensions') + '<\/span><\/button>';\r\n    }).join('') + '<button type=\"button\" class=\"tpl\" data-t=\"libre\" aria-pressed=\"' + (r.tpl === 'libre') + '\"><svg viewBox=\"0 0 120 40\" aria-hidden=\"true\">' + ICON.libre + '<\/svg><b>' + TN.libre[0] + '<\/b><span>' + TN.libre[1] + '<\/span><\/button>';\r\n    var showC = r.tpl === 'peine' && !an.fallback;\r\n    $('f-cierre').style.display = showC ? '' : 'none';\r\n    if (showC) {\r\n      [].forEach.call($('in-ci').children, function (b) { b.setAttribute('aria-pressed', (b.dataset.v === '1') === !!r.cierre); });\r\n      $('ci-info').textContent = !r.cierre ? 'Each line ends in an end stop.' : an.cierre ? 'The lines are joined at the back by a collector rail: trolleys can come back along another line.' : 'There is no circuit to close here: you need at least two lines and a little more depth.';\r\n    }\r\n    $('f-tri').style.display = an.cierres ? '' : 'none';\r\n    if (an.cierres) {\r\n      [].forEach.call($('in-tri').children, function (b) { b.setAttribute('aria-pressed', (b.dataset.v === '1') === !!r.tri); });\r\n      $('tri-info').textContent = r.tri ? 'Where a line closes the circuit there is a triple switch (' + eur(B.CAT.triple.p) + '): trolleys can turn both ways.' : 'Where a line closes the circuit there is a single switch (' + eur(B.CAT.desvio.p) + '), with its bend towards the entry side. With triple switches (' + eur(B.CAT.triple.p) + ') trolleys can turn both ways.';\r\n    }\r\n    var showL = (r.tpl === 'peine' || r.tpl === 'malla') && !an.fallback;\r\n    $('f-lines').style.display = showL ? '' : 'none';\r\n    if (showL) {\r\n      $('out-lines').textContent = an.n; $('lines-max').textContent = 'max. ' + an.max;\r\n      $('bt-lm').disabled = an.n <= (r.tpl === 'malla' ? 2 : 1); $('bt-lp').disabled = an.n >= an.max;\r\n    }\r\n\r\n    \/\/ montaje\r\n    $('nr-info').innerHTML = (r.nr ? 'Set by hand. <button type=\"button\" class=\"lnk\" id=\"bt-nrauto\">Back to recommended<\/button>' : 'Automatic.') + ' Recommended for ' + sp.n.toLowerCase() + ': ' + (sp.nrMin === sp.nrMax ? nf(sp.nrMin) : nf(sp.nrMin) + ' to ' + nf(sp.nrMax)) + ' mm.';\r\n    var sel = $('in-car'), auto = sp.carro ? 'By species: ' + B.CAT[sp.carro].n.toLowerCase() : 'No trolleys (choose a model)';\r\n    sel.innerHTML = '<option value=\"auto\">' + auto + '<\/option>' + B.CARROS.map(function (k) { return '<option value=\"' + k + '\">' + B.CAT[k].n + ' \u00b7 ' + eur(B.CAT[k].p) + '<\/option>'; }).join('') + '<option value=\"\">No trolleys<\/option>';\r\n    sel.value = r.carro;\r\n    $('in-ncar').disabled = !an.carroKey;\r\n    $('car-info').innerHTML = !an.carroKey ? 'Approximate capacity: ' + nf(an.cap) + ' ' + sp.ud + '.' : (r.nCar === null ? 'Automatic: one trolley per ' + (r.sp === 'ovino' ? 'metre of storage rail' : 'half carcass') + ' (' + nf(an.cap) + ' ' + sp.ud + ').' : 'Set by hand. <button type=\"button\" class=\"lnk\" id=\"bt-carauto\">Back to automatic<\/button>');\r\n\r\n    \/\/ vistas y herramientas\r\n    [].forEach.call($('views').children, function (b) { b.setAttribute('aria-selected', b.dataset.v === S.view); });\r\n    var cv = $('cv'), cw = Math.max(cv.clientWidth - 16, 260), edit = S.view === 'planta' && r.tpl === 'libre';\r\n    cv.className = 'cv' + (edit ? (S.tool === 'erase' ? ' erase' : ' edit') : '');\r\n    $('tools').innerHTML = S.view === 'plano' ? '<button type=\"button\" id=\"bt-pdf\">Download PDF<\/button>' : S.view !== 'planta' ? '' : edit ?\r\n      '<button type=\"button\" data-tool=\"draw\" aria-pressed=\"' + (S.tool === 'draw') + '\">Draw<\/button><button type=\"button\" data-tool=\"erase\" aria-pressed=\"' + (S.tool === 'erase') + '\">Erase section<\/button><button type=\"button\" id=\"bt-undo\"' + (S.undo.length ? '' : ' disabled') + '>Undo<\/button>' :\r\n      '<button type=\"button\" id=\"bt-edit\">Edit the layout by hand<\/button>';\r\n    cv.innerHTML = S.view === 'planta' ? planSVG(r, an, cw, { erase: edit && S.tool === 'erase' }) : S.view === 'alzado' ? altSVG(r, an, cw) : S.view === 'iso' ? isoSVG(r, an, cw) : sheetHTML(r, an, bm, Math.max(cw, 600));\r\n    $('hint').textContent = S.view === 'planta' ? (edit ? (S.tool === 'erase' ? 'Click a section to remove it.' : 'Drag on the plan to draw a straight section. It snaps to 100 mm and to existing lines; bends and switches are placed automatically. A short section between two close lines is built as a 45\u00b0 crossover.') : 'Drag the blue handle to move the door. Hover over a part to see its price.') :\r\n      S.view === 'alzado' ? 'Schematic elevation with the real mounting dimensions: 110 mm between the running level and the beam underside.' :\r\n        S.view === 'iso' ? (an.nCar ? 'Schematic view with ' + nf(Math.min(an.nCar, 320)) + ' loaded trolleys.' : 'Schematic view of the rail at its height.') : 'Fill in client and location and download the sheet as a PDF: one per room, with the project parts list.';\r\n    var c = an.count;\r\n    $('legend').innerHTML = S.view === 'alzado' ? '' : '<span><i style=\"background:var(--navy)\"><\/i>Straight rail \u00b7 ' + mt(an.straight) + ' m<\/span>' + (c.curva ? '<span><i style=\"background:var(--blue)\"><\/i>' + pl(c.curva, '90\u00b0 bend', '90\u00b0 bends') + '<\/span>' : '') + (c.desvio ? '<span><i style=\"background:var(--orange)\"><\/i>' + pl(c.desvio, 'single switch', 'single switches') + '<\/span>' : '') + (c.desvio45 ? '<span><i style=\"background:var(--orange)\"><\/i>' + pl(c.desvio45, '45\u00b0 switch', '45\u00b0 switches') + '<\/span>' : '') + (c.triple ? '<span><i style=\"background:var(--teal)\"><\/i>' + pl(c.triple, 'triple switch', 'triple switches') + '<\/span>' : '') + (c.tope ? '<span><i style=\"background:var(--err)\"><\/i>' + pl(c.tope, 'end stop', 'end stops') + '<\/span>' : '');\r\n\r\n    \/\/ comprobaciones\r\n    var ord = { err: 0, warn: 1, ok: 2 }, sym = { err: '!', warn: '!', ok: '\u2713' };\r\n    $('checks').innerHTML = an.issues.slice().sort(function (a, b) { return ord[a.lvl] - ord[b.lvl]; }).map(function (q) {\r\n      return '<li><span class=\"ic ' + q.lvl + '\" aria-hidden=\"true\">' + sym[q.lvl] + '<\/span><span><b>' + esc(q.t) + '<\/b>' + (q.d ? '<small>' + esc(q.d) + '<\/small>' : '') + '<\/span><\/li>';\r\n    }).join('');\r\n    $('chk-pill').innerHTML = an.errs ? '<span class=\"pill err\">' + pl(an.errs, 'error', 'errors') + '<\/span>' : an.warns ? '<span class=\"pill warn\">' + pl(an.warns, 'warning', 'warnings') + '<\/span>' : '<span class=\"pill ok\">All correct<\/span>';\r\n\r\n    \/\/ despiece\r\n    var nR = S.rooms.length, errs = ans.reduce(function (t, a) { return t + a.errs; }, 0);\r\n    $('tot-lb').textContent = nR > 1 ? 'Project total \u00b7 ' + nR + ' rooms' : 'Installation total';\r\n    $('tot-big').innerHTML = eur(bm.sub) + ' <span>+ VAT<\/span>';\r\n    $('tot-sub').textContent = eur(bm.total) + ' incl. VAT (21%)';\r\n    var capT = ans.reduce(function (t, a) { return t + a.cap; }, 0), kpT = ans.reduce(function (t, a) { return t + a.carga; }, 0);\r\n    var mix = S.rooms.some(function (x) { return x.sp !== r.sp; });\r\n    $('kpi').innerHTML = '<li><b class=\"num\">' + nf(bm.metros) + ' m<\/b><span>of straight rail: ' + pl(bm.barras, 'bar', 'bars') + ' of 4 m for ' + mt(bm.neto * 1000) + ' m net<\/span><\/li>' +\r\n      '<li><b class=\"num\">' + nf(capT) + '<\/b><span>' + (mix ? 'carcasses and half carcasses' : sp.ud) + ', approximate capacity<\/span><\/li>' +\r\n      '<li><b class=\"num\">' + nf(kpT) + ' kp<\/b><span>design load for the structure<\/span><\/li>' +\r\n      '<li><b class=\"num\">' + nf(an.nr) + ' mm<\/b><span>running level' + (nR > 1 ? ' in ' + esc(r.name) : '') + '<\/span><\/li>';\r\n    var grp = {}; an.runs.forEach(function (v) { grp[v] = (grp[v] || 0) + 1; });\r\n    var tramos = Object.keys(grp).sort(function (a, b) { return b - a; }).slice(0, 8).map(function (v) { return grp[v] + ' \u00d7 ' + mt(+v) + ' m'; }).join(' \u00b7 ');\r\n    $('tec-dl').innerHTML = [['Net straight rail' + (nR > 1 ? ' (this room)' : ''), mt(an.straight) + ' m'], ['Straight sections', tramos || '\u2014'], ['Brackets included, approx.', nf(an.silletas)], ['M8 fixing bolts', nf(an.m8)],\r\n      ['Beam underside and column height', nf(an.pilar) + ' mm'], ['Minimum clear height', nf(an.hNec) + ' mm'], ['Design load', sp.kp + ' kp\/m'], ['Maximum beam span', nf(4000) + ' mm']]\r\n      .map(function (d) { return '<dt>' + d[0] + '<\/dt><dd>' + d[1] + '<\/dd>'; }).join('');\r\n    $('bom').innerHTML = bm.lines.map(function (l) {\r\n      return '<tr><td class=\"num\">' + nf(l.q) + ' ' + l.u + '<\/td><td><a href=\"' + esc(X.u[l.k] || '#') + '\" target=\"_blank\" rel=\"noopener\">' + esc(l.n) + '<\/a><small>Ref. ' + l.sku + ' \u00b7 ' + eur(l.p) + (l.u === 'm' ? '\/m' : '') + '<\/small><\/td><td class=\"num\">' + eur(l.t) + '<\/td><\/tr>';\r\n    }).join('') || '<tr><td colspan=\"3\">Draw a layout to see the parts list.<\/td><\/tr>';\r\n    $('bom-f').innerHTML = '<tr><td colspan=\"2\">Subtotal excl. VAT<\/td><td class=\"num\">' + eur(bm.sub) + '<\/td><\/tr><tr><td colspan=\"2\">VAT 21%<\/td><td class=\"num\">' + eur(bm.iva) + '<\/td><\/tr><tr class=\"g\"><td colspan=\"2\">Total<\/td><td class=\"num\">' + eur(bm.total) + '<\/td><\/tr>';\r\n    var nov = bm.lines.filter(function (l) { return X.ok[l.k] === false || !(l.p > 0); }).length;\r\n    var cta = $('bt-cart'); cta.disabled = !!errs || !bm.lines.length || !!nov || busy;\r\n    cta.textContent = errs ? 'Fix the errors to buy' : nov ? 'Ask for a quote on WhatsApp' : 'Add everything to the cart \u00b7 ' + pl(bm.lines.length, 'line', 'lines');\r\n    if (sig !== bm.lines.map(function (l) { return l.k + l.q; }).join() + S.sil) { sig = bm.lines.map(function (l) { return l.k + l.q; }).join() + S.sil; $('cart-msg').hidden = true; }\r\n    $('bar-tot').textContent = eur(bm.sub); $('bar-sub').textContent = '+ VAT \u00b7 ' + nf(bm.metros) + ' m of rail';\r\n    $('bt-cart2').disabled = cta.disabled;\r\n\r\n    \/\/ WhatsApp y c\u00f3digo\r\n    var code = encode();\r\n    var msg = 'Hello, I have designed a BIRRAIL overhead rail system on your website and would like you to review it.\\n' + S.rooms.map(function (x, j) {\r\n      var a = ans[j]; return '\u00b7 ' + x.name + ': ' + B.SP[x.sp].n.toLowerCase() + ', ' + mt(x.W) + ' \u00d7 ' + mt(x.D) + ' \u00d7 ' + mt(x.H) + ' m, ' + mt(a.straight) + ' m of rail, ' + a.count.curva + ' bends, ' + (a.count.desvio + a.count.desvio45 + a.count.triple) + ' switches';\r\n    }).join('\\n') + '\\nTotal: ' + eur(bm.sub) + ' + VAT\\nDesign code: ' + code;\r\n    $('bt-wa').href = 'https:\/\/wa.me\/' + (X.wa || '34670863794') + '?text=' + encodeURIComponent(msg + '\\n' + location.href.split('#')[0] + '#' + code);\r\n    try { history.replaceState(null, '', '#' + code); } catch (e) { \/* sin historial en este marco *\/ }\r\n  }\r\n\r\n  \/* ---------- acciones ---------- *\/\r\n  var tt, busy = false, sig = '';\r\n  function addCart() {\r\n    if (busy || !X.ajax) return;\r\n    var bm = last.bm, el = $('cart-msg'), btn = $('bt-cart');\r\n    busy = true; btn.disabled = true; $('bt-cart2').disabled = true; btn.textContent = 'Adding\u2026';\r\n    fetch(X.ajax, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application\/x-www-form-urlencoded; charset=UTF-8' },\r\n      body: 'items=' + encodeURIComponent(bm.lines.map(function (l) { return l.k + ':' + l.q; }).join(',')) + '&sil=' + S.sil + '&code=' + encodeURIComponent(encode()) })\r\n      .then(function (res) { return res.json(); })\r\n      .then(function (j) {\r\n        if (!j || !j.ok) throw { bxr: true, message: (j && j.msg) || '' };\r\n        el.removeAttribute('data-err'); el.textContent = (j.msg || 'Added to the cart.') + ' ';\r\n        var a = document.createElement('a'); a.href = j.cart || X.cart || '#'; a.textContent = 'View cart'; el.appendChild(a);\r\n        if (window.jQuery) window.jQuery(document.body).trigger('wc_fragment_refresh');\r\n      })\r\n      .catch(function (err) { el.setAttribute('data-err', '1'); el.textContent = (err && err.bxr && err.message) || 'Could not add to the cart. Please try again or message us on WhatsApp.'; })\r\n      .then(function () { busy = false; render(); el.hidden = false; if (el.scrollIntoView && innerWidth <= 820) el.scrollIntoView({ block: 'center' }); });\r\n  }\r\n  function printPlan() {\r\n    var w = window.open('', '_blank');\r\n    if (!w) { toast('Allow pop-ups for this page to download the drawing.'); return; }\r\n    var css = root.querySelector('style').textContent;\r\n    var html = S.rooms.map(function (r, j) { return sheetHTML(r, last.ans[j], last.bm, 1040, true); }).join('<div class=\"pb\"><\/div>');\r\n    w.document.write('<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>BIRRAIL overhead rail drawing \u00b7 Bernad<\/title><sty' + 'le>' + css +\r\n      '@page{size:A4 landscape;margin:8mm}html,body{margin:0;background:#fff}.app{background:#fff;padding:0;border-radius:0}.sheetw{overflow:visible}.sheet{margin:0;min-width:0;page-break-inside:avoid;grid-template-columns:minmax(0,1fr) 250px}.sheet .dr svg{max-width:100%;height:auto}.pb{page-break-after:always;height:0}' +\r\n      '<\/sty' + 'le><\/head><body class=\"app\">' + html + '<\/body><\/html>');\r\n    w.document.close(); w.focus(); setTimeout(function () { try { w.print(); } catch (e) { \/* el usuario puede imprimir desde el men\u00fa *\/ } }, 400);\r\n  }\r\n  function toast(t) { var el = $('toast'); el.textContent = t; el.classList.add('on'); clearTimeout(tt); tt = setTimeout(function () { el.classList.remove('on'); }, 4200); }\r\n  function copy(text, okMsg) {\r\n    try { navigator.clipboard.writeText(text).then(function () { toast(okMsg); }, function () { toast('Could not copy.'); }); }\r\n    catch (e) { toast('Could not copy.'); }\r\n  }\r\n  function pushUndo() { S.undo.push(JSON.stringify(R().segs || [])); if (S.undo.length > 40) S.undo.shift(); }\r\n  function toLibre() {\r\n    var r = R(), an = last.an;\r\n    r.segs = an.edges.map(function (e) { return [e.a.x, e.a.y, e.b.x, e.b.y]; });\r\n    r.tpl = 'libre'; S.undo = []; S.tool = 'draw';\r\n  }\r\n  function num(el, lo, hi, def) { var v = parseFloat(String(el.value).replace(',', '.')); return isFinite(v) ? clamp(v, lo, hi) : def; }\r\n\r\n  function onInput(e) {\r\n    var r = R(), id = e.target.id, dk = e.target.getAttribute('data-dk'), row = dk && e.target.closest('.door'), dr = row && r.doors[+row.getAttribute('data-d')];\r\n    if (dr) {\r\n      if (dk === 'w') { dr.w = e.target.value; dr.p = dr.w === 'N' || dr.w === 'S' ? r.xd : r10(r.D \/ 2); if (dr.w === 'S') dr.p = r.xd > r.W \/ 2 ? r10(r.xd \/ 2) : r10((r.xd + r.W) \/ 2); }\r\n      else if (dk === 'p') dr.p = r10(num(e.target, 0, 60, dr.p \/ 1000) * 1000);\r\n      else dr.dw = r10(num(e.target, 0.6, 4, dr.dw \/ 1000) * 1000);\r\n      r.nCar = null;\r\n    }\r\n    else if (id === 'in-name') r.name = e.target.value || 'Room ' + (S.cur + 1);\r\n    else if (id === 'in-w') { var c = r.xd === r10(r.W \/ 2); r.W = r10(num(e.target, 1.2, 40, r.W \/ 1000) * 1000); if (c) r.xd = r10(r.W \/ 2); }\r\n    else if (id === 'in-d') r.D = r10(num(e.target, 1.5, 60, r.D \/ 1000) * 1000);\r\n    else if (id === 'in-h') r.H = r10(num(e.target, 2, 8, r.H \/ 1000) * 1000);\r\n    else if (id === 'in-xd') r.xd = r10(num(e.target, 0, 40, r.xd \/ 1000) * 1000);\r\n    else if (id === 'in-xdr') r.xd = Math.round((r.dw \/ 2 + (r.W - r.dw) * e.target.value \/ 100) \/ 50) * 50;\r\n    else if (id === 'in-dw') r.dw = r10(num(e.target, 0.6, 4, r.dw \/ 1000) * 1000);\r\n    else if (id === 'in-ext') { var old = r.ext; r.ext = Math.round(num(e.target, 0, 30, 0) * 10) * 100; if (r.tpl === 'libre' && r.segs) r.segs = r.segs.map(function (s) { return s.map(function (v, j) { return j % 2 && v === -old && s[0] === s[2] ? -r.ext : v; }); }); }\r\n    else if (id === 'in-nr') r.nr = r10(num(e.target, 1800, 6000, 0));\r\n    else if (id === 'in-ncar') r.nCar = Math.round(num(e.target, 0, 5000, 0));\r\n    else if (id === 'in-car') { r.carro = e.target.value; r.nCar = null; }\r\n    else if (id === 'cj-cli') { S.cliente = e.target.value; return; }\r\n    else if (id === 'cj-sit') { S.sitio = e.target.value; return; }\r\n    else if (id === 'in-eg') { if (EJ[e.target.value]) { S.rooms = EJ[e.target.value](); S.cur = 0; S.undo = []; S.view = 'planta'; } }\r\n    else return;\r\n    r = R(); r.dw = Math.min(r.dw, r.W); r.xd = clamp(r.xd, r.dw \/ 2, r.W - r.dw \/ 2); fixDoors(r);\r\n    render();\r\n  }\r\n\r\n  function onClick(e) {\r\n    var t = e.target, r = R(), b;\r\n    if ((b = t.closest('[data-x]'))) { S.rooms.splice(+b.dataset.x, 1); S.cur = Math.min(S.cur, S.rooms.length - 1); S.undo = []; }\r\n    else if ((b = t.closest('[data-r]'))) { S.cur = +b.dataset.r; S.undo = []; }\r\n    else if (t.id === 'bt-add') { S.rooms.push(room({ name: 'Room ' + (S.rooms.length + 1), sp: r.sp })); S.cur = S.rooms.length - 1; S.undo = []; }\r\n    else if ((b = t.closest('#in-sp button'))) { r.sp = b.dataset.v; r.nr = 0; r.nCar = null; if (r.carro !== '') r.carro = 'auto'; }\r\n    else if ((b = t.closest('#in-sil button'))) S.sil = b.dataset.v;\r\n    else if ((b = t.closest('.tpl'))) { if (b.disabled) return; if (b.dataset.t === 'libre') { if (r.tpl !== 'libre') toLibre(); S.view = 'planta'; } else { r.tpl = b.dataset.t; r.nLines = 0; r.nCar = null; } }\r\n    else if (t.id === 'bt-lm' || t.id === 'bt-lp') { var n = last.an.n + (t.id === 'bt-lp' ? 1 : -1); r.nLines = n >= last.an.max ? 0 : n; r.nCar = null; }\r\n    else if (t.id === 'bt-center') r.xd = r10(r.W \/ 2);\r\n    else if ((b = t.closest('#in-ci button'))) { r.cierre = b.dataset.v === '1'; r.nCar = null; }\r\n    else if ((b = t.closest('#in-tri button'))) { r.tri = b.dataset.v === '1'; r.nCar = null; }\r\n    else if (t.id === 'bt-door') {\r\n      if (r.doors.length >= 5) return;\r\n      var us = r.doors.map(function (d) { return d.w; }), w = us.indexOf('N') < 0 ? 'N' : us.indexOf('W') < 0 ? 'W' : us.indexOf('E') < 0 ? 'E' : 'N';\r\n      r.doors.push({ w: w, p: w === 'N' ? r.xd : r10(r.D \/ 2), dw: r.dw }); fixDoors(r); r.nCar = null;\r\n    }\r\n    else if ((b = t.closest('[data-dx]'))) { r.doors.splice(+b.getAttribute('data-dx'), 1); r.nCar = null; }\r\n    else if (t.id === 'bt-nrauto') r.nr = 0;\r\n    else if (t.id === 'bt-carauto') r.nCar = null;\r\n    else if ((b = t.closest('#views button'))) S.view = b.dataset.v;\r\n    else if (t.id === 'bt-edit') toLibre();\r\n    else if ((b = t.closest('[data-tool]'))) S.tool = b.dataset.tool;\r\n    else if (t.id === 'bt-undo') { if (S.undo.length) r.segs = JSON.parse(S.undo.pop()); }\r\n    else if ((b = t.closest('.hit'))) { pushUndo(); var idx = +b.getAttribute('data-e'); r.segs = last.an.edges.filter(function (_, j) { return j !== idx; }).map(function (q) { return [q.a.x, q.a.y, q.b.x, q.b.y]; }); r.nCar = null; }\r\n    else if (t.id === 'bt-cart' || t.id === 'bt-cart2') { addCart(); return; }\r\n    else if (t.id === 'bt-pdf' || t.id === 'bt-pdf2') { printPlan(); return; }\r\n    else if (t.id === 'bt-copy') {\r\n      copy('BIRRAIL overhead rail \u00b7 parts list\\n' + last.bm.lines.map(function (l) { return nf(l.q) + ' ' + l.u + '\\tRef. ' + l.sku + '\\t' + l.n + '\\t' + eur(l.p) + '\\t' + eur(l.t); }).join('\\n') + '\\nSubtotal excl. VAT\\t' + eur(last.bm.sub) + '\\nVAT 21%\\t' + eur(last.bm.iva) + '\\nTotal\\t' + eur(last.bm.total), 'Parts list copied.'); return;\r\n    }\r\n    else if (t.id === 'bt-link') { copy(location.href.split('#')[0] + '#' + encode(), 'Link copied: it opens this same design.'); return; }\r\n    else return;\r\n    render();\r\n  }\r\n\r\n  \/* ---------- puntero: mover la puerta y dibujar ---------- *\/\r\n  var drag = null;\r\n  function mm(ev) {\r\n    var svg = $('cv').querySelector('svg'), p = svg.createSVGPoint(); p.x = ev.clientX; p.y = ev.clientY;\r\n    var q = p.matrixTransform(svg.getScreenCTM().inverse()); return { x: q.x, y: R().D - q.y, svg: svg };\r\n  }\r\n  function snap(p) {\r\n    var r = R(), x = Math.round(p.x \/ 100) * 100, y = Math.round(p.y \/ 100) * 100, bx = 130, by = 130;\r\n    var dx = [r.xd], dy = [0, -r.ext];\r\n    r.doors.forEach(function (d) { if (d.w === 'N' || d.w === 'S') { dx.push(d.p); dy.push(d.w === 'N' ? r.D : 0); } else { dy.push(d.p); dx.push(d.w === 'E' ? r.W : 0); } });\r\n    dx.concat(last.an.nodes.map(function (n) { return n.x; })).forEach(function (v) { if (Math.abs(p.x - v) < bx) { bx = Math.abs(p.x - v); x = v; } });\r\n    dy.concat(last.an.nodes.map(function (n) { return n.y; })).forEach(function (v) { if (Math.abs(p.y - v) < by) { by = Math.abs(p.y - v); y = v; } });\r\n    return { x: x, y: y };\r\n  }\r\n  function down(ev) {\r\n    if (S.view !== 'planta') return;\r\n    var r = R();\r\n    var hd = ev.target.closest && ev.target.closest('.handle');\r\n    if (hd) {\r\n      drag = { k: 'door', h: hd.getAttribute('data-h'), t: [] };\r\n      var dd = r.doors[+drag.h.slice(1)];\r\n      if (dd) { \/\/ la puerta se ajusta a las l\u00edneas y colectores que tiene enfrente\r\n        var c0 = {}; for (var kk in r) c0[kk] = r[kk]; c0.doors = [];\r\n        var ver = dd.w === 'N' || dd.w === 'S';\r\n        B.generate(c0).forEach(function (sg) { if (ver ? sg[0] === sg[2] : sg[1] === sg[3]) drag.t.push(ver ? sg[0] : sg[1]); });\r\n      }\r\n      ev.preventDefault(); return;\r\n    }\r\n    if (r.tpl !== 'libre' || S.tool !== 'draw' || !ev.target.closest('svg')) return;\r\n    var p = mm(ev), a = snap(p), ln = document.createElementNS('http:\/\/www.w3.org\/2000\/svg', 'path');\r\n    ln.setAttribute('class', 'prev'); ln.setAttribute('stroke-width', f1(3 * p.svg.viewBox.baseVal.width \/ p.svg.clientWidth));\r\n    p.svg.appendChild(ln); drag = { k: 'draw', a: a, b: a, ln: ln }; ev.preventDefault();\r\n  }\r\n  function move(ev) {\r\n    if (!drag) return;\r\n    var r = R(), p = mm(ev);\r\n    if (drag.k === 'door' && drag.h !== 'door') {\r\n      var d = r.doors[+drag.h.slice(1)]; if (!d) return;\r\n      var raw = d.w === 'N' || d.w === 'S' ? p.x : p.y, L = wallLen(r, d), val = Math.round(raw \/ 50) * 50;\r\n      drag.t.forEach(function (c) { if (Math.abs(raw - c) < 90) val = c; });\r\n      d.p = clamp(val, d.dw \/ 2, L - d.dw \/ 2); r.nCar = null; render(); return;\r\n    }\r\n    if (drag.k === 'door') { r.xd = clamp(Math.round(p.x \/ 50) * 50, r.dw \/ 2, r.W - r.dw \/ 2); if (Math.abs(r.xd - r.W \/ 2) < 80) r.xd = r10(r.W \/ 2); render(); return; }\r\n    var b = snap(p);\r\n    if (Math.abs(b.x - drag.a.x) >= Math.abs(b.y - drag.a.y)) b.y = drag.a.y; else b.x = drag.a.x;\r\n    drag.b = b; drag.ln.setAttribute('d', 'M' + drag.a.x + ' ' + (r.D - drag.a.y) + 'L' + b.x + ' ' + (r.D - b.y));\r\n  }\r\n  function up() {\r\n    if (!drag) return;\r\n    var d = drag, r = R(); drag = null;\r\n    if (d.k === 'draw' && Math.abs(d.b.x - d.a.x) + Math.abs(d.b.y - d.a.y) >= 300) { pushUndo(); r.segs = (r.segs || []).concat([[d.a.x, d.a.y, d.b.x, d.b.y]]); r.nCar = null; }\r\n    render();\r\n  }\r\n\r\n  \/* ---------- arranque ---------- *\/\r\n  function init() {\r\n    try { if (!decode(location.hash) && X.init) decode(X.init); } catch (e) { \/* sin hash *\/ }\r\n    root.addEventListener('input', onInput);\r\n    root.addEventListener('focusout', function (e) { if (\/^(in-|d[wpd]\\d)\/.test(e.target.id || '') && last.an) setTimeout(function () { syncInputs(R(), last.an); }, 0); });\r\n    root.addEventListener('click', onClick);\r\n    $('cv').addEventListener('pointerdown', down);\r\n    window.addEventListener('pointermove', move);\r\n    window.addEventListener('pointerup', up);\r\n    window.addEventListener('pointercancel', up);\r\n    var rz; window.addEventListener('resize', function () { clearTimeout(rz); rz = setTimeout(render, 120); });\r\n    window.addEventListener('hashchange', function () { if (decode(location.hash)) render(); });\r\n    \/\/ en m\u00f3vil la tienda tiene una barra fija abajo: la barra del total se coloca encima\r\n    var setBot = function () { var mb = document.getElementById('bernad-mbar'), on = mb && mb.offsetHeight && getComputedStyle(mb).position === 'fixed'; host.style.setProperty('--bxr-bot', (on ? mb.offsetHeight : 0) + 'px'); };\r\n    setBot(); window.addEventListener('resize', setBot); setTimeout(setBot, 1500);\r\n    render();\r\n  }\r\n  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init); else init();\r\n})();<\/script>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":4922,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"100-width.php","meta":{"_joinchat":[],"footnotes":""},"class_list":["post-418841","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria<\/title>\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.tiendabernad.com\/en\/birrail-overhead-rail-designer\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/\" \/>\n<meta property=\"og:site_name\" content=\"Tienda Jose Bernad | Equipamiento Industria Alimentaria\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/JoseBernad.SL\/\" \/>\n<meta property=\"article:modified_time\" content=\"2026-11-15T10:23:46+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@josebernadsl\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/birrail-overhead-rail-designer\\\/\",\"url\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/birrail-overhead-rail-designer\\\/\",\"name\":\"BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#website\"},\"datePublished\":\"2026-11-15T10:22:29+00:00\",\"dateModified\":\"2026-11-15T10:23:46+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/birrail-overhead-rail-designer\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/birrail-overhead-rail-designer\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/birrail-overhead-rail-designer\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Portada\",\"item\":\"https:\\\/\\\/www.tiendabernad.com\\\/en\\\/home\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"BIRRAIL overhead rail designer\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#website\",\"url\":\"https:\\\/\\\/www.tiendabernad.com\\\/\",\"name\":\"Tienda Jose Bernad | Equipamiento Industria Alimentaria\",\"description\":\"Empresa especializada en el suministro de maquinaria para la industria alimentaria y hosteler\u00eda\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.tiendabernad.com\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":[\"Organization\",\"Store\"],\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#organization\",\"name\":\"Tienda Jos\u00e9 Bernad\",\"url\":\"https:\\\/\\\/www.tiendabernad.com\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.tiendabernad.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/logo-tienda-bernad.png\",\"contentUrl\":\"https:\\\/\\\/www.tiendabernad.com\\\/wp-content\\\/uploads\\\/2019\\\/11\\\/logo-tienda-bernad.png\",\"width\":350,\"height\":129,\"caption\":\"Tienda Jos\u00e9 Bernad\"},\"image\":{\"@id\":\"https:\\\/\\\/www.tiendabernad.com\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/www.facebook.com\\\/JoseBernad.SL\\\/\",\"https:\\\/\\\/x.com\\\/josebernadsl\",\"https:\\\/\\\/www.instagram.com\\\/josebernad.sl\\\/\",\"https:\\\/\\\/www.linkedin.com\\\/company\\\/josebernadsl\\\/\",\"https:\\\/\\\/www.pinterest.com\\\/josebernadsl\\\/\",\"https:\\\/\\\/www.youtube.com\\\/@Jos\u00e9BernadS.L\"],\"legalName\":\"JOS\u00c9 BERNAD SL\",\"telephone\":\"+34 967 219 649\",\"email\":\"info@tiendabernad.com\",\"foundingDate\":\"1987\",\"address\":{\"@type\":\"PostalAddress\",\"streetAddress\":\"Parque Empresarial Campollano, Avda. Adeca 17\",\"postalCode\":\"02007\",\"addressLocality\":\"Albacete\",\"addressRegion\":\"Albacete\",\"addressCountry\":\"ES\"},\"openingHoursSpecification\":[{\"@type\":\"OpeningHoursSpecification\",\"dayOfWeek\":[\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\"],\"opens\":\"07:00\",\"closes\":\"15:15\"}]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria","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.tiendabernad.com\/en\/birrail-overhead-rail-designer\/","og_locale":"en_US","og_type":"article","og_title":"BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria","og_url":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/","og_site_name":"Tienda Jose Bernad | Equipamiento Industria Alimentaria","article_publisher":"https:\/\/www.facebook.com\/JoseBernad.SL\/","article_modified_time":"2026-11-15T10:23:46+00:00","twitter_card":"summary_large_image","twitter_site":"@josebernadsl","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/","url":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/","name":"BIRRAIL overhead rail designer - Tienda Jose Bernad | Equipamiento Industria Alimentaria","isPartOf":{"@id":"https:\/\/www.tiendabernad.com\/#website"},"datePublished":"2026-11-15T10:22:29+00:00","dateModified":"2026-11-15T10:23:46+00:00","breadcrumb":{"@id":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/www.tiendabernad.com\/en\/birrail-overhead-rail-designer\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Portada","item":"https:\/\/www.tiendabernad.com\/en\/home\/"},{"@type":"ListItem","position":2,"name":"BIRRAIL overhead rail designer"}]},{"@type":"WebSite","@id":"https:\/\/www.tiendabernad.com\/#website","url":"https:\/\/www.tiendabernad.com\/","name":"Tienda Jose Bernad | Equipamiento Industria Alimentaria","description":"Empresa especializada en el suministro de maquinaria para la industria alimentaria y hosteler\u00eda","publisher":{"@id":"https:\/\/www.tiendabernad.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.tiendabernad.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":["Organization","Store"],"@id":"https:\/\/www.tiendabernad.com\/#organization","name":"Tienda Jos\u00e9 Bernad","url":"https:\/\/www.tiendabernad.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.tiendabernad.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.tiendabernad.com\/wp-content\/uploads\/2019\/11\/logo-tienda-bernad.png","contentUrl":"https:\/\/www.tiendabernad.com\/wp-content\/uploads\/2019\/11\/logo-tienda-bernad.png","width":350,"height":129,"caption":"Tienda Jos\u00e9 Bernad"},"image":{"@id":"https:\/\/www.tiendabernad.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/JoseBernad.SL\/","https:\/\/x.com\/josebernadsl","https:\/\/www.instagram.com\/josebernad.sl\/","https:\/\/www.linkedin.com\/company\/josebernadsl\/","https:\/\/www.pinterest.com\/josebernadsl\/","https:\/\/www.youtube.com\/@Jos\u00e9BernadS.L"],"legalName":"JOS\u00c9 BERNAD SL","telephone":"+34 967 219 649","email":"info@tiendabernad.com","foundingDate":"1987","address":{"@type":"PostalAddress","streetAddress":"Parque Empresarial Campollano, Avda. Adeca 17","postalCode":"02007","addressLocality":"Albacete","addressRegion":"Albacete","addressCountry":"ES"},"openingHoursSpecification":[{"@type":"OpeningHoursSpecification","dayOfWeek":["Monday","Tuesday","Wednesday","Thursday","Friday"],"opens":"07:00","closes":"15:15"}]}]}},"_links":{"self":[{"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/pages\/418841","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/users\/4922"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/comments?post=418841"}],"version-history":[{"count":1,"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/pages\/418841\/revisions"}],"predecessor-version":[{"id":418842,"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/pages\/418841\/revisions\/418842"}],"wp:attachment":[{"href":"https:\/\/www.tiendabernad.com\/en\/wp-json\/wp\/v2\/media?parent=418841"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}