<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.pandoraburo.ru/" xml:lang="ru">
  <id>https://www.pandoraburo.ru/tumby-shvartovye-drugoy-variant-naglyadnosti</id>
  <link href="https://www.pandoraburo.ru/tumby-shvartovye-drugoy-variant-naglyadnosti.xml" rel="self"/>
  <title>Тумбы швартовые другой вариант наглядности</title>
  <subtitle type="html">
&lt;html lang="ru"&gt;
&lt;head&gt;
&lt;meta charset="UTF-8"&gt;
&lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&gt;
&lt;title&gt;ТСО · Калькулятор литья · Маржа · PRO&lt;/title&gt;
&lt;style&gt;
  :root{--gold:#FFD700;--gold-light:#FFE55C;--gold-deep:#B8860B;--bg:#0a0a0a;--muted:#9a8040;--border:rgba(212,175,55,.35);--green:#7dff7d;--red:#ff8a7a;--orange:#ffb060;}
  *{box-sizing:border-box;margin:0;padding:0}
  body{background:#050505;color:#e8e8e8;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif;padding:20px 14px;line-height:1.5;}
  .wrap{max-width:1800px;margin:0 auto}

  .header{background:radial-gradient(120% 120% at 20% 0%,rgba(255,215,0,.25),transparent 55%),linear-gradient(135deg,#1a1408,#0a0a0a);border:2px solid var(--gold);border-radius:14px;padding:28px 24px;text-align:center;margin-bottom:16px;box-shadow:0 0 40px rgba(255,215,0,.25);}
  .tag{display:inline-block;background:var(--gold);color:#0a0a0a;font-size:10px;font-weight:800;letter-spacing:2px;padding:4px 14px;border-radius:999px;margin-bottom:14px;box-shadow:0 0 16px rgba(255,215,0,.7);}
  .header h1{font-size:28px;font-weight:900;letter-spacing:3px;color:var(--gold);text-transform:uppercase;margin-bottom:12px;text-shadow:0 0 10px rgba(255,215,0,.95),0 0 25px rgba(255,199,0,.8);animation:goldPulse 3s ease-in-out infinite;}
  @keyframes goldPulse{0%,100%{text-shadow:0 0 10px rgba(255,215,0,.95),0 0 25px rgba(255,199,0,.8);}50%{text-shadow:0 0 15px rgba(255,215,0,1),0 0 35px rgba(255,199,0,.95);}}
  .line{width:80px;height:3px;background:var(--gold);margin:0 auto 12px;border-radius:2px;box-shadow:0 0 15px var(--gold);}
  .header p{color:var(--gold-light);font-size:14px;opacity:.95;}

  .cast-block{background:linear-gradient(135deg,rgba(255,140,0,.1),rgba(10,10,10,.95));border:3px solid var(--orange);border-radius:16px;padding:26px 22px;margin-bottom:16px;position:relative;box-shadow:0 0 30px rgba(255,140,0,.2);}
  .cast-block::before{content:'? ГЛОБАЛЬНЫЕ ПАРАМЕТРЫ РАСЧЁТА ?';position:absolute;top:-14px;left:50%;transform:translateX(-50%);background:#0a0a0a;color:var(--orange);padding:4px 22px;border:2px solid var(--orange);border-radius:999px;font-size:11px;font-weight:900;letter-spacing:2px;text-shadow:0 0 15px rgba(255,140,0,1);white-space:nowrap;box-shadow:0 0 20px rgba(255,140,0,.6);}
  .cast-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;}
  @media(max-width:900px){.cast-grid{grid-template-columns:repeat(2,1fr);}}
  @media(max-width:500px){.cast-grid{grid-template-columns:1fr;}}
  .cast-ctrl{background:rgba(0,0,0,.5);border:1px solid var(--orange);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:8px;}
  .cast-ctrl label{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:1.5px;color:var(--orange);}
  .cast-ctrl input{padding:10px 12px;border:2px solid var(--orange);border-radius:9px;font-size:18px;font-weight:800;background:#0a0a0a;color:var(--orange);outline:none;font-family:inherit;text-align:center;font-variant-numeric:tabular-nums;}
  .cast-ctrl input:focus{box-shadow:0 0 0 4px rgba(255,140,0,.25);}
  .cast-ctrl .unit{font-size:11px;color:var(--muted);text-align:center;font-weight:700;}

  .stats{display:grid;grid-template-columns:repeat(6,1fr);gap:12px;margin-bottom:16px;}
  @media(max-width:1200px){.stats{grid-template-columns:repeat(3,1fr);}}
  @media(max-width:600px){.stats{grid-template-columns:repeat(2,1fr);}}
  .stat{background:var(--bg);border:1px solid var(--gold);border-radius:12px;padding:14px;text-align:center;}
  .lbl{font-size:10px;font-weight:700;letter-spacing:1px;text-transform:uppercase;color:var(--gold);opacity:.7;margin-bottom:6px;}
  .val{font-size:18px;font-weight:800;color:var(--gold-light);line-height:1.1;}
  .val.green{color:var(--green);}
  .val.red{color:var(--red);}
  .val.sm{font-size:14px;}

  .toolbar{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:16px;align-items:center;}
  .search{flex:1;min-width:250px;padding:10px 14px;border:1px solid var(--gold);border-radius:9px;font-size:13px;background:#0a0a0a;color:var(--gold-light);outline:none;font-family:inherit;font-weight:600;}
  .search::placeholder{color:rgba(212,175,55,.4);}
  .counter{color:var(--gold);font-size:13px;white-space:nowrap;padding:10px 16px;border:1px solid var(--gold);border-radius:9px;background:#0a0a0a;font-weight:800;}
  .btn{display:inline-flex;align-items:center;gap:6px;padding:10px 16px;border-radius:9px;border:1px solid var(--gold);background:transparent;color:var(--gold);font-size:13px;font-weight:700;cursor:pointer;transition:.15s;font-family:inherit;}
  .btn:hover{background:var(--gold);color:#0a0a0a;}
  .btn.primary{background:var(--gold);color:#0a0a0a;}
  .btn.green{background:linear-gradient(135deg,#7dff7d,#3aa03a);color:#0a0a0a;border-color:#7dff7d;}
  .btn.cyan{background:linear-gradient(135deg,#6ee8e4,#3aa0a0);color:#0a0a0a;border-color:#6ee8e4;font-weight:900;}
  .btn.red{background:transparent;color:var(--red);border-color:var(--red);}
  .btn.red:hover{background:var(--red);color:#0a0a0a;}

  .table-block{background:var(--bg);border:1px solid var(--gold);border-radius:14px;padding:20px;margin-bottom:16px;position:relative;}
  .block-title{color:var(--gold);font-size:12px;font-weight:800;letter-spacing:2px;text-align:center;margin-bottom:16px;padding-bottom:12px;border-bottom:1px solid var(--border);text-transform:uppercase;text-shadow:0 0 12px rgba(255,215,0,.6);}
  .select-hint{position:absolute;top:-14px;left:0;background:linear-gradient(135deg,#FFD700,#FF8C00);color:#0a0a0a;font-size:11px;font-weight:900;letter-spacing:1.5px;padding:6px 18px;border-radius:999px;box-shadow:0 0 20px rgba(255,215,0,.7);text-transform:uppercase;animation:hintPulse 1.5s ease-in-out infinite;z-index:5;white-space:nowrap;}
  @keyframes hintPulse{0%,100%{box-shadow:0 0 20px rgba(255,215,0,.7);transform:scale(1);}50%{box-shadow:0 0 35px rgba(255,215,0,1),0 0 60px rgba(255,140,0,.6);transform:scale(1.05);}}
  
  .table-wrap{overflow:auto;max-height:65vh;border-radius:8px;border:1px solid var(--gold);}
  table{width:100%;table-layout:auto;border-collapse:collapse;background:#0a0a0a;font-size:13px;}
  th,td{padding:8px 6px;text-align:left;border-bottom:1px solid rgba(212,175,55,.2);vertical-align:middle;color:var(--gold);background:transparent;white-space:nowrap;}
  th{position:sticky;top:0;z-index:2;background:#0a0a0a;color:var(--gold);font-weight:800;text-transform:uppercase;font-size:10px;letter-spacing:1px;border-bottom:1px solid var(--gold);cursor:pointer;user-select:none;}
  th:hover{background:#1a1408;}
  th.sortable::after{content:' ⇅';opacity:.5;margin-left:4px;font-size:9px;}
  th.asc::after{content:' ▲';opacity:1;color:var(--gold-light);}
  th.desc::after{content:' ▼';opacity:1;color:var(--gold-light);}
  th.num,td.num{text-align:right;font-variant-numeric:tabular-nums;}
  th.col-star,td.col-star{width:44px;text-align:center;padding:8px 6px;position:sticky;top:0;background:linear-gradient(180deg,#1a1408,#0a0a0a) !important;}
  th.col-star::before{content:'▼';position:absolute;top:100%;left:50%;transform:translateX(-50%);color:var(--gold);font-size:20px;line-height:1;text-shadow:0 0 12px rgba(255,215,0,1);animation:arrowBounce 1s ease-in-out infinite;pointer-events:none;}
  @keyframes arrowBounce{0%,100%{transform:translateX(-50%) translateY(0);opacity:1;}50%{transform:translateX(-50%) translateY(6px);opacity:.7;}}

  tbody tr{transition:background .15s;}
  tbody tr:hover{background:#1a1408;}
  tbody tr.picked{background:linear-gradient(90deg,rgba(255,215,0,.15),rgba(255,215,0,.04));box-shadow:inset 4px 0 0 var(--gold);}
  tbody tr.loss{background:rgba(255,50,50,.06);}
  
  td.article{font-family:'Courier New',monospace;font-size:12px;color:var(--gold-light);}
  td b{color:#fff;}
  td.margin-pos{color:var(--green);font-weight:800;}
  td.margin-neg{color:var(--red);font-weight:800;}
  
  /* Редактируемые ячейки */
  input.cell-edit{width:100%;padding:5px 6px;border:1px solid transparent;border-radius:6px;background:rgba(255,215,0,.05);color:var(--gold-light);font-size:13px;font-weight:700;text-align:right;font-family:inherit;outline:none;font-variant-numeric:tabular-nums;transition:.2s;}
  input.cell-edit:hover{border-color:var(--border);}
  input.cell-edit:focus{border-color:var(--gold);background:#0a0a0a;box-shadow:0 0 0 3px rgba(255,215,0,.2);}
  input.cell-edit.empty{color:var(--muted);font-style:italic;}

  .mat-select{background:#0a0a0a;color:var(--gold-light);border:1px solid var(--border);border-radius:6px;padding:5px 8px;font-size:12px;font-family:inherit;cursor:pointer;font-weight:600;outline:none;transition:.2s;width:100%;}
  .mat-select:hover{border-color:var(--gold);}
  
  .star{cursor:pointer;font-size:22px;color:rgba(212,175,55,.35);transition:all .2s;user-select:none;text-align:center;display:inline-block;}
  .star:hover{color:var(--gold);transform:scale(1.3);}
  .star.on{color:var(--gold-light);text-shadow:0 0 12px var(--gold-light),0 0 24px rgba(255,215,0,.5);}
  
  .badge{display:inline-block;padding:2px 6px;border-radius:10px;font-size:9px;font-weight:800;text-transform:uppercase;background:rgba(212,175,55,.15);color:var(--gold-light);border:1px solid rgba(212,175,55,.5);white-space:nowrap;}
  .badge.tso16{background:rgba(78,205,196,.12);color:#7fdcd4;border-color:rgba(78,205,196,.4);}
  .badge.tso25{background:rgba(116,185,255,.12);color:#a8cfff;border-color:rgba(116,185,255,.4);}
  .badge.tso40{background:rgba(255,159,67,.12);color:#ffb980;border-color:rgba(255,159,67,.4);}
  .badge.tso63{background:rgba(255,107,107,.12);color:#ff9a9a;border-color:rgba(255,107,107,.4);}
  .badge.tso100{background:rgba(232,67,147,.12);color:#ff8fc0;border-color:rgba(232,67,147,.4);}
  .badge.tso160{background:rgba(156,39,176,.12);color:#ce93d8;border-color:rgba(156,39,176,.4);}

  .footnote{display:flex;flex-wrap:wrap;gap:20px;margin-top:14px;padding-top:12px;border-top:1px solid var(--gold);}
  .fn{color:var(--gold);font-size:12px;position:relative;padding-left:14px;opacity:.9;}
  .fn::before{content:"◆";position:absolute;left:0;color:var(--gold);font-size:8px;top:4px;}

  /* КОММЕРЧЕСКИЙ РАСЧЁТ */
  .calc-block{background:linear-gradient(135deg,rgba(255,215,0,.08),rgba(10,10,10,.98));border:3px solid var(--gold);border-radius:18px;padding:26px 22px;margin:20px 0;box-shadow:0 0 50px rgba(255,215,0,.35),inset 0 0 30px rgba(255,215,0,.06);position:relative;}
  .calc-block::before{content:'◆ КОММЕРЧЕСКИЙ РАСЧЁТ ◆';position:absolute;top:-14px;left:50%;transform:translateX(-50%);background:#0a0a0a;color:var(--gold);padding:4px 20px;border:2px solid var(--gold);border-radius:999px;font-size:11px;font-weight:900;letter-spacing:3px;text-shadow:0 0 12px rgba(255,215,0,.8);white-space:nowrap;}
  .calc-header{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:14px;margin-bottom:16px;}
  @media(max-width:1000px){.calc-header{grid-template-columns:1fr 1fr;}}
  @media(max-width:600px){.calc-header{grid-template-columns:1fr;}}
  .calc-ctrl{background:rgba(0,0,0,.5);border:1px solid var(--gold);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:8px;}
  .calc-ctrl label{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:1.5px;color:var(--gold);}
  .calc-ctrl input{padding:10px 14px;border:2px solid var(--gold);border-radius:9px;font-size:20px;font-weight:800;background:#0a0a0a;color:var(--gold-light);outline:none;font-family:inherit;text-align:center;font-variant-numeric:tabular-nums;}
  .calc-ctrl .unit{font-size:12px;color:var(--muted);text-align:center;font-weight:700;}
  
  .discount-panel{background:rgba(0,0,0,.5);border:1px dashed var(--gold);border-radius:12px;padding:14px 16px;margin-bottom:18px;}
  .discount-title{color:var(--gold);font-size:11px;font-weight:800;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:12px;text-align:center;}
  .discount-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:10px;}
  @media(max-width:600px){.discount-grid{grid-template-columns:1fr;}}
  .discount-item{display:flex;flex-direction:column;gap:5px;background:rgba(255,215,0,.04);border:1px solid var(--border);border-radius:8px;padding:10px;}
  .discount-item label{font-size:10px;font-weight:700;text-transform:uppercase;color:var(--gold);letter-spacing:.8px;}
  .discount-item input{padding:8px 10px;border:1px solid var(--gold);border-radius:7px;background:#0a0a0a;color:var(--gold-light);font-size:15px;font-weight:800;text-align:center;font-family:inherit;outline:none;}
  
  .calc-table-wrap{overflow:auto;max-height:50vh;border-radius:8px;border:1px solid var(--gold);background:#000;margin-bottom:16px;}
  .calc-table{width:100%;border-collapse:collapse;font-size:13px;}
  .calc-table th,.calc-table td{padding:10px 12px;text-align:left;border-bottom:1px solid rgba(212,175,55,.25);color:var(--gold);background:transparent;white-space:nowrap;}
  .calc-table th{background:#0a0a0a;color:var(--gold);font-weight:800;text-transform:uppercase;font-size:10px;letter-spacing:1px;border-bottom:2px solid var(--gold);position:sticky;top:0;z-index:1;}
  .calc-table td.num{text-align:right;font-variant-numeric:tabular-nums;}
  .calc-table input.qty{width:70px;padding:6px 10px;border:1px solid var(--gold);border-radius:6px;background:#0a0a0a;color:var(--gold-light);font-size:14px;font-weight:800;text-align:center;font-family:inherit;outline:none;}
  .calc-table .rm{cursor:pointer;color:#ff8a7a;font-weight:800;font-size:16px;text-align:center;user-select:none;}
  .calc-table .rm:hover{color:#ff5555;}
  .calc-table .disc{background:rgba(57,255,20,.08);color:#b9ff9f;font-weight:800;border-radius:4px;padding:2px 6px;font-size:11px;}
  
  .calc-empty{text-align:center;padding:40px 20px;color:var(--muted);font-size:14px;font-style:italic;}
  .calc-empty b{color:var(--gold);font-style:normal;}
  
  .calc-totals{display:grid;grid-template-columns:repeat(5,1fr);gap:12px;padding-top:16px;border-top:2px solid var(--gold);}
  @media(max-width:1100px){.calc-totals{grid-template-columns:repeat(3,1fr);}}
  @media(max-width:700px){.calc-totals{grid-template-columns:repeat(2,1fr);}}
  @media(max-width:450px){.calc-totals{grid-template-columns:1fr;}}
  .calc-total-item{background:rgba(0,0,0,.6);border:1px solid var(--gold);border-radius:12px;padding:14px 16px;text-align:center;}
  .calc-total-item .t-lbl{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:1.2px;color:var(--gold);opacity:.85;margin-bottom:6px;}
  .calc-total-item .t-val{font-size:20px;font-weight:900;color:var(--gold-light);font-variant-numeric:tabular-nums;line-height:1.1;}
  .calc-total-item.margin{background:linear-gradient(135deg,rgba(57,255,20,.08),rgba(0,0,0,.6));border-color:#7dff7d;}
  .calc-total-item.margin .t-lbl{color:#b9ff9f;}.calc-total-item.margin .t-val{color:#b9ff9f;}
  .calc-total-item.discount{background:linear-gradient(135deg,rgba(255,87,34,.08),rgba(0,0,0,.6));border-color:#ff8a65;}
  .calc-total-item.discount .t-lbl{color:#ff8a65;}.calc-total-item.discount .t-val{color:#ff8a65;}
  .calc-total-item.final{background:linear-gradient(135deg,rgba(255,215,0,.15),rgba(10,10,10,.7));border:2px solid var(--gold);box-shadow:0 0 30px rgba(255,215,0,.3);}
  .calc-total-item.final .t-val{font-size:26px;color:var(--gold);text-shadow:0 0 20px rgba(255,215,0,.6);}
  
  .calc-actions{display:flex;gap:10px;margin-top:18px;flex-wrap:wrap;justify-content:center;}
  .calc-actions .btn{padding:12px 24px;font-size:14px;}
  
  .cta{text-align:center;background:var(--bg);border:1px solid var(--gold);border-radius:14px;padding:28px 20px;margin-top:16px;}
  .cta-text{font-size:16px;font-weight:600;color:var(--gold);margin-bottom:16px;}
  .cta-btn{display:inline-block;background:var(--gold);color:#0a0a0a;text-decoration:none;padding:16px 40px;font-size:20px;font-weight:800;letter-spacing:1px;border-radius:999px;transition:.25s;margin:0 6px;}
  .cta-btn:hover{background:var(--gold-light);transform:scale(1.03);}
  .cta-mail{display:inline-block;color:var(--gold);text-decoration:none;padding:16px 30px;font-size:16px;font-weight:700;border:1px solid var(--gold);border-radius:999px;transition:.25s;margin:0 6px;}
  .cta-mail:hover{background:var(--gold);color:#0a0a0a;}
  .manager{margin-top:16px;font-size:13px;color:var(--muted);}

  .wrap ::-webkit-scrollbar{width:14px;height:14px;}
  .wrap ::-webkit-scrollbar-track{background:#050505;border:1px solid #2a2210;border-radius:9px;}
  .wrap ::-webkit-scrollbar-thumb{background:linear-gradient(180deg,#FFE55C,#FFD700,#FF8C00);border-radius:9px;border:1px solid #FFD700;box-shadow:0 0 10px rgba(255,215,0,.9);}

  @media print{
    body{background:#fff;color:#000;padding:0;}
    .header{background:#000 !important;color:#FFD700 !important;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
    .header h1{color:#FFD700 !important;animation:none !important;}
    .tag{background:#FFD700 !important;color:#000 !important;}
    .line{background:#FFD700 !important;}
    .stats,.table-block,.calc-block{background:#fff !important;border-color:#666 !important;box-shadow:none !important;}
    .toolbar,.search,.counter,.select-hint,th.col-star::before,.calc-actions{display:none !important;}
    .stat,.cast-ctrl,.calc-total-item{background:#fff !important;border-color:#999 !important;}
    .lbl,.cast-ctrl label,.calc-ctrl label{color:#000 !important;}
    .val{color:#000 !important;}
    .block-title{color:#000 !important;}
    th{background:#000 !important;color:#FFD700 !important;-webkit-print-color-adjust:exact;print-color-adjust:exact;}
    td{color:#000 !important;border-color:#ccc !important;}
    input{background:#fff !important;color:#000 !important;border-color:#ccc !important;}
    .calc-total-item.final .t-val{color:#000 !important;text-shadow:none !important;}
  }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div class="wrap"&gt;

  &lt;div class="header"&gt;
    &lt;div class="tag"&gt;КАЛЬКУЛЯТОР ЛИТЬЯ · ГОСТ 17424-72 · PRO&lt;/div&gt;
    &lt;h1&gt;Тумбы швартовые морские (ТСО)&lt;/h1&gt;
    &lt;div class="line"&gt;&lt;/div&gt;
    &lt;p&gt;Портовая инфраструктура · Крановое оборудование · Редактируемые ячейки · Мгновенный пересчёт&lt;/p&gt;
  &lt;/div&gt;

  &lt;div class="cast-block"&gt;
    &lt;div class="cast-grid"&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;? Себестоимость литья&lt;/label&gt;
        &lt;input type="number" id="pCastCost" value="110" min="0" step="5" oninput="globalRender()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (ваша себестоимость)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;? Цена продажи литья&lt;/label&gt;
        &lt;input type="number" id="pCastSale" value="240" min="0" step="5" oninput="globalRender()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (рыночная цена)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;⚙️ Мех. обработка&lt;/label&gt;
        &lt;input type="number" id="pMachining" value="25" min="0" step="5" oninput="globalRender()" /&gt;
        &lt;div class="unit"&gt;₽ за кг (обдирка, сверловка)&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="cast-ctrl"&gt;
        &lt;label&gt;◆ НДС&lt;/label&gt;
        &lt;input type="number" id="pVAT" value="22" min="0" max="30" step="1" oninput="globalRender()" /&gt;
        &lt;div class="unit"&gt;%&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="stats"&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Всего позиций&lt;/div&gt;&lt;div class="val" id="statTotal"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Общий вес, кг&lt;/div&gt;&lt;div class="val sm" id="statWeight"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Себестоимость партии&lt;/div&gt;&lt;div class="val sm" id="statCost"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Сумма продажи&lt;/div&gt;&lt;div class="val sm green" id="statSale"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Маржа&lt;/div&gt;&lt;div class="val green" id="statMargin"&gt;—&lt;/div&gt;&lt;/div&gt;
    &lt;div class="stat"&gt;&lt;div class="lbl"&gt;Рентабельность&lt;/div&gt;&lt;div class="val green" id="statMarginPct"&gt;—&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="toolbar"&gt;
    &lt;input type="text" class="search" id="search" placeholder="? Поиск по обозначению, наименованию или материалу..." /&gt;
    &lt;div class="counter" id="counter"&gt;Найдено: 0&lt;/div&gt;
    &lt;button class="btn cyan" onclick="openRequestModal()"&gt;? Запрос заводу&lt;/button&gt;
    &lt;button class="btn green" onclick="openInvoiceModal()"&gt;? Счёт клиенту&lt;/button&gt;
    &lt;button class="btn primary" onclick="dlCSV()"&gt;⬇ CSV&lt;/button&gt;
    &lt;button class="btn primary" onclick="dlExcel()"&gt;⬇ Excel&lt;/button&gt;
    &lt;button class="btn" onclick="saveLocal()"&gt;? Сохранить&lt;/button&gt;
    &lt;button class="btn" onclick="loadLocal()"&gt;? Загрузить&lt;/button&gt;
    &lt;button class="btn" onclick="window.print()"&gt;? Печать&lt;/button&gt;
    &lt;button class="btn red" onclick="resetAll()"&gt;✕ Сброс&lt;/button&gt;
  &lt;/div&gt;

  &lt;div class="table-block"&gt;
    &lt;div class="select-hint"&gt;→ НАЖМИ ★ ДЛЯ РАСЧЁТА →&lt;/div&gt;
    &lt;div class="block-title"&gt;Редактируемый прайс-лист · ТСО&lt;/div&gt;
    &lt;div class="table-wrap"&gt;
      &lt;table id="tbl"&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th class="col-star" title="Отметь позиции для расчёта"&gt;★&lt;/th&gt;
            &lt;th class="num sortable" data-key="num"&gt;№&lt;/th&gt;
            &lt;th class="sortable" data-key="catLabel"&gt;Типоразмер&lt;/th&gt;
            &lt;th class="sortable" data-key="article"&gt;Артикул&lt;/th&gt;
            &lt;th class="sortable" data-key="name"&gt;Наименование&lt;/th&gt;
            &lt;th&gt;Материал&lt;/th&gt;
            &lt;th class="num"&gt;Вес, кг&lt;/th&gt;
            &lt;th class="num"&gt;Себест. 1 шт, ₽&lt;/th&gt;
            &lt;th class="num"&gt;Цена 1 шт, ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="margin"&gt;Маржа 1 шт, ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="marginPct"&gt;Маржа, %&lt;/th&gt;
            &lt;th class="num"&gt;Ср. конкур., ₽&lt;/th&gt;
            &lt;th class="num sortable" data-key="diff"&gt;Δ, %&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody id="tbody"&gt;&lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;
    &lt;div class="footnote"&gt;
      &lt;div class="fn"&gt;Любую ячейку &lt;b&gt;Вес&lt;/b&gt; или &lt;b&gt;Ср. конкур.&lt;/b&gt; можно изменить (даже на 0 или пусто)&lt;/div&gt;
      &lt;div class="fn"&gt;Себестоимость 1 шт = Вес × (Себест. литья + Мех. обработка)&lt;/div&gt;
      &lt;div class="fn"&gt;Цена 1 шт = Вес × Цена продажи литья&lt;/div&gt;
      &lt;div class="fn"&gt;Клик по ★ добавляет позицию в нижний блок «Коммерческий расчёт»&lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="calc-block"&gt;
    &lt;div class="calc-header"&gt;
      &lt;div class="calc-ctrl"&gt;
        &lt;label&gt;◆ Наценка, %&lt;/label&gt;
        &lt;input type="number" id="calcMarkup" value="30" min="0" max="500" step="5" oninput="calcRender()" /&gt;
        &lt;div class="unit"&gt;процент к себестоимости&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="calc-ctrl"&gt;
        &lt;label&gt;◆ Позиций в расчёте&lt;/label&gt;
        &lt;input type="number" id="calcCount" value="0" readonly="readonly" /&gt;
        &lt;div class="unit"&gt;отметьте ★ в таблице&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="calc-ctrl"&gt;
        &lt;label&gt;◆ Сумма без скидки&lt;/label&gt;
        &lt;input type="number" id="calcSumRaw" value="0" readonly="readonly" /&gt;
        &lt;div class="unit"&gt;до применения скидки&lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="calc-ctrl"&gt;
        &lt;label&gt;◆ Маржа с 1 позиции&lt;/label&gt;
        &lt;input type="number" id="calcAvgMargin" value="0" readonly="readonly" /&gt;
        &lt;div class="unit"&gt;средняя по партии, ₽&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="discount-panel"&gt;
      &lt;div class="discount-title"&gt;◆ Скидка от объёма (по количеству, шт) ◆&lt;/div&gt;
      &lt;div class="discount-grid"&gt;
        &lt;div class="discount-item"&gt;&lt;label&gt;От 5 шт — скидка %&lt;/label&gt;&lt;input type="number" id="disc5" value="5" min="0" max="50" step="1" oninput="calcRender()" /&gt;&lt;/div&gt;
        &lt;div class="discount-item"&gt;&lt;label&gt;От 10 шт — скидка %&lt;/label&gt;&lt;input type="number" id="disc10" value="10" min="0" max="50" step="1" oninput="calcRender()" /&gt;&lt;/div&gt;
        &lt;div class="discount-item"&gt;&lt;label&gt;От 20 шт — скидка %&lt;/label&gt;&lt;input type="number" id="disc20" value="15" min="0" max="50" step="1" oninput="calcRender()" /&gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="calc-table-wrap"&gt;
      &lt;table class="calc-table"&gt;
        &lt;thead&gt;
          &lt;tr&gt;
            &lt;th&gt;№&lt;/th&gt;&lt;th&gt;Типоразмер&lt;/th&gt;&lt;th&gt;Артикул&lt;/th&gt;&lt;th&gt;Материал&lt;/th&gt;
            &lt;th class="num"&gt;Вес, кг&lt;/th&gt;&lt;th class="num"&gt;Себест., ₽&lt;/th&gt;
            &lt;th class="num"&gt;Кол-во, шт&lt;/th&gt;&lt;th class="num"&gt;Скидка&lt;/th&gt;
            &lt;th class="num"&gt;Цена с наценкой, ₽&lt;/th&gt;&lt;th class="num"&gt;Итог, ₽&lt;/th&gt;
            &lt;th class="num"&gt;Ср. конкур., ₽&lt;/th&gt;&lt;th class="num"&gt;Δ к конкур.&lt;/th&gt;&lt;th&gt;&lt;/th&gt;
          &lt;/tr&gt;
        &lt;/thead&gt;
        &lt;tbody id="calcBody"&gt;&lt;/tbody&gt;
      &lt;/table&gt;
    &lt;/div&gt;

    &lt;div id="calcEmpty" class="calc-empty"&gt;
      Отметьте позиции знаком &lt;b&gt;★&lt;/b&gt; в таблице выше — они появятся здесь.
    &lt;/div&gt;

    &lt;div class="calc-totals" id="calcTotals" style="display:none"&gt;
      &lt;div class="calc-total-item"&gt;&lt;div class="t-lbl"&gt;Себестоимость&lt;/div&gt;&lt;div class="t-val" id="calcTotalCost"&gt;0 ₽&lt;/div&gt;&lt;/div&gt;
      &lt;div class="calc-total-item discount"&gt;&lt;div class="t-lbl"&gt;Скидка&lt;/div&gt;&lt;div class="t-val" id="calcTotalDiscount"&gt;0 ₽&lt;/div&gt;&lt;/div&gt;
      &lt;div class="calc-total-item margin"&gt;&lt;div class="t-lbl"&gt;Маржа&lt;/div&gt;&lt;div class="t-val" id="calcTotalMargin"&gt;0 ₽&lt;/div&gt;&lt;/div&gt;
      &lt;div class="calc-total-item"&gt;&lt;div class="t-lbl"&gt;Без НДС&lt;/div&gt;&lt;div class="t-val" id="calcTotalNoVat"&gt;0 ₽&lt;/div&gt;&lt;/div&gt;
      &lt;div class="calc-total-item final"&gt;&lt;div class="t-lbl"&gt;ИТОГО с НДС 22%&lt;/div&gt;&lt;div class="t-val" id="calcTotalFinal"&gt;0 ₽&lt;/div&gt;&lt;/div&gt;
    &lt;/div&gt;

    &lt;div class="calc-actions"&gt;
      &lt;button class="btn primary" onclick="calcSendQuote()"&gt;? Сформировать счёт&lt;/button&gt;
      &lt;button class="btn" onclick="calcCopy()"&gt;? Копировать расчёт&lt;/button&gt;
      &lt;button class="btn" onclick="calcExportExcel()"&gt;⬇ Excel расчёт&lt;/button&gt;
      &lt;button class="btn" onclick="calcPDF()"&gt;? Скачать PDF&lt;/button&gt;
      &lt;button class="btn" onclick="calcClear()"&gt;✕ Очистить расчёт&lt;/button&gt;
    &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="cta"&gt;
    &lt;p class="cta-text"&gt;Изготовим ТСО любого типоразмера по вашим чертежам и ТУ&lt;/p&gt;
    &lt;a href="tel:+79028733690" class="cta-btn"&gt;8 (902) 873-36-90&lt;/a&gt;
    &lt;a href="mailto:arsenal-detal@list.ru?subject=Запрос%20на%20ТСО" class="cta-mail"&gt;✉ arsenal-detal@list.ru&lt;/a&gt;
    &lt;p class="manager"&gt;Менеджер: Ольга · ООО «Арсенал Деталь»&lt;/p&gt;
  &lt;/div&gt;

&lt;/div&gt;

&lt;script&gt;
/* ==================== МАТЕРИАЛЫ ==================== */
var MATERIALS=[
  {key:'25Л', k:1.00}, {key:'35Л', k:1.15}, {key:'09Г2С', k:1.10}, {key:'30ХГСА', k:1.40}
];

/* ==================== ДАННЫЕ ==================== */
var data=[
  {num:1, cat:'tso16', catLabel:'ТСО-16', article:'ТСО-16-01', name:'Тумба швартовая ТСО-16', material:'25Л', weight:200, competitor:52000},
  {num:2, cat:'tso16', catLabel:'ТСО-16', article:'ТСО-16-02', name:'Тумба швартовая ТСО-16 (усил.)', material:'35Л', weight:210, competitor:58000},
  {num:3, cat:'tso25', catLabel:'ТСО-25', article:'ТСО-25-01', name:'Тумба швартовая ТСО-25', material:'25Л', weight:330, competitor:78000},
  {num:4, cat:'tso25', catLabel:'ТСО-25', article:'ТСО-25-02', name:'Тумба швартовая ТСО-25', material:'35Л', weight:340, competitor:85000},
  {num:5, cat:'tso40', catLabel:'ТСО-40', article:'ТСО-40-01', name:'Тумба швартовая ТСО-40', material:'35Л', weight:630, competitor:145000},
  {num:6, cat:'tso40', catLabel:'ТСО-40', article:'ТСО-40-02', name:'Тумба швартовая ТСО-40 (легир.)', material:'30ХГСА', weight:650, competitor:205000},
  {num:7, cat:'tso63', catLabel:'ТСО-63', article:'ТСО-63-01', name:'Тумба швартовая ТСО-63', material:'35Л', weight:1050, competitor:245000},
  {num:8, cat:'tso63', catLabel:'ТСО-63', article:'ТСО-63-02', name:'Тумба швартовая ТСО-63', material:'09Г2С', weight:1000, competitor:275000},
  {num:9, cat:'tso80', catLabel:'ТСО-80', article:'ТСО-80-01', name:'Тумба швартовая ТСО-80', material:'35Л', weight:1350, competitor:310000},
  {num:10, cat:'tso100', catLabel:'ТСО-100', article:'ТСО-100-01', name:'Тумба швартовая ТСО-100', material:'35Л', weight:1600, competitor:385000},
  {num:11, cat:'tso100', catLabel:'ТСО-100', article:'ТСО-100-02', name:'Тумба швартовая ТСО-100 (легир.)', material:'30ХГСА', weight:1650, competitor:540000},
  {num:12, cat:'tso160', catLabel:'ТСО-160', article:'ТСО-160-01', name:'Тумба швартовая ТСО-160', material:'35Л', weight:2800, competitor:620000},
  {num:13, cat:'tso160', catLabel:'ТСО-160', article:'ТСО-160-02', name:'Тумба швартовая ТСО-160 (спец.)', material:'30ХГСА', weight:2900, competitor:750000}
];

/* ==================== СОСТОЯНИЕ ==================== */
var params={castCost:110, castSale:240, machining:25, vat:22};
var state={search:'', sort:{key:'num', dir:1}};
var fav=[];
var calcState={markup:30, qty:{}};

function fmt(n){return Math.round(n).toLocaleString('ru-RU');}
function pct(n){return (n&gt;0?'+':'')+n.toFixed(1)+'%';}

/* ==================== РАСЧЁТ ==================== */
function calc(r){
  var mat = MATERIALS.find(m=&gt;m.key===r.material) || {k:1};
  var effectiveCostPerKg = (params.castCost + params.machining) * mat.k;
  var costPerUnit = r.weight * effectiveCostPerKg;
  var salePerUnit = r.weight * params.castSale;
  var marginPerUnit = salePerUnit - costPerUnit;
  var marginPct = costPerUnit &gt; 0 ? (marginPerUnit / costPerUnit * 100) : 0;
  var diff = r.competitor &gt; 0 ? ((salePerUnit - r.competitor) / r.competitor * 100) : 0;
  return {costPerUnit, salePerUnit, marginPerUnit, marginPct, diff};
}

/* ==================== ФИЛЬТРАЦИЯ ==================== */
function getFiltered(){
  var rows=data.slice();
  if(state.search){
    var q=state.search.trim().toLowerCase();
    rows=rows.filter(function(r){
      return String(r.num).indexOf(q)!==-1 ||
             (r.article||'').toLowerCase().indexOf(q)!==-1 ||
             (r.name||'').toLowerCase().indexOf(q)!==-1 ||
             (r.material||'').toLowerCase().indexOf(q)!==-1 ||
             String(r.weight).indexOf(q)!==-1;
    });
  }
  if(state.sort.key){
    var k=state.sort.key, d=state.sort.dir;
    rows.sort(function(a,b){
      var va, vb;
      if(['margin','marginPct','diff'].indexOf(k)!==-1){
        va=calc(a)[k]; vb=calc(b)[k];
      } else { va=a[k]; vb=b[k]; }
      if(typeof va==='number' &amp;&amp; typeof vb==='number') return (va-vb)*d;
      return String(va).localeCompare(String(vb),'ru')*d;
    });
  }
  return rows;
}

/* ==================== СТАТИСТИКА ==================== */
function updateStats(){
  var totalWeight=0, totalCost=0, totalSale=0, totalMargin=0;
  data.forEach(function(r){
    var c=calc(r);
    var q = fav.includes(r.num) ? (calcState.qty[r.num]||1) : 0;
    totalWeight += r.weight * q;
    totalCost += c.costPerUnit * q;
    totalSale += c.salePerUnit * q;
    totalMargin += (c.salePerUnit - c.costPerUnit) * q;
  });
  document.getElementById('statTotal').textContent = data.length;
  document.getElementById('statWeight').textContent = fmt(totalWeight) + ' кг';
  document.getElementById('statCost').textContent = fmt(totalCost) + ' ₽';
  document.getElementById('statSale').textContent = fmt(totalSale) + ' ₽';
  document.getElementById('statMargin').textContent = fmt(totalMargin) + ' ₽';
  document.getElementById('statMarginPct').textContent = (totalCost&gt;0 ? Math.round(totalMargin/totalCost*100)+'%' : '—');
}

/* ==================== РЕНДЕР ТАБЛИЦЫ ==================== */
function render(){
  var rows=getFiltered();
  var tbody=document.getElementById('tbody');
  
  // Сохраняем фокус перед перерисовкой
  const active = document.activeElement;
  const activeId = active &amp;&amp; active.classList.contains('cell-edit') ? (active.dataset.num + '-' + active.dataset.field) : null;
  const activePos = active ? active.selectionStart : null;

  tbody.innerHTML='';
  document.getElementById('counter').textContent = 'Найдено: ' + rows.length;
  
  if(!rows.length){
    tbody.innerHTML='&lt;tr&gt;&lt;td colspan="13" style="text-align:center;padding:40px;opacity:.6"&gt;Ничего не найдено&lt;/td&gt;&lt;/tr&gt;';
    updateStats(); return;
  }
  
  rows.forEach(function(r){
    var c=calc(r);
    var isFav = fav.includes(r.num);
    var marginCls = c.marginPerUnit &gt; 0 ? 'margin-pos' : (c.marginPerUnit &lt; 0 ? 'margin-neg' : '');
    var diffCls = c.diff &gt; 0 ? 'margin-neg' : (c.diff &lt; 0 ? 'margin-pos' : '');
    
    var matOptions = MATERIALS.map(function(m){
      return '&lt;option value="'+m.key+'"'+(m.key===r.material?' selected':'')+'&gt;'+m.key+'&lt;/option&gt;';
    }).join('');

    var tr=document.createElement('tr');
    if(isFav) tr.classList.add('picked');
    if(c.marginPerUnit &lt; 0) tr.classList.add('loss');
    
    tr.innerHTML=
      '&lt;td class="col-star"&gt;&lt;span class="star'+(isFav?' on':'')+'" data-num="'+r.num+'"&gt;'+(isFav?'★':'☆')+'&lt;/span&gt;&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+r.num+'&lt;/td&gt;'+
      '&lt;td&gt;&lt;span class="badge '+r.cat+'"&gt;'+r.catLabel+'&lt;/span&gt;&lt;/td&gt;'+
      '&lt;td class="article"&gt;'+r.article+'&lt;/td&gt;'+
      '&lt;td&gt;&lt;b&gt;'+r.name+'&lt;/b&gt;&lt;/td&gt;'+
      '&lt;td&gt;&lt;select class="mat-select" data-num="'+r.num+'" data-field="material"&gt;'+matOptions+'&lt;/select&gt;&lt;/td&gt;'+
      '&lt;td class="num"&gt;&lt;input type="number" class="cell-edit" data-num="'+r.num+'" data-field="weight" value="'+r.weight+'" min="0" step="1"&gt;&lt;/td&gt;'+
      '&lt;td class="num" style="color:#ffb060;font-weight:700"&gt;'+fmt(c.costPerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num" style="color:#FFE55C;font-weight:700"&gt;'+fmt(c.salePerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num '+marginCls+'"&gt;'+fmt(c.marginPerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num '+marginCls+'"&gt;'+Math.round(c.marginPct)+'%&lt;/td&gt;'+
      '&lt;td class="num"&gt;&lt;input type="number" class="cell-edit" data-num="'+r.num+'" data-field="competitor" value="'+(r.competitor||'')+'" min="0" step="100" placeholder="0"&gt;&lt;/td&gt;'+
      '&lt;td class="num '+diffCls+'"&gt;'+(r.competitor?pct(c.diff):'—')+'&lt;/td&gt;';
    tbody.appendChild(tr);
  });
  
  updateStats();
  bindEdits();
  bindStars();
  bindMatSelects();
  
  // Восстанавливаем фокус
  if(activeId){
    const newEl = document.querySelector(`[data-num="${activeId.split('-')[0]}"][data-field="${activeId.split('-')[1]}"]`);
    if(newEl){
      newEl.focus();
      if(activePos !== null) newEl.setSelectionRange(activePos, activePos);
    }
  }
}

function bindEdits(){
  document.querySelectorAll('.cell-edit').forEach(function(inp){
    inp.addEventListener('input', function(){
      var n = parseInt(this.dataset.num);
      var field = this.dataset.field;
      var val = parseFloat(this.value);
      var item = data.find(x =&gt; x.num === n);
      if(item){
        item[field] = isNaN(val) ? 0 : val; // Разрешаем 0 или пусто (станет 0)
        if(isNaN(val)) this.classList.add('empty');
        else this.classList.remove('empty');
        
        // Точечное обновление без полной перерисовки для плавности
        var c = calc(item);
        var row = this.closest('tr');
        var cells = row.querySelectorAll('td');
        cells[7].textContent = fmt(c.costPerUnit);
        cells[8].textContent = fmt(c.salePerUnit);
        cells[9].textContent = fmt(c.marginPerUnit);
        cells[9].className = 'num ' + (c.marginPerUnit&gt;0?'margin-pos':(c.marginPerUnit&lt;0?'margin-neg':''));
        cells[10].textContent = Math.round(c.marginPct)+'%';
        cells[10].className = 'num ' + (c.marginPerUnit&gt;0?'margin-pos':(c.marginPerUnit&lt;0?'margin-neg':''));
        cells[12].textContent = item.competitor ? pct(c.diff) : '—';
        cells[12].className = 'num ' + (c.diff&gt;0?'margin-neg':(c.diff&lt;0?'margin-pos':''));
        
        updateStats();
        if(fav.includes(n)) calcRender();
      }
    });
  });
}

function bindMatSelects(){
  document.querySelectorAll('.mat-select').forEach(function(sel){
    sel.addEventListener('change', function(){
      var n = parseInt(this.dataset.num);
      var item = data.find(x =&gt; x.num === n);
      if(item){
        item.material = this.value;
        render(); // Полная перерисовка для обновления расчётов
        if(fav.includes(n)) calcRender();
      }
    });
  });
}

function bindStars(){
  document.querySelectorAll('.star').forEach(function(el){
    el.addEventListener('click', function(){
      var n = parseInt(this.dataset.num);
      var idx = fav.indexOf(n);
      if(idx===-1) {
        fav.push(n);
        if(!calcState.qty[n]) calcState.qty[n]=1;
      } else {
        fav.splice(idx,1);
        delete calcState.qty[n];
      }
      render();
      calcRender();
    });
  });
}

/* ==================== КОММЕРЧЕСКИЙ РАСЧЁТ ==================== */
function getDiscount(qty){
  var d5=parseFloat(document.getElementById('disc5').value)||0;
  var d10=parseFloat(document.getElementById('disc10').value)||0;
  var d20=parseFloat(document.getElementById('disc20').value)||0;
  if(qty&gt;=20) return d20;
  if(qty&gt;=10) return d10;
  if(qty&gt;=5) return d5;
  return 0;
}

function calcRender(){
  var tbody=document.getElementById('calcBody');
  var empty=document.getElementById('calcEmpty');
  var totals=document.getElementById('calcTotals');
  tbody.innerHTML='';
  document.getElementById('calcCount').value=fav.length;
  
  if(!fav.length){
    empty.style.display='block';totals.style.display='none';
    document.getElementById('calcAvgMargin').value=0;
    document.getElementById('calcSumRaw').value=0;
    return;
  }
  empty.style.display='none';totals.style.display='grid';
  
  var markup=parseFloat(document.getElementById('calcMarkup').value)||0;
  var totalCost=0,totalMargin=0,totalSum=0,totalNoVat=0,totalDiscount=0,sumRaw=0;
  
  fav.forEach(function(n){
    var r=data.find(function(x){return x.num===n;});
    if(!r)return;
    var c=calc(r);
    var qty=calcState.qty[r.num]||1;
    var priceWithMarkup=Math.round(c.costPerUnit*(1+markup/100)/100)*100;
    var sumBeforeDiscount=priceWithMarkup*qty;
    var discountPct=getDiscount(qty);
    var sumAfterDiscount=sumBeforeDiscount*(1-discountPct/100);
    var discountAmount=sumBeforeDiscount-sumAfterDiscount;
    var margin=(priceWithMarkup-c.costPerUnit)*qty-discountAmount;
    
    var diffToComp=r.competitor&gt;0?((priceWithMarkup-r.competitor)/r.competitor*100):0;
    var diffCls=diffToComp&gt;0?'margin-neg':(diffToComp&lt;0?'margin-pos':'');
    
    totalCost+=c.costPerUnit*qty;totalMargin+=margin;totalSum+=sumAfterDiscount;
    totalNoVat+=sumAfterDiscount/(1+params.vat/100);totalDiscount+=discountAmount;sumRaw+=sumBeforeDiscount;
    
    var discBadge=discountPct?'&lt;span class="disc"&gt;−'+discountPct+'%&lt;/span&gt;':'—';
    var compDiffHtml=r.competitor?'&lt;span class="'+diffCls+'"&gt;'+pct(diffToComp)+'&lt;/span&gt;':'—';
    
    var tr=document.createElement('tr');
    tr.innerHTML=
      '&lt;td&gt;'+r.num+'&lt;/td&gt;'+
      '&lt;td&gt;&lt;span class="badge '+r.cat+'"&gt;'+r.catLabel+'&lt;/span&gt;&lt;/td&gt;'+
      '&lt;td class="article"&gt;'+r.article+'&lt;/td&gt;'+
      '&lt;td&gt;'+r.material+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+r.weight+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+fmt(c.costPerUnit)+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;&lt;input type="number" class="qty" data-num="'+r.num+'" value="'+qty+'" min="1" max="9999"&gt;&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+discBadge+'&lt;/td&gt;'+
      '&lt;td class="num" style="color:#FFE55C;font-weight:800"&gt;'+fmt(priceWithMarkup)+'&lt;/td&gt;'+
      '&lt;td class="num" style="color:#b9ff9f;font-weight:800"&gt;'+fmt(sumAfterDiscount)+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+(r.competitor?'~'+fmt(r.competitor):'—')+'&lt;/td&gt;'+
      '&lt;td class="num"&gt;'+compDiffHtml+'&lt;/td&gt;'+
      '&lt;td class="rm" data-num="'+r.num+'"&gt;✕&lt;/td&gt;';
    tbody.appendChild(tr);
  });
  
  document.getElementById('calcTotalCost').textContent=fmt(totalCost)+' ₽';
  document.getElementById('calcTotalDiscount').textContent='−'+fmt(totalDiscount)+' ₽';
  document.getElementById('calcTotalMargin').textContent=fmt(totalMargin)+' ₽';
  document.getElementById('calcTotalNoVat').textContent=fmt(totalNoVat)+' ₽';
  document.getElementById('calcTotalFinal').textContent=fmt(totalSum)+' ₽';
  document.getElementById('calcAvgMargin').value=fav.length?Math.round(totalMargin/fav.length):0;
  document.getElementById('calcSumRaw').value=Math.round(sumRaw);
  
  tbody.querySelectorAll('.qty').forEach(function(inp){
    inp.addEventListener('input',function(){
      var n=parseInt(this.dataset.num,10);
      calcState.qty[n]=Math.max(1,parseInt(this.value,10)||1);
      calcRender();
    });
  });
  tbody.querySelectorAll('.rm').forEach(function(el){
    el.addEventListener('click',function(){
      var n=parseInt(this.dataset.num,10);
      var idx=fav.indexOf(n);
      if(idx!==-1)fav.splice(idx,1);
      render();calcRender();
    });
  });
}

function calcClear(){fav=[];calcState.qty={};render();calcRender();}

/* ==================== МОДАЛЬНЫЕ ОКНА И ЭКСПОРТ ==================== */
function openModal(title,body){
  // Простая реализация модалки через prompt/confirm для компактности, или можно добавить div
  var w=window.open('','_blank','width=800,height=600');
  w.document.write('&lt;pre style="font-family:monospace;white-space:pre-wrap;padding:20px;"&gt;'+title+'\n\n'+body+'&lt;/pre&gt;');
}

function openRequestModal(){
  var text='Добрый день!\n\nООО «Арсенал-Деталь» просит дать актуальное КП на изготовление швартовых тумб по ГОСТ 17424-72.\n\nСПЕЦИФИКАЦИЯ:\n';
  data.forEach(function(r){
    var q = fav.includes(r.num) ? (calcState.qty[r.num]||1) : 0;
    if(q &gt; 0) text += '• '+r.article+' ('+r.material+') — Вес: '+r.weight+' кг, Кол-во: '+q+' шт.\n';
  });
  text += '\nПрошу указать цену за кг/шт, сроки и условия доставки.\nС уважением, ООО «Арсенал Деталь» · 8 902 873-36-90';
  openModal('? ЗАПРОС ЗАВОДУ', text);
}

function openInvoiceModal(){
  var text='КОММЕРЧЕСКОЕ ПРЕДЛОЖЕНИЕ\nООО «Арсенал-Деталь» · 8 (902) 873-36-90\nДата: '+new Date().toLocaleDateString('ru-RU')+'\n\n';
  var total=0;
  fav.forEach(function(n){
    var r=data.find(x=&gt;x.num===n);
    var c=calc(r);
    var q=calcState.qty[r.num]||1;
    var sum=c.salePerUnit*q;
    total+=sum;
    text += r.article+' | '+r.name+' | '+r.material+' | '+fmt(c.salePerUnit)+' ₽/шт x '+q+' = '+fmt(sum)+' ₽\n';
  });
  text += '\nИТОГО: '+fmt(total)+' ₽ (в т.ч. НДС '+params.vat+'%)\nУсловия: 50% предоплата, срок 15-20 дней.';
  openModal('? СЧЁТ КЛИЕНТУ', text);
}

function saveLocal(){
  localStorage.setItem('tso_params', JSON.stringify(params));
  localStorage.setItem('tso_data', JSON.stringify(data));
  localStorage.setItem('tso_fav', JSON.stringify(fav));
  localStorage.setItem('tso_qty', JSON.stringify(calcState.qty));
  alert('✅ Сохранено в браузере');
}
function loadLocal(){
  try{
    var p=localStorage.getItem('tso_params');
    var d=localStorage.getItem('tso_data');
    var f=localStorage.getItem('tso_fav');
    var q=localStorage.getItem('tso_qty');
    if(p) { params=JSON.parse(p); document.getElementById('pCastCost').value=params.castCost; document.getElementById('pCastSale').value=params.castSale; document.getElementById('pMachining').value=params.machining; document.getElementById('pVAT').value=params.vat; }
    if(d) data=JSON.parse(d);
    if(f) fav=JSON.parse(f);
    if(q) calcState.qty=JSON.parse(q);
    render(); calcRender();
    alert('✅ Загружено из браузера');
  }catch(e){alert('Ошибка загрузки');}
}
function resetAll(){
  if(!confirm('Сбросить все данные и редактирования?')) return;
  location.reload();
}

function dlCSV(){
  var rows=getFiltered();
  var header=['№','Типоразмер','Артикул','Наименование','Материал','Вес,кг','Себест/шт,₽','Цена/шт,₽','Маржа/шт,₽','Ср.конкур.,₽'];
  var body=rows.map(function(r){
    var c=calc(r);
    return [r.num,r.catLabel,r.article,r.name,r.material,r.weight,Math.round(c.costPerUnit),Math.round(c.salePerUnit),Math.round(c.marginPerUnit),r.competitor||''];
  });
  var esc=function(v){return '"'+String(v==null?'':v).replace(/"/g,'""')+'"';};
  var csv=[header].concat(body).map(function(r){return r.map(esc).join(';');}).join('\r\n');
  var blob=new Blob(['\uFEFF'+csv],{type:'text/csv;charset=utf-8;'});
  var url=URL.createObjectURL(blob);var a=document.createElement('a');a.href=url;a.download='TSO_calc_'+new Date().toISOString().slice(0,10)+'.csv';document.body.appendChild(a);a.click();a.remove();
}

function dlExcel(){
  var rows=getFiltered();
  var header=['№','Типоразмер','Артикул','Наименование','Материал','Вес,кг','Себест/шт,₽','Цена/шт,₽','Маржа/шт,₽','Ср.конкур.,₽'];
  var body=rows.map(function(r){
    var c=calc(r);
    return [r.num,r.catLabel,r.article,r.name,r.material,r.weight,Math.round(c.costPerUnit),Math.round(c.salePerUnit),Math.round(c.marginPerUnit),r.competitor||''];
  });
  var esc=function(v){return String(v==null?'':v).replace(/&amp;/g,'&amp;amp;').replace(/&lt;/g,'&amp;lt;').replace(/&gt;/g,'&amp;gt;');};
  var table='&lt;table border="1"&gt;&lt;thead&gt;&lt;tr&gt;'+header.map(function(h){return '&lt;th&gt;'+esc(h)+'&lt;/th&gt;';}).join('')+'&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;'+body.map(function(r){return '&lt;tr&gt;'+r.map(function(c){return '&lt;td&gt;'+esc(c)+'&lt;/td&gt;';}).join('')+'&lt;/tr&gt;';}).join('')+'&lt;/tbody&gt;&lt;/table&gt;';
  var html='&lt;html xmlns:x="urn:schemas-microsoft-com:office:excel"&gt;&lt;head&gt;&lt;meta charset="UTF-8"&gt;&lt;/head&gt;&lt;body&gt;&lt;h2&gt;Расчёт ТСО&lt;/h2&gt;'+table+'&lt;/body&gt;&lt;/html&gt;';
  var blob=new Blob(['\uFEFF'+html],{type:'application/vnd.ms-excel;charset=utf-8;'});
  var url=URL.createObjectURL(blob);var a=document.createElement('a');a.href=url;a.download='TSO_calc_'+new Date().toISOString().slice(0,10)+'.xls';document.body.appendChild(a);a.click();a.remove();
}

function calcCopy(){
  if(!fav.length){alert('Нет позиций');return;}
  var text='КОММЕРЧЕСКИЙ РАСЧЁТ ТСО\n';
  fav.forEach(function(n){
    var r=data.find(x=&gt;x.num===n);
    var c=calc(r);
    var q=calcState.qty[r.num]||1;
    text += r.article+' ('+r.material+') '+r.weight+'кг x '+q+'шт = '+fmt(c.salePerUnit*q)+' ₽\n';
  });
  navigator.clipboard.writeText(text).then(function(){alert('Скопировано!');});
}
function calcPDF(){openInvoiceModal(); setTimeout(function(){window.print();},500);}
function calcExportExcel(){dlExcel();}
function calcSendQuote(){openInvoiceModal();}

/* ==================== ИНИЦИАЛИЗАЦИЯ ==================== */
document.addEventListener('DOMContentLoaded',function(){
  document.getElementById('search').addEventListener('input',function(e){state.search=e.target.value;render();});
  document.querySelectorAll('#tbl th.sortable').forEach(function(th){
    th.addEventListener('click',function(){
      var k=th.dataset.key;
      if(state.sort.key===k) state.sort.dir*=-1;
      else state.sort={key:k, dir:-1};
      render();
    });
  });
  if(localStorage.getItem('tso_data')) loadLocal();
  else { render(); calcRender(); }
});

function globalRender(){
  params.castCost=parseFloat(document.getElementById('pCastCost').value)||0;
  params.castSale=parseFloat(document.getElementById('pCastSale').value)||0;
  params.machining=parseFloat(document.getElementById('pMachining').value)||0;
  params.vat=parseFloat(document.getElementById('pVAT').value)||22;
  render();
  calcRender();
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;</subtitle>
  <updated>2026-10-12T05:31:23Z</updated>
  <logo>https://pandoraburo.ru/ssl/u/pic/07/26436cc3c611f19d9efcd8c4a10874/-/0c723f71762611f1b3964aaba1665535.jpg</logo>
  <author>
    <name>1592962</name>
    <uri>https://www.pandoraburo.ru/</uri>
  </author>
</feed>
