{"id":25005,"date":"2026-10-28T06:03:09","date_gmt":"2026-10-28T06:03:09","guid":{"rendered":"https:\/\/www.mechstream.com\/?p=25005"},"modified":"2026-10-28T06:03:12","modified_gmt":"2026-10-28T06:03:12","slug":"spindle-speed-calculator","status":"publish","type":"post","link":"https:\/\/www.mechstream.com\/fr\/spindle-speed-calculator\/","title":{"rendered":"Spindle Speed Calculator"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">The <strong>Spindle Speed Calculator<\/strong> is a practical online tool designed to help CNC operators, machinists, and manufacturing engineers calculate spindle speed, cutting speed, and feed rate for various machining operations. By entering cutting speed, tool diameter, feed per tooth, and other machining parameters, users can quickly determine the appropriate spindle RPM for milling, turning, and drilling. This free calculator also supports unit conversions and machine speed limit checks, making it ideal for CNC machining, metal fabrication, precision manufacturing, and industrial engineering applications.<\/p>\n\n\n\n<iframe class=\"ssc-frame skip-lazy no-lazyload\" data-no-lazy=\"1\" data-skip-lazy=\"1\" title=\"Spindle Speed Calculator\" scrolling=\"no\" style=\"display:block !important;width:100% !important;max-width:1200px !important;height:1500px;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;Spindle Speed Calculator | CNC RPM &amp;amp; Feed Rate&lt;\/title&gt;&#10;&lt;meta name=&quot;description&quot; content=&quot;Calculate spindle speed, cutting speed, and feed rate for CNC milling, turning, and drilling using this free Spindle Speed Calculator.&quot;&gt;&#10;&lt;style&gt;&#10;\/* =====================================================================&#10;   Spindle Speed Calculator \u2014 every selector is scoped under .ssc-root&#10;   so the calculator never changes how the rest of a WordPress page looks.&#10;   ===================================================================== *\/&#10;.ssc-root{&#10;  --ssc-blue:#1e3a8a;--ssc-blue-2:#1d4ed8;--ssc-blue-soft:#e8eefb;--ssc-blue-line:#c7d4f0;&#10;  --ssc-bg:#f3f5f8;--ssc-card:#ffffff;--ssc-text:#1f2937;--ssc-heading:#111827;--ssc-muted:#5b6574;&#10;  --ssc-border:#d6dce5;--ssc-field:#fbfcfe;--ssc-danger:#b42318;--ssc-danger-bg:#fef3f2;&#10;  --ssc-ok:#067647;--ssc-ok-bg:#ecfdf3;--ssc-warn:#b54708;--ssc-warn-bg:#fffaeb;&#10;  --ssc-radius:12px;--ssc-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(--ssc-text);background:var(--ssc-bg);&#10;  max-width:1200px;margin:0 auto;padding:28px 16px 36px;text-align:left;border-radius:16px;&#10;  -webkit-text-size-adjust:100%;&#10;}&#10;.ssc-root *,.ssc-root *::before,.ssc-root *::after{box-sizing:border-box}&#10;.ssc-root [hidden]{display:none !important}&#10;.ssc-root h1,.ssc-root h2,.ssc-root h3,.ssc-root h4{font-family:inherit;color:var(--ssc-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;.ssc-root h1{font-size:clamp(1.7rem,3.4vw,2.35rem)}&#10;.ssc-root h2{font-size:clamp(1.22rem,2.2vw,1.5rem)}&#10;.ssc-root h3{font-size:1.06rem}&#10;.ssc-root h4{font-size:.86rem;color:var(--ssc-muted);margin:0 0 .45em}&#10;.ssc-root p{margin:0 0 .85em;padding:0}&#10;.ssc-root ul,.ssc-root ol{margin:0 0 .9em;padding-left:1.35em}&#10;.ssc-root li{margin:0 0 .35em}&#10;.ssc-root a{color:var(--ssc-blue-2)}&#10;.ssc-root strong{font-weight:700}&#10;.ssc-root code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:.92em;background:var(--ssc-blue-soft);padding:1px 5px;border-radius:4px;color:var(--ssc-blue)}&#10;.ssc-root .ssc-sr{position:absolute !important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;margin:-1px;padding:0;border:0}&#10;&#10;\/* ---------- layout ---------- *\/&#10;.ssc-root .ssc-grid{display:grid;grid-template-columns:minmax(0,1fr);gap:22px;align-items:start}&#10;.ssc-root .ssc-card{background:var(--ssc-card);border:1px solid var(--ssc-border);border-radius:var(--ssc-radius);box-shadow:var(--ssc-shadow);padding:22px;min-width:0}&#10;.ssc-root .ssc-card-title{display:flex;align-items:center;gap:10px;margin-bottom:14px;flex-wrap:wrap}&#10;.ssc-root .ssc-card-title h2{margin:0;flex:1 1 auto}&#10;.ssc-root .ssc-icon{width:22px;height:22px;flex:0 0 auto;color:var(--ssc-blue)}&#10;&#10;\/* ---------- mode switch ---------- *\/&#10;.ssc-root .ssc-modes{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:4px;background:#e9edf4;padding:4px;border-radius:10px;margin:0 0 6px;border:0;min-width:0}&#10;.ssc-root .ssc-modes legend{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}&#10;.ssc-root .ssc-mode{position:relative;display:block;margin:0;cursor:pointer}&#10;.ssc-root .ssc-mode input{position:absolute;opacity:0;width:1px;height:1px;margin:0}&#10;.ssc-root .ssc-mode span{display:block;text-align:center;padding:9px 4px;border-radius:8px;font-size:.84rem;font-weight:650;color:var(--ssc-muted);line-height:1.2;transition:background .15s,color .15s}&#10;.ssc-root .ssc-mode span small{display:block;font-weight:500;font-size:.72rem;opacity:.85;margin-top:2px}&#10;.ssc-root .ssc-mode:hover span{color:var(--ssc-blue)}&#10;.ssc-root .ssc-mode input:checked + span{background:#fff;color:var(--ssc-blue);box-shadow:0 1px 3px rgba(16,24,40,.12)}&#10;.ssc-root .ssc-mode input:focus-visible + span{outline:2px solid var(--ssc-blue-2);outline-offset:1px}&#10;.ssc-root .ssc-mode-hint{font-size:.88rem;color:var(--ssc-muted);margin:6px 2px 16px}&#10;&#10;\/* ---------- fieldsets &amp; fields ---------- *\/&#10;.ssc-root .ssc-set{border:1px solid var(--ssc-border);border-radius:10px;padding:14px 14px 4px;margin:0 0 14px;min-width:0;background:#fff}&#10;.ssc-root .ssc-set &gt; legend{font-weight:700;font-size:.92rem;color:var(--ssc-blue);padding:0 6px;margin:0;width:auto;border:0;float:none}&#10;.ssc-root .ssc-row{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 12px}&#10;.ssc-root .ssc-span2{grid-column:1 \/ -1}&#10;.ssc-root .ssc-field{margin:0 0 12px;min-width:0}&#10;.ssc-root .ssc-field &gt; label,.ssc-root .ssc-label{display:block;font-size:.86rem;font-weight:600;color:var(--ssc-text);margin:0 0 5px;line-height:1.3}&#10;.ssc-root .ssc-opt{font-weight:500;color:var(--ssc-muted);font-size:.78rem}&#10;.ssc-root .ssc-inline{display:flex;gap:8px;align-items:stretch}&#10;.ssc-root .ssc-inline .ssc-input{flex:1 1 auto;min-width:0}&#10;.ssc-root .ssc-inline .ssc-unit{flex:0 0 auto;width:auto;min-width:104px;max-width:48%}&#10;.ssc-root .ssc-input,.ssc-root .ssc-select{&#10;  display:block;width:100%;height:42px;margin:0;padding:8px 12px;font:inherit;font-size:15px;line-height:1.3;color:var(--ssc-text);&#10;  background-color:var(--ssc-field);border:1px solid var(--ssc-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;.ssc-root .ssc-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='http:\/\/www.w3.org\/2000\/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%231e3a8a' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'\/%3E%3C\/svg%3E&quot;);&#10;  background-repeat:no-repeat;background-position:right 11px center;background-size:12px 8px}&#10;.ssc-root .ssc-input:hover,.ssc-root .ssc-select:hover{border-color:#b6c0ce}&#10;.ssc-root .ssc-input:focus,.ssc-root .ssc-select:focus{border-color:var(--ssc-blue-2);box-shadow:0 0 0 3px rgba(29,78,216,.18);background-color:#fff}&#10;.ssc-root .ssc-input.ssc-invalid{border-color:var(--ssc-danger);background-color:var(--ssc-danger-bg)}&#10;.ssc-root .ssc-err{color:var(--ssc-danger);font-size:.82rem;margin:5px 0 0;line-height:1.35}&#10;.ssc-root .ssc-help{color:var(--ssc-muted);font-size:.8rem;margin:5px 0 0;line-height:1.4}&#10;.ssc-root .ssc-opnote{font-size:.83rem;color:var(--ssc-muted);background:#f6f8fb;border-left:3px solid var(--ssc-blue-line);padding:8px 11px;border-radius:0 8px 8px 0;margin:0 0 12px}&#10;&#10;\/* ---------- buttons ---------- *\/&#10;.ssc-root .ssc-btns{display:flex;flex-wrap:wrap;gap:10px;margin-top:6px;align-items:center}&#10;.ssc-root .ssc-btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;height:42px;padding:0 16px;margin:0;font:inherit;font-size:.9rem;font-weight:650;line-height:1;&#10;  border-radius:8px;border:1px solid var(--ssc-border);background:#fff;color:var(--ssc-blue);cursor:pointer;text-decoration:none;text-transform:none;letter-spacing:normal;box-shadow:none;&#10;  transition:background .15s,border-color .15s,color .15s;white-space:nowrap;width:auto;min-width:0;float:none}&#10;.ssc-root .ssc-btn svg{width:16px;height:16px;flex:0 0 auto}&#10;.ssc-root .ssc-btn:hover{background:var(--ssc-blue-soft);border-color:var(--ssc-blue-line);color:var(--ssc-blue)}&#10;.ssc-root .ssc-btn:focus-visible{outline:2px solid var(--ssc-blue-2);outline-offset:2px}&#10;.ssc-root .ssc-btn[disabled]{opacity:.5;cursor:not-allowed}&#10;.ssc-root .ssc-btn.ssc-btn-primary{background:var(--ssc-blue);border-color:var(--ssc-blue);color:#fff}&#10;.ssc-root .ssc-btn.ssc-btn-primary:hover{background:#172f73;border-color:#172f73;color:#fff}&#10;.ssc-root .ssc-btn.ssc-btn-sm{height:34px;padding:0 11px;font-size:.8rem}&#10;.ssc-root .ssc-btn.ssc-btn-danger{color:var(--ssc-danger)}&#10;.ssc-root .ssc-btn.ssc-btn-danger:hover{background:var(--ssc-danger-bg);border-color:#fecdca;color:var(--ssc-danger)}&#10;.ssc-root .ssc-calc-wrap{display:block !important;width:100% !important;text-align:center !important;margin:6px 0 0 !important;padding:0 !important;float:none !important;clear:both}&#10;.ssc-root .ssc-calc-wrap .ssc-btn{display:inline-flex !important;float:none !important;width:auto !important;min-width:200px;margin:0 auto !important;position:static !important}&#10;.ssc-root .ssc-actions{margin-top:14px;padding-top:12px;border-top:1px dashed var(--ssc-border)}&#10;.ssc-root .ssc-actions .ssc-select{width:auto;flex:1 1 220px;min-width:0}&#10;.ssc-root .ssc-status{min-height:1.3em;font-size:.84rem;color:var(--ssc-ok);margin:10px 0 0}&#10;&#10;\/* ---------- results ---------- *\/&#10;.ssc-root .ssc-alert{border:1px solid #fecdca;background:var(--ssc-danger-bg);color:var(--ssc-danger);border-radius:10px;padding:12px 14px;margin:0 0 14px;font-size:.9rem}&#10;.ssc-root .ssc-alert ul{margin:6px 0 0;padding-left:1.2em}&#10;.ssc-root .ssc-pcard{border-radius:12px;padding:16px 18px 14px;background:linear-gradient(135deg,#1e3a8a 0%,#1d4ed8 100%);color:#fff;margin-bottom:12px}&#10;.ssc-root .ssc-pcard-label{font-size:.8rem;font-weight:700;text-transform:uppercase;letter-spacing:.05em;opacity:.92;margin-bottom:4px}&#10;.ssc-root .ssc-pcard-value{font-size:clamp(1.9rem,4vw,2.6rem);font-weight:800;line-height:1.12;word-break:break-word;font-variant-numeric:tabular-nums}&#10;.ssc-root .ssc-pcard-unit{font-size:.48em;font-weight:700;opacity:.92;margin-left:6px}&#10;.ssc-root .ssc-pcard-sub{font-size:.88rem;opacity:.92;margin-top:6px}&#10;.ssc-root .ssc-stats{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin-bottom:12px}&#10;.ssc-root .ssc-stat{border:1px solid #e3e8f0;border-radius:10px;padding:10px 12px;background:#fafbfd;min-width:0}&#10;.ssc-root .ssc-stat-l{display:block;font-size:.76rem;font-weight:700;color:var(--ssc-muted);text-transform:uppercase;letter-spacing:.03em;line-height:1.3}&#10;.ssc-root .ssc-stat-v{display:block;font-size:1.18rem;font-weight:800;color:var(--ssc-heading);font-variant-numeric:tabular-nums;word-break:break-word;margin-top:2px}&#10;.ssc-root .ssc-stat-v small{font-size:.7em;font-weight:700;color:var(--ssc-muted);margin-left:3px}&#10;.ssc-root .ssc-stat-s{display:block;font-size:.78rem;color:var(--ssc-muted);margin-top:2px;line-height:1.35}&#10;.ssc-root .ssc-stat.ssc-st-ok{border-color:#abefc6;background:var(--ssc-ok-bg)}&#10;.ssc-root .ssc-stat.ssc-st-ok .ssc-stat-v{color:var(--ssc-ok)}&#10;.ssc-root .ssc-stat.ssc-st-bad{border-color:#fedf89;background:var(--ssc-warn-bg)}&#10;.ssc-root .ssc-stat.ssc-st-bad .ssc-stat-v{color:var(--ssc-warn)}&#10;.ssc-root .ssc-msgs{list-style:none;padding:0;margin:0 0 12px}&#10;.ssc-root .ssc-msg{display:flex;gap:9px;align-items:flex-start;border-radius:10px;padding:10px 12px;margin:0 0 8px;font-size:.87rem;line-height:1.45;border:1px solid}&#10;.ssc-root .ssc-msg svg{width:18px;height:18px;flex:0 0 auto;margin-top:1px}&#10;.ssc-root .ssc-msg-warn{background:var(--ssc-warn-bg);border-color:#fedf89;color:#7a2e0e}&#10;.ssc-root .ssc-msg-warn svg{color:var(--ssc-warn)}&#10;.ssc-root .ssc-msg-info{background:#f6f8fb;border-color:#dfe5ee;color:#344054}&#10;.ssc-root .ssc-msg-info svg{color:var(--ssc-blue-2)}&#10;.ssc-root .ssc-msg-ok{background:var(--ssc-ok-bg);border-color:#abefc6;color:#054f31}&#10;.ssc-root .ssc-msg-ok svg{color:var(--ssc-ok)}&#10;.ssc-root .ssc-res-block{border-top:1px solid #e7ebf1;padding-top:14px;margin-top:14px}&#10;.ssc-root .ssc-dl{display:grid;grid-template-columns:minmax(120px,44%) 1fr;gap:0;margin:0;font-size:.88rem;border:1px solid #e7ebf1;border-radius:8px;overflow:hidden}&#10;.ssc-root .ssc-dl dt,.ssc-root .ssc-dl dd{margin:0;padding:7px 10px;border-bottom:1px solid #eef1f5;word-break:break-word}&#10;.ssc-root .ssc-dl dt{font-weight:600;color:var(--ssc-muted);background:#fafbfd}&#10;.ssc-root .ssc-dl dt:last-of-type,.ssc-root .ssc-dl dd:last-of-type{border-bottom:0}&#10;.ssc-root .ssc-eq-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}&#10;.ssc-root .ssc-eq{border:1px solid #e3e8f0;border-radius:8px;padding:8px 10px;background:#fafbfd;min-width:0}&#10;.ssc-root .ssc-eq-v{display:block;font-weight:700;font-size:.95rem;word-break:break-word;font-variant-numeric:tabular-nums;color:var(--ssc-heading)}&#10;.ssc-root .ssc-eq-u{display:block;font-size:.76rem;color:var(--ssc-muted)}&#10;.ssc-root .ssc-small{font-size:.8rem;color:var(--ssc-muted);margin:8px 0 0}&#10;&#10;\/* ---------- responsive ---------- *\/&#10;@media (max-width:960px){&#10;  .ssc-root .ssc-grid{grid-template-columns:minmax(0,1fr)}&#10;}&#10;@media (max-width:560px){&#10;  .ssc-root{padding:20px 12px 28px;border-radius:0}&#10;  .ssc-root .ssc-card{padding:16px}&#10;  .ssc-root .ssc-modes{grid-template-columns:repeat(2,minmax(0,1fr))}&#10;  .ssc-root .ssc-row{grid-template-columns:minmax(0,1fr)}&#10;  .ssc-root .ssc-stats{grid-template-columns:minmax(0,1fr)}&#10;  .ssc-root .ssc-btns .ssc-btn{flex:1 1 auto}&#10;  .ssc-root .ssc-dl{grid-template-columns:minmax(0,1fr)}&#10;  .ssc-root .ssc-dl dt{border-bottom:0;padding-bottom:2px}&#10;}&#10;@media (prefers-reduced-motion:reduce){&#10;  .ssc-root *{transition:none !important}&#10;}&#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;ssc-root&quot; id=&quot;ssc-app&quot;&gt;&#10;&#10;  &lt;div class=&quot;ssc-grid&quot;&gt;&#10;&#10;    &lt;!-- ================= INPUT PANEL ================= --&gt;&#10;    &lt;section class=&quot;ssc-card&quot; aria-labelledby=&quot;ssc-inputs-title&quot;&gt;&#10;      &lt;div class=&quot;ssc-card-title&quot;&gt;&#10;        &lt;svg class=&quot;ssc-icon&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.8&quot; stroke-linecap=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 6h10M18 6h2M4 12h4M12 12h8M4 18h12&quot;\/&gt;&lt;circle cx=&quot;16&quot; cy=&quot;6&quot; r=&quot;2&quot;\/&gt;&lt;circle cx=&quot;10&quot; cy=&quot;12&quot; r=&quot;2&quot;\/&gt;&lt;circle cx=&quot;18&quot; cy=&quot;18&quot; r=&quot;2&quot;\/&gt;&lt;\/svg&gt;&#10;        &lt;h2 id=&quot;ssc-inputs-title&quot;&gt;Machining Inputs&lt;\/h2&gt;&#10;      &lt;\/div&gt;&#10;&#10;      &lt;form id=&quot;ssc-form&quot; novalidate autocomplete=&quot;off&quot;&gt;&#10;        &lt;fieldset class=&quot;ssc-modes&quot;&gt;&#10;          &lt;legend&gt;Calculation mode&lt;\/legend&gt;&#10;          &lt;label class=&quot;ssc-mode&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;ssc-mode&quot; value=&quot;rpm&quot; checked&gt;&lt;span&gt;Spindle Speed&lt;small&gt;N from Vc, D&lt;\/small&gt;&lt;\/span&gt;&lt;\/label&gt;&#10;          &lt;label class=&quot;ssc-mode&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;ssc-mode&quot; value=&quot;vc&quot;&gt;&lt;span&gt;Cutting Speed&lt;small&gt;Vc from N, D&lt;\/small&gt;&lt;\/span&gt;&lt;\/label&gt;&#10;          &lt;label class=&quot;ssc-mode&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;ssc-mode&quot; value=&quot;feed&quot;&gt;&lt;span&gt;Feed Rate&lt;small&gt;F from N, feed&lt;\/small&gt;&lt;\/span&gt;&lt;\/label&gt;&#10;          &lt;label class=&quot;ssc-mode&quot;&gt;&lt;input type=&quot;radio&quot; name=&quot;ssc-mode&quot; value=&quot;limit&quot;&gt;&lt;span&gt;Max RPM Check&lt;small&gt;min(N, Nmax)&lt;\/small&gt;&lt;\/span&gt;&lt;\/label&gt;&#10;        &lt;\/fieldset&gt;&#10;        &lt;p class=&quot;ssc-mode-hint&quot; id=&quot;ssc-mode-hint&quot;&gt;&lt;\/p&gt;&#10;&#10;        &lt;!-- Operation &amp; material --&gt;&#10;        &lt;fieldset class=&quot;ssc-set&quot;&gt;&#10;          &lt;legend&gt;Operation &amp;amp; Material&lt;\/legend&gt;&#10;          &lt;div class=&quot;ssc-row&quot;&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-op&quot;&gt;Machining operation&lt;\/label&gt;&#10;              &lt;select id=&quot;ssc-op&quot; class=&quot;ssc-select&quot;&gt;&#10;                &lt;option value=&quot;milling&quot;&gt;CNC Milling&lt;\/option&gt;&#10;                &lt;option value=&quot;turning&quot;&gt;CNC Turning&lt;\/option&gt;&#10;                &lt;option value=&quot;drilling&quot;&gt;Drilling&lt;\/option&gt;&#10;                &lt;option value=&quot;boring&quot;&gt;Boring&lt;\/option&gt;&#10;                &lt;option value=&quot;reaming&quot;&gt;Reaming&lt;\/option&gt;&#10;                &lt;option value=&quot;tapping&quot;&gt;Tapping&lt;\/option&gt;&#10;                &lt;option value=&quot;face&quot;&gt;Face Milling&lt;\/option&gt;&#10;                &lt;option value=&quot;end&quot;&gt;End Milling&lt;\/option&gt;&#10;                &lt;option value=&quot;slot&quot;&gt;Slot Milling&lt;\/option&gt;&#10;                &lt;option value=&quot;custom&quot;&gt;Custom Operation&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-tool&quot;&gt;Tool material&lt;\/label&gt;&#10;              &lt;select id=&quot;ssc-tool&quot; class=&quot;ssc-select&quot;&gt;&#10;                &lt;option value=&quot;hss&quot;&gt;High-Speed Steel (HSS)&lt;\/option&gt;&#10;                &lt;option value=&quot;carbide&quot;&gt;Carbide (uncoated)&lt;\/option&gt;&#10;                &lt;option value=&quot;coated&quot;&gt;Coated Carbide&lt;\/option&gt;&#10;                &lt;option value=&quot;ceramic&quot;&gt;Ceramic&lt;\/option&gt;&#10;                &lt;option value=&quot;other&quot;&gt;Other \/ Not specified&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-customop-wrap&quot; hidden&gt;&#10;              &lt;label for=&quot;ssc-customop&quot;&gt;Custom operation name &lt;span class=&quot;ssc-opt&quot;&gt;(optional)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-customop&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; maxlength=&quot;60&quot; placeholder=&quot;e.g. Thread milling&quot;&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-feedtype-wrap&quot; hidden&gt;&#10;              &lt;label for=&quot;ssc-feedtype&quot;&gt;Feed model&lt;\/label&gt;&#10;              &lt;select id=&quot;ssc-feedtype&quot; class=&quot;ssc-select&quot;&gt;&#10;                &lt;option value=&quot;tooth&quot;&gt;Feed per tooth \u00d7 teeth&lt;\/option&gt;&#10;                &lt;option value=&quot;rev&quot;&gt;Feed per revolution&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;          &lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-opnote&quot; id=&quot;ssc-opnote&quot;&gt;&lt;\/p&gt;&#10;          &lt;div class=&quot;ssc-row&quot;&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-mat-search&quot;&gt;Search material category&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-mat-search&quot; class=&quot;ssc-input&quot; type=&quot;search&quot; placeholder=&quot;e.g. 304, brass, Inconel&quot; aria-describedby=&quot;ssc-mat-count&quot;&gt;&#10;              &lt;p class=&quot;ssc-help&quot; id=&quot;ssc-mat-count&quot; aria-live=&quot;polite&quot;&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-mat&quot;&gt;Workpiece material category&lt;\/label&gt;&#10;              &lt;select id=&quot;ssc-mat&quot; class=&quot;ssc-select&quot;&gt;&lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field ssc-span2&quot; id=&quot;ssc-custommat-wrap&quot; hidden&gt;&#10;              &lt;label for=&quot;ssc-custommat&quot;&gt;Custom material name &lt;span class=&quot;ssc-opt&quot;&gt;(optional)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-custommat&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; maxlength=&quot;60&quot; placeholder=&quot;e.g. Hardened tool steel 52 HRC&quot;&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field ssc-span2&quot;&gt;&#10;              &lt;label for=&quot;ssc-coating&quot;&gt;Tool coating \/ grade &lt;span class=&quot;ssc-opt&quot;&gt;(optional, for your records)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-coating&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; maxlength=&quot;60&quot; placeholder=&quot;e.g. TiAlN, uncoated polished, grade P25&quot;&gt;&#10;            &lt;\/div&gt;&#10;          &lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-help&quot; style=&quot;margin:-4px 0 12px&quot;&gt;The material category is recorded with the results. No cutting speed is assigned automatically.&lt;\/p&gt;&#10;        &lt;\/fieldset&gt;&#10;&#10;        &lt;!-- Cutting conditions --&gt;&#10;        &lt;fieldset class=&quot;ssc-set&quot;&gt;&#10;          &lt;legend&gt;Cutting Conditions&lt;\/legend&gt;&#10;          &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-vc-wrap&quot;&gt;&#10;            &lt;label for=&quot;ssc-vc&quot;&gt;Cutting speed (Vc)&lt;\/label&gt;&#10;            &lt;div class=&quot;ssc-inline&quot;&gt;&#10;              &lt;input id=&quot;ssc-vc&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-vc-err ssc-vc-help&quot;&gt;&#10;              &lt;select id=&quot;ssc-vc-unit&quot; class=&quot;ssc-select ssc-unit&quot; data-ssc-conv=&quot;vc&quot; data-ssc-for=&quot;ssc-vc&quot; aria-label=&quot;Cutting speed unit&quot;&gt;&#10;                &lt;option value=&quot;mmin&quot;&gt;m\/min&lt;\/option&gt;&#10;                &lt;option value=&quot;ftmin&quot;&gt;ft\/min (SFM)&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-vc-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;p class=&quot;ssc-help&quot; id=&quot;ssc-vc-help&quot;&gt;Enter the cutting speed from your tool supplier. Cutting speed depends on material grade, tool, coating and coolant.&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-d-wrap&quot;&gt;&#10;            &lt;label for=&quot;ssc-d&quot; id=&quot;ssc-d-label&quot;&gt;Tool (cutter) diameter (D)&lt;\/label&gt;&#10;            &lt;div class=&quot;ssc-inline&quot;&gt;&#10;              &lt;input id=&quot;ssc-d&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-d-err ssc-d-help&quot;&gt;&#10;              &lt;select id=&quot;ssc-d-unit&quot; class=&quot;ssc-select ssc-unit&quot; data-ssc-conv=&quot;len&quot; data-ssc-for=&quot;ssc-d&quot; aria-label=&quot;Diameter unit&quot;&gt;&#10;                &lt;option value=&quot;mm&quot;&gt;mm&lt;\/option&gt;&#10;                &lt;option value=&quot;cm&quot;&gt;cm&lt;\/option&gt;&#10;                &lt;option value=&quot;in&quot;&gt;inches&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-d-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;p class=&quot;ssc-help&quot; id=&quot;ssc-d-help&quot;&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-n-wrap&quot; hidden&gt;&#10;            &lt;label for=&quot;ssc-n&quot;&gt;Spindle speed (N)&lt;\/label&gt;&#10;            &lt;div class=&quot;ssc-inline&quot;&gt;&#10;              &lt;input id=&quot;ssc-n&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-n-err&quot;&gt;&#10;              &lt;span class=&quot;ssc-input ssc-unit&quot; style=&quot;display:flex;align-items:center;justify-content:center;background:#f3f6fb;color:var(--ssc-muted)&quot; aria-hidden=&quot;true&quot;&gt;RPM&lt;\/span&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-n-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;        &lt;\/fieldset&gt;&#10;&#10;        &lt;!-- Feed --&gt;&#10;        &lt;fieldset class=&quot;ssc-set&quot; id=&quot;ssc-feed-set&quot;&gt;&#10;          &lt;legend id=&quot;ssc-feed-legend&quot;&gt;Feed Parameters&lt;\/legend&gt;&#10;          &lt;div class=&quot;ssc-row&quot; id=&quot;ssc-tooth-wrap&quot;&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-fz&quot;&gt;Feed per tooth (fz)&lt;\/label&gt;&#10;              &lt;div class=&quot;ssc-inline&quot;&gt;&#10;                &lt;input id=&quot;ssc-fz&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-fz-err&quot;&gt;&#10;                &lt;select id=&quot;ssc-fz-unit&quot; class=&quot;ssc-select ssc-unit&quot; data-ssc-conv=&quot;flen&quot; data-ssc-for=&quot;ssc-fz&quot; aria-label=&quot;Feed per tooth unit&quot;&gt;&#10;                  &lt;option value=&quot;mm&quot;&gt;mm\/tooth&lt;\/option&gt;&#10;                  &lt;option value=&quot;in&quot;&gt;in\/tooth&lt;\/option&gt;&#10;                &lt;\/select&gt;&#10;              &lt;\/div&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-fz-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-z&quot;&gt;Number of cutting teeth (z)&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-z&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;numeric&quot; aria-describedby=&quot;ssc-z-err&quot;&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-z-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-rev-wrap&quot; hidden&gt;&#10;            &lt;label for=&quot;ssc-fr&quot;&gt;Feed per revolution (f)&lt;\/label&gt;&#10;            &lt;div class=&quot;ssc-inline&quot;&gt;&#10;              &lt;input id=&quot;ssc-fr&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-fr-err&quot;&gt;&#10;              &lt;select id=&quot;ssc-fr-unit&quot; class=&quot;ssc-select ssc-unit&quot; data-ssc-conv=&quot;flen&quot; data-ssc-for=&quot;ssc-fr&quot; aria-label=&quot;Feed per revolution unit&quot;&gt;&#10;                &lt;option value=&quot;mm&quot;&gt;mm\/rev&lt;\/option&gt;&#10;                &lt;option value=&quot;in&quot;&gt;in\/rev&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-fr-err&quot; hidden&gt;&lt;\/p&gt;&#10;          &lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-row&quot; id=&quot;ssc-pitch-wrap&quot; hidden&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-thread&quot;&gt;Thread specification&lt;\/label&gt;&#10;              &lt;select id=&quot;ssc-thread&quot; class=&quot;ssc-select&quot;&gt;&#10;                &lt;option value=&quot;mm&quot;&gt;Metric pitch (mm\/rev)&lt;\/option&gt;&#10;                &lt;option value=&quot;tpi&quot;&gt;Inch thread (threads per inch)&lt;\/option&gt;&#10;              &lt;\/select&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-pitch&quot; id=&quot;ssc-pitch-label&quot;&gt;Thread pitch (P)&lt;\/label&gt;&#10;              &lt;input id=&quot;ssc-pitch&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-pitch-err&quot;&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-pitch-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;          &lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-help&quot; id=&quot;ssc-feed-help&quot; style=&quot;margin:-4px 0 12px&quot;&gt;&lt;\/p&gt;&#10;        &lt;\/fieldset&gt;&#10;&#10;        &lt;!-- Machine --&gt;&#10;        &lt;fieldset class=&quot;ssc-set&quot;&gt;&#10;          &lt;legend&gt;Machine Parameters&lt;\/legend&gt;&#10;          &lt;div class=&quot;ssc-row&quot;&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-max&quot;&gt;Maximum spindle speed &lt;span class=&quot;ssc-opt&quot; id=&quot;ssc-max-opt&quot;&gt;(optional)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;div class=&quot;ssc-inline&quot;&gt;&#10;                &lt;input id=&quot;ssc-max&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-max-err&quot;&gt;&#10;                &lt;span class=&quot;ssc-input ssc-unit&quot; style=&quot;display:flex;align-items:center;justify-content:center;background:#f3f6fb;color:var(--ssc-muted);min-width:64px&quot; aria-hidden=&quot;true&quot;&gt;RPM&lt;\/span&gt;&#10;              &lt;\/div&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-max-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot; id=&quot;ssc-optgt-wrap&quot;&gt;&#10;              &lt;label for=&quot;ssc-optgt&quot;&gt;Operator-selected RPM &lt;span class=&quot;ssc-opt&quot;&gt;(optional)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;div class=&quot;ssc-inline&quot;&gt;&#10;                &lt;input id=&quot;ssc-optgt&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-optgt-err&quot;&gt;&#10;                &lt;span class=&quot;ssc-input ssc-unit&quot; style=&quot;display:flex;align-items:center;justify-content:center;background:#f3f6fb;color:var(--ssc-muted);min-width:64px&quot; aria-hidden=&quot;true&quot;&gt;RPM&lt;\/span&gt;&#10;              &lt;\/div&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-optgt-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-inc&quot;&gt;Spindle speed increment &lt;span class=&quot;ssc-opt&quot;&gt;(optional)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;div class=&quot;ssc-inline&quot;&gt;&#10;                &lt;input id=&quot;ssc-inc&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-inc-err&quot; placeholder=&quot;e.g. 10 or 50&quot;&gt;&#10;                &lt;span class=&quot;ssc-input ssc-unit&quot; style=&quot;display:flex;align-items:center;justify-content:center;background:#f3f6fb;color:var(--ssc-muted);min-width:64px&quot; aria-hidden=&quot;true&quot;&gt;RPM&lt;\/span&gt;&#10;              &lt;\/div&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-inc-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;            &lt;div class=&quot;ssc-field&quot;&gt;&#10;              &lt;label for=&quot;ssc-power&quot;&gt;Machine power &lt;span class=&quot;ssc-opt&quot;&gt;(reference only)&lt;\/span&gt;&lt;\/label&gt;&#10;              &lt;div class=&quot;ssc-inline&quot;&gt;&#10;                &lt;input id=&quot;ssc-power&quot; class=&quot;ssc-input&quot; type=&quot;text&quot; inputmode=&quot;decimal&quot; aria-describedby=&quot;ssc-power-err&quot;&gt;&#10;                &lt;select id=&quot;ssc-power-unit&quot; class=&quot;ssc-select ssc-unit&quot; data-ssc-conv=&quot;power&quot; data-ssc-for=&quot;ssc-power&quot; aria-label=&quot;Power unit&quot; style=&quot;min-width:76px&quot;&gt;&#10;                  &lt;option value=&quot;kw&quot;&gt;kW&lt;\/option&gt;&#10;                  &lt;option value=&quot;hp&quot;&gt;hp&lt;\/option&gt;&#10;                &lt;\/select&gt;&#10;              &lt;\/div&gt;&#10;              &lt;p class=&quot;ssc-err&quot; id=&quot;ssc-power-err&quot; hidden&gt;&lt;\/p&gt;&#10;            &lt;\/div&gt;&#10;          &lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-help&quot; style=&quot;margin:-4px 0 12px&quot;&gt;Power is recorded for reference only. A power-limited spindle speed needs cutting-force or torque data and is not calculated here.&lt;\/p&gt;&#10;        &lt;\/fieldset&gt;&#10;&#10;        &lt;div class=&quot;ssc-calc-wrap&quot;&gt;&#10;          &lt;button type=&quot;submit&quot; class=&quot;ssc-btn ssc-btn-primary&quot; id=&quot;ssc-btn-calc&quot;&gt;Calculate&lt;\/button&gt;&#10;        &lt;\/div&gt;&#10;&#10;        &lt;div class=&quot;ssc-actions&quot;&gt;&#10;          &lt;span class=&quot;ssc-label&quot; id=&quot;ssc-ex-label&quot;&gt;Illustrative examples&lt;\/span&gt;&#10;          &lt;div class=&quot;ssc-btns&quot;&gt;&#10;            &lt;select id=&quot;ssc-example&quot; class=&quot;ssc-select&quot; aria-labelledby=&quot;ssc-ex-label&quot;&gt;&#10;              &lt;option value=&quot;1&quot;&gt;Example 1 \u00b7 End milling, 150 m\/min, \u00d810 mm&lt;\/option&gt;&#10;              &lt;option value=&quot;2&quot;&gt;Example 2 \u00b7 CNC turning, 180 m\/min, \u00d860 mm&lt;\/option&gt;&#10;              &lt;option value=&quot;3&quot;&gt;Example 3 \u00b7 Machine speed limit, 15,000 \u2192 10,000 RPM&lt;\/option&gt;&#10;            &lt;\/select&gt;&#10;            &lt;button type=&quot;button&quot; class=&quot;ssc-btn&quot; id=&quot;ssc-btn-example&quot;&gt;Load Example&lt;\/button&gt;&#10;            &lt;button type=&quot;button&quot; class=&quot;ssc-btn&quot; id=&quot;ssc-btn-reset&quot;&gt;Reset&lt;\/button&gt;&#10;          &lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-help&quot;&gt;Examples are illustrative calculations, not universal machining recommendations.&lt;\/p&gt;&#10;        &lt;\/div&gt;&#10;        &lt;p class=&quot;ssc-status&quot; id=&quot;ssc-input-status&quot; role=&quot;status&quot; aria-live=&quot;polite&quot;&gt;&lt;\/p&gt;&#10;      &lt;\/form&gt;&#10;    &lt;\/section&gt;&#10;&#10;    &lt;!-- ================= RESULTS PANEL ================= --&gt;&#10;    &lt;section class=&quot;ssc-card&quot; aria-labelledby=&quot;ssc-results-title&quot; id=&quot;ssc-results&quot; tabindex=&quot;-1&quot; hidden&gt;&#10;      &lt;div class=&quot;ssc-card-title&quot;&gt;&#10;        &lt;svg class=&quot;ssc-icon&quot; viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;1.8&quot; stroke-linecap=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M4 20V10M10 20V4M16 20v-7M22 20H2&quot;\/&gt;&lt;\/svg&gt;&#10;        &lt;h2 id=&quot;ssc-results-title&quot;&gt;Results&lt;\/h2&gt;&#10;      &lt;\/div&gt;&#10;&#10;      &lt;div class=&quot;ssc-alert&quot; id=&quot;ssc-error&quot; role=&quot;alert&quot; hidden&gt;&lt;\/div&gt;&#10;&#10;      &lt;div id=&quot;ssc-result-body&quot;&gt;&#10;        &lt;div class=&quot;ssc-pcard&quot; aria-live=&quot;polite&quot;&gt;&#10;          &lt;div class=&quot;ssc-pcard-label&quot; id=&quot;ssc-p-label&quot;&gt;Calculated Spindle Speed&lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-pcard-value&quot;&gt;&lt;span id=&quot;ssc-p-value&quot;&gt;\u2014&lt;\/span&gt;&lt;span class=&quot;ssc-pcard-unit&quot; id=&quot;ssc-p-unit&quot;&gt;&lt;\/span&gt;&lt;\/div&gt;&#10;          &lt;div class=&quot;ssc-pcard-sub&quot; id=&quot;ssc-p-sub&quot;&gt;&lt;\/div&gt;&#10;        &lt;\/div&gt;&#10;&#10;        &lt;div class=&quot;ssc-stats&quot; id=&quot;ssc-stats&quot;&gt;&lt;\/div&gt;&#10;        &lt;ul class=&quot;ssc-msgs&quot; id=&quot;ssc-msgs&quot;&gt;&lt;\/ul&gt;&#10;        &lt;div class=&quot;ssc-res-block&quot;&gt;&#10;          &lt;h3&gt;Additional Results&lt;\/h3&gt;&#10;          &lt;dl class=&quot;ssc-dl&quot; id=&quot;ssc-details&quot;&gt;&lt;\/dl&gt;&#10;        &lt;\/div&gt;&#10;        &lt;div class=&quot;ssc-res-block&quot;&gt;&#10;          &lt;h3&gt;Unit-Converted Values&lt;\/h3&gt;&#10;          &lt;div class=&quot;ssc-eq-grid&quot; id=&quot;ssc-equiv&quot;&gt;&lt;\/div&gt;&#10;          &lt;p class=&quot;ssc-small&quot;&gt;Inputs are converted to metric internal units (m\/min, mm, mm\/rev, mm\/min) with full precision; only displayed values are rounded.&lt;\/p&gt;&#10;        &lt;\/div&gt;&#10;      &lt;\/div&gt;&#10;    &lt;\/section&gt;&#10;  &lt;\/div&gt;&#10;&#10;&#10;&lt;\/div&gt;&#10;&#10;&lt;script&gt;&#10;\/* =====================================================================&#10;   Spindle Speed Calculator \u2014 Vanilla JavaScript&#10;   Wrapped in an IIFE: no global variables are created.&#10;   Internal units: cutting speed m\/min, lengths mm, feeds mm\/rev or&#10;   mm\/tooth, feed rate mm\/min, power kW.&#10;   ===================================================================== *\/&#10;(function () {&#10;  'use strict';&#10;&#10;  var root = document.getElementById('ssc-app');&#10;  if (!root || root.getAttribute('data-ssc-ready') === '1') return;&#10;  root.setAttribute('data-ssc-ready', '1');&#10;&#10;  function $(id) { return document.getElementById(id); }&#10;  function each(sel, fn) { Array.prototype.forEach.call(root.querySelectorAll(sel), fn); }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     1. Constants and unit factors (factor converts the unit to internal)&#10;     ------------------------------------------------------------------ *\/&#10;  var PI = Math.PI;               \/\/ \u03c0 \u2248 3.14159265&#10;  var MM_PER_IN = 25.4;           \/\/ 1 in = 25.4 mm (exact)&#10;  var M_PER_FT = 0.3048;          \/\/ 1 ft = 0.3048 m (exact)&#10;  var KW_PER_HP = 0.745699872;    \/\/ 1 mechanical hp in kW&#10;&#10;  var CONV = {&#10;    vc: { mmin: { sym: 'm\/min', f: 1 }, ftmin: { sym: 'ft\/min', f: M_PER_FT } },&#10;    len: { mm: { sym: 'mm', f: 1 }, cm: { sym: 'cm', f: 10 }, in: { sym: 'in', f: MM_PER_IN } },&#10;    flen: { mm: { sym: 'mm', f: 1 }, in: { sym: 'in', f: MM_PER_IN } },&#10;    power: { kw: { sym: 'kW', f: 1 }, hp: { sym: 'hp', f: KW_PER_HP } }&#10;  };&#10;&#10;  \/* ------------------------------------------------------------------&#10;     2. Operations, materials, tools&#10;     ------------------------------------------------------------------ *\/&#10;  var GENERIC_NOTE = ' The calculator provides mathematical estimates, not validated machining parameters for this operation.';&#10;  var OPS = {&#10;    milling:  { name: 'CNC Milling', feed: 'tooth', dia: 'Tool (cutter) diameter', role: 'Tool diameter',&#10;                help: 'Effective cutting diameter of the milling cutter.',&#10;                note: 'Milling uses the tool diameter and feed per tooth. For ball-nose or chamfer tools, the effective diameter at the actual depth of cut can be smaller than the nominal diameter.' },&#10;    turning:  { name: 'CNC Turning', feed: 'rev', dia: 'Workpiece diameter at the cutting point', role: 'Workpiece diameter',&#10;                help: 'Diameter of the workpiece where the tool is cutting.',&#10;                note: 'Turning uses the workpiece diameter at the cutting point and feed per revolution. As the tool moves toward the centre, the RPM needed for constant cutting speed rises; constant-surface-speed (CSS) control clamps it at a programmed maximum.' },&#10;    drilling: { name: 'Drilling', feed: 'rev', dia: 'Drill diameter', role: 'Tool diameter',&#10;                help: 'Nominal drill diameter.',&#10;                note: 'Drilling uses the drill diameter and feed per revolution. Cutting speed is highest at the outer corners and falls to zero at the chisel edge; point geometry and hole depth strongly affect suitable parameters.' },&#10;    boring:   { name: 'Boring', feed: 'rev', dia: 'Bore diameter at the cutting edge', role: 'Bore (cutting) diameter',&#10;                help: 'Diameter being cut by the boring tool.',&#10;                note: 'Boring uses the diameter being cut and feed per revolution. Boring bar overhang and rigidity often limit usable speed more than the formula does.' },&#10;    reaming:  { name: 'Reaming', feed: 'rev', dia: 'Reamer diameter', role: 'Tool diameter',&#10;                help: 'Nominal reamer diameter.',&#10;                note: 'Reaming uses the reamer diameter and feed per revolution. Reamers are typically run at lower cutting speeds than drills in the same material; follow the reamer manufacturer\\'s data.' },&#10;    tapping:  { name: 'Tapping', feed: 'pitch', dia: 'Tap nominal diameter', role: 'Tool diameter',&#10;                help: 'Nominal thread diameter of the tap.',&#10;                note: 'Tapping feed must be synchronised with spindle rotation. The feed shown is a theoretical single-start reference (RPM \u00d7 pitch) and does not replace machine-specific rigid-tapping settings.' },&#10;    face:     { name: 'Face Milling', feed: 'tooth', dia: 'Face mill (cutter) diameter', role: 'Tool diameter',&#10;                help: 'Effective cutting diameter of the face mill.',&#10;                note: 'Face milling uses the cutter diameter, number of inserts and feed per tooth. Width of cut relative to the cutter diameter affects actual chip thickness.' },&#10;    end:      { name: 'End Milling', feed: 'tooth', dia: 'End mill diameter', role: 'Tool diameter',&#10;                help: 'Cutting diameter of the end mill.',&#10;                note: 'End milling uses the tool diameter, number of flutes and feed per tooth. Radial engagement and stick-out strongly influence suitable parameters.' },&#10;    slot:     { name: 'Slot Milling', feed: 'tooth', dia: 'Slot cutter \/ end mill diameter', role: 'Tool diameter',&#10;                help: 'Cutting diameter of the slotting tool.',&#10;                note: 'Slot milling engages the full tool diameter, so chip evacuation and heat are more demanding than in side milling.' },&#10;    custom:   { name: 'Custom Operation', feed: 'choose', dia: 'Effective cutting diameter', role: 'Effective diameter',&#10;                help: 'Tool or workpiece diameter at the cutting point, whichever rotates against the cut.',&#10;                note: 'Custom operation: choose the feed model that matches your process.' }&#10;  };&#10;&#10;  var MATS = [&#10;    { k: 'carbon', name: 'Carbon Steel', kw: 'mild steel low carbon 1018 1020 1045 c45 s235 a36' },&#10;    { k: 'alloy', name: 'Alloy Steel', kw: '4140 4340 42crmo4 chromoly tool steel' },&#10;    { k: 'stainless', name: 'Stainless Steel', kw: '304 316 303 17-4 410 austenitic inox' },&#10;    { k: 'castiron', name: 'Cast Iron', kw: 'gray grey ductile nodular gg25 gjl gjs' },&#10;    { k: 'aluminum', name: 'Aluminum Alloy', kw: 'aluminium 6061 7075 2024 5083 cast' },&#10;    { k: 'copper', name: 'Copper Alloy', kw: 'brass bronze c360 cuzn' },&#10;    { k: 'titanium', name: 'Titanium Alloy', kw: 'ti-6al-4v grade 5 ti6al4v' },&#10;    { k: 'nickel', name: 'Nickel Alloy', kw: 'inconel 718 625 hastelloy monel superalloy' },&#10;    { k: 'plastics', name: 'Plastics', kw: 'pom acetal delrin nylon pa ptfe peek abs acrylic' },&#10;    { k: 'other', name: 'Other Materials', kw: 'composite graphite wood' },&#10;    { k: 'custom', name: 'Custom Material', kw: 'custom user' }&#10;  ];&#10;  var MAT_NAME = {}; MATS.forEach(function (m) { MAT_NAME[m.k] = m.name; });&#10;&#10;  var TOOLS = { hss: 'High-Speed Steel (HSS)', carbide: 'Carbide (uncoated)', coated: 'Coated Carbide', ceramic: 'Ceramic', other: 'Other \/ Not specified' };&#10;&#10;  \/* ------------------------------------------------------------------&#10;     4. Number parsing and formatting helpers&#10;     ------------------------------------------------------------------ *\/&#10;  \/** Parses user text. Accepts &quot;1,5&quot; (decimal comma), &quot;1,000.5&quot; and scientific notation. *\/&#10;  function parseNum(raw) {&#10;    var s = String(raw == null ? '' : raw).trim().replace(\/\\s+\/g, '');&#10;    if (s === '') return { empty: true, ok: false };&#10;    if (\/^[+-]?\\d{1,3}(,\\d{3})+(\\.\\d+)?$\/.test(s)) s = s.replace(\/,\/g, '');&#10;    else if (\/^[+-]?\\d*,\\d+$\/.test(s)) s = s.replace(',', '.');&#10;    if (!\/^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$\/i.test(s)) return { empty: false, ok: false };&#10;    var n = Number(s);&#10;    if (!isFinite(n)) return { empty: false, ok: false };&#10;    return { empty: false, ok: true, n: n };&#10;  }&#10;  \/** Fixed number of decimals, thousands separators, trailing zeros trimmed. *\/&#10;  function fx(n, dec) {&#10;    if (typeof n !== 'number' || !isFinite(n)) return '\u2014';&#10;    return n.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: dec });&#10;  }&#10;  \/** Adaptive decimals by magnitude (for lengths, speeds, feeds). *\/&#10;  function fa(n) {&#10;    if (typeof n !== 'number' || !isFinite(n)) return '\u2014';&#10;    var a = Math.abs(n);&#10;    if (a === 0) return '0';&#10;    if (a &gt;= 1000) return fx(n, 1);&#10;    if (a &gt;= 100) return fx(n, 2);&#10;    if (a &gt;= 1) return fx(n, 3);&#10;    if (a &gt;= 0.01) return fx(n, 4);&#10;    if (a &gt;= 0.0001) return fx(n, 6);&#10;    return n.toExponential(3);&#10;  }&#10;  function rpmCalc(n) { return fx(n, Math.abs(n) &gt;= 100 ? 1 : 2); }   \/\/ calculated RPM: 1 decimal&#10;  function rpmInt(n) { return fx(n, Math.abs(n) &gt;= 10 ? 0 : 1); }     \/\/ practical RPM: whole numbers&#10;  function plain(n) { return (typeof n === 'number' &amp;&amp; isFinite(n)) ? String(Number(n.toPrecision(12))) : ''; }&#10;  function fmtInput(n) { return (typeof n === 'number' &amp;&amp; isFinite(n)) ? String(Number(n.toPrecision(8))) : ''; }&#10;  function esc(s) {&#10;    return String(s).replace(\/[&amp;&lt;&gt;&quot;']\/g, function (c) {&#10;      return { '&amp;': '&amp;amp;', '&lt;': '&amp;lt;', '&gt;': '&amp;gt;', '&quot;': '&amp;quot;', &quot;'&quot;: '&amp;#39;' }[c];&#10;    });&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     5. Core calculation functions (pure, metric internal units)&#10;     ------------------------------------------------------------------ *\/&#10;  \/** Spindle speed, metric: N = (1000 \u00d7 Vc) \/ (\u03c0 \u00d7 D); Vc m\/min, D mm. *\/&#10;  function spindleSpeed(vcMmin, dMm) { return (1000 * vcMmin) \/ (PI * dMm); }&#10;  \/** Cutting speed, metric: Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 1000; D mm, N RPM \u2192 m\/min. *\/&#10;  function cuttingSpeed(dMm, n) { return (PI * dMm * n) \/ 1000; }&#10;  \/** Turning \/ drilling feed rate: F = N \u00d7 f. *\/&#10;  function feedRev(n, f) { return n * f; }&#10;  \/** Milling feed rate: F = N \u00d7 z \u00d7 fz. *\/&#10;  function feedMill(n, z, fz) { return n * z * fz; }&#10;  \/** Tapping, metric pitch (single start): F (mm\/min) = RPM \u00d7 P (mm\/rev). *\/&#10;  function tapFeedMetric(n, pMm) { return n * pMm; }&#10;  \/** Tapping, inch thread (single start): F (in\/min) = RPM \/ TPI. *\/&#10;  function tapFeedTpi(n, tpi) { return n \/ tpi; }&#10;  \/** Machine limit: Actual target RPM = min(Recommended RPM, Machine maximum RPM). *\/&#10;  function machineLimited(n, max) { return max == null ? n : Math.min(n, max); }&#10;  \/** Rounds down to a spindle speed increment so the selected Vc is not exceeded. *\/&#10;  function toIncrement(n, inc) { return Math.floor(n \/ inc + 1e-9) * inc; }&#10;&#10;  \/**&#10;   * Runs the full calculation from validated inputs (internal units).&#10;   * in: { mode, op, feedType, vc, d, n, fz, z, fr, thread, pitch, max, opTgt, inc, ... }&#10;   *\/&#10;  function calculate(inp) {&#10;    var R = { inp: inp, warn: [], info: [] };&#10;    var useVc = inp.mode === 'rpm' || inp.mode === 'limit';&#10;&#10;    \/\/ Spindle speed (calculated \/ entered)&#10;    R.nCalc = useVc ? spindleSpeed(inp.vc, inp.d) : inp.n;&#10;    R.vcTarget = useVc ? inp.vc : (inp.d != null ? cuttingSpeed(inp.d, inp.n) : null);&#10;&#10;    \/\/ Machine limit&#10;    R.exceeded = inp.max != null &amp;&amp; R.nCalc &gt; inp.max;&#10;    R.nLimited = machineLimited(R.nCalc, inp.max);&#10;    R.nRecRounded = Math.round(R.nCalc) || R.nCalc;&#10;&#10;    \/\/ Practical target RPM (rounded, machine-limited)&#10;    if (useVc) {&#10;      var p = null;&#10;      if (inp.inc != null) {&#10;        p = toIncrement(R.nLimited, inp.inc);&#10;        if (p &lt;= 0) { R.incIgnored = true; p = null; }&#10;      }&#10;      if (p === null) {&#10;        p = Math.round(R.nLimited);&#10;        if (inp.max != null &amp;&amp; p &gt; inp.max) p = Math.floor(inp.max);&#10;        if (p &lt;= 0) p = R.nLimited;&#10;      }&#10;      R.nPractical = p;&#10;    } else {&#10;      R.nPractical = R.nLimited;   \/\/ an entered speed is used as given (capped if above the limit)&#10;    }&#10;&#10;    \/\/ Operating spindle speed: operator selection, otherwise the practical target&#10;    R.opTgtUsed = useVc &amp;&amp; inp.opTgt != null;&#10;    if (R.opTgtUsed) {&#10;      R.opTgtExceeded = inp.max != null &amp;&amp; inp.opTgt &gt; inp.max;&#10;      R.nOperating = machineLimited(inp.opTgt, inp.max);&#10;    } else {&#10;      R.nOperating = R.nPractical;&#10;    }&#10;&#10;    R.vcActual = inp.d != null ? cuttingSpeed(inp.d, R.nOperating) : null;&#10;    R.vcAtLimit = (R.exceeded &amp;&amp; inp.d != null) ? cuttingSpeed(inp.d, inp.max) : null;&#10;    R.vcDev = (R.vcTarget &amp;&amp; R.vcActual != null) ? (R.vcActual - R.vcTarget) \/ R.vcTarget * 100 : null;&#10;&#10;    \/\/ Feed&#10;    R.feedOp = null; R.feedCalc = null; R.fRev = null; R.feedInMin = null;&#10;    if (inp.feedType === 'tooth' &amp;&amp; inp.fz != null &amp;&amp; inp.z != null) {&#10;      R.fRev = inp.z * inp.fz;&#10;      R.feedOp = feedMill(R.nOperating, inp.z, inp.fz);&#10;      R.feedCalc = feedMill(R.nCalc, inp.z, inp.fz);&#10;    } else if (inp.feedType === 'rev' &amp;&amp; inp.fr != null) {&#10;      R.fRev = inp.fr;&#10;      R.feedOp = feedRev(R.nOperating, inp.fr);&#10;      R.feedCalc = feedRev(R.nCalc, inp.fr);&#10;    } else if (inp.feedType === 'pitch' &amp;&amp; inp.pitch != null) {&#10;      if (inp.thread === 'tpi') {&#10;        R.fRev = MM_PER_IN \/ inp.pitch;                        \/\/ pitch = 1\/TPI in = 25.4\/TPI mm&#10;        R.feedInMin = tapFeedTpi(R.nOperating, inp.pitch);&#10;        R.feedOp = R.feedInMin * MM_PER_IN;&#10;        R.feedCalc = tapFeedTpi(R.nCalc, inp.pitch) * MM_PER_IN;&#10;      } else {&#10;        R.fRev = inp.pitch;&#10;        R.feedOp = tapFeedMetric(R.nOperating, inp.pitch);&#10;        R.feedCalc = tapFeedMetric(R.nCalc, inp.pitch);&#10;      }&#10;    }&#10;    if (R.feedOp != null &amp;&amp; R.feedInMin == null) R.feedInMin = R.feedOp \/ MM_PER_IN;&#10;&#10;    \/\/ Machine limit status&#10;    if (inp.max == null) R.status = 'none';&#10;    else R.status = (R.exceeded || R.opTgtExceeded) ? 'exceeded' : 'ok';&#10;&#10;    var ok = [R.nCalc, R.nOperating].every(function (v) { return isFinite(v); }) &amp;&amp;&#10;      (R.vcActual == null || isFinite(R.vcActual)) &amp;&amp; (R.feedOp == null || isFinite(R.feedOp));&#10;    R.finite = ok;&#10;    return R;&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     6. State, defaults and form reading&#10;     ------------------------------------------------------------------ *\/&#10;  var MODE_INFO = {&#10;    rpm: { label: 'Spindle Speed Calculation', hint: 'Enter cutting speed and diameter to calculate spindle speed: N = (1000 \u00d7 Vc) \/ (\u03c0 \u00d7 D). Feed and machine-limit checks run when those inputs are filled in.' },&#10;    vc: { label: 'Cutting Speed Calculation', hint: 'Enter spindle speed and diameter to calculate cutting speed: Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 1000.' },&#10;    feed: { label: 'Feed Rate Calculation', hint: 'Enter spindle speed and feed parameters to calculate feed rate: F = N \u00d7 f (turning, drilling) or F = N \u00d7 z \u00d7 fz (milling).' },&#10;    limit: { label: 'Maximum Spindle Speed Check', hint: 'Calculates the spindle speed from Vc and D, compares it with the machine maximum and shows the achievable cutting speed: target = min(N, Nmax).' }&#10;  };&#10;&#10;  var DEFAULTS = {&#10;    mode: 'rpm', op: 'end', tool: 'coated', mat: 'aluminum', coating: '', customOp: '', customMat: '', feedType: 'tooth',&#10;    vc: '150', vcUnit: 'mmin', d: '10', dUnit: 'mm', n: '3000',&#10;    fz: '0.05', fzUnit: 'mm', z: '4', fr: '0.2', frUnit: 'mm', thread: 'mm', pitch: '1.5',&#10;    max: '12000', optgt: '', inc: '', power: '', powerUnit: 'kw'&#10;  };&#10;&#10;  var state = { mode: 'rpm', last: null };&#10;&#10;  function feedTypeOf(op) { return OPS[op].feed === 'choose' ? $('ssc-feedtype').value : OPS[op].feed; }&#10;&#10;  function setSel(id, v) { var s = $(id); s.value = v; s.setAttribute('data-prev', v); }&#10;&#10;  function clearErrors() {&#10;    each('#ssc-form .ssc-invalid', function (el) { el.classList.remove('ssc-invalid'); el.removeAttribute('aria-invalid'); });&#10;    each('#ssc-form .ssc-err', function (el) { el.hidden = true; el.textContent = ''; });&#10;  }&#10;  function markError(id, msg) {&#10;    var el = $(id), er = $(id + '-err');&#10;    if (el) { el.classList.add('ssc-invalid'); el.setAttribute('aria-invalid', 'true'); }&#10;    if (er) { er.textContent = msg; er.hidden = false; }&#10;  }&#10;  \/**&#10;   * Reads a numeric field. opt: required, positive (&gt;0), nonneg (&gt;=0), integer.&#10;   * Returns a number, or null when empty-and-optional or invalid (error recorded).&#10;   *\/&#10;  function readNum(id, label, errs, opt) {&#10;    opt = opt || {};&#10;    var p = parseNum($(id).value), msg = null;&#10;    if (p.empty) { if (opt.required) msg = label + ' is required.'; else return null; }&#10;    else if (!p.ok) msg = label + ' must be a valid number.';&#10;    else if (p.n &lt; 0) msg = label + ' cannot be negative.';&#10;    else if (opt.positive &amp;&amp; p.n === 0) msg = label + ' must be greater than zero.';&#10;    else if (opt.integer &amp;&amp; Math.floor(p.n) !== p.n) msg = label + ' must be a whole number (positive integer).';&#10;    if (msg) { markError(id, msg); errs.push(msg); return null; }&#10;    return p.n;&#10;  }&#10;&#10;  \/** Reads and validates the form. Returns { inp, errs }. *\/&#10;  function readForm() {&#10;    clearErrors();&#10;    var errs = [], mode = state.mode, op = $('ssc-op').value, ft = feedTypeOf(op);&#10;    var useVc = mode === 'rpm' || mode === 'limit';&#10;    var vcU = CONV.vc[$('ssc-vc-unit').value], dU = CONV.len[$('ssc-d-unit').value];&#10;    var fzU = CONV.flen[$('ssc-fz-unit').value], frU = CONV.flen[$('ssc-fr-unit').value];&#10;    var pwU = CONV.power[$('ssc-power-unit').value];&#10;    var inp = {&#10;      mode: mode, op: op, opName: op === 'custom' ? ($('ssc-customop').value.trim() || 'Custom Operation') : OPS[op].name,&#10;      feedType: ft, mat: $('ssc-mat').value, tool: $('ssc-tool').value, coating: $('ssc-coating').value.trim(),&#10;      vcUnit: $('ssc-vc-unit').value, dUnit: $('ssc-d-unit').value, fzUnit: $('ssc-fz-unit').value, frUnit: $('ssc-fr-unit').value,&#10;      thread: $('ssc-thread').value, powerUnit: $('ssc-power-unit').value,&#10;      vc: null, vcIn: null, d: null, dIn: null, n: null, fz: null, fzIn: null, z: null, fr: null, frIn: null, pitch: null,&#10;      max: null, opTgt: null, inc: null, power: null, powerIn: null&#10;    };&#10;    inp.matName = inp.mat === 'custom' ? ($('ssc-custommat').value.trim() || 'Custom Material') : MAT_NAME[inp.mat];&#10;&#10;    if (useVc) {&#10;      inp.vcIn = readNum('ssc-vc', 'Cutting speed', errs, { required: true, positive: true });&#10;      if (inp.vcIn != null) inp.vc = inp.vcIn * vcU.f;&#10;    }&#10;    inp.dIn = readNum('ssc-d', 'Diameter', errs, { required: mode !== 'feed', positive: true });&#10;    if (inp.dIn != null) inp.d = inp.dIn * dU.f;&#10;    if (!useVc) inp.n = readNum('ssc-n', 'Spindle speed', errs, { required: true, positive: true });&#10;&#10;    var needFeed = mode === 'feed';&#10;    if (ft === 'tooth') {&#10;      inp.fzIn = readNum('ssc-fz', 'Feed per tooth', errs, { required: needFeed, nonneg: true });&#10;      if (inp.fzIn != null) inp.fz = inp.fzIn * fzU.f;&#10;      inp.z = readNum('ssc-z', 'Number of teeth', errs, { required: needFeed || inp.fzIn != null, positive: true, integer: true });&#10;    } else if (ft === 'rev') {&#10;      inp.frIn = readNum('ssc-fr', 'Feed per revolution', errs, { required: needFeed, nonneg: true });&#10;      if (inp.frIn != null) inp.fr = inp.frIn * frU.f;&#10;    } else {&#10;      inp.pitch = readNum('ssc-pitch', inp.thread === 'tpi' ? 'Threads per inch' : 'Thread pitch', errs, { required: needFeed, positive: true });&#10;    }&#10;&#10;    inp.max = readNum('ssc-max', 'Maximum spindle speed', errs, { required: mode === 'limit', positive: true });&#10;    if (useVc) inp.opTgt = readNum('ssc-optgt', 'Operator-selected spindle speed', errs, { positive: true });&#10;    inp.inc = readNum('ssc-inc', 'Spindle speed increment', errs, { positive: true });&#10;    inp.powerIn = readNum('ssc-power', 'Machine power', errs, { positive: true });&#10;    if (inp.powerIn != null) inp.power = inp.powerIn * pwU.f;&#10;    return { inp: inp, errs: errs };&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     7. Result assembly: text for display&#10;     ------------------------------------------------------------------ *\/&#10;  function vcDisplayUnit(inp) {&#10;    if (inp.mode === 'rpm' || inp.mode === 'limit') return inp.vcUnit;&#10;    return inp.dUnit === 'in' ? 'ftmin' : 'mmin';&#10;  }&#10;  function feedImperial(inp) {&#10;    if (inp.feedType === 'tooth') return inp.fzUnit === 'in';&#10;    if (inp.feedType === 'rev') return inp.frUnit === 'in';&#10;    return inp.thread === 'tpi';&#10;  }&#10;  function vcTxt(mmin, unitKey) {&#10;    if (mmin == null) return '\u2014';&#10;    return unitKey === 'ftmin' ? fa(mmin \/ M_PER_FT) + ' ft\/min' : fa(mmin) + ' m\/min';&#10;  }&#10;  function vcBoth(mmin) { return mmin == null ? '\u2014' : fa(mmin) + ' m\/min (' + fa(mmin \/ M_PER_FT) + ' ft\/min)'; }&#10;  function feedTxt(mmMin, imp) {&#10;    if (mmMin == null) return '\u2014';&#10;    return imp ? fa(mmMin \/ MM_PER_IN) + ' in\/min' : fa(mmMin) + ' mm\/min';&#10;  }&#10;  function dTxt(mm, unitKey) { return mm == null ? '\u2014' : fa(mm \/ CONV.len[unitKey].f) + ' ' + CONV.len[unitKey].sym; }&#10;&#10;  function buildOutput(R) {&#10;    var inp = R.inp, O = { steps: [], formula: [], where: [], details: [], equiv: [], stats: [], msgs: [] };&#10;    var vdu = vcDisplayUnit(inp), fimp = feedImperial(inp), useVc = inp.mode === 'rpm' || inp.mode === 'limit';&#10;    var imperialCoherent = inp.dUnit === 'in' &amp;&amp; (!useVc || inp.vcUnit === 'ftmin');&#10;    var dInIn = inp.d != null ? inp.d \/ MM_PER_IN : null;&#10;&#10;    \/* ---- Primary result ---- *\/&#10;    if (inp.mode === 'rpm') {&#10;      O.primary = { label: 'Calculated Spindle Speed', value: rpmCalc(R.nCalc), unit: 'RPM',&#10;        sub: 'Practical target: ' + rpmInt(R.nPractical) + ' RPM' + (R.exceeded ? ' (limited by machine maximum)' : '') };&#10;    } else if (inp.mode === 'vc') {&#10;      O.primary = { label: 'Calculated Cutting Speed', value: fa(vdu === 'ftmin' ? R.vcTarget \/ M_PER_FT : R.vcTarget), unit: CONV.vc[vdu].sym,&#10;        sub: '= ' + (vdu === 'ftmin' ? fa(R.vcTarget) + ' m\/min' : fa(R.vcTarget \/ M_PER_FT) + ' ft\/min (SFM)') + ' at ' + rpmInt(inp.n) + ' RPM' };&#10;    } else if (inp.mode === 'feed') {&#10;      O.primary = { label: inp.feedType === 'pitch' ? 'Theoretical Tapping Feed Rate' : 'Calculated Feed Rate',&#10;        value: fa(fimp ? R.feedInMin : R.feedOp), unit: fimp ? 'in\/min' : 'mm\/min',&#10;        sub: '= ' + (fimp ? fa(R.feedOp) + ' mm\/min' : fa(R.feedInMin) + ' in\/min') + ' at ' + rpmInt(R.nOperating) + ' RPM' };&#10;    } else {&#10;      O.primary = { label: 'Machine-Limited Target RPM', value: rpmInt(R.nPractical), unit: 'RPM',&#10;        sub: 'Calculated ' + rpmCalc(R.nCalc) + ' RPM \u00b7 machine maximum ' + rpmInt(inp.max) + ' RPM' };&#10;    }&#10;&#10;    \/* ---- Stat cards ---- *\/&#10;    O.stats.push({ l: useVc ? 'Calculated spindle speed' : 'Entered spindle speed', v: rpmCalc(R.nCalc), u: 'RPM', s: useVc ? 'Theoretical value from Vc and D' : 'Value entered' });&#10;    if (useVc) O.stats.push({ l: 'Practical target RPM', v: rpmInt(R.nPractical), u: 'RPM', s: (inp.inc != null &amp;&amp; !R.incIgnored ? 'Rounded down to ' + fa(inp.inc) + ' RPM steps' : 'Rounded to whole RPM') + (inp.max != null ? ', machine-limited' : '') });&#10;    O.stats.push({ l: 'Operating spindle speed', v: rpmInt(R.nOperating), u: 'RPM', s: R.opTgtUsed ? 'Operator-selected' + (R.opTgtExceeded ? ', capped at machine maximum' : '') : (useVc ? 'Equal to practical target' : (R.exceeded ? 'Capped at machine maximum' : 'As entered')) });&#10;    O.stats.push({ l: 'Cutting speed at operating RPM', v: R.vcActual == null ? '\u2014' : fa(vdu === 'ftmin' ? R.vcActual \/ M_PER_FT : R.vcActual), u: R.vcActual == null ? '' : CONV.vc[vdu].sym,&#10;      s: R.vcActual == null ? 'Enter a diameter to calculate' : (R.vcDev != null &amp;&amp; Math.abs(R.vcDev) &gt;= 0.05 ? (R.vcDev &gt; 0 ? '+' : '\u2212') + fx(Math.abs(R.vcDev), 1) + '% vs ' + (useVc ? 'selected' : 'entered') + ' Vc' : (vdu === 'ftmin' ? fa(R.vcActual) + ' m\/min' : fa(R.vcActual \/ M_PER_FT) + ' ft\/min')) });&#10;    O.stats.push({ l: inp.feedType === 'pitch' ? 'Tapping feed (theoretical)' : 'Estimated feed rate', v: R.feedOp == null ? '\u2014' : fa(fimp ? R.feedInMin : R.feedOp), u: R.feedOp == null ? '' : (fimp ? 'in\/min' : 'mm\/min'),&#10;      s: R.feedOp == null ? 'Enter feed parameters to calculate' : 'At ' + rpmInt(R.nOperating) + ' RPM \u00b7 ' + (fimp ? fa(R.feedOp) + ' mm\/min' : fa(R.feedInMin) + ' in\/min') });&#10;    var st = R.status === 'none' ? { v: 'Not checked', s: 'Enter the machine maximum RPM', c: '' } :&#10;      R.status === 'ok' ? { v: 'Within limit', s: fx(R.nCalc \/ inp.max * 100, 1) + '% of ' + rpmInt(inp.max) + ' RPM', c: 'ssc-st-ok' } :&#10;      { v: 'Limit exceeded', s: (R.exceeded ? 'Calculated' : 'Operator') + ' speed above ' + rpmInt(inp.max) + ' RPM', c: 'ssc-st-bad' };&#10;    O.stats.push({ l: 'Machine speed limit status', v: st.v, u: '', s: st.s, c: st.c });&#10;&#10;    \/* ---- Messages ---- *\/&#10;    if (R.exceeded) {&#10;      O.msgs.push({ t: 'warn', m: 'The ' + (useVc ? 'calculated' : 'entered') + ' spindle speed (' + rpmCalc(R.nCalc) + ' RPM) exceeds the machine maximum (' + rpmInt(inp.max) + ' RPM). The machine cannot reach it, so the target is capped at ' + rpmInt(R.nLimited) + ' RPM.' +&#10;        (R.vcAtLimit != null ? ' At the capped speed the actual cutting speed is ' + vcTxt(R.vcAtLimit, vdu) + ' instead of ' + vcTxt(R.vcTarget, vdu) + ' (\u2212' + fx(Math.abs((R.vcAtLimit - R.vcTarget) \/ R.vcTarget * 100), 1) + '%).' : '') +&#10;        (R.feedOp != null ? ' Feed rate is calculated at the capped speed, so feed per tooth \/ revolution is preserved.' : '') });&#10;    } else if (R.status === 'ok') {&#10;      O.msgs.push({ t: 'ok', m: 'The ' + (useVc ? 'calculated' : 'entered') + ' spindle speed is within the machine maximum of ' + rpmInt(inp.max) + ' RPM.' });&#10;    }&#10;    if (R.opTgtExceeded) O.msgs.push({ t: 'warn', m: 'The operator-selected speed (' + rpmInt(inp.opTgt) + ' RPM) is above the machine maximum and has been capped at ' + rpmInt(inp.max) + ' RPM.' });&#10;    if (R.opTgtUsed &amp;&amp; inp.opTgt &gt; R.nCalc &amp;&amp; !R.opTgtExceeded) O.msgs.push({ t: 'warn', m: 'The operator-selected speed is higher than the calculated spindle speed, so the actual cutting speed (' + vcTxt(R.vcActual, vdu) + ') is above the selected cutting speed.' });&#10;    if (R.incIgnored) O.msgs.push({ t: 'warn', m: 'The spindle speed increment (' + fa(inp.inc) + ' RPM) is larger than the target speed, so it was ignored and the target was rounded to whole RPM.' });&#10;    if (R.opTgtUsed) O.msgs.push({ t: 'info', m: 'Calculated RPM is a theoretical target. Results below use the operator-selected operating speed of ' + rpmInt(R.nOperating) + ' RPM.' });&#10;    if (inp.feedType === 'pitch') O.msgs.push({ t: 'info', m: 'Tapping feed is a theoretical synchronisation reference for a single-start thread. It does not replace machine-specific rigid-tapping or tapping-head settings.' });&#10;    if (inp.power != null) O.msgs.push({ t: 'info', m: 'Machine power ' + fa(inp.powerIn) + ' ' + CONV.power[inp.powerUnit].sym + (inp.powerUnit === 'kw' ? ' (' + fa(inp.power \/ KW_PER_HP) + ' hp)' : ' (' + fa(inp.power) + ' kW)') + ' is shown for reference only. It is not used to limit spindle speed, because that requires cutting-force or torque data.' });&#10;    if (R.feedOp == null &amp;&amp; inp.mode !== 'feed') O.msgs.push({ t: 'info', m: 'Feed rate not calculated: enter ' + (inp.feedType === 'tooth' ? 'feed per tooth and number of teeth' : inp.feedType === 'rev' ? 'feed per revolution' : 'the thread pitch') + ' to include it.' });&#10;&#10;    \/* ---- Formula ---- *\/&#10;    if (useVc) {&#10;      O.formula.push(imperialCoherent ? 'N = (12 \u00d7 Vc) \/ (\u03c0 \u00d7 D)' : 'N = (1000 \u00d7 Vc) \/ (\u03c0 \u00d7 D)');&#10;      O.where.push('N = spindle speed (RPM)', imperialCoherent ? 'Vc = cutting speed (ft\/min)' : 'Vc = cutting speed (m\/min)', imperialCoherent ? 'D = ' + OPS[inp.op].role.toLowerCase() + ' (in)' : 'D = ' + OPS[inp.op].role.toLowerCase() + ' (mm)');&#10;      if (inp.max != null) O.formula.push('Target RPM = min(N, Nmax)');&#10;    } else if (inp.mode === 'vc' || inp.d != null) {&#10;      O.formula.push(imperialCoherent ? 'Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 12' : 'Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 1000');&#10;      O.where.push(imperialCoherent ? 'Vc = cutting speed (ft\/min)' : 'Vc = cutting speed (m\/min)', imperialCoherent ? 'D = diameter (in)' : 'D = diameter (mm)', 'N = spindle speed (RPM)');&#10;    }&#10;    if (inp.feedType === 'tooth') { O.formula.push('F = N \u00d7 z \u00d7 fz'); O.where.push('F = feed rate', 'z = number of cutting teeth', 'fz = feed per tooth'); }&#10;    else if (inp.feedType === 'rev') { O.formula.push('F = N \u00d7 f'); O.where.push('F = feed rate', 'f = feed per revolution'); }&#10;    else if (inp.thread === 'tpi') { O.formula.push('F (in\/min) = N \/ TPI'); O.where.push('TPI = threads per inch (single-start)'); }&#10;    else { O.formula.push('F (mm\/min) = N \u00d7 P'); O.where.push('P = thread pitch (mm\/rev, single-start)'); }&#10;&#10;    \/* ---- Steps ---- *\/&#10;    var S = O.steps;&#10;    if (useVc) {&#10;      if (imperialCoherent) {&#10;        S.push('Inputs: Vc = ' + fa(inp.vcIn) + ' ft\/min, D = ' + fa(inp.dIn) + ' in (' + OPS[inp.op].role.toLowerCase() + ').');&#10;        S.push('N = (12 \u00d7 Vc) \/ (\u03c0 \u00d7 D) = (12 \u00d7 ' + fa(inp.vcIn) + ') \/ (\u03c0 \u00d7 ' + fa(inp.dIn) + ') = ' + rpmCalc(R.nCalc) + ' RPM');&#10;      } else {&#10;        S.push('Cutting speed: ' + fa(inp.vcIn) + ' ' + CONV.vc[inp.vcUnit].sym + (inp.vcUnit !== 'mmin' ? ' \u00d7 0.3048 = ' + fa(inp.vc) + ' m\/min' : ''));&#10;        S.push(OPS[inp.op].role + ': ' + fa(inp.dIn) + ' ' + CONV.len[inp.dUnit].sym + (inp.dUnit !== 'mm' ? ' = ' + fa(inp.d) + ' mm' : ''));&#10;        S.push('N = (1000 \u00d7 Vc) \/ (\u03c0 \u00d7 D) = (1000 \u00d7 ' + fa(inp.vc) + ') \/ (\u03c0 \u00d7 ' + fa(inp.d) + ') = ' + rpmCalc(R.nCalc) + ' RPM');&#10;      }&#10;      if (inp.max != null) S.push('Machine limit: min(' + rpmCalc(R.nCalc) + ', ' + rpmInt(inp.max) + ') = ' + rpmCalc(R.nLimited) + ' RPM' + (R.exceeded ? ' \u2014 calculated speed exceeds the machine maximum' : ''));&#10;      S.push('Practical target: ' + (inp.inc != null &amp;&amp; !R.incIgnored ? 'round down to ' + fa(inp.inc) + ' RPM steps' : 'round to whole RPM') + ' \u2192 ' + rpmInt(R.nPractical) + ' RPM');&#10;      if (R.opTgtUsed) S.push('Operating speed selected by operator: ' + rpmInt(inp.opTgt) + ' RPM' + (R.opTgtExceeded ? ' \u2192 capped at ' + rpmInt(R.nOperating) + ' RPM' : ''));&#10;      if (R.vcDev != null &amp;&amp; Math.abs(R.vcDev) &gt;= 0.0005) {&#10;        S.push('Actual cutting speed at ' + rpmInt(R.nOperating) + ' RPM: Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 1000 = (\u03c0 \u00d7 ' + fa(inp.d) + ' \u00d7 ' + rpmInt(R.nOperating) + ') \/ 1000 = ' + fa(R.vcActual) + ' m\/min');&#10;      }&#10;    } else {&#10;      if (inp.d != null) {&#10;        if (imperialCoherent) {&#10;          S.push('Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 12 = (\u03c0 \u00d7 ' + fa(inp.dIn) + ' \u00d7 ' + rpmCalc(inp.n) + ') \/ 12 = ' + fa(R.vcTarget \/ M_PER_FT) + ' ft\/min');&#10;          S.push('Convert: ' + fa(R.vcTarget \/ M_PER_FT) + ' ft\/min \u00d7 0.3048 = ' + fa(R.vcTarget) + ' m\/min');&#10;        } else {&#10;          if (inp.dUnit !== 'mm') S.push('Diameter: ' + fa(inp.dIn) + ' ' + CONV.len[inp.dUnit].sym + ' = ' + fa(inp.d) + ' mm');&#10;          S.push('Vc = (\u03c0 \u00d7 D \u00d7 N) \/ 1000 = (\u03c0 \u00d7 ' + fa(inp.d) + ' \u00d7 ' + rpmCalc(inp.n) + ') \/ 1000 = ' + fa(R.vcTarget) + ' m\/min (' + fa(R.vcTarget \/ M_PER_FT) + ' ft\/min)');&#10;        }&#10;      } else {&#10;        S.push('Spindle speed entered: N = ' + rpmCalc(inp.n) + ' RPM (no diameter entered, so cutting speed is not calculated).');&#10;      }&#10;      if (inp.max != null) S.push('Machine limit: min(' + rpmCalc(inp.n) + ', ' + rpmInt(inp.max) + ') = ' + rpmCalc(R.nLimited) + ' RPM');&#10;      if (R.exceeded &amp;&amp; R.vcActual != null) S.push('Cutting speed at the capped ' + rpmInt(R.nOperating) + ' RPM = (\u03c0 \u00d7 ' + fa(inp.d) + ' \u00d7 ' + rpmInt(R.nOperating) + ') \/ 1000 = ' + fa(R.vcActual) + ' m\/min');&#10;    }&#10;    var nTxt = rpmInt(R.nOperating);&#10;    if (R.feedOp != null) {&#10;      if (inp.feedType === 'tooth') {&#10;        S.push('Feed rate: F = N \u00d7 z \u00d7 fz = ' + nTxt + ' \u00d7 ' + inp.z + ' \u00d7 ' + fa(inp.fzIn) + ' ' + (inp.fzUnit === 'in' ? 'in' : 'mm') + ' = ' + feedTxt(R.feedOp, fimp));&#10;      } else if (inp.feedType === 'rev') {&#10;        S.push('Feed rate: F = N \u00d7 f = ' + nTxt + ' \u00d7 ' + fa(inp.frIn) + ' ' + (inp.frUnit === 'in' ? 'in\/rev' : 'mm\/rev') + ' = ' + feedTxt(R.feedOp, fimp));&#10;      } else if (inp.thread === 'tpi') {&#10;        S.push('Tapping feed: F = N \/ TPI = ' + nTxt + ' \/ ' + fa(inp.pitch) + ' = ' + fa(R.feedInMin) + ' in\/min (pitch = ' + fa(R.fRev) + ' mm)');&#10;      } else {&#10;        S.push('Tapping feed: F = N \u00d7 P = ' + nTxt + ' \u00d7 ' + fa(inp.pitch) + ' mm = ' + fa(R.feedOp) + ' mm\/min');&#10;      }&#10;      if (Math.abs(R.feedCalc - R.feedOp) &gt; 1e-9 * Math.max(1, R.feedOp)) S.push('For reference, the feed rate at the unrounded ' + rpmCalc(R.nCalc) + ' RPM would be ' + feedTxt(R.feedCalc, fimp));&#10;      if (fimp) S.push('Metric equivalent: ' + fa(R.feedOp) + ' mm\/min'); else S.push('Imperial equivalent: ' + fa(R.feedOp) + ' \u00f7 25.4 = ' + fa(R.feedInMin) + ' in\/min');&#10;    }&#10;&#10;    \/* ---- Details (additional results) ---- *\/&#10;    var D = O.details;&#10;    D.push(['Calculation mode', MODE_INFO[inp.mode].label]);&#10;    D.push(['Selected machining operation', inp.opName]);&#10;    D.push(['Material category', inp.matName]);&#10;    D.push(['Tool material', TOOLS[inp.tool] + (inp.coating ? ' \u00b7 ' + inp.coating : '')]);&#10;    if (useVc) D.push(['Selected cutting speed', vcBoth(inp.vc)]);&#10;    D.push([OPS[inp.op].role + ' (D)', inp.d == null ? 'Not entered' : dTxt(inp.d, inp.dUnit) + (inp.dUnit !== 'mm' ? ' = ' + fa(inp.d) + ' mm' : ' = ' + fa(dInIn) + ' in')]);&#10;    if (inp.feedType === 'tooth') {&#10;      D.push(['Number of cutting teeth (z)', inp.z == null ? '\u2014' : String(inp.z)]);&#10;      D.push(['Feed per tooth (fz)', inp.fz == null ? '\u2014' : fa(inp.fz) + ' mm\/tooth (' + fa(inp.fz \/ MM_PER_IN) + ' in\/tooth)']);&#10;      D.push(['Feed per revolution (z \u00d7 fz)', R.fRev == null ? '\u2014' : fa(R.fRev) + ' mm\/rev (' + fa(R.fRev \/ MM_PER_IN) + ' in\/rev)']);&#10;    } else if (inp.feedType === 'rev') {&#10;      D.push(['Feed per revolution (f)', inp.fr == null ? '\u2014' : fa(inp.fr) + ' mm\/rev (' + fa(inp.fr \/ MM_PER_IN) + ' in\/rev)']);&#10;    } else {&#10;      D.push(['Thread', inp.pitch == null ? '\u2014' : (inp.thread === 'tpi' ? fa(inp.pitch) + ' TPI (pitch ' + fa(R.fRev) + ' mm)' : 'P = ' + fa(inp.pitch) + ' mm (' + fa(MM_PER_IN \/ inp.pitch) + ' TPI)') + ', single-start']);&#10;    }&#10;    D.push([useVc ? 'Calculated spindle speed' : 'Entered spindle speed', rpmCalc(R.nCalc) + ' RPM']);&#10;    if (useVc) D.push(['Recommended target RPM (rounded, before machine limit)', rpmInt(R.nRecRounded) + ' RPM']);&#10;    D.push(['Machine maximum spindle speed', inp.max == null ? 'Not entered' : rpmInt(inp.max) + ' RPM']);&#10;    D.push(['Machine-limited target RPM', rpmInt(useVc ? R.nPractical : R.nLimited) + ' RPM']);&#10;    D.push(['Operating spindle speed used', rpmInt(R.nOperating) + ' RPM' + (R.opTgtUsed ? ' (operator-selected)' : '')]);&#10;    D.push(['Actual cutting speed', vcBoth(R.vcActual)]);&#10;    D.push(['Feed rate at operating speed', R.feedOp == null ? '\u2014' : fa(R.feedOp) + ' mm\/min (' + fa(R.feedInMin) + ' in\/min)']);&#10;    if (inp.inc != null) D.push(['Spindle speed increment', fa(inp.inc) + ' RPM' + (R.incIgnored ? ' (ignored)' : '')]);&#10;    if (inp.power != null) D.push(['Machine power (reference only)', fa(inp.power) + ' kW (' + fa(inp.power \/ KW_PER_HP) + ' hp)']);&#10;&#10;    \/* ---- Equivalent units ---- *\/&#10;    var E = O.equiv;&#10;    if (R.vcActual != null) { E.push([fa(R.vcActual), 'Actual Vc, m\/min']); E.push([fa(R.vcActual \/ M_PER_FT), 'Actual Vc, ft\/min (SFM)']); }&#10;    if (useVc) { E.push([fa(inp.vc), 'Selected Vc, m\/min']); E.push([fa(inp.vc \/ M_PER_FT), 'Selected Vc, ft\/min']); }&#10;    if (inp.d != null) { E.push([fa(inp.d), 'Diameter, mm']); E.push([fa(inp.d \/ 10), 'Diameter, cm']); E.push([fa(dInIn), 'Diameter, in']); }&#10;    if (R.feedOp != null) { E.push([fa(R.feedOp), 'Feed rate, mm\/min']); E.push([fa(R.feedOp \/ 1000), 'Feed rate, m\/min']); E.push([fa(R.feedInMin), 'Feed rate, in\/min']); }&#10;    if (R.fRev != null) { E.push([fa(R.fRev), 'Feed, mm\/rev']); E.push([fa(R.fRev \/ MM_PER_IN), 'Feed, in\/rev']); }&#10;    if (inp.fz != null) { E.push([fa(inp.fz), 'Feed per tooth, mm']); E.push([fa(inp.fz \/ MM_PER_IN), 'Feed per tooth, in']); }&#10;    E.push([rpmCalc(R.nOperating \/ 60), 'Operating speed, rev\/s']);&#10;    return O;&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     8. Rendering&#10;     ------------------------------------------------------------------ *\/&#10;  var ICON = {&#10;    warn: '&lt;svg viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;path d=&quot;M12 3l10 18H2z&quot;\/&gt;&lt;path d=&quot;M12 10v5M12 18v.5&quot;\/&gt;&lt;\/svg&gt;',&#10;    info: '&lt;svg viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;9&quot;\/&gt;&lt;path d=&quot;M12 11v6M12 7.5v.5&quot;\/&gt;&lt;\/svg&gt;',&#10;    ok: '&lt;svg viewBox=&quot;0 0 24 24&quot; fill=&quot;none&quot; stroke=&quot;currentColor&quot; stroke-width=&quot;2&quot; stroke-linecap=&quot;round&quot; stroke-linejoin=&quot;round&quot; aria-hidden=&quot;true&quot;&gt;&lt;circle cx=&quot;12&quot; cy=&quot;12&quot; r=&quot;9&quot;\/&gt;&lt;path d=&quot;M8 12l3 3 5-6&quot;\/&gt;&lt;\/svg&gt;'&#10;  };&#10;  var MSG_LABEL = { warn: 'Warning: ', info: 'Note: ', ok: 'OK: ' };&#10;&#10;  function showErrors(errs) {&#10;    var box = $('ssc-error');&#10;    box.innerHTML = '&lt;strong&gt;Please correct the following input' + (errs.length &gt; 1 ? 's' : '') + ':&lt;\/strong&gt;&lt;ul&gt;' +&#10;      errs.map(function (e) { return '&lt;li&gt;' + esc(e) + '&lt;\/li&gt;'; }).join('') + '&lt;\/ul&gt;';&#10;    box.hidden = false;&#10;    $('ssc-result-body').hidden = true;&#10;  }&#10;&#10;  function render(R, O) {&#10;    $('ssc-error').hidden = true;&#10;    $('ssc-result-body').hidden = false;&#10;    $('ssc-p-label').textContent = O.primary.label;&#10;    $('ssc-p-value').textContent = O.primary.value;&#10;    $('ssc-p-unit').textContent = O.primary.unit;&#10;    $('ssc-p-sub').textContent = O.primary.sub;&#10;    $('ssc-stats').innerHTML = O.stats.map(function (s) {&#10;      return '&lt;div class=&quot;ssc-stat ' + (s.c || '') + '&quot;&gt;&lt;span class=&quot;ssc-stat-l&quot;&gt;' + esc(s.l) + '&lt;\/span&gt;&lt;span class=&quot;ssc-stat-v&quot;&gt;' + esc(s.v) +&#10;        (s.u ? '&lt;small&gt;' + esc(s.u) + '&lt;\/small&gt;' : '') + '&lt;\/span&gt;&lt;span class=&quot;ssc-stat-s&quot;&gt;' + esc(s.s) + '&lt;\/span&gt;&lt;\/div&gt;';&#10;    }).join('');&#10;    $('ssc-msgs').innerHTML = O.msgs.map(function (m) {&#10;      return '&lt;li class=&quot;ssc-msg ssc-msg-' + m.t + '&quot;&gt;' + ICON[m.t] + '&lt;span&gt;&lt;span class=&quot;ssc-sr&quot;&gt;' + MSG_LABEL[m.t] + '&lt;\/span&gt;' + esc(m.m) + '&lt;\/span&gt;&lt;\/li&gt;';&#10;    }).join('');&#10;    $('ssc-details').innerHTML = O.details.map(function (d) { return '&lt;dt&gt;' + esc(d[0]) + '&lt;\/dt&gt;&lt;dd&gt;' + esc(d[1]) + '&lt;\/dd&gt;'; }).join('');&#10;    $('ssc-equiv').innerHTML = O.equiv.map(function (e) {&#10;      return '&lt;div class=&quot;ssc-eq&quot;&gt;&lt;span class=&quot;ssc-eq-v&quot;&gt;' + esc(e[0]) + '&lt;\/span&gt;&lt;span class=&quot;ssc-eq-u&quot;&gt;' + esc(e[1]) + '&lt;\/span&gt;&lt;\/div&gt;';&#10;    }).join('');&#10;  }&#10;&#10;  \/** Main entry: read \u2192 validate \u2192 calculate \u2192 render. *\/&#10;  function run() {&#10;    var f = readForm();&#10;    if (f.errs.length) { state.last = null; showErrors(f.errs); return false; }&#10;    var R = calculate(f.inp);&#10;    if (!R.finite) {&#10;      state.last = null;&#10;      showErrors(['The result is outside the numeric range that can be represented. Check the magnitude of your inputs.']);&#10;      return false;&#10;    }&#10;    var O = buildOutput(R);&#10;    state.last = { R: R, O: O, time: new Date() };&#10;    render(R, O);&#10;    return true;&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     9. UI adaptation for mode and operation&#10;     ------------------------------------------------------------------ *\/&#10;  function updateUI() {&#10;    var mode = state.mode, op = $('ssc-op').value, ft = feedTypeOf(op), useVc = mode === 'rpm' || mode === 'limit';&#10;    $('ssc-mode-hint').textContent = MODE_INFO[mode].hint;&#10;    $('ssc-vc-wrap').hidden = !useVc;&#10;    $('ssc-n-wrap').hidden = useVc;&#10;    $('ssc-optgt-wrap').hidden = !useVc;&#10;    $('ssc-d-label').innerHTML = esc(OPS[op].dia) + ' (D)' + (mode === 'feed' ? ' &lt;span class=&quot;ssc-opt&quot;&gt;(optional \u2014 for cutting speed)&lt;\/span&gt;' : '');&#10;    $('ssc-d-help').textContent = OPS[op].help;&#10;    $('ssc-opnote').textContent = OPS[op].note + GENERIC_NOTE;&#10;    $('ssc-customop-wrap').hidden = op !== 'custom';&#10;    $('ssc-feedtype-wrap').hidden = op !== 'custom';&#10;    $('ssc-tooth-wrap').hidden = ft !== 'tooth';&#10;    $('ssc-rev-wrap').hidden = ft !== 'rev';&#10;    $('ssc-pitch-wrap').hidden = ft !== 'pitch';&#10;    $('ssc-feed-legend').textContent = ft === 'pitch' ? 'Tapping Feed Synchronisation' : 'Feed Parameters' + (mode === 'feed' ? '' : ' (optional)');&#10;    $('ssc-feed-help').textContent = ft === 'tooth' ? 'Milling: F = N \u00d7 z \u00d7 fz. Feed per tooth is the advance per cutting edge, not per revolution.' :&#10;      ft === 'rev' ? 'Turning \/ drilling: F = N \u00d7 f. Feed per revolution is the advance per spindle turn.' :&#10;      'Single-start thread: F = N \u00d7 P (metric) or F = N \/ TPI (inch).';&#10;    $('ssc-pitch-label').textContent = $('ssc-thread').value === 'tpi' ? 'Threads per inch (TPI)' : 'Thread pitch P (mm\/rev)';&#10;    $('ssc-max-opt').textContent = mode === 'limit' ? '(required)' : '(optional)';&#10;    $('ssc-custommat-wrap').hidden = $('ssc-mat').value !== 'custom';&#10;  }&#10;&#10;  function setMode(mode) {&#10;    state.mode = mode;&#10;    each('input[name=&quot;ssc-mode&quot;]', function (r) { r.checked = r.value === mode; });&#10;    updateUI();&#10;    run();&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     10. Material search&#10;     ------------------------------------------------------------------ *\/&#10;  function buildMatOptions(keep) {&#10;    var sel = $('ssc-mat'), q = $('ssc-mat-search').value.trim().toLowerCase();&#10;    var cur = keep || sel.value || DEFAULTS.mat;&#10;    var list = MATS.filter(function (m) { return !q || (m.name + ' ' + m.kw).toLowerCase().indexOf(q) &gt;= 0; });&#10;    if (!list.length) list = MATS.filter(function (m) { return m.k === 'custom'; });&#10;    sel.innerHTML = list.map(function (m) { return '&lt;option value=&quot;' + m.k + '&quot;&gt;' + esc(m.name) + '&lt;\/option&gt;'; }).join('');&#10;    var ids = list.map(function (m) { return m.k; });&#10;    sel.value = ids.indexOf(cur) &gt;= 0 ? cur : ids[0];&#10;    $('ssc-mat-count').textContent = q ? list.length + ' categor' + (list.length === 1 ? 'y' : 'ies') + ' match.' : 'Type to filter by grade or name.';&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     11. Unit conversion of entered values (keeps the physical quantity)&#10;     ------------------------------------------------------------------ *\/&#10;  function convertField(sel) {&#10;    var type = sel.getAttribute('data-ssc-conv'), input = $(sel.getAttribute('data-ssc-for'));&#10;    var prev = sel.getAttribute('data-prev') || sel.value, p = parseNum(input.value);&#10;    if (p.ok &amp;&amp; prev !== sel.value) input.value = fmtInput(p.n * CONV[type][prev].f \/ CONV[type][sel.value].f);&#10;    sel.setAttribute('data-prev', sel.value);&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     12. Reset and examples&#10;     ------------------------------------------------------------------ *\/&#10;  function applyValues(v) {&#10;    $('ssc-op').value = v.op; $('ssc-tool').value = v.tool;&#10;    $('ssc-feedtype').value = v.feedType || 'tooth';&#10;    $('ssc-customop').value = v.customOp || ''; $('ssc-custommat').value = v.customMat || '';&#10;    $('ssc-coating').value = v.coating || '';&#10;    $('ssc-mat-search').value = ''; buildMatOptions(v.mat);&#10;    $('ssc-vc').value = v.vc; setSel('ssc-vc-unit', v.vcUnit);&#10;    $('ssc-d').value = v.d; setSel('ssc-d-unit', v.dUnit);&#10;    $('ssc-n').value = v.n;&#10;    $('ssc-fz').value = v.fz; setSel('ssc-fz-unit', v.fzUnit); $('ssc-z').value = v.z;&#10;    $('ssc-fr').value = v.fr; setSel('ssc-fr-unit', v.frUnit);&#10;    $('ssc-thread').value = v.thread; $('ssc-pitch').value = v.pitch;&#10;    $('ssc-max').value = v.max; $('ssc-optgt').value = v.optgt; $('ssc-inc').value = v.inc;&#10;    $('ssc-power').value = v.power; setSel('ssc-power-unit', v.powerUnit);&#10;  }&#10;  function merge(over) { var o = {}, k; for (k in DEFAULTS) o[k] = DEFAULTS[k]; for (k in over) o[k] = over[k]; return o; }&#10;&#10;  var EXAMPLES = {&#10;    1: { v: merge({ mode: 'rpm', op: 'end', vc: '150', d: '10', fz: '0.05', z: '4', max: '12000' }),&#10;         msg: 'Example 1 loaded: end milling, N = (1000 \u00d7 150) \/ (\u03c0 \u00d7 10) \u2248 4,775 RPM; F \u2248 955 mm\/min. Illustrative only.' },&#10;    2: { v: merge({ mode: 'rpm', op: 'turning', mat: 'carbon', vc: '180', d: '60', fr: '0.2', max: '3000' }),&#10;         msg: 'Example 2 loaded: CNC turning, N = (1000 \u00d7 180) \/ (\u03c0 \u00d7 60) \u2248 955 RPM; F \u2248 191 mm\/min. Illustrative only.' },&#10;    3: { v: merge({ mode: 'limit', op: 'end', tool: 'carbide', vc: '471.24', d: '10', fz: '0.05', z: '3', max: '10000' }),&#10;         msg: 'Example 3 loaded: Vc chosen so the calculated speed is 15,000 RPM; the 10,000 RPM spindle caps it and the actual cutting speed drops to \u2248 314 m\/min.' }&#10;  };&#10;  function loadExample(n) {&#10;    var ex = EXAMPLES[n];&#10;    applyValues(ex.v);&#10;    status('ssc-input-status', ex.msg);&#10;    setMode(ex.v.mode);&#10;  }&#10;  function resetAll() {&#10;    applyValues(DEFAULTS);&#10;    setMode(DEFAULTS.mode);&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     13. Status messages&#10;     ------------------------------------------------------------------ *\/&#10;  var timers = {};&#10;  function status(id, msg, isError) {&#10;    var el = $(id);&#10;    el.textContent = msg;&#10;    el.style.color = isError ? 'var(--ssc-danger)' : 'var(--ssc-ok)';&#10;    clearTimeout(timers[id]);&#10;    timers[id] = setTimeout(function () { el.textContent = ''; }, 6000);&#10;  }&#10;&#10;  \/* ------------------------------------------------------------------&#10;     17. Event wiring (delegated, scoped to the calculator root)&#10;     ------------------------------------------------------------------ *\/&#10;  root.addEventListener('input', function (e) {&#10;    var t = e.target;&#10;    if (t.id === 'ssc-mat-search') { buildMatOptions(); updateUI(); run(); return; }&#10;    if (t.closest &amp;&amp; t.closest('#ssc-form') &amp;&amp; t.classList.contains('ssc-input')) run();&#10;  });&#10;&#10;  root.addEventListener('change', function (e) {&#10;    var t = e.target;&#10;    if (t.name === 'ssc-mode') { setMode(t.value); return; }&#10;    if (t.id === 'ssc-example') return;&#10;    if (t.getAttribute('data-ssc-conv')) { convertField(t); run(); return; }&#10;    if (t.id === 'ssc-mat' || t.id === 'ssc-tool') { updateUI(); run(); return; }&#10;    if (t.closest &amp;&amp; t.closest('#ssc-form') &amp;&amp; t.tagName === 'SELECT') { updateUI(); run(); return; }&#10;  });&#10;&#10;  $('ssc-form').addEventListener('submit', function (e) {&#10;    e.preventDefault();&#10;    \/\/ Results are revealed only after the Calculate button is used&#10;    var res = $('ssc-results');&#10;    res.hidden = false;&#10;    var ok = run();&#10;    if (res.scrollIntoView) res.scrollIntoView({ behavior: 'smooth', block: 'start' });&#10;    if (ok) {&#10;      status('ssc-input-status', 'Calculation updated.');&#10;    } else {&#10;      status('ssc-input-status', 'Please correct the highlighted inputs.', true);&#10;      var bad = root.querySelector('#ssc-form .ssc-invalid');&#10;      if (bad) bad.focus();&#10;    }&#10;  });&#10;&#10;  root.addEventListener('click', function (e) {&#10;    var b = e.target.closest ? e.target.closest('button') : null;&#10;    if (!b || !root.contains(b)) return;&#10;    var id = b.id;&#10;    if (id === 'ssc-btn-example') { loadExample(Number($('ssc-example').value)); return; }&#10;    if (id === 'ssc-btn-reset') { resetAll(); $('ssc-results').hidden = true; status('ssc-input-status', 'Inputs restored to default values.'); return; }&#10;  });&#10;&#10;  \/* ------------------------------------------------------------------&#10;     18. Initialisation&#10;     ------------------------------------------------------------------ *\/&#10;  resetAll();&#10;})();&#10;&lt;\/script&gt;&#10;&lt;script&gt;&#10;\/* Auto-height helper: only active when this page runs inside an iframe (e.g. WordPress embed). *\/&#10;(function () {&#10;  var f = null;&#10;  try { f = window.frameElement; } catch (e) { f = null; }&#10;  if (!f) return;&#10;  var root = document.getElementById('ssc-app');&#10;  function fit() {&#10;    var h = Math.ceil((root || document.body).getBoundingClientRect().height);&#10;    if (h &gt; 0) f.style.setProperty('height', h + 'px', 'important');&#10;  }&#10;  fit();&#10;  if (window.ResizeObserver) new ResizeObserver(fit).observe(root || document.body);&#10;  window.addEventListener('load', fit);&#10;  window.addEventListener('resize', fit);&#10;  setTimeout(fit, 300);&#10;})();&#10;&lt;\/script&gt;&#10;&lt;\/body&gt;&#10;&lt;\/html&gt;&#10;\"><\/iframe>\n","protected":false},"excerpt":{"rendered":"<p>The Spindle Speed Calculator is a practical online tool designed to help CNC operators, machinists, and manufacturing engineers calculate spindle speed, cutting speed, and feed&#8230;<\/p>","protected":false},"author":2,"featured_media":25006,"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_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[8808],"tags":[3800,8470,8467,8528,8809],"class_list":["post-25005","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-manufacturing-and-processes","tag-cnc-machining","tag-cutting-speed","tag-feed-rate","tag-rpm-calculator","tag-spindle-speed","pmpro-has-access"],"acf":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/www.mechstream.com\/wp-content\/uploads\/2026\/10\/Spindle-Speed-Calculator.png","_links":{"self":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts\/25005","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/comments?post=25005"}],"version-history":[{"count":1,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts\/25005\/revisions"}],"predecessor-version":[{"id":25007,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/posts\/25005\/revisions\/25007"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/media\/25006"}],"wp:attachment":[{"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/media?parent=25005"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/categories?post=25005"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mechstream.com\/fr\/wp-json\/wp\/v2\/tags?post=25005"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}