{"id":25048,"date":"2026-11-05T07:55:10","date_gmt":"2026-11-05T07:55:10","guid":{"rendered":"https:\/\/www.mechstream.com\/?p=25048"},"modified":"2026-11-05T07:55:24","modified_gmt":"2026-11-05T07:55:24","slug":"cutting-tool-selection-matching-calculator","status":"publish","type":"post","link":"https:\/\/www.mechstream.com\/ru\/cutting-tool-selection-matching-calculator\/","title":{"rendered":"Cutting Tool Selection &amp; Matching Calculator"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Cutting Tool Selection &amp; Matching Calculator<\/strong> helps machinists compare cutting tool options with their machining requirements. Enter details such as the machining operation, workpiece material, hardness, cutting speed, feed rate, depth of cut, and coolant condition to identify potential tool candidates and review their match scores. Use this calculator for preliminary tool selection and process planning. Always confirm the final choice and cutting parameters with the tool manufacturer\u2019s recommendations.<\/p>\n\n\n\n<iframe class=\"tsm-frame skip-lazy no-lazyload\" data-no-lazy=\"1\" data-skip-lazy=\"1\" title=\"Cutting Tool Selection &amp; Matching Calculator\" scrolling=\"no\" style=\"display:block !important;width:100% !important;max-width:1000px !important;height:620px;border:0 !important;margin:0 auto !important;padding:0 !important;overflow:hidden;background:transparent;float:none !important\" srcdoc=\"&lt;!DOCTYPE html&gt;&#10;&lt;html lang=&quot;en&quot;&gt;&#10;&lt;head&gt;&#10;&lt;meta charset=&quot;UTF-8&quot;&gt;&#10;&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1&quot;&gt;&#10;&lt;title&gt;Cutting Tool Selection &amp;amp; Matching Calculator&lt;\/title&gt;&#10;&lt;style&gt;&#10;\/* =====================================================================&#10;   Cutting Tool Selection &amp; Matching Calculator&#10;   All selectors are scoped under .tsm-root so the calculator never&#10;   changes how the rest of a WordPress page looks.&#10;   ===================================================================== *\/&#10;.tsm-root{&#10;  --tsm-blue:#1e3a8a;--tsm-blue-2:#1d4ed8;--tsm-blue-soft:#e8eefb;--tsm-blue-line:#c7d4f0;&#10;  --tsm-bg:#f3f5f8;--tsm-card:#ffffff;--tsm-text:#1f2937;--tsm-heading:#111827;--tsm-muted:#4b5563;&#10;  --tsm-border:#d6dce5;--tsm-field:#fbfcfe;--tsm-danger:#b42318;--tsm-danger-bg:#fef3f2;&#10;  --tsm-ok:#067647;--tsm-ok-bg:#ecfdf3;--tsm-ok-line:#abefc6;&#10;  --tsm-warn:#93370d;--tsm-warn-bg:#fffaeb;--tsm-warn-line:#fedf89;&#10;  --tsm-radius:12px;--tsm-shadow:0 1px 2px rgba(16,24,40,.06),0 6px 16px rgba(16,24,40,.06);&#10;  font-family:system-ui,-apple-system,&quot;Segoe UI&quot;,Roboto,&quot;Helvetica Neue&quot;,Arial,&quot;Noto Sans&quot;,sans-serif;&#10;  font-size:16px;line-height:1.55;color:var(--tsm-text);background:var(--tsm-bg);&#10;  max-width:1000px;margin:0 auto;padding:28px 16px 36px;text-align:left;border-radius:16px;&#10;  -webkit-text-size-adjust:100%;&#10;}&#10;.tsm-root *,.tsm-root *::before,.tsm-root *::after{box-sizing:border-box}&#10;.tsm-root [hidden]{display:none !important}&#10;.tsm-root h2,.tsm-root h3,.tsm-root h4{font-family:inherit;color:var(--tsm-heading);line-height:1.25;margin:0 0 .5em;padding:0;letter-spacing:normal;text-transform:none;font-weight:700;border:0;background:none}&#10;.tsm-root h2{font-size:clamp(1.2rem,2.4vw,1.5rem)}&#10;.tsm-root h3{font-size:1.05rem}&#10;.tsm-root h4{font-size:.9rem}&#10;.tsm-root p{margin:0 0 .85em;padding:0}&#10;.tsm-root ul{margin:0;padding-left:1.2em}&#10;.tsm-root li{margin:0 0 .3em}&#10;&#10;\/* ---------- layout ---------- *\/&#10;.tsm-root .tsm-stack{display:grid;grid-template-columns:minmax(0,1fr);gap:22px}&#10;.tsm-root .tsm-card{background:var(--tsm-card);border:1px solid var(--tsm-border);border-radius:var(--tsm-radius);box-shadow:var(--tsm-shadow);padding:22px}&#10;&#10;\/* ---------- fields ---------- *\/&#10;.tsm-root .tsm-row{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 14px}&#10;.tsm-root .tsm-field{margin:0 0 14px;min-width:0}&#10;.tsm-root .tsm-field &gt; label{display:block;font-size:.86rem;font-weight:600;color:var(--tsm-text);margin:0 0 5px;line-height:1.3}&#10;.tsm-root .tsm-req{color:var(--tsm-danger);margin-left:2px}&#10;.tsm-root .tsm-inline{display:flex;gap:8px;align-items:stretch}&#10;.tsm-root .tsm-inline .tsm-input{flex:1 1 auto;min-width:0}&#10;.tsm-root .tsm-inline .tsm-unit{flex:0 0 auto;width:auto;min-width:104px;max-width:50%}&#10;.tsm-root .tsm-input,.tsm-root .tsm-select{&#10;  display:block;width:100%;height:42px;margin:0;padding:8px 12px;font:inherit;font-size:15px;line-height:1.3;color:var(--tsm-text);&#10;  background-color:var(--tsm-field);border:1px solid var(--tsm-border);border-radius:8px;box-shadow:none;outline:none;&#10;  transition:border-color .15s,box-shadow .15s;max-width:none;min-height:0;text-transform:none;letter-spacing:normal}&#10;.tsm-root .tsm-select{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding-right:32px;cursor:pointer;&#10;  background-image:url(&quot;data:image\/svg+xml,%3Csvg xmlns=&#x27;http:\/\/www.w3.org\/2000\/svg&#x27; width=&#x27;12&#x27; height=&#x27;8&#x27; viewBox=&#x27;0 0 12 8&#x27;%3E%3Cpath d=&#x27;M1 1.5l5 5 5-5&#x27; fill=&#x27;none&#x27; stroke=&#x27;%231e3a8a&#x27; stroke-width=&#x27;1.8&#x27; stroke-linecap=&#x27;round&#x27; stroke-linejoin=&#x27;round&#x27;\/%3E%3C\/svg%3E&quot;);&#10;  background-repeat:no-repeat;background-position:right 11px center;background-size:12px 8px}&#10;.tsm-root .tsm-input:hover,.tsm-root .tsm-select:hover{border-color:#b6c0ce}&#10;.tsm-root .tsm-input:focus,.tsm-root .tsm-select:focus{border-color:var(--tsm-blue-2);box-shadow:0 0 0 3px rgba(29,78,216,.22);background-color:#fff}&#10;.tsm-root .tsm-invalid{border-color:var(--tsm-danger) !important;background-color:var(--tsm-danger-bg) !important}&#10;.tsm-root .tsm-err{color:var(--tsm-danger);font-size:.82rem;margin:5px 0 0;line-height:1.35}&#10;.tsm-root .tsm-help{color:var(--tsm-muted);font-size:.8rem;margin:5px 0 0;line-height:1.35}&#10;&#10;\/* ---------- buttons ---------- *\/&#10;.tsm-root .tsm-actions{display:flex !important;flex-wrap:wrap;justify-content:center;gap:10px;margin:8px 0 0 !important;padding:0 !important;float:none !important;clear:both}&#10;.tsm-root .tsm-btn{display:inline-flex !important;align-items:center;justify-content:center;float:none !important;width:auto !important;min-width:150px;height:42px;padding:0 20px;margin:0 !important;&#10;  position:static !important;font:inherit;font-size:.95rem;font-weight:650;line-height:1;border-radius:8px;border:1px solid var(--tsm-blue);background:var(--tsm-blue);color:#fff;cursor:pointer;&#10;  text-decoration:none;text-transform:none;letter-spacing:normal;box-shadow:none;transition:background .15s,border-color .15s,color .15s}&#10;.tsm-root .tsm-btn:hover{background:#172f73;border-color:#172f73;color:#fff}&#10;.tsm-root .tsm-btn-sec{background:#fff;color:var(--tsm-blue);border-color:var(--tsm-blue-line)}&#10;.tsm-root .tsm-btn-sec:hover{background:var(--tsm-blue-soft);color:var(--tsm-blue);border-color:var(--tsm-blue)}&#10;.tsm-root .tsm-btn-sm{min-width:0;height:34px;padding:0 12px;font-size:.85rem}&#10;.tsm-root .tsm-btn-x{min-width:0;width:32px !important;height:32px;padding:0;font-size:1.1rem;background:#fff;color:var(--tsm-danger);border-color:#f3c3bd}&#10;.tsm-root .tsm-btn-x:hover{background:var(--tsm-danger-bg);color:var(--tsm-danger);border-color:var(--tsm-danger)}&#10;.tsm-root .tsm-btn:focus-visible,.tsm-root summary:focus-visible{outline:3px solid var(--tsm-blue-2);outline-offset:2px}&#10;&#10;\/* ---------- collapsible ---------- *\/&#10;.tsm-root .tsm-details{border:1px solid var(--tsm-border);border-radius:10px;overflow:hidden;background:#fff}&#10;.tsm-root .tsm-details &gt; summary{cursor:pointer;list-style:none;padding:12px 14px;font-weight:650;font-size:.95rem;color:var(--tsm-blue);background:#f6f8fb;display:flex;align-items:center;justify-content:space-between;gap:10px}&#10;.tsm-root .tsm-details &gt; summary::-webkit-details-marker{display:none}&#10;.tsm-root .tsm-details &gt; summary::after{content:&quot;&quot;;flex:0 0 auto;width:8px;height:8px;border-right:2px solid var(--tsm-blue);border-bottom:2px solid var(--tsm-blue);transform:rotate(45deg);transition:transform .15s;margin:-4px 4px 0 10px}&#10;.tsm-root .tsm-details[open] &gt; summary::after{transform:rotate(-135deg);margin-top:4px}&#10;.tsm-root .tsm-details-body{padding:14px}&#10;.tsm-root .tsm-card &gt; .tsm-details{border:0;border-radius:0;overflow:visible}&#10;.tsm-root .tsm-card &gt; .tsm-details &gt; summary{background:none;padding:0;font-size:clamp(1.05rem,2vw,1.2rem);color:var(--tsm-heading);font-weight:700}&#10;.tsm-root .tsm-card &gt; .tsm-details &gt; .tsm-details-body{padding:14px 0 0}&#10;&#10;\/* ---------- tables ---------- *\/&#10;.tsm-root .tsm-scroll{overflow-x:auto;-webkit-overflow-scrolling:touch;border:1px solid var(--tsm-border);border-radius:10px}&#10;.tsm-root table{border-collapse:collapse;width:100%;margin:0;border:0;font-size:.85rem;background:#fff}&#10;.tsm-root th,.tsm-root td{border:0;border-bottom:1px solid #e5e9f0;padding:6px;text-align:left;vertical-align:top;background:none}&#10;.tsm-root thead th{background:#f6f8fb;color:var(--tsm-heading);font-weight:700;font-size:.78rem;white-space:nowrap;position:sticky;top:0}&#10;&#10;\/* ---------- results ---------- *\/&#10;.tsm-root .tsm-alert{border:1px solid #fecdca;background:var(--tsm-danger-bg);color:var(--tsm-danger);border-radius:10px;padding:12px 14px;margin:0 0 14px;font-size:.9rem}&#10;.tsm-root .tsm-alert ul{margin:6px 0 0;padding-left:1.2em}&#10;.tsm-root .tsm-nomatch{border:1px solid #fecdca;background:var(--tsm-danger-bg);color:#7a271a;border-radius:10px;padding:14px;margin:0 0 16px}&#10;.tsm-root .tsm-nomatch strong{color:var(--tsm-danger)}&#10;.tsm-root .tsm-summary{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:8px;margin:0 0 14px}&#10;.tsm-root .tsm-sum{background:var(--tsm-blue-soft);border:1px solid var(--tsm-blue-line);border-radius:10px;padding:9px 11px;min-width:0}&#10;.tsm-root .tsm-sum span{display:block;font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--tsm-blue)}&#10;.tsm-root .tsm-sum b{display:block;font-size:.92rem;color:var(--tsm-heading);word-break:break-word}&#10;.tsm-root .tsm-sum small{display:block;font-size:.76rem;color:var(--tsm-muted)}&#10;.tsm-root .tsm-method{font-size:.85rem;color:var(--tsm-text);margin:0 0 16px}&#10;.tsm-root .tsm-method .tsm-details-body p:last-child{margin-bottom:0}&#10;.tsm-root .tsm-chips{display:flex;flex-wrap:wrap;gap:6px;margin:0 0 10px;padding:0;list-style:none}&#10;.tsm-root .tsm-chips li{margin:0;background:#f1f4f9;border:1px solid var(--tsm-border);border-radius:999px;padding:3px 10px;font-size:.8rem;font-weight:600}&#10;.tsm-root .tsm-dbnote{font-size:.82rem;color:var(--tsm-warn);background:var(--tsm-warn-bg);border:1px solid var(--tsm-warn-line);border-radius:8px;padding:8px 11px;margin:0 0 14px}&#10;.tsm-root .tsm-dbnote ul{margin:4px 0 0}&#10;&#10;.tsm-root .tsm-list{display:grid;gap:14px;margin:0;padding:0;list-style:none}&#10;.tsm-root .tsm-list &gt; li{margin:0}&#10;.tsm-root .tsm-res{border:1px solid var(--tsm-border);border-radius:12px;padding:16px;background:#fff}&#10;.tsm-root .tsm-res-head{display:flex;gap:12px;align-items:flex-start;justify-content:space-between;flex-wrap:wrap}&#10;.tsm-root .tsm-res-title{min-width:0;flex:1 1 260px}&#10;.tsm-root .tsm-res-title h4{font-size:1rem;margin:0 0 4px}&#10;.tsm-root .tsm-rank{display:inline-block;font-size:.75rem;font-weight:700;color:var(--tsm-muted);margin-right:6px}&#10;.tsm-root .tsm-tag{display:inline-block;font-size:.74rem;font-weight:700;color:var(--tsm-blue);background:var(--tsm-blue-soft);border:1px solid var(--tsm-blue-line);border-radius:999px;padding:1px 9px;margin-right:6px}&#10;.tsm-root .tsm-score{text-align:right;flex:0 0 auto}&#10;.tsm-root .tsm-score-val{font-size:1.75rem;font-weight:800;line-height:1;color:var(--tsm-heading);font-variant-numeric:tabular-nums}&#10;.tsm-root .tsm-badge{display:inline-block;margin-top:6px;font-size:.78rem;font-weight:700;border-radius:999px;padding:3px 10px;border:1px solid}&#10;.tsm-root .tsm-badge.s-strong{color:var(--tsm-ok);background:var(--tsm-ok-bg);border-color:var(--tsm-ok-line)}&#10;.tsm-root .tsm-badge.s-possible{color:var(--tsm-warn);background:var(--tsm-warn-bg);border-color:var(--tsm-warn-line)}&#10;.tsm-root .tsm-badge.s-review{color:var(--tsm-danger);background:var(--tsm-danger-bg);border-color:#fecdca}&#10;.tsm-root .tsm-bar{height:8px;background:#e9edf3;border-radius:999px;overflow:hidden;margin:12px 0}&#10;.tsm-root .tsm-bar &gt; span{display:block;height:100%;border-radius:999px;background:var(--tsm-blue-2)}&#10;.tsm-root .tsm-bar.s-strong &gt; span{background:#079455}.tsm-root .tsm-bar.s-possible &gt; span{background:#dc6803}.tsm-root .tsm-bar.s-review &gt; span{background:#d92d20}&#10;.tsm-root .tsm-why{font-size:.9rem;margin:0 0 10px}&#10;.tsm-root .tsm-cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:0 0 10px}&#10;.tsm-root .tsm-cols ul{list-style:none;padding:0;font-size:.85rem}&#10;.tsm-root .tsm-cols li{padding-left:22px;position:relative}&#10;.tsm-root .tsm-cols li::before{position:absolute;left:0;top:0;font-weight:800}&#10;.tsm-root .tsm-ok li::before{content:&quot;\\2713&quot;;color:var(--tsm-ok)}&#10;.tsm-root .tsm-bad li.m-near::before{content:&quot;\\2248&quot;;color:var(--tsm-warn)}&#10;.tsm-root .tsm-bad li.m-mismatch::before{content:&quot;\\2717&quot;;color:var(--tsm-danger)}&#10;.tsm-root .tsm-bad li.m-unspecified::before{content:&quot;\\2013&quot;;color:var(--tsm-muted)}&#10;.tsm-root .tsm-notes{font-size:.83rem;color:var(--tsm-muted);background:#f6f8fb;border-left:3px solid var(--tsm-blue-line);padding:7px 11px;border-radius:0 8px 8px 0;margin:0 0 10px}&#10;.tsm-root .tsm-break table{font-size:.82rem}&#10;.tsm-root .tsm-break th,.tsm-root .tsm-break td{padding:7px 9px}&#10;.tsm-root .tsm-break td.num,.tsm-root .tsm-break th.num{text-align:right;white-space:nowrap;font-variant-numeric:tabular-nums}&#10;.tsm-root .tsm-formula{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.82rem;background:#0f1f45;color:#e6edff;border-radius:8px;padding:10px 12px;margin:10px 0 0;word-break:break-word}&#10;.tsm-root .tsm-other{margin-top:16px}&#10;.tsm-root .tsm-reminder{font-size:.88rem;border:1px solid var(--tsm-blue-line);background:var(--tsm-blue-soft);color:#172554;border-radius:10px;padding:12px 14px;margin:16px 0 0}&#10;&#10;\/* ---------- responsive ---------- *\/&#10;@media (max-width:760px){&#10;  .tsm-root .tsm-row{grid-template-columns:repeat(2,minmax(0,1fr))}&#10;  .tsm-root .tsm-summary{grid-template-columns:repeat(2,minmax(0,1fr))}&#10;  .tsm-root .tsm-cols{grid-template-columns:minmax(0,1fr)}&#10;}&#10;@media (max-width:560px){&#10;  .tsm-root{padding:20px 12px 28px;border-radius:0}&#10;  .tsm-root .tsm-card{padding:16px}&#10;  .tsm-root .tsm-row{grid-template-columns:minmax(0,1fr)}&#10;  .tsm-root .tsm-actions .tsm-btn{flex:1 1 100%}&#10;  .tsm-root .tsm-score{text-align:left}&#10;}&#10;@media (prefers-reduced-motion:reduce){.tsm-root *{transition:none !important}}&#10;html,body{margin:0;padding:0;background:transparent}&#10;&lt;\/style&gt;&#10;&lt;\/head&gt;&#10;&lt;body&gt;&#10;&#10;&lt;div class=&quot;tsm-root&quot; id=&quot;tsm-app&quot;&gt;&#10;  &lt;div class=&quot;tsm-stack&quot;&gt;&#10;&#10;    &lt;!-- ================= INPUTS ================= --&gt;&#10;    &lt;section class=&quot;tsm-card&quot; aria-label=&quot;Cutting Tool Selection &amp;amp; Matching Calculator&quot;&gt;&#10;&#10;      &lt;form id=&quot;tsm-form&quot; novalidate autocomplete=&quot;off&quot;&gt;&#10;        &lt;div class=&quot;tsm-row&quot;&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-op&quot;&gt;Machining Operation&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;select id=&quot;tsm-op&quot; class=&quot;tsm-select&quot; required aria-describedby=&quot;tsm-op-err&quot;&gt;&#10;              &lt;option value=&quot;&quot;&gt;Select operation\u2026&lt;\/option&gt;&#10;            &lt;\/select&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-op-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-mat&quot;&gt;Workpiece Material&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;select id=&quot;tsm-mat&quot; class=&quot;tsm-select&quot; required aria-describedby=&quot;tsm-mat-err&quot;&gt;&#10;              &lt;option value=&quot;&quot;&gt;Select material\u2026&lt;\/option&gt;&#10;            &lt;\/select&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-mat-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-cool&quot;&gt;Coolant Condition&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;select id=&quot;tsm-cool&quot; class=&quot;tsm-select&quot; required aria-describedby=&quot;tsm-cool-err&quot;&gt;&#10;              &lt;option value=&quot;&quot;&gt;Select coolant\u2026&lt;\/option&gt;&#10;            &lt;\/select&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-cool-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;        &lt;\/div&gt;&#10;&#10;        &lt;div class=&quot;tsm-row&quot;&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-hard&quot;&gt;Workpiece Hardness&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;div class=&quot;tsm-inline&quot;&gt;&#10;              &lt;input id=&quot;tsm-hard&quot; class=&quot;tsm-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; required aria-describedby=&quot;tsm-hard-err tsm-hard-help&quot;&gt;&#10;              &lt;select id=&quot;tsm-hard-unit&quot; class=&quot;tsm-select tsm-unit&quot; aria-label=&quot;Hardness unit&quot;&gt;&#10;                &lt;option value=&quot;HB&quot;&gt;HB&lt;\/option&gt;&lt;option value=&quot;HRC&quot;&gt;HRC&lt;\/option&gt;&lt;option value=&quot;HV&quot;&gt;HV&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-hard-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;p class=&quot;tsm-help&quot; id=&quot;tsm-hard-help&quot;&gt;Converted to approx. HV for comparison (HRC: 20\u201368).&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-vc&quot;&gt;Cutting Speed (Vc)&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;div class=&quot;tsm-inline&quot;&gt;&#10;              &lt;input id=&quot;tsm-vc&quot; class=&quot;tsm-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; required aria-describedby=&quot;tsm-vc-err&quot;&gt;&#10;              &lt;select id=&quot;tsm-vc-unit&quot; class=&quot;tsm-select tsm-unit&quot; aria-label=&quot;Cutting speed unit&quot;&gt;&#10;                &lt;option value=&quot;mmin&quot;&gt;m\/min&lt;\/option&gt;&lt;option value=&quot;ftmin&quot;&gt;ft\/min&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-vc-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-prio&quot;&gt;Priority&lt;\/label&gt;&#10;            &lt;select id=&quot;tsm-prio&quot; class=&quot;tsm-select&quot; aria-describedby=&quot;tsm-prio-help&quot;&gt;&lt;\/select&gt;&#10;            &lt;p class=&quot;tsm-help&quot; id=&quot;tsm-prio-help&quot;&gt;Used for tie-breaking and notes; not scored.&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;        &lt;\/div&gt;&#10;&#10;        &lt;div class=&quot;tsm-row&quot;&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-feed&quot; id=&quot;tsm-feed-label&quot;&gt;Feed Rate&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;div class=&quot;tsm-inline&quot;&gt;&#10;              &lt;input id=&quot;tsm-feed&quot; class=&quot;tsm-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; required aria-describedby=&quot;tsm-feed-err tsm-feed-help&quot;&gt;&#10;              &lt;select id=&quot;tsm-feed-unit&quot; class=&quot;tsm-select tsm-unit&quot; aria-label=&quot;Feed rate unit&quot;&gt;&lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-feed-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;p class=&quot;tsm-help&quot; id=&quot;tsm-feed-help&quot;&gt;Select an operation to show the applicable feed unit.&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;tsm-field&quot;&gt;&#10;            &lt;label for=&quot;tsm-ap&quot;&gt;Depth of Cut (ap)&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&lt;\/label&gt;&#10;            &lt;div class=&quot;tsm-inline&quot;&gt;&#10;              &lt;input id=&quot;tsm-ap&quot; class=&quot;tsm-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; required aria-describedby=&quot;tsm-ap-err&quot;&gt;&#10;              &lt;select id=&quot;tsm-ap-unit&quot; class=&quot;tsm-select tsm-unit&quot; aria-label=&quot;Depth of cut unit&quot;&gt;&#10;                &lt;option value=&quot;mm&quot;&gt;mm&lt;\/option&gt;&lt;option value=&quot;in&quot;&gt;in&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;tsm-err&quot; id=&quot;tsm-ap-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;        &lt;\/div&gt;&#10;&#10;        &lt;div class=&quot;tsm-actions&quot;&gt;&#10;          &lt;button type=&quot;submit&quot; class=&quot;tsm-btn&quot; id=&quot;tsm-btn-find&quot;&gt;Calculate&lt;\/button&gt;&#10;        &lt;\/div&gt;&#10;      &lt;\/form&gt;&#10;    &lt;\/section&gt;&#10;&#10;    &lt;!-- ================= RESULTS ================= --&gt;&#10;    &lt;section class=&quot;tsm-card&quot; aria-labelledby=&quot;tsm-results-title&quot; id=&quot;tsm-results&quot; tabindex=&quot;-1&quot; hidden&gt;&#10;      &lt;h2 id=&quot;tsm-results-title&quot;&gt;Matching Results&lt;\/h2&gt;&#10;      &lt;div class=&quot;tsm-alert&quot; id=&quot;tsm-error&quot; role=&quot;alert&quot; hidden&gt;&lt;\/div&gt;&#10;      &lt;div id=&quot;tsm-result-body&quot; aria-live=&quot;polite&quot;&gt;&lt;\/div&gt;&#10;    &lt;\/section&gt;&#10;&#10;  &lt;\/div&gt;&#10;&lt;\/div&gt;&#10;&#10;&lt;script&gt;&#10;\/* =====================================================================&#10;   Cutting Tool Selection &amp; Matching Calculator \u2014 vanilla JavaScript&#10;   Internal units: hardness \u2192 approx. HV, cutting speed \u2192 m\/min,&#10;   feed \u2192 mm\/rev or mm\/tooth, depth of cut \u2192 mm.&#10;   ===================================================================== *\/&#10;(function () {&#10;  &#x27;use strict&#x27;;&#10;&#10;  var root = document.getElementById(&#x27;tsm-app&#x27;);&#10;  if (!root || root.getAttribute(&#x27;data-tsm-ready&#x27;) === &#x27;1&#x27;) return;&#10;  root.setAttribute(&#x27;data-tsm-ready&#x27;, &#x27;1&#x27;);&#10;  function $(id) { return document.getElementById(id); }&#10;&#10;  \/* =================================================================&#10;     1. EDITABLE CONFIGURATION \u2014 website editors can customise below&#10;     ================================================================= *\/&#10;&#10;  \/\/ Criterion weights (points). The final score = points earned \/ total weight \u00d7 100.&#10;  var WEIGHTS = { operation: 25, material: 25, hardness: 20, speed: 20, coolant: 10 };&#10;&#10;  \/\/ Hardness \/ speed slightly outside a candidate range earn partial credit (&quot;Near range&quot;).&#10;  var NEAR_TOLERANCE = 0.10;   \/\/ \u00b110 % beyond the range limits&#10;  var NEAR_CREDIT = 0.5;       \/\/ 50 % of the criterion weight&#10;&#10;  \/\/ Status thresholds (operation AND material must be matched for Strong \/ Possible;&#10;  \/\/ Strong also requires that no criterion is a Mismatch).&#10;  var THRESHOLDS = { strong: 80, possible: 55 };&#10;&#10;  var OPERATIONS = [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;, &#x27;Grooving&#x27;, &#x27;Parting&#x27;, &#x27;Drilling&#x27;, &#x27;Milling&#x27;];&#10;  var MATERIALS = [&#x27;Aluminum&#x27;, &#x27;Low-Carbon Steel&#x27;, &#x27;Alloy Steel&#x27;, &#x27;Stainless Steel&#x27;, &#x27;Cast Iron&#x27;, &#x27;Titanium Alloy&#x27;, &#x27;Other&#x27;];&#10;  var COOLANTS = [&#x27;Dry&#x27;, &#x27;Flood Coolant&#x27;, &#x27;MQL&#x27;, &#x27;Through-Tool Coolant&#x27;];&#10;  var PRIORITIES = [&#x27;Balanced Performance&#x27;, &#x27;Tool Life&#x27;, &#x27;Surface Finish&#x27;, &#x27;Productivity&#x27;];&#10;  var TOOL_MATERIALS = [&#x27;Carbide&#x27;, &#x27;HSS&#x27;, &#x27;Ceramic&#x27;, &#x27;CBN&#x27;, &#x27;PCD&#x27;];&#10;  var PER_TOOTH_OPERATIONS = [&#x27;Milling&#x27;];   \/\/ operations whose feed is entered per tooth&#10;&#10;  \/* Sample tool candidates \u2014 generic, illustrative ranges only (no manufacturer grades).&#10;     hardness: { min, max, unit: &#x27;HB&#x27; | &#x27;HRC&#x27; | &#x27;HV&#x27; } \u2014 use null for &quot;Not specified&quot;.&#10;     vc:       { min, max } in m\/min \u2014 use null for &quot;Not specified&quot;.            *\/&#10;  var SAMPLE_CANDIDATES = [&#10;    { name: &#x27;CVD-coated carbide turning insert&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;], materials: [&#x27;Low-Carbon Steel&#x27;, &#x27;Alloy Steel&#x27;, &#x27;Cast Iron&#x27;],&#10;      hardness: { min: 120, max: 350, unit: &#x27;HB&#x27; }, vc: { min: 150, max: 350 },&#10;      coolant: [&#x27;Dry&#x27;, &#x27;Flood Coolant&#x27;, &#x27;Through-Tool Coolant&#x27;], strengths: [&#x27;Tool Life&#x27;, &#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;Multilayer CVD coating (e.g., TiCN \/ Al2O3); medium chip-breaker for general steel turning.&#x27; },&#10;    { name: &#x27;PVD-coated carbide insert, sharp positive geometry&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;, &#x27;Grooving&#x27;], materials: [&#x27;Stainless Steel&#x27;, &#x27;Titanium Alloy&#x27;, &#x27;Alloy Steel&#x27;],&#10;      hardness: { min: 150, max: 320, unit: &#x27;HB&#x27; }, vc: { min: 60, max: 200 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;Through-Tool Coolant&#x27;, &#x27;MQL&#x27;], strengths: [&#x27;Surface Finish&#x27;, &#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;PVD coating (e.g., TiAlN); sharp honed edge to limit work hardening and built-up edge.&#x27; },&#10;    { name: &#x27;Carbide grooving \/ parting insert&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Grooving&#x27;, &#x27;Parting&#x27;], materials: [&#x27;Low-Carbon Steel&#x27;, &#x27;Alloy Steel&#x27;, &#x27;Stainless Steel&#x27;, &#x27;Cast Iron&#x27;],&#10;      hardness: null, vc: { min: 80, max: 200 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;Through-Tool Coolant&#x27;], strengths: [&#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;Hardness range not entered (shown as Not specified). Narrow blade style; direct coolant at the cut.&#x27; },&#10;    { name: &#x27;Coated solid carbide drill&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Drilling&#x27;], materials: [&#x27;Low-Carbon Steel&#x27;, &#x27;Alloy Steel&#x27;, &#x27;Stainless Steel&#x27;, &#x27;Cast Iron&#x27;],&#10;      hardness: { min: 150, max: 450, unit: &#x27;HV&#x27; }, vc: { min: 50, max: 150 },&#10;      coolant: [&#x27;Through-Tool Coolant&#x27;, &#x27;Flood Coolant&#x27;, &#x27;MQL&#x27;], strengths: [&#x27;Productivity&#x27;, &#x27;Tool Life&#x27;],&#10;      notes: &#x27;140\u00b0 point; internal coolant channels preferred for deeper holes.&#x27; },&#10;    { name: &#x27;Polished uncoated carbide end mill (non-ferrous)&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Milling&#x27;], materials: [&#x27;Aluminum&#x27;],&#10;      hardness: { min: 30, max: 150, unit: &#x27;HB&#x27; }, vc: { min: 200, max: 1000 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;MQL&#x27;, &#x27;Dry&#x27;], strengths: [&#x27;Productivity&#x27;, &#x27;Surface Finish&#x27;],&#10;      notes: &#x27;High helix, 2\u20133 flutes, polished flutes for chip evacuation in aluminum.&#x27; },&#10;    { name: &#x27;Variable-helix coated carbide end mill&#x27;, toolMaterial: &#x27;Carbide&#x27;,&#10;      operations: [&#x27;Milling&#x27;], materials: [&#x27;Titanium Alloy&#x27;, &#x27;Stainless Steel&#x27;, &#x27;Alloy Steel&#x27;],&#10;      hardness: { min: 200, max: 380, unit: &#x27;HB&#x27; }, vc: { min: 30, max: 120 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;Through-Tool Coolant&#x27;], strengths: [&#x27;Tool Life&#x27;, &#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;Variable pitch \/ helix to reduce chatter; PVD coating; corner radius for edge strength.&#x27; },&#10;    { name: &#x27;HSS-Co twist drill&#x27;, toolMaterial: &#x27;HSS&#x27;,&#10;      operations: [&#x27;Drilling&#x27;], materials: [&#x27;Aluminum&#x27;, &#x27;Low-Carbon Steel&#x27;, &#x27;Alloy Steel&#x27;, &#x27;Cast Iron&#x27;],&#10;      hardness: { min: 80, max: 280, unit: &#x27;HB&#x27; }, vc: { min: 15, max: 60 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;MQL&#x27;], strengths: [&#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;Cobalt HSS; 118\u00b0 \/ 135\u00b0 point; suited to lower-speed or less rigid setups.&#x27; },&#10;    { name: &#x27;HSS parting \/ grooving blade&#x27;, toolMaterial: &#x27;HSS&#x27;,&#10;      operations: [&#x27;Parting&#x27;, &#x27;Grooving&#x27;], materials: [&#x27;Aluminum&#x27;, &#x27;Low-Carbon Steel&#x27;],&#10;      hardness: { min: 60, max: 250, unit: &#x27;HB&#x27; }, vc: { min: 20, max: 60 },&#10;      coolant: [&#x27;Flood Coolant&#x27;], strengths: [&#x27;Balanced Performance&#x27;],&#10;      notes: &#x27;Ground HSS blade; keep overhang short and feed steadily.&#x27; },&#10;    { name: &#x27;Silicon nitride ceramic insert&#x27;, toolMaterial: &#x27;Ceramic&#x27;,&#10;      operations: [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;, &#x27;Milling&#x27;], materials: [&#x27;Cast Iron&#x27;],&#10;      hardness: { min: 150, max: 300, unit: &#x27;HB&#x27; }, vc: { min: 300, max: 800 },&#10;      coolant: [&#x27;Dry&#x27;], strengths: [&#x27;Productivity&#x27;],&#10;      notes: &#x27;Negative rake, chamfered edge; generally run dry \u2014 intermittent coolant risks thermal cracking.&#x27; },&#10;    { name: &#x27;CBN insert for hardened steel&#x27;, toolMaterial: &#x27;CBN&#x27;,&#10;      operations: [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;, &#x27;Grooving&#x27;], materials: [&#x27;Alloy Steel&#x27;, &#x27;Cast Iron&#x27;],&#10;      hardness: { min: 45, max: 65, unit: &#x27;HRC&#x27; }, vc: { min: 100, max: 250 },&#10;      coolant: [&#x27;Dry&#x27;, &#x27;Flood Coolant&#x27;], strengths: [&#x27;Surface Finish&#x27;, &#x27;Tool Life&#x27;],&#10;      notes: &#x27;Chamfered and honed edge (T-land) for finishing hardened parts; requires a rigid setup.&#x27; },&#10;    { name: &#x27;PCD-tipped insert (non-ferrous)&#x27;, toolMaterial: &#x27;PCD&#x27;,&#10;      operations: [&#x27;Turning&#x27;, &#x27;Face Turning&#x27;, &#x27;Milling&#x27;], materials: [&#x27;Aluminum&#x27;],&#10;      hardness: { min: 30, max: 150, unit: &#x27;HB&#x27; }, vc: { min: 300, max: 2500 },&#10;      coolant: [&#x27;Flood Coolant&#x27;, &#x27;MQL&#x27;, &#x27;Dry&#x27;], strengths: [&#x27;Surface Finish&#x27;, &#x27;Tool Life&#x27;, &#x27;Productivity&#x27;],&#10;      notes: &#x27;Not for ferrous materials. Sharp polished edge for high-speed finishing of aluminum alloys.&#x27; }&#10;  ];&#10;&#10;  \/* =================================================================&#10;     2. UNIT CONVERSION&#10;     ================================================================= *\/&#10;  var FEED_UNITS = {&#10;    mmrev:   { label: &#x27;mm\/rev&#x27;,   toMm: 1,    perTooth: false },&#10;    inrev:   { label: &#x27;in\/rev&#x27;,   toMm: 25.4, perTooth: false },&#10;    mmtooth: { label: &#x27;mm\/tooth&#x27;, toMm: 1,    perTooth: true },&#10;    intooth: { label: &#x27;in\/tooth&#x27;, toMm: 25.4, perTooth: true }&#10;  };&#10;  var VC_TO_MMIN = { mmin: 1, ftmin: 0.3048 };&#10;  var AP_TO_MM = { mm: 1, in: 25.4 };&#10;&#10;  \/\/ Approximate HRC \u2192 HV table (steel, after ASTM E140 style tables)&#10;  var HRC_HV = [[20, 238], [22, 248], [25, 266], [28, 286], [30, 302], [32, 318], [35, 345], [38, 372], [40, 392],&#10;                [42, 412], [45, 446], [48, 484], [50, 513], [52, 544], [55, 595], [58, 653], [60, 697], [62, 746],&#10;                [64, 800], [65, 832], [66, 865], [68, 940]];&#10;  var HRC_MIN = 20, HRC_MAX = 68, HB_TO_HV = 1.05;&#10;&#10;  \/** Converts hardness to approximate HV. Returns NaN outside the supported conversion range. *\/&#10;  function toHV(v, unit) {&#10;    if (typeof v !== &#x27;number&#x27; || !isFinite(v) || v &lt;= 0) return NaN;&#10;    if (unit === &#x27;HV&#x27;) return v;&#10;    if (unit === &#x27;HB&#x27;) return v * HB_TO_HV;&#10;    if (unit === &#x27;HRC&#x27;) {&#10;      if (v &lt; HRC_MIN || v &gt; HRC_MAX) return NaN;&#10;      for (var i = 0; i &lt; HRC_HV.length - 1; i++) {&#10;        var a = HRC_HV[i], b = HRC_HV[i + 1];&#10;        if (v &gt;= a[0] &amp;&amp; v &lt;= b[0]) return a[1] + (v - a[0]) * (b[1] - a[1]) \/ (b[0] - a[0]);&#10;      }&#10;    }&#10;    return NaN;&#10;  }&#10;&#10;  \/* =================================================================&#10;     3. HELPERS&#10;     ================================================================= *\/&#10;  function parseNum(raw) {&#10;    var s = String(raw == null ? &#x27;&#x27; : raw).trim().replace(\/\\s+\/g, &#x27;&#x27;);&#10;    if (s === &#x27;&#x27;) return { empty: true, ok: false };&#10;    if (\/^[+-]?\\d{1,3}(,\\d{3})+(\\.\\d+)?$\/.test(s)) s = s.replace(\/,\/g, &#x27;&#x27;);&#10;    else if (\/^[+-]?\\d*,\\d+$\/.test(s)) s = s.replace(&#x27;,&#x27;, &#x27;.&#x27;);&#10;    if (!\/^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$\/i.test(s)) return { empty: false, ok: false };&#10;    var n = Number(s);&#10;    return isFinite(n) ? { empty: false, ok: true, n: n } : { empty: false, ok: false };&#10;  }&#10;  function g(n) {&#10;    if (typeof n !== &#x27;number&#x27; || !isFinite(n)) return &#x27;\u2014&#x27;;&#10;    return Number(n.toPrecision(6)).toLocaleString(&#x27;en-US&#x27;, { maximumFractionDigits: 4 });&#10;  }&#10;  function r0(n) { return Math.round(n).toLocaleString(&#x27;en-US&#x27;); }&#10;  function esc(s) {&#10;    return String(s == null ? &#x27;&#x27; : s).replace(\/[&amp;&lt;&gt;&quot;&#x27;]\/g, function (c) {&#10;      return { &#x27;&amp;&#x27;: &#x27;&amp;amp;&#x27;, &#x27;&lt;&#x27;: &#x27;&amp;lt;&#x27;, &#x27;&gt;&#x27;: &#x27;&amp;gt;&#x27;, &#x27;&quot;&#x27;: &#x27;&amp;quot;&#x27;, &quot;&#x27;&quot;: &#x27;&amp;#39;&#x27; }[c];&#10;    });&#10;  }&#10;  function fillSelect(sel, list, keepFirst) {&#10;    var first = keepFirst ? sel.options[0].outerHTML : &#x27;&#x27;;&#10;    sel.innerHTML = first + list.map(function (v) { return &#x27;&lt;option value=&quot;&#x27; + esc(v) + &#x27;&quot;&gt;&#x27; + esc(v) + &#x27;&lt;\/option&gt;&#x27;; }).join(&#x27;&#x27;);&#10;  }&#10;  function fmtRange(min, max, unit) {&#10;    var u = unit ? &#x27; &#x27; + unit : &#x27;&#x27;;&#10;    if (min != null &amp;&amp; max != null) return g(min) + &#x27;\u2013&#x27; + g(max) + u;&#10;    if (min != null) return &#x27;\u2265 &#x27; + g(min) + u;&#10;    if (max != null) return &#x27;\u2264 &#x27; + g(max) + u;&#10;    return &#x27;Not specified&#x27;;&#10;  }&#10;&#10;  \/** Splits a comma\/semicolon list and maps each item to an allowed canonical value. *\/&#10;  var ALIASES = { &#x27;flood&#x27;: &#x27;Flood Coolant&#x27;, &#x27;through tool&#x27;: &#x27;Through-Tool Coolant&#x27;, &#x27;through-tool&#x27;: &#x27;Through-Tool Coolant&#x27;,&#10;                  &#x27;ttc&#x27;: &#x27;Through-Tool Coolant&#x27;, &#x27;balanced&#x27;: &#x27;Balanced Performance&#x27;, &#x27;aluminium&#x27;: &#x27;Aluminum&#x27;,&#10;                  &#x27;titanium&#x27;: &#x27;Titanium Alloy&#x27;, &#x27;stainless&#x27;: &#x27;Stainless Steel&#x27;, &#x27;face&#x27;: &#x27;Face Turning&#x27; };&#10;  function parseList(str, allowed) {&#10;    var out = [], unknown = [];&#10;    String(str || &#x27;&#x27;).split(\/[,;]\/).forEach(function (raw) {&#10;      var t = raw.trim(); if (!t) return;&#10;      var low = t.toLowerCase(), hit = null;&#10;      for (var i = 0; i &lt; allowed.length; i++) if (allowed[i].toLowerCase() === low) { hit = allowed[i]; break; }&#10;      if (!hit &amp;&amp; ALIASES[low] &amp;&amp; allowed.indexOf(ALIASES[low]) !== -1) hit = ALIASES[low];&#10;      if (hit) { if (out.indexOf(hit) === -1) out.push(hit); } else unknown.push(t);&#10;    });&#10;    return { vals: out, unknown: unknown };&#10;  }&#10;&#10;  \/* =================================================================&#10;     4. CANDIDATE DATA \u2192 normalised objects&#10;     ================================================================= *\/&#10;  function fieldsOf(c) {&#10;    var h = c.hardness || {}, v = c.vc || {};&#10;    function str(x) { return x == null ? &#x27;&#x27; : String(x); }&#10;    return { name: c.name, toolMaterial: c.toolMaterial, operations: (c.operations || []).join(&#x27;,&#x27;),&#10;             materials: (c.materials || []).join(&#x27;,&#x27;), coolant: (c.coolant || []).join(&#x27;,&#x27;), strengths: (c.strengths || []).join(&#x27;,&#x27;),&#10;             hMin: str(h.min), hMax: str(h.max), hUnit: h.unit || &#x27;HB&#x27;, vMin: str(v.min), vMax: str(v.max), notes: c.notes || &#x27;&#x27; };&#10;  }&#10;&#10;  \/** Normalises SAMPLE_CANDIDATES (plus data warnings for editors). *\/&#10;  function readTable() {&#10;    var list = [], warnings = [];&#10;    for (var i = 0; i &lt; SAMPLE_CANDIDATES.length; i++) {&#10;      var f = fieldsOf(SAMPLE_CANDIDATES[i]);&#10;      var name = (f.name || &#x27;&#x27;).trim() || (&#x27;Candidate &#x27; + (i + 1));&#10;      var warn = [];&#10;      var ops = parseList(f.operations, OPERATIONS), mats = parseList(f.materials, MATERIALS);&#10;      var cool = parseList(f.coolant, COOLANTS), str = parseList(f.strengths, PRIORITIES);&#10;      [ops, mats, cool, str].forEach(function (p) { p.unknown.forEach(function (u) { warn.push(&#x27;unrecognized value \u201c&#x27; + u + &#x27;\u201d ignored&#x27;); }); });&#10;&#10;      function num(raw, label) {&#10;        var p = parseNum(raw);&#10;        if (p.empty) return null;&#10;        if (!p.ok || p.n &lt;= 0) { warn.push(label + &#x27; \u201c&#x27; + raw + &#x27;\u201d is not a positive number \u2014 treated as Not specified&#x27;); return null; }&#10;        return p.n;&#10;      }&#10;      var hMin = num(f.hMin, &#x27;hardness min&#x27;), hMax = num(f.hMax, &#x27;hardness max&#x27;);&#10;      var vMin = num(f.vMin, &#x27;Vc min&#x27;), vMax = num(f.vMax, &#x27;Vc max&#x27;);&#10;      if (hMin != null &amp;&amp; hMax != null &amp;&amp; hMin &gt; hMax) { warn.push(&#x27;hardness min &gt; max \u2014 values swapped&#x27;); var t = hMin; hMin = hMax; hMax = t; }&#10;      if (vMin != null &amp;&amp; vMax != null &amp;&amp; vMin &gt; vMax) { warn.push(&#x27;Vc min &gt; max \u2014 values swapped&#x27;); var t2 = vMin; vMin = vMax; vMax = t2; }&#10;&#10;      var hUnit = f.hUnit || &#x27;HB&#x27;, hard = null;&#10;      if (hMin != null || hMax != null) {&#10;        var loHV = hMin != null ? toHV(hMin, hUnit) : null, hiHV = hMax != null ? toHV(hMax, hUnit) : null;&#10;        if ((hMin != null &amp;&amp; isNaN(loHV)) || (hMax != null &amp;&amp; isNaN(hiHV))) {&#10;          warn.push(&#x27;hardness range cannot be converted (HRC must be 20\u201368) \u2014 treated as Not specified&#x27;);&#10;        } else hard = { min: hMin, max: hMax, unit: hUnit, loHV: loHV, hiHV: hiHV };&#10;      }&#10;      if (warn.length) warnings.push({ name: name, items: warn });&#10;      list.push({ idx: i, name: name, toolMaterial: f.toolMaterial || &#x27;&#x27;, operations: ops.vals, materials: mats.vals,&#10;                  hardness: hard, vc: (vMin != null || vMax != null) ? { min: vMin, max: vMax } : null,&#10;                  coolant: cool.vals, strengths: str.vals, notes: (f.notes || &#x27;&#x27;).trim() });&#10;    }&#10;    return { list: list, warnings: warnings };&#10;  }&#10;&#10;  \/* =================================================================&#10;     5. INPUTS &amp; VALIDATION&#10;     ================================================================= *\/&#10;  fillSelect($(&#x27;tsm-op&#x27;), OPERATIONS, true);&#10;  fillSelect($(&#x27;tsm-mat&#x27;), MATERIALS, true);&#10;  fillSelect($(&#x27;tsm-cool&#x27;), COOLANTS, true);&#10;  fillSelect($(&#x27;tsm-prio&#x27;), PRIORITIES, false);&#10;&#10;  \/** Shows only the feed units that apply to the selected operation and relabels the field. *\/&#10;  function updateFeedUnits() {&#10;    var op = $(&#x27;tsm-op&#x27;).value, sel = $(&#x27;tsm-feed-unit&#x27;), prev = sel.value;&#10;    var imperial = prev === &#x27;inrev&#x27; || prev === &#x27;intooth&#x27;;&#10;    var keys, label, help;&#10;    if (!op) {&#10;      keys = [&#x27;mmrev&#x27;, &#x27;mmtooth&#x27;, &#x27;inrev&#x27;, &#x27;intooth&#x27;]; label = &#x27;Feed Rate&#x27;;&#10;      help = &#x27;Select an operation to show the applicable feed unit.&#x27;;&#10;    } else if (PER_TOOTH_OPERATIONS.indexOf(op) !== -1) {&#10;      keys = [&#x27;mmtooth&#x27;, &#x27;intooth&#x27;]; label = &#x27;Feed per Tooth (fz)&#x27;;&#10;      help = op + &#x27; feed is entered per tooth.&#x27;;&#10;    } else {&#10;      keys = [&#x27;mmrev&#x27;, &#x27;inrev&#x27;]; label = &#x27;Feed per Revolution (f)&#x27;;&#10;      help = op + &#x27; feed is entered per revolution.&#x27;;&#10;    }&#10;    sel.innerHTML = keys.map(function (k) { return &#x27;&lt;option value=&quot;&#x27; + k + &#x27;&quot;&gt;&#x27; + FEED_UNITS[k].label + &#x27;&lt;\/option&gt;&#x27;; }).join(&#x27;&#x27;);&#10;    if (keys.indexOf(prev) !== -1) sel.value = prev;&#10;    else sel.value = keys.filter(function (k) { return (k.indexOf(&#x27;in&#x27;) === 0) === imperial; })[0] || keys[0];&#10;    $(&#x27;tsm-feed-label&#x27;).innerHTML = esc(label) + &#x27;&lt;span class=&quot;tsm-req&quot; aria-hidden=&quot;true&quot;&gt;*&lt;\/span&gt;&#x27;;&#10;    $(&#x27;tsm-feed-help&#x27;).textContent = help;&#10;  }&#10;&#10;  var FIELD_LABEL = { op: &#x27;Machining operation&#x27;, mat: &#x27;Workpiece material&#x27;, cool: &#x27;Coolant condition&#x27;,&#10;                      hard: &#x27;Workpiece hardness&#x27;, vc: &#x27;Cutting speed (Vc)&#x27;, feed: &#x27;Feed rate&#x27;, ap: &#x27;Depth of cut (ap)&#x27; };&#10;  var FIELDS = [&#x27;op&#x27;, &#x27;mat&#x27;, &#x27;cool&#x27;, &#x27;hard&#x27;, &#x27;vc&#x27;, &#x27;feed&#x27;, &#x27;ap&#x27;];&#10;&#10;  function setErr(key, msg) {&#10;    var el = $(&#x27;tsm-&#x27; + key), er = $(&#x27;tsm-&#x27; + key + &#x27;-err&#x27;);&#10;    if (msg) { el.classList.add(&#x27;tsm-invalid&#x27;); el.setAttribute(&#x27;aria-invalid&#x27;, &#x27;true&#x27;); er.textContent = msg; er.hidden = false; }&#10;    else { el.classList.remove(&#x27;tsm-invalid&#x27;); el.removeAttribute(&#x27;aria-invalid&#x27;); er.textContent = &#x27;&#x27;; er.hidden = true; }&#10;  }&#10;  function check(key) {&#10;    var el = $(&#x27;tsm-&#x27; + key);&#10;    if (el.tagName === &#x27;SELECT&#x27;) return el.value ? null : &#x27;Please select a &#x27; + FIELD_LABEL[key].toLowerCase() + &#x27;.&#x27;;&#10;    var p = parseNum(el.value);&#10;    if (p.empty) return FIELD_LABEL[key] + &#x27; is required.&#x27;;&#10;    if (!p.ok) return FIELD_LABEL[key] + &#x27; must be a valid number.&#x27;;&#10;    if (p.n &lt;= 0) return FIELD_LABEL[key] + &#x27; must be a positive value greater than zero.&#x27;;&#10;    if (key === &#x27;hard&#x27;) {&#10;      var u = $(&#x27;tsm-hard-unit&#x27;).value;&#10;      if (u === &#x27;HRC&#x27; &amp;&amp; (p.n &lt; HRC_MIN || p.n &gt; HRC_MAX)) return &#x27;HRC values must be between &#x27; + HRC_MIN + &#x27; and &#x27; + HRC_MAX + &#x27; for conversion. Use HB or HV for softer or harder materials.&#x27;;&#10;      if (u === &#x27;HB&#x27; &amp;&amp; p.n &gt; 750) return &#x27;HB value looks too high (max 750). Check the unit.&#x27;;&#10;      if (u === &#x27;HV&#x27; &amp;&amp; p.n &gt; 2500) return &#x27;HV value looks too high (max 2500). Check the unit.&#x27;;&#10;    }&#10;    return null;&#10;  }&#10;  function validateAll() {&#10;    var errs = [];&#10;    FIELDS.forEach(function (k) { var m = check(k); setErr(k, m); if (m) errs.push(m); });&#10;    return errs;&#10;  }&#10;&#10;  \/* =================================================================&#10;     6. MATCHING LOGIC&#10;     ================================================================= *\/&#10;  var CRIT_LABEL = { operation: &#x27;Operation&#x27;, material: &#x27;Workpiece material&#x27;, hardness: &#x27;Hardness&#x27;, speed: &#x27;Cutting speed&#x27;, coolant: &#x27;Coolant&#x27; };&#10;  var STATUS_LABEL = { match: &#x27;Match&#x27;, near: &#x27;Near range&#x27;, mismatch: &#x27;Mismatch&#x27;, unspecified: &#x27;Not specified&#x27; };&#10;&#10;  function listCrit(key, value, list) {&#10;    if (!list.length) return { key: key, status: &#x27;unspecified&#x27;, cand: &#x27;Not specified&#x27;, text: CRIT_LABEL[key] + &#x27;: candidate data not specified&#x27; };&#10;    if (list.indexOf(value) !== -1) return { key: key, status: &#x27;match&#x27;, cand: list.join(&#x27;, &#x27;), text: CRIT_LABEL[key] + &#x27;: &#x27; + value + &#x27; is listed&#x27; };&#10;    var t = CRIT_LABEL[key] + &#x27;: &#x27; + value + &#x27; is not listed (candidate: &#x27; + list.join(&#x27;, &#x27;) + &#x27;)&#x27;;&#10;    if (key === &#x27;material&#x27; &amp;&amp; value === &#x27;Other&#x27;) t = &#x27;Workpiece material: \u201cOther\u201d cannot be confirmed against the listed materials (&#x27; + list.join(&#x27;, &#x27;) + &#x27;)&#x27;;&#10;    return { key: key, status: &#x27;mismatch&#x27;, cand: list.join(&#x27;, &#x27;), text: t };&#10;  }&#10;  function rangeCrit(key, v, lo, hi, candText, userText) {&#10;    if (lo == null &amp;&amp; hi == null) return { key: key, status: &#x27;unspecified&#x27;, cand: &#x27;Not specified&#x27;, text: CRIT_LABEL[key] + &#x27;: candidate range not specified&#x27; };&#10;    var inLo = lo == null || v &gt;= lo, inHi = hi == null || v &lt;= hi;&#10;    if (inLo &amp;&amp; inHi) return { key: key, status: &#x27;match&#x27;, cand: candText, text: CRIT_LABEL[key] + &#x27;: &#x27; + userText + &#x27; is within &#x27; + candText };&#10;    var nearLo = lo == null || v &gt;= lo * (1 - NEAR_TOLERANCE), nearHi = hi == null || v &lt;= hi * (1 + NEAR_TOLERANCE);&#10;    var side = inLo ? &#x27;above&#x27; : &#x27;below&#x27;;&#10;    if (nearLo &amp;&amp; nearHi) return { key: key, status: &#x27;near&#x27;, cand: candText, text: CRIT_LABEL[key] + &#x27;: &#x27; + userText + &#x27; is slightly &#x27; + side + &#x27; &#x27; + candText + &#x27; (within \u00b1&#x27; + (NEAR_TOLERANCE * 100) + &#x27;%)&#x27; };&#10;    return { key: key, status: &#x27;mismatch&#x27;, cand: candText, text: CRIT_LABEL[key] + &#x27;: &#x27; + userText + &#x27; is &#x27; + side + &#x27; &#x27; + candText };&#10;  }&#10;&#10;  function evaluate(c, inp) {&#10;    var crit = [];&#10;    crit.push(listCrit(&#x27;operation&#x27;, inp.op, c.operations));&#10;    crit.push(listCrit(&#x27;material&#x27;, inp.mat, c.materials));&#10;    var h = c.hardness;&#10;    crit.push(h ? rangeCrit(&#x27;hardness&#x27;, inp.hv, h.loHV, h.hiHV,&#10;                  fmtRange(h.min, h.max, h.unit) + (h.unit !== &#x27;HV&#x27; ? &#x27; (\u2248 &#x27; + fmtRange(h.loHV != null ? Math.round(h.loHV) : null, h.hiHV != null ? Math.round(h.hiHV) : null, &#x27;HV&#x27;) + &#x27;)&#x27; : &#x27;&#x27;),&#10;                  inp.hardText)&#10;                : rangeCrit(&#x27;hardness&#x27;, inp.hv, null, null));&#10;    crit.push(c.vc ? rangeCrit(&#x27;speed&#x27;, inp.vc, c.vc.min, c.vc.max, fmtRange(c.vc.min, c.vc.max, &#x27;m\/min&#x27;), g(inp.vc) + &#x27; m\/min&#x27;)&#10;                   : rangeCrit(&#x27;speed&#x27;, inp.vc, null, null));&#10;    crit.push(listCrit(&#x27;coolant&#x27;, inp.cool, c.coolant));&#10;&#10;    var earned = 0, total = 0;&#10;    crit.forEach(function (k) {&#10;      k.weight = WEIGHTS[k.key] || 0;&#10;      k.points = k.status === &#x27;match&#x27; ? k.weight : k.status === &#x27;near&#x27; ? k.weight * NEAR_CREDIT : 0;&#10;      earned += k.points; total += k.weight;&#10;    });&#10;    var score = total &gt; 0 ? earned \/ total * 100 : 0;&#10;    var basicOK = crit[0].status === &#x27;match&#x27; &amp;&amp; crit[1].status === &#x27;match&#x27;;&#10;    var anyMismatch = crit.some(function (k) { return k.status === &#x27;mismatch&#x27;; });&#10;    var status = !basicOK ? &#x27;review&#x27;&#10;               : (score &gt;= THRESHOLDS.strong &amp;&amp; !anyMismatch) ? &#x27;strong&#x27;&#10;               : score &gt;= THRESHOLDS.possible ? &#x27;possible&#x27; : &#x27;review&#x27;;&#10;    var prioFit = c.strengths.indexOf(inp.prio) !== -1;&#10;    return { c: c, crit: crit, earned: earned, total: total, score: score, basicOK: basicOK, status: status, prioFit: prioFit };&#10;  }&#10;&#10;  function explain(R, inp) {&#10;    var c = R.c, by = {};&#10;    R.crit.forEach(function (k) { by[k.key] = k; });&#10;    var parts = [];&#10;    if (R.basicOK) parts.push(&#x27;Listed for &#x27; + inp.op.toLowerCase() + &#x27; of &#x27; + inp.mat.toLowerCase());&#10;    else parts.push(&#x27;Does not meet the basic operation and material requirements in the current data&#x27;);&#10;    var rng = [];&#10;    if (by.hardness.status === &#x27;match&#x27;) rng.push(&#x27;your hardness falls inside its range&#x27;);&#10;    if (by.speed.status === &#x27;match&#x27;) rng.push(&#x27;your cutting speed is within its listed Vc range&#x27;);&#10;    if (by.coolant.status === &#x27;match&#x27;) rng.push(inp.cool.toLowerCase() + &#x27; is a listed coolant condition&#x27;);&#10;    var s = parts[0] + (rng.length ? &#x27;; &#x27; + rng.join(&#x27;, &#x27;) : &#x27;&#x27;) + &#x27;.&#x27;;&#10;    if (c.toolMaterial) {&#10;      var tmNote = { Carbide: &#x27;Carbide offers a general-purpose balance of toughness and wear resistance.&#x27;,&#10;                     HSS: &#x27;HSS is tough and forgiving but is normally limited to lower cutting speeds.&#x27;,&#10;                     Ceramic: &#x27;Ceramic tools favour high speeds and need stable, rigid conditions.&#x27;,&#10;                     CBN: &#x27;CBN is typically aimed at hardened ferrous materials and needs a rigid setup.&#x27;,&#10;                     PCD: &#x27;PCD is typically aimed at non-ferrous materials and is generally not used on steel.&#x27; }[c.toolMaterial];&#10;      if (tmNote) s += &#x27; &#x27; + tmNote;&#10;    }&#10;    if (R.prioFit) s += &#x27; The candidate data lists &#x27; + inp.prio.toLowerCase() + &#x27; as a strength, which aligns with your selected priority.&#x27;;&#10;    else if (c.strengths.length) s += &#x27; Listed strengths (&#x27; + c.strengths.join(&#x27;, &#x27;) + &#x27;) differ from your selected priority (&#x27; + inp.prio + &#x27;).&#x27;;&#10;    return s;&#10;  }&#10;&#10;  \/* =================================================================&#10;     7. RENDERING&#10;     ================================================================= *\/&#10;  var STATUS_TEXT = { strong: &#x27;Strong Match&#x27;, possible: &#x27;Possible Match&#x27;, review: &#x27;Review Required&#x27; };&#10;&#10;  function cardHTML(R, rank, inp) {&#10;    var c = R.c, ok = [], bad = [];&#10;    R.crit.forEach(function (k) {&#10;      if (k.status === &#x27;match&#x27;) ok.push(&#x27;&lt;li&gt;&#x27; + esc(k.text) + &#x27;&lt;\/li&gt;&#x27;);&#10;      else bad.push(&#x27;&lt;li class=&quot;m-&#x27; + k.status + &#x27;&quot;&gt;&lt;span style=&quot;position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)&quot;&gt;&#x27; + STATUS_LABEL[k.status] + &#x27;: &lt;\/span&gt;&#x27; + esc(k.text) + &#x27;&lt;\/li&gt;&#x27;);&#10;    });&#10;    var pct = Math.round(R.score);&#10;    var rows = R.crit.map(function (k) {&#10;      return &#x27;&lt;tr&gt;&lt;th scope=&quot;row&quot;&gt;&#x27; + esc(CRIT_LABEL[k.key]) + &#x27;&lt;\/th&gt;&lt;td&gt;&#x27; + esc(k.cand) + &#x27;&lt;\/td&gt;&lt;td&gt;&#x27; + STATUS_LABEL[k.status] +&#10;             &#x27;&lt;\/td&gt;&lt;td class=&quot;num&quot;&gt;&#x27; + g(k.points) + &#x27; \/ &#x27; + g(k.weight) + &#x27;&lt;\/td&gt;&lt;\/tr&gt;&#x27;;&#10;    }).join(&#x27;&#x27;);&#10;    var formula = &#x27;Score = (&#x27; + R.crit.map(function (k) { return g(k.points); }).join(&#x27; + &#x27;) + &#x27;) \/ &#x27; + g(R.total) +&#10;                  &#x27; \u00d7 100 = &#x27; + g(R.earned) + &#x27; \/ &#x27; + g(R.total) + &#x27; \u00d7 100 = &#x27; + g(Math.round(R.score * 10) \/ 10) + &#x27;%&#x27;;&#10;    return &#x27;&lt;li&gt;&lt;article class=&quot;tsm-res&quot; aria-label=&quot;&#x27; + esc(c.name + &#x27;, &#x27; + pct + &#x27;% match, &#x27; + STATUS_TEXT[R.status]) + &#x27;&quot;&gt;&#x27; +&#10;      &#x27;&lt;div class=&quot;tsm-res-head&quot;&gt;&lt;div class=&quot;tsm-res-title&quot;&gt;&#x27; +&#10;        &#x27;&lt;h4&gt;&lt;span class=&quot;tsm-rank&quot;&gt;#&#x27; + rank + &#x27;&lt;\/span&gt;&#x27; + esc(c.name) + &#x27;&lt;\/h4&gt;&#x27; +&#10;        (c.toolMaterial ? &#x27;&lt;span class=&quot;tsm-tag&quot;&gt;&#x27; + esc(c.toolMaterial) + &#x27;&lt;\/span&gt;&#x27; : &#x27;&#x27;) +&#10;        (R.prioFit ? &#x27;&lt;span class=&quot;tsm-tag&quot;&gt;Priority: &#x27; + esc(inp.prio) + &#x27;&lt;\/span&gt;&#x27; : &#x27;&#x27;) +&#10;      &#x27;&lt;\/div&gt;&lt;div class=&quot;tsm-score&quot;&gt;&lt;div class=&quot;tsm-score-val&quot;&gt;&#x27; + pct + &#x27;%&lt;\/div&gt;&#x27; +&#10;        &#x27;&lt;span class=&quot;tsm-badge s-&#x27; + R.status + &#x27;&quot;&gt;&#x27; + STATUS_TEXT[R.status] + &#x27;&lt;\/span&gt;&lt;\/div&gt;&lt;\/div&gt;&#x27; +&#10;      &#x27;&lt;div class=&quot;tsm-bar s-&#x27; + R.status + &#x27;&quot; role=&quot;img&quot; aria-label=&quot;Match score &#x27; + pct + &#x27; percent&quot;&gt;&lt;span style=&quot;width:&#x27; + Math.max(0, Math.min(100, pct)) + &#x27;%&quot;&gt;&lt;\/span&gt;&lt;\/div&gt;&#x27; +&#10;      &#x27;&lt;p class=&quot;tsm-why&quot;&gt;&lt;strong&gt;Why it may fit:&lt;\/strong&gt; &#x27; + esc(explain(R, inp)) + &#x27;&lt;\/p&gt;&#x27; +&#10;      &#x27;&lt;div class=&quot;tsm-cols&quot;&gt;&#x27; +&#10;        &#x27;&lt;div&gt;&lt;h4&gt;Criteria matched&lt;\/h4&gt;&#x27; + (ok.length ? &#x27;&lt;ul class=&quot;tsm-ok&quot;&gt;&#x27; + ok.join(&#x27;&#x27;) + &#x27;&lt;\/ul&gt;&#x27; : &#x27;&lt;p class=&quot;tsm-help&quot;&gt;None confirmed.&lt;\/p&gt;&#x27;) + &#x27;&lt;\/div&gt;&#x27; +&#10;        &#x27;&lt;div&gt;&lt;h4&gt;Mismatches &amp;amp; not specified&lt;\/h4&gt;&#x27; + (bad.length ? &#x27;&lt;ul class=&quot;tsm-bad&quot;&gt;&#x27; + bad.join(&#x27;&#x27;) + &#x27;&lt;\/ul&gt;&#x27; : &#x27;&lt;p class=&quot;tsm-help&quot;&gt;None.&lt;\/p&gt;&#x27;) + &#x27;&lt;\/div&gt;&#x27; +&#10;      &#x27;&lt;\/div&gt;&#x27; +&#10;      (c.notes ? &#x27;&lt;p class=&quot;tsm-notes&quot;&gt;&lt;strong&gt;Coating \/ geometry notes:&lt;\/strong&gt; &#x27; + esc(c.notes) + &#x27;&lt;\/p&gt;&#x27; : &#x27;&#x27;) +&#10;      &#x27;&lt;details class=&quot;tsm-details tsm-break&quot;&gt;&lt;summary&gt;Score breakdown&lt;\/summary&gt;&lt;div class=&quot;tsm-details-body&quot;&gt;&#x27; +&#10;        &#x27;&lt;div class=&quot;tsm-scroll&quot;&gt;&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th scope=&quot;col&quot;&gt;Criterion&lt;\/th&gt;&lt;th scope=&quot;col&quot;&gt;Candidate data&lt;\/th&gt;&lt;th scope=&quot;col&quot;&gt;Result&lt;\/th&gt;&lt;th scope=&quot;col&quot; class=&quot;num&quot;&gt;Points&lt;\/th&gt;&lt;\/tr&gt;&lt;\/thead&gt;&#x27; +&#10;        &#x27;&lt;tbody&gt;&#x27; + rows + &#x27;&lt;\/tbody&gt;&lt;\/table&gt;&lt;\/div&gt;&#x27; +&#10;        &#x27;&lt;p class=&quot;tsm-formula&quot;&gt;&#x27; + esc(formula) + &#x27;&lt;\/p&gt;&#x27; +&#10;      &#x27;&lt;\/div&gt;&lt;\/details&gt;&#x27; +&#10;    &#x27;&lt;\/article&gt;&lt;\/li&gt;&#x27;;&#10;  }&#10;&#10;  function render(inp, results, warnings) {&#10;    var good = results.filter(function (r) { return r.basicOK; });&#10;    var rest = results.filter(function (r) { return !r.basicOK; });&#10;    var fu = FEED_UNITS[inp.feedUnit];&#10;    var html = &#x27;&#x27;;&#10;&#10;    html += &#x27;&lt;div class=&quot;tsm-summary&quot;&gt;&#x27; +&#10;      sum(&#x27;Operation&#x27;, inp.op) + sum(&#x27;Material&#x27;, inp.mat) +&#10;      sum(&#x27;Hardness&#x27;, inp.hardText, inp.hardUnit === &#x27;HV&#x27; ? &#x27;&#x27; : &#x27;\u2248 &#x27; + r0(inp.hv) + &#x27; HV (approx.)&#x27;) +&#10;      sum(&#x27;Cutting speed&#x27;, g(inp.vc) + &#x27; m\/min&#x27;, inp.vcUnit === &#x27;ftmin&#x27; ? &#x27;from &#x27; + g(inp.vcRaw) + &#x27; ft\/min&#x27; : &#x27;\u2248 &#x27; + r0(inp.vc \/ 0.3048) + &#x27; ft\/min&#x27;) +&#10;      sum(fu.perTooth ? &#x27;Feed per tooth&#x27; : &#x27;Feed per rev&#x27;, g(inp.feedMm) + (fu.perTooth ? &#x27; mm\/tooth&#x27; : &#x27; mm\/rev&#x27;), fu.toMm !== 1 ? &#x27;from &#x27; + g(inp.feedRaw) + &#x27; &#x27; + fu.label : &#x27;Not scored&#x27;) +&#10;      sum(&#x27;Depth of cut&#x27;, g(inp.apMm) + &#x27; mm&#x27;, inp.apUnit === &#x27;in&#x27; ? &#x27;from &#x27; + g(inp.apRaw) + &#x27; in&#x27; : &#x27;Not scored&#x27;) +&#10;      sum(&#x27;Coolant&#x27;, inp.cool) + sum(&#x27;Priority&#x27;, inp.prio, &#x27;Not scored&#x27;) +&#10;    &#x27;&lt;\/div&gt;&#x27;;&#10;&#10;    var totalW = 0; Object.keys(WEIGHTS).forEach(function (k) { totalW += WEIGHTS[k]; });&#10;    html += &#x27;&lt;details class=&quot;tsm-details tsm-method&quot;&gt;&lt;summary&gt;How the score is calculated&lt;\/summary&gt;&lt;div class=&quot;tsm-details-body&quot;&gt;&#x27; +&#10;      &#x27;&lt;ul class=&quot;tsm-chips&quot;&gt;&#x27; + Object.keys(WEIGHTS).map(function (k) { return &#x27;&lt;li&gt;&#x27; + esc(CRIT_LABEL[k]) + &#x27;: &#x27; + WEIGHTS[k] + &#x27; pts&lt;\/li&gt;&#x27;; }).join(&#x27;&#x27;) +&#10;      &#x27;&lt;li&gt;Total: &#x27; + totalW + &#x27; pts&lt;\/li&gt;&lt;\/ul&gt;&#x27; +&#10;      &#x27;&lt;p&gt;Each criterion earns its full weight when the candidate data confirms a match. Hardness and cutting speed that fall up to \u00b1&#x27; + (NEAR_TOLERANCE * 100) +&#10;      &#x27;% beyond the candidate range earn &#x27; + (NEAR_CREDIT * 100) + &#x27;% of the weight (\u201cNear range\u201d). Mismatches and data marked \u201cNot specified\u201d earn 0 points \u2014 missing data is never counted as a confirmed match.&lt;\/p&gt;&#x27; +&#10;      &#x27;&lt;p&gt;&lt;strong&gt;Match score&lt;\/strong&gt; = points earned \u00f7 &#x27; + totalW + &#x27; \u00d7 100. &lt;strong&gt;Strong Match&lt;\/strong&gt;: operation and material matched, no criterion mismatched, and score \u2265 &#x27; + THRESHOLDS.strong +&#10;      &#x27;%. &lt;strong&gt;Possible Match&lt;\/strong&gt;: operation and material matched and score \u2265 &#x27; + THRESHOLDS.possible + &#x27;%. Anything else is &lt;strong&gt;Review Required&lt;\/strong&gt;.&lt;\/p&gt;&#x27; +&#10;      &#x27;&lt;p&gt;Hardness is compared in approximate HV (HB \u00d7 &#x27; + HB_TO_HV + &#x27;; HRC via a steel conversion table). Feed rate and depth of cut are validated and normalized but not scored, because the candidate data contains no feed or depth ranges. Priority is not scored; it breaks ties between equal scores and tailors the notes.&lt;\/p&gt;&#x27; +&#10;      &#x27;&lt;\/div&gt;&lt;\/details&gt;&#x27;;&#10;&#10;    if (warnings.length) {&#10;      html += &#x27;&lt;div class=&quot;tsm-dbnote&quot;&gt;&lt;strong&gt;Candidate data notes:&lt;\/strong&gt;&lt;ul&gt;&#x27; + warnings.map(function (w) {&#10;        return &#x27;&lt;li&gt;&#x27; + esc(w.name) + &#x27;: &#x27; + esc(w.items.join(&#x27;; &#x27;)) + &#x27;&lt;\/li&gt;&#x27;; }).join(&#x27;&#x27;) + &#x27;&lt;\/ul&gt;&lt;\/div&gt;&#x27;;&#10;    }&#10;&#10;    if (!results.length) {&#10;      html += &#x27;&lt;div class=&quot;tsm-nomatch&quot;&gt;&lt;strong&gt;No tool candidates are configured.&lt;\/strong&gt;&lt;\/div&gt;&#x27;;&#10;    } else if (!good.length) {&#10;      html += &#x27;&lt;div class=&quot;tsm-nomatch&quot; role=&quot;status&quot;&gt;&lt;strong&gt;No suitable candidate was found in the current database.&lt;\/strong&gt; None of the &#x27; + results.length +&#10;        &#x27; candidates is listed for both &lt;em&gt;&#x27; + esc(inp.op) + &#x27;&lt;\/em&gt; and &lt;em&gt;&#x27; + esc(inp.mat) + &#x27;&lt;\/em&gt;&#x27; +&#10;        (inp.mat === &#x27;Other&#x27; ? &#x27; (the material \u201cOther\u201d cannot be confirmed against candidate data)&#x27; : &#x27;&#x27;) +&#10;        &#x27;. Consult the tool supplier for a suitable option.&lt;\/div&gt;&#x27;;&#10;    } else {&#10;      html += &#x27;&lt;h3&gt;Ranked candidates (&#x27; + good.length + &#x27;)&lt;\/h3&gt;&lt;ol class=&quot;tsm-list&quot;&gt;&#x27; +&#10;        good.map(function (r, i) { return cardHTML(r, i + 1, inp); }).join(&#x27;&#x27;) + &#x27;&lt;\/ol&gt;&#x27;;&#10;    }&#10;&#10;    if (rest.length) {&#10;      html += &#x27;&lt;details class=&quot;tsm-details tsm-other&quot;&#x27; + (good.length ? &#x27;&#x27; : &#x27; open&#x27;) + &#x27;&gt;&lt;summary&gt;Candidates not meeting the operation \/ material requirements (&#x27; + rest.length + &#x27;)&lt;\/summary&gt;&#x27; +&#10;        &#x27;&lt;div class=&quot;tsm-details-body&quot;&gt;&lt;p class=&quot;tsm-help&quot; style=&quot;margin-top:0&quot;&gt;Shown for reference only. These are not recommended for the selected application based on the current data.&lt;\/p&gt;&#x27; +&#10;        &#x27;&lt;ol class=&quot;tsm-list&quot;&gt;&#x27; + rest.map(function (r, i) { return cardHTML(r, good.length + i + 1, inp); }).join(&#x27;&#x27;) + &#x27;&lt;\/ol&gt;&lt;\/div&gt;&lt;\/details&gt;&#x27;;&#10;    }&#10;&#10;    html += &#x27;&lt;p class=&quot;tsm-reminder&quot;&gt;&lt;strong&gt;Next step:&lt;\/strong&gt; Treat these results as a shortlist for further review. Confirm the final tool choice, grade, geometry and cutting parameters with the tool supplier\u2019s catalog and machining recommendations.&lt;\/p&gt;&#x27;;&#10;&#10;    $(&#x27;tsm-error&#x27;).hidden = true;&#10;    $(&#x27;tsm-result-body&#x27;).innerHTML = html;&#10;    $(&#x27;tsm-result-body&#x27;).hidden = false;&#10;  }&#10;  function sum(label, value, sub) {&#10;    return &#x27;&lt;div class=&quot;tsm-sum&quot;&gt;&lt;span&gt;&#x27; + esc(label) + &#x27;&lt;\/span&gt;&lt;b&gt;&#x27; + esc(value) + &#x27;&lt;\/b&gt;&#x27; + (sub ? &#x27;&lt;small&gt;&#x27; + esc(sub) + &#x27;&lt;\/small&gt;&#x27; : &#x27;&#x27;) + &#x27;&lt;\/div&gt;&#x27;;&#10;  }&#10;  function showErrors(errs) {&#10;    var box = $(&#x27;tsm-error&#x27;);&#10;    box.innerHTML = &#x27;&lt;strong&gt;Please correct the following before matching:&lt;\/strong&gt;&lt;ul&gt;&#x27; + errs.map(function (e) { return &#x27;&lt;li&gt;&#x27; + esc(e) + &#x27;&lt;\/li&gt;&#x27;; }).join(&#x27;&#x27;) + &#x27;&lt;\/ul&gt;&#x27;;&#10;    box.hidden = false;&#10;    $(&#x27;tsm-result-body&#x27;).hidden = true;&#10;  }&#10;&#10;  \/* =================================================================&#10;     8. MAIN&#10;     ================================================================= *\/&#10;  function run() {&#10;    var errs = validateAll();&#10;    if (errs.length) { showErrors(errs); return false; }&#10;&#10;    var hardRaw = parseNum($(&#x27;tsm-hard&#x27;).value).n, hardUnit = $(&#x27;tsm-hard-unit&#x27;).value;&#10;    var vcRaw = parseNum($(&#x27;tsm-vc&#x27;).value).n, vcUnit = $(&#x27;tsm-vc-unit&#x27;).value;&#10;    var feedRaw = parseNum($(&#x27;tsm-feed&#x27;).value).n, feedUnit = $(&#x27;tsm-feed-unit&#x27;).value;&#10;    var apRaw = parseNum($(&#x27;tsm-ap&#x27;).value).n, apUnit = $(&#x27;tsm-ap-unit&#x27;).value;&#10;&#10;    var inp = {&#10;      op: $(&#x27;tsm-op&#x27;).value, mat: $(&#x27;tsm-mat&#x27;).value, cool: $(&#x27;tsm-cool&#x27;).value, prio: $(&#x27;tsm-prio&#x27;).value,&#10;      hardRaw: hardRaw, hardUnit: hardUnit, hardText: g(hardRaw) + &#x27; &#x27; + hardUnit, hv: toHV(hardRaw, hardUnit),&#10;      vcRaw: vcRaw, vcUnit: vcUnit, vc: vcRaw * VC_TO_MMIN[vcUnit],&#10;      feedRaw: feedRaw, feedUnit: feedUnit, feedMm: feedRaw * FEED_UNITS[feedUnit].toMm,&#10;      apRaw: apRaw, apUnit: apUnit, apMm: apRaw * AP_TO_MM[apUnit]&#10;    };&#10;    if (!isFinite(inp.hv) || !isFinite(inp.vc)) { showErrors([&#x27;An input could not be converted. Check the values and units.&#x27;]); return false; }&#10;&#10;    var db = readTable();&#10;    var results = db.list.map(function (c) { return evaluate(c, inp); });&#10;    results.sort(function (a, b) {&#10;      if (a.basicOK !== b.basicOK) return a.basicOK ? -1 : 1;&#10;      if (Math.abs(b.score - a.score) &gt; 1e-9) return b.score - a.score;&#10;      if (a.prioFit !== b.prioFit) return a.prioFit ? -1 : 1;&#10;      return a.c.idx - b.c.idx;&#10;    });&#10;    render(inp, results, db.warnings);&#10;    return true;&#10;  }&#10;&#10;  \/* =================================================================&#10;     9. EVENTS&#10;     ================================================================= *\/&#10;  $(&#x27;tsm-form&#x27;).addEventListener(&#x27;submit&#x27;, function (e) {&#10;    e.preventDefault();&#10;    var res = $(&#x27;tsm-results&#x27;), first = res.hidden;&#10;    res.hidden = false;&#10;    if (!run()) {&#10;      var bad = $(&#x27;tsm-form&#x27;).querySelector(&#x27;.tsm-invalid&#x27;);&#10;      if (bad) bad.focus();&#10;    } else if (first &amp;&amp; res.scrollIntoView) {&#10;      res.scrollIntoView({ behavior: &#x27;smooth&#x27;, block: &#x27;start&#x27; });&#10;    }&#10;  });&#10;&#10;  $(&#x27;tsm-op&#x27;).addEventListener(&#x27;change&#x27;, updateFeedUnits);&#10;  \/\/ Re-check hardness when its unit changes (e.g. 220 is valid HB but not valid HRC)&#10;  $(&#x27;tsm-hard-unit&#x27;).addEventListener(&#x27;change&#x27;, function () { if ($(&#x27;tsm-hard&#x27;).value.trim()) setErr(&#x27;hard&#x27;, check(&#x27;hard&#x27;)); });&#10;&#10;  $(&#x27;tsm-form&#x27;).addEventListener(&#x27;input&#x27;, clearIfValid);&#10;  $(&#x27;tsm-form&#x27;).addEventListener(&#x27;change&#x27;, clearIfValid);&#10;  function clearIfValid(e) {&#10;    var t = e.target, key = (t.id || &#x27;&#x27;).replace(&#x27;tsm-&#x27;, &#x27;&#x27;);&#10;    if (FIELDS.indexOf(key) !== -1 &amp;&amp; t.classList.contains(&#x27;tsm-invalid&#x27;) &amp;&amp; !check(key)) setErr(key, null);&#10;  }&#10;&#10;  \/* ---------- init ---------- *\/&#10;  updateFeedUnits();&#10;})();&#10;&lt;\/script&gt;&#10;&lt;script&gt;&#10;\/* Auto-fit height when embedded in an iframe (no effect on a normal page). *\/&#10;(function () {&#10;  var f = null;&#10;  try { f = window.frameElement; } catch (e) { f = null; }&#10;  if (!f) return;&#10;  var root = document.getElementById(&#x27;tsm-app&#x27;);&#10;  function fit() {&#10;    var h = Math.ceil((root || document.body).getBoundingClientRect().height);&#10;    if (h &gt; 0) f.style.setProperty(&#x27;height&#x27;, h + &#x27;px&#x27;, &#x27;important&#x27;);&#10;  }&#10;  fit();&#10;  if (window.ResizeObserver) new ResizeObserver(fit).observe(root || document.body);&#10;  window.addEventListener(&#x27;load&#x27;, fit);&#10;  window.addEventListener(&#x27;resize&#x27;, fit);&#10;  setTimeout(fit, 300);&#10;})();&#10;&lt;\/script&gt;&#10;&lt;\/body&gt;&#10;&lt;\/html&gt;&#10;\"><\/iframe>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Cutting Tool Selection &amp; Matching Calculator helps machinists compare cutting tool options with their machining requirements. Enter details such as the machining operation, workpiece material,&#8230;<\/p>","protected":false},"author":2,"featured_media":25049,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pmpro_default_level":"","_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_hide_featured_image":false,"_jetpack_featured_image_duplicate_dismissed":0,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[8808],"tags":[3800,1690,8469,8833],"class_list":["post-25048","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-manufacturing-and-processes","tag-cnc-machining","tag-cutting-tool","tag-machining-calculator","tag-tool-selection","pmpro-has-access"],"acf":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/www.mechstream.com\/wp-content\/uploads\/2026\/11\/Cutting-Tool-Selection-Matching-Calculator.png","_links":{"self":[{"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/posts\/25048","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/comments?post=25048"}],"version-history":[{"count":1,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/posts\/25048\/revisions"}],"predecessor-version":[{"id":25050,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/posts\/25048\/revisions\/25050"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/media\/25049"}],"wp:attachment":[{"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/media?parent=25048"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/categories?post=25048"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mechstream.com\/ru\/wp-json\/wp\/v2\/tags?post=25048"}],"curies":[{"name":"WP","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}