<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>토크동력변환 &#8211; MyEngNote</title>
	<atom:link href="https://myengnote.com/tag/%ED%86%A0%ED%81%AC%EB%8F%99%EB%A0%A5%EB%B3%80%ED%99%98/feed/" rel="self" type="application/rss+xml" />
	<link>https://myengnote.com</link>
	<description></description>
	<lastBuildDate>Thu, 28 May 2026 23:33:36 +0000</lastBuildDate>
	<language>ko-KR</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>
	<item>
		<title>토크 &#038; 동력 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/torque-power-converter-simulator/</link>
					<comments>https://myengnote.com/torque-power-converter-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Thu, 28 May 2026 00:00:00 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[2D시뮬레이터]]></category>
		<category><![CDATA[마력변환]]></category>
		<category><![CDATA[모터출력계산]]></category>
		<category><![CDATA[토크계산기]]></category>
		<category><![CDATA[토크동력변환]]></category>
		<guid isPermaLink="false">https://myengnote.com/torque-power-converter-simulator/</guid>

					<description><![CDATA[동력(kW, HP), 토크(N·m, kgf·m), 회전 속도(RPM) 간의 상호 변환을 계산하고, 부하에 따른 모터의 회전 및 발열 상호작용을 시각화하는 초정밀 2D 시뮬레이터입니다.]]></description>
										<content:encoded><![CDATA[
<h2 style="font-size: 1.6em; font-weight: 800; color: #0c0e25; border-bottom: 2px solid #00f2fe; padding-bottom: 8px; margin-bottom: 20px;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f680.png" alt="🚀" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 토크 &#038; 동력 계산기 &#038; 시뮬레이터</h2>



<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&#038;family=Outfit:wght@400;500;600;700;800&#038;display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Scoped & Isolated Styles for torquepower-calculator-wrapper */

        /* Modern Reset and Design Tokens */
        .torquepower-calculator-wrapper *, .torquepower-calculator-wrapper *::before, .torquepower-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        .torquepower-calculator-wrapper {
            --color-bg-dark: #f8f9fd;
            --color-bg-deep: #ffffff;
            --color-panel-bg: rgba(255, 255, 255, 0.85);
            --color-border: rgba(0, 0, 0, 0.06);
            --color-border-hover: rgba(0, 0, 0, 0.12);
            
            --color-text-main: #1e293b;
            --color-text-muted: #475569;
            --color-text-dark: #94a3b8;

            --color-cyan: #0284c7;
            --color-cyan-glow: rgba(2, 132, 199, 0.15);
            --color-magenta: #db2777;
            --color-magenta-glow: rgba(219, 39, 119, 0.15);
            --color-purple: #7c3aed;
            --color-purple-glow: rgba(124, 58, 237, 0.12);
            --color-success: #10b981;
            --color-warning: #f59e0b;

            --gradient-primary: linear-gradient(135deg, var(--color-cyan) 0%, var(--color-purple) 100%);
            --gradient-accent: linear-gradient(135deg, var(--color-magenta) 0%, var(--color-purple) 100%);
            --gradient-panel: linear-gradient(180deg, rgba(255, 255, 255, 0.9) 0%, rgba(248, 250, 252, 0.95) 100%);

            --font-heading: 'Outfit', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            --font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
            
            --shadow-neon-cyan: 0 0 15px var(--color-cyan-glow);
            --shadow-neon-magenta: 0 0 15px var(--color-magenta-glow);
            --shadow-card: 0 8px 32px 0 rgba(15, 23, 42, 0.05);
            --blur-glass: blur(16px);
        }

        .torquepower-calculator-wrapper {
            background-color: var(--color-bg-dark);
            color: var(--color-text-main);
            font-family: var(--font-body);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            overflow-x: hidden;
            position: relative;
        }

        .app-background-glow {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
            overflow: hidden;
            pointer-events: none;
        }

        .app-background-glow::before, 
        .app-background-glow::after {
            content: '';
            position: absolute;
            width: 600px;
            height: 600px;
            border-radius: 50%;
            filter: blur(140px);
            opacity: 0.12;
        }

        .app-background-glow::before {
            background: var(--color-cyan);
            top: -10%;
            right: -5%;
            animation: pulse-slow 15s infinite alternate;
        }

        .app-background-glow::after {
            background: var(--color-purple);
            bottom: -10%;
            left: -5%;
            animation: pulse-slow 20s infinite alternate-reverse;
        }

        @keyframes pulse-slow {
            0% { transform: scale(1) translate(0, 0); opacity: 0.08; }
            100% { transform: scale(1.2) translate(50px, 50px); opacity: 0.15; }
        }

        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
            container-type: inline-size;
            container-name: torquepower-container;
        }

        .app-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 20px 24px;
            background: var(--color-panel-bg);
            backdrop-filter: var(--blur-glass);
            border: 1px solid var(--color-border);
            border-radius: 16px;
            box-shadow: var(--shadow-card);
        }

        .logo-area {
            display: flex;
            align-items: center;
            gap: 16px;
        }

        .logo-icon {
            font-size: 32px;
            background: linear-gradient(135deg, var(--color-cyan), var(--color-magenta));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .logo-area h1 {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 24px;
            letter-spacing: 1.5px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-text-dark));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .logo-area .subtitle {
            font-size: 12px;
            color: var(--color-text-muted);
            font-weight: 500;
            letter-spacing: 0.5px;
        }

        .header-badge {
            display: flex;
            align-items: center;
            gap: 10px;
            background: rgba(16, 185, 129, 0.1);
            border: 1px solid rgba(16, 185, 129, 0.2);
            padding: 6px 14px;
            border-radius: 20px;
        }

        .pulse-dot {
            width: 8px;
            height: 8px;
            background-color: var(--color-success);
            border-radius: 50%;
            box-shadow: 0 0 10px var(--color-success);
            animation: pulse-dot-anim 1.5s infinite;
        }

        @keyframes pulse-dot-anim {
            0% { transform: scale(0.9); opacity: 0.6; }
            50% { transform: scale(1.2); opacity: 1; }
            100% { transform: scale(0.9); opacity: 0.6; }
        }

        .badge-text {
            font-size: 11px;
            font-weight: 600;
            color: var(--color-success);
            letter-spacing: 0.5px;
        }

        .app-main-grid {
            display: grid;
            grid-template-columns: 340px 1fr 340px;
            gap: 24px;
            align-items: start;
        }

        .panel {
            background: var(--gradient-panel);
            backdrop-filter: var(--blur-glass);
            border: 1px solid var(--color-border);
            border-radius: 20px;
            padding: 24px;
            box-shadow: var(--shadow-card);
            display: flex;
            flex-direction: column;
            gap: 20px;
            transition: border-color 0.3s ease, box-shadow 0.3s ease;
        }

        .panel:hover {
            border-color: var(--color-border-hover);
        }

        .panel-header {
            display: flex;
            align-items: center;
            gap: 12px;
            border-bottom: 1px solid var(--color-border);
            padding-bottom: 14px;
        }

        .panel-header i {
            font-size: 18px;
        }

        .panel-header h2 {
            font-family: var(--font-heading);
            font-size: 18px;
            font-weight: 600;
            letter-spacing: 0.5px;
        }

        .text-cyan { color: var(--color-cyan); }
        .text-magenta { color: var(--color-magenta); }
        .text-purple { color: color: var(--color-purple); }

        .control-panel {
            grid-column: 1;
        }

        /* Mode Select Segmented Button */
        .mode-select-container {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .mode-label {
            font-size: 12px;
            font-weight: 700;
            color: var(--color-text-muted);
            letter-spacing: 0.5px;
        }

        .segmented-control {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            background: rgba(0, 0, 0, 0.03);
            border: 1px solid var(--color-border);
            padding: 4px;
            border-radius: 12px;
            gap: 4px;
        }

        .segmented-btn {
            background: transparent;
            border: none;
            color: var(--color-text-muted);
            padding: 8px 4px;
            border-radius: 8px;
            font-size: 11px;
            font-weight: 700;
            cursor: pointer;
            transition: all 0.2s ease;
            text-align: center;
        }

        .segmented-btn:hover {
            color: var(--color-text-main);
            background: rgba(0, 0, 0, 0.015);
        }

        .segmented-btn.active {
            background: #ffffff;
            color: var(--color-cyan);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
        }

        .input-group {
            display: flex;
            flex-direction: column;
            gap: 12px;
            transition: all 0.3s ease;
        }

        .input-group.disabled {
            opacity: 0.45;
            pointer-events: none;
        }

        .input-label-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .input-label-row label {
            font-size: 13px;
            font-weight: 600;
            color: var(--color-text-main);
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .helper-text {
            font-size: 10px;
            color: var(--color-text-muted);
            font-weight: 500;
        }

        .number-input-wrapper {
            display: flex;
            position: relative;
            border-radius: 8px;
            overflow: hidden;
            border: 1px solid var(--color-border);
            background: #ffffff;
            transition: all 0.2s ease;
        }

        .number-input-wrapper:focus-within {
            border-color: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }

        .custom-number-input {
            width: 100%;
            background: #f8fafc !important;
            border: none !important;
            outline: none !important;
            color: #0f172a !important;
            padding: 10px 14px !important;
            font-family: var(--font-body) !important;
            font-size: 15px !important;
            font-weight: 700 !important;
            text-align: right !important;
            padding-right: 60px !important;
            text-shadow: none !important;
            box-shadow: none !important;
            box-shadow: none !important;
            -webkit-appearance: none !important;
            -moz-appearance: textfield !important;
        }

        .custom-number-input::-webkit-outer-spin-button,
        .custom-number-input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }

        .unit-badge {
            position: absolute;
            right: 1px;
            top: 1px;
            bottom: 1px;
            background: rgba(0, 0, 0, 0.03);
            color: var(--color-text-muted);
            font-size: 11px;
            font-weight: 700;
            padding: 0 14px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-left: 1px solid var(--color-border);
            pointer-events: none;
            letter-spacing: 0.5px;
        }

        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 8px 0;
            transition: background 0.2s ease;
        }

        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease, box-shadow 0.2s ease;
        }

        /* Thumbs Styling */
        #slider-power::-webkit-slider-thumb {
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
        }
        #slider-power::-webkit-slider-thumb:hover {
            transform: scale(1.2);
            box-shadow: 0 0 15px var(--color-cyan), 0 0 5px #fff;
        }
        #slider-power::-moz-range-thumb {
            width: 18px;
            height: 18px;
            border: none;
            border-radius: 50%;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
            cursor: pointer;
        }

        #slider-torque::-webkit-slider-thumb {
            background: var(--color-magenta);
            box-shadow: 0 0 10px var(--color-magenta);
        }
        #slider-torque::-webkit-slider-thumb:hover {
            transform: scale(1.2);
            box-shadow: 0 0 15px var(--color-magenta), 0 0 5px #fff;
        }
        #slider-torque::-moz-range-thumb {
            width: 18px;
            height: 18px;
            border: none;
            border-radius: 50%;
            background: var(--color-magenta);
            box-shadow: 0 0 10px var(--color-magenta);
            cursor: pointer;
        }

        #slider-rpm::-webkit-slider-thumb {
            background: var(--color-purple);
            box-shadow: 0 0 10px var(--color-purple);
        }
        #slider-rpm::-webkit-slider-thumb:hover {
            transform: scale(1.2);
            box-shadow: 0 0 15px var(--color-purple), 0 0 5px #fff;
        }
        #slider-rpm::-moz-range-thumb {
            width: 18px;
            height: 18px;
            border: none;
            border-radius: 50%;
            background: var(--color-purple);
            box-shadow: 0 0 10px var(--color-purple);
            cursor: pointer;
        }

        .control-actions {
            display: grid;
            grid-template-columns: 1fr;
            gap: 10px;
            margin-top: 10px;
        }

        .action-btn {
            background: rgba(0, 0, 0, 0.02);
            border: 1px solid var(--color-border);
            color: var(--color-text-main);
            padding: 10px;
            border-radius: 10px;
            font-size: 12px;
            font-weight: 600;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            transition: all 0.2s ease;
        }

        .action-btn:hover {
            background: rgba(0, 0, 0, 0.04);
            border-color: var(--color-border-hover);
        }

        .presets-section {
            display: flex;
            flex-direction: column;
            gap: 12px;
            border-top: 1px solid var(--color-border);
            padding-top: 18px;
        }

        .presets-section h3 {
            font-size: 13px;
            font-weight: 700;
            color: var(--color-text-main);
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .presets-grid {
            display: grid;
            grid-template-columns: 1fr;
            gap: 8px;
        }

        .preset-btn {
            background: rgba(0, 0, 0, 0.015);
            border: 1px solid var(--color-border);
            border-radius: 10px;
            padding: 8px 12px;
            cursor: pointer;
            color: var(--color-text-main);
            display: flex;
            align-items: center;
            gap: 12px;
            text-align: left;
            transition: all 0.2s ease;
        }

        .preset-btn:hover {
            background: rgba(0, 0, 0, 0.05);
            transform: translateX(4px);
        }

        .preset-btn.active {
            background: linear-gradient(90deg, var(--color-cyan-glow), var(--color-magenta-glow));
            border-color: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }

        .preset-icon {
            width: 28px;
            height: 28px;
            background: rgba(0, 0, 0, 0.02);
            border-radius: 8px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            color: var(--color-cyan);
            transition: all 0.2s ease;
        }

        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
            box-shadow: var(--shadow-neon-cyan);
        }

        .preset-name {
            font-size: 12px;
            font-weight: 600;
            letter-spacing: 0.25px;
        }

        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
            min-width: 0;
        }

        .simulation-panel .panel-header {
            justify-content: space-between;
        }

        .canvas-scale-indicator {
            font-size: 11px;
            color: var(--color-text-muted);
            background: rgba(0, 0, 0, 0.02);
            padding: 4px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
        }

        .canvas-wrapper {
            position: relative;
            width: 100%;
            background: radial-gradient(circle at center, #ffffff 0%, #f1f5f9 100%);
            border: 1px solid rgba(0, 0, 0, 0.06);
            border-radius: 16px;
            overflow: hidden;
        }

        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 10;
        }

        .canvas-overlay-data {
            position: absolute;
            top: 16px;
            left: 16px;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            gap: 6px;
        }

        .overlay-item {
            background: transparent;
            color: var(--color-text-main);
            padding: 4px 0;
            display: flex;
            align-items: center;
            gap: 6px;
            font-size: 11px;
        }

        .overlay-item .label {
            color: var(--color-text-muted);
            font-weight: 500;
        }

        .overlay-item .value {
            font-weight: 700;
        }

        .simulation-metrics-strip {
            display: flex;
            background: rgba(0, 0, 0, 0.01);
            border: 1px solid var(--color-border);
            border-radius: 12px;
            padding: 12px 20px;
            justify-content: space-around;
            align-items: center;
            gap: 10px;
            margin-top: 10px;
        }

        .mini-metric {
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 4px;
            text-align: center;
        }

        .mini-metric .label {
            font-size: 10px;
            font-weight: 600;
            color: var(--color-text-muted);
            text-transform: uppercase;
            letter-spacing: 0.5px;
        }

        .mini-metric .value {
            font-size: 14px;
            font-weight: 700;
            color: var(--color-text-main);
            font-family: var(--font-heading);
        }

        .mini-divider {
            width: 1px;
            height: 24px;
            background: var(--color-border);
        }

        .results-panel {
            grid-column: 3;
        }

        .ratio-readout-box {
            background: linear-gradient(135deg, var(--color-cyan-glow) 0%, var(--color-purple-glow) 100%);
            border: 1px solid rgba(2, 132, 199, 0.15);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02);
        }

        .ratio-title {
            font-size: 11px;
            font-weight: 700;
            color: var(--color-cyan);
            letter-spacing: 1px;
            text-transform: uppercase;
        }

        .ratio-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 26px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-cyan));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }

        .ratio-type {
            font-size: 11px;
            font-weight: 600;
            color: var(--color-text-muted);
        }

        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }

        .result-card {
            background: rgba(255, 255, 255, 0.85);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
            border: 1px solid var(--color-border);
            border-radius: 12px;
            padding: 12px 16px;
            display: flex;
            align-items: center;
            gap: 16px;
            transition: all 0.2s ease;
        }

        .result-card:hover {
            transform: translateY(-2px);
            border-color: var(--color-cyan);
            background: var(--color-cyan-glow);
        }

        .card-icon {
            width: 38px;
            height: 38px;
            background: rgba(0, 0, 0, 0.015);
            border: 1px solid var(--color-border);
            border-radius: 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 14px;
            color: var(--color-text-muted);
            transition: all 0.2s ease;
        }

        .result-card:hover .card-icon {
            color: var(--color-cyan);
            border-color: var(--color-cyan);
            background: var(--color-cyan-glow);
        }

        .card-content {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }

        .card-unit {
            font-size: 11px;
            font-weight: 600;
            color: var(--color-text-muted);
        }

        .card-value {
            font-family: var(--font-heading);
            font-weight: 700;
            font-size: 17px;
            color: var(--color-text-main);
        }

        .formula-card {
            background: #ffffff;
            border: 1px solid var(--color-border);
            border-radius: 12px;
            padding: 16px;
            display: flex;
            flex-direction: column;
            gap: 8px;
            font-size: 12px;
        }

        .formula-card h4 {
            font-weight: 700;
            color: var(--color-text-main);
        }

        .formula-equation {
            font-family: 'Outfit', 'Cambria Math', 'Times New Roman', monospace;
            color: #0284c7;
            font-size: 13px;
            background: #f8fafc;
            border-color: var(--color-border);
            padding: 8px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
            font-weight: 700;
        }

        /* Container Query for Responsiveness inside WP */
        @container torquepower-container (max-width: 1050px) {
            .app-main-grid {
                grid-template-columns: 1fr 1fr;
                gap: 20px;
            }
            .control-panel { grid-column: 1; }
            .simulation-panel { grid-column: 2; }
            .results-panel { grid-column: 1 / span 2; }
        }
        @container torquepower-container (max-width: 580px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel, .simulation-panel, .results-panel {
                grid-column: 1;
            }
            .simulation-panel {
                order: -1;
            }
        }
    
</style>

<div class="torquepower-calculator-wrapper" style="position: relative; width: 100%; box-sizing: border-box; overflow: hidden; margin: 30px auto; border-radius: 20px;">
    <div class="app-background-glow" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; overflow: hidden;"></div>
    <div style="position: relative; z-index: 2; width: 100%;">
        
    
    
    <div class="app-container">
        <!-- Header -->
        <header class="app-header">
            <div class="logo-area">
                <div class="logo-icon"><i class="fa-solid fa-bolt-lightning"></i></div>
                <div>
                    <h1>TORQUE &#038; POWER</h1>
                    <div class="subtitle">토크 &#038; 동력 변환 계산기 및 2D 모터 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">MOTOR PHYSICS MODEL ACTIVE</div>
            </div>
        </header>

        <!-- Main Layout Grid -->
        <main class="app-main-grid">
            <!-- Left Panel: Controls -->
            <section class="panel control-panel">
                <div class="panel-header">
                    <i class="fa-solid fa-sliders text-cyan"></i>
                    <h2>시뮬레이션 제어 변수</h2>
                </div>

                <!-- Mode Selection -->
                <div class="mode-select-container">
                    <span class="mode-label">계산 모드 선택</span>
                    <div class="segmented-control">
                        <button class="segmented-btn active" id="btn-mode-p" title="동력 계산 모드">동력(P) 계산</button>
                        <button class="segmented-btn" id="btn-mode-t" title="토크 계산 모드">토크(T) 계산</button>
                        <button class="segmented-btn" id="btn-mode-n" title="회전수 계산 모드">회전수(N) 계산</button>
                    </div>
                </div>

                <!-- Power input (kW) -->
                <div class="input-group" id="group-power">
                    <div class="input-label-row">
                        <label for="input-power"><i class="fa-solid fa-plug text-cyan"></i> 전동기 동력 (P)</label>
                        <span class="helper-text" id="power-range-label">입력 (0.1 ~ 250 kW)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-power" class="custom-number-input" min="0.1" max="250" value="11" step="any">
                        <div class="unit-badge">kW</div>
                    </div>
                    <input type="range" id="slider-power" class="custom-slider" min="1" max="250" value="11">
                </div>

                <!-- Torque input (N·m) -->
                <div class="input-group" id="group-torque">
                    <div class="input-label-row">
                        <label for="input-torque"><i class="fa-solid fa-wrench text-magenta"></i> 발생 토크 (T)</label>
                        <span class="helper-text" id="torque-range-label">입력 (1 ~ 1000 N·m)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-torque" class="custom-number-input" min="1" max="1000" value="70" step="any">
                        <div class="unit-badge">N·m</div>
                    </div>
                    <input type="range" id="slider-torque" class="custom-slider" min="1" max="1000" value="70">
                </div>

                <!-- Speed input (RPM) -->
                <div class="input-group" id="group-rpm">
                    <div class="input-label-row">
                        <label for="input-rpm"><i class="fa-solid fa-gauge text-purple"></i> 회전 속도 (N)</label>
                        <span class="helper-text" id="rpm-range-label">입력 (10 ~ 5000 RPM)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-rpm" class="custom-number-input" min="10" max="5000" value="1500" step="any">
                        <div class="unit-badge">RPM</div>
                    </div>
                    <input type="range" id="slider-rpm" class="custom-slider" min="10" max="5000" value="1500">
                </div>

                <!-- Presets -->
                <div class="presets-section">
                    <h3><i class="fa-solid fa-tags text-cyan"></i> 전형적인 부하 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn active" data-preset="standard">
                            <div class="preset-icon"><i class="fa-solid fa-industry"></i></div>
                            <div class="preset-name">표준 산업용 삼상모터 (11kW, 1500RPM)</div>
                        </button>
                        <button class="preset-btn" data-preset="crane">
                            <div class="preset-icon"><i class="fa-solid fa-anchor"></i></div>
                            <div class="preset-name">고하중 건설 호이스트 크레인 (저속 고토크)</div>
                        </button>
                        <button class="preset-btn" data-preset="spindle">
                            <div class="preset-icon"><i class="fa-solid fa-compass"></i></div>
                            <div class="preset-name">고속가공용 정밀 CNC 스핀들 (고속 저토크)</div>
                        </button>
                    </div>
                </div>
            </section>

            <!-- Center Panel: Physics Simulator & Integrated Results -->
            <section class="panel simulation-panel">
                <div class="panel-header">
                    <i class="fa-solid fa-dharmachakra text-magenta"></i>
                    <h2>실시간 2D 모터 &#038; 윈치 윈드 시뮬레이션</h2>
                    <span class="canvas-scale-indicator" id="btn-reset-pos"><i class="fa-solid fa-arrows-rotate"></i> 초기화</span>
                </div>
                
                <div class="canvas-wrapper">
                    <canvas id="physics-canvas" width="640" height="400"></canvas>
                    <div class="canvas-overlay-data">
                        <div class="overlay-item">
                            <span class="label">운전 상태:</span>
                            <span class="value" id="overlay-run-status" style="color: var(--color-success);">정상 운전 중</span>
                        </div>
                    </div>
                </div>

                <div class="simulation-metrics-strip">
                    <div class="mini-metric">
                        <span class="label">각속도 (Angular Velocity)</span>
                        <span class="value" id="val-omega">157.1 rad/s</span>
                    </div>
                    <div class="mini-divider"></div>
                    <div class="mini-metric">
                        <span class="label">초당 마력 회전량 (Watts)</span>
                        <span class="value" id="val-watts">11,000 W</span>
                    </div>
                    <div class="mini-divider"></div>
                    <div class="mini-metric">
                        <span class="label">로프 리프팅 속도 (Speed)</span>
                        <span class="value" id="val-rope-speed">7.85 m/s</span>
                    </div>
                </div>

                <!-- Integrated Results Analysis Section -->
                <div class="simulation-results-section" style="display: grid; grid-template-columns: 1fr 1.2fr; gap: 20px; border-top: 1px solid var(--color-border); padding-top: 20px; margin-top: 10px;">
                    <!-- Left Column: Primary Readout & Formula -->
                    <div style="display: flex; flex-direction: column; gap: 16px;">
                        <div class="ratio-readout-box" style="padding: 16px; min-height: 100px;">
                            <div class="ratio-title">계산된 전동기 출력</div>
                            <div class="ratio-value" id="txt-primary-output" style="font-size: 24px;">11.00 kW</div>
                            <div class="ratio-type" id="txt-secondary-output" style="font-size: 11px;">14.75 마력 (HP)</div>
                        </div>
                        <div class="formula-card" style="padding: 12px; font-size: 11px; background: rgba(0, 0, 0, 0.01);">
                            <h4 style="font-size: 12px; margin-bottom: 6px;"><i class="fa-solid fa-info-circle text-cyan"></i> 토크-동력 핵심 관계식</h4>
                            <div class="formula-equation" style="font-size: 11px; padding: 6px; margin-bottom: 6px;">
                                P [kW] = T [N·m] × N [RPM] / 9549.3
                            </div>
                            <div class="formula-equation" style="font-size: 11px; padding: 6px;">
                                T [kgf·m] = T [N·m] / 9.80665
                            </div>
                        </div>
                    </div>

                    <!-- Right Column: Quantitative Results Cards -->
                    <div class="results-grid" style="display: flex; flex-direction: column; gap: 10px; justify-content: center;">
                        <div class="result-card" style="padding: 10px 14px; gap: 12px;">
                            <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px;"><i class="fa-solid fa-bolt"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">전동기 정격 동력 (P)</span>
                                <span class="card-value" id="res-power" style="font-size: 15px;">11.00 kW</span>
                            </div>
                        </div>

                        <div class="result-card" style="padding: 10px 14px; gap: 12px;">
                            <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px;"><i class="fa-solid fa-horse"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">미터법 마력 출력 (HP)</span>
                                <span class="card-value" id="res-hp" style="font-size: 15px;">14.96 HP</span>
                            </div>
                        </div>

                        <div class="result-card" style="padding: 10px 14px; gap: 12px;">
                            <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px;"><i class="fa-solid fa-gears"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">발생 물리 토크 (T)</span>
                                <span class="card-value" id="res-torque-nm" style="font-size: 15px;">70.03 N·m</span>
                            </div>
                        </div>

                        <div class="result-card" style="padding: 10px 14px; gap: 12px;">
                            <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px;"><i class="fa-solid fa-wrench"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">중력 단위 토크 (kgf·m)</span>
                                <span class="card-value" id="res-torque-kgm" style="font-size: 15px;">7.14 kgf·m</span>
                            </div>
                        </div>
                    </div>
                </div>
            </section>
        </main>
    </div>

    <!-- CORE INTERACTIVE ENGINE -->
    <script>
        (function() {
            // Safe guard against duplicate initiation
            if (window.__torquepower_initialized) return;
            window.__torquepower_initialized = true;

            // 1. Core DOM queries
            const btnModeP = document.getElementById('btn-mode-p');
            const btnModeT = document.getElementById('btn-mode-t');
            const btnModeN = document.getElementById('btn-mode-n');

            const groupPower = document.getElementById('group-power');
            const groupTorque = document.getElementById('group-torque');
            const groupRpm = document.getElementById('group-rpm');

            const inputPower = document.getElementById('input-power');
            const sliderPower = document.getElementById('slider-power');
            const inputTorque = document.getElementById('input-torque');
            const sliderTorque = document.getElementById('slider-torque');
            const inputRpm = document.getElementById('input-rpm');
            const sliderRpm = document.getElementById('slider-rpm');

            const txtPrimaryOutput = document.getElementById('txt-primary-output');
            const txtSecondaryOutput = document.getElementById('txt-secondary-output');

            const resPower = document.getElementById('res-power');
            const resHp = document.getElementById('res-hp');
            const resTorqueNm = document.getElementById('res-torque-nm');
            const resTorqueKgm = document.getElementById('res-torque-kgm');

            const valOmega = document.getElementById('val-omega');
            const valWatts = document.getElementById('val-watts');
            const valRopeSpeed = document.getElementById('val-rope-speed');

            const overlayRunStatus = document.getElementById('overlay-run-status');
            const btnResetPos = document.getElementById('btn-reset-pos');

            // 2. Constants & Physics State
            const state = {
                mode: 'P', // 'P' (calc Power), 'T' (calc Torque), 'N' (calc RPM)
                power: 11.0,  // kW
                torque: 70.0, // N·m
                rpm: 1500.0,  // RPM
                angle: 0.0,
                ropeY: 180.0,
                ropeDir: 1.0,
                sparks: []
            };

            // Range definition (Min / Max)
            const ranges = {
                power: { min: 0.1, max: 250.0 },
                torque: { min: 1.0, max: 1000.0 },
                rpm: { min: 10.0, max: 5000.0 }
            };

            // 3. Mathematical Solvers
            function calculateThirdVariable() {
                if (state.mode === 'P') {
                    // P = T * N / 9549.3
                    state.power = (state.torque * state.rpm) / 9549.3;
                    // Apply bounds check
                    if (state.power < ranges.power.min) state.power = ranges.power.min;
                    if (state.power > ranges.power.max) state.power = ranges.power.max;
                } else if (state.mode === 'T') {
                    // T = 9549.3 * P / N
                    if (state.rpm > 0) {
                        state.torque = (9549.3 * state.power) / state.rpm;
                    } else {
                        state.torque = ranges.torque.min;
                    }
                    if (state.torque < ranges.torque.min) state.torque = ranges.torque.min;
                    if (state.torque > ranges.torque.max) state.torque = ranges.torque.max;
                } else if (state.mode === 'N') {
                    // N = 9549.3 * P / T
                    if (state.torque > 0) {
                        state.rpm = (9549.3 * state.power) / state.torque;
                    } else {
                        state.rpm = ranges.rpm.min;
                    }
                    if (state.rpm < ranges.rpm.min) state.rpm = ranges.rpm.min;
                    if (state.rpm > ranges.rpm.max) state.rpm = ranges.rpm.max;
                }
                updateUIValues();
            }

            // Sync text inputs and range sliders without aggressive clamping
            function syncPowerFromInput() {
                let val = parseFloat(inputPower.value);
                if (isNaN(val)) val = ranges.power.min;
                if (val < ranges.power.min) val = ranges.power.min;
                if (val > ranges.power.max) val = ranges.power.max;
                state.power = val;
                inputPower.value = val.toFixed(2);
                sliderPower.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncPowerFromSlider() {
                state.power = parseFloat(sliderPower.value);
                inputPower.value = state.power.toFixed(1);
                calculateThirdVariable();
            }

            function syncTorqueFromInput() {
                let val = parseFloat(inputTorque.value);
                if (isNaN(val)) val = ranges.torque.min;
                if (val < ranges.torque.min) val = ranges.torque.min;
                if (val > ranges.torque.max) val = ranges.torque.max;
                state.torque = val;
                inputTorque.value = val.toFixed(1);
                sliderTorque.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncTorqueFromSlider() {
                state.torque = parseFloat(sliderTorque.value);
                inputTorque.value = state.torque.toFixed(0);
                calculateThirdVariable();
            }

            function syncRpmFromInput() {
                let val = parseFloat(inputRpm.value);
                if (isNaN(val)) val = ranges.rpm.min;
                if (val < ranges.rpm.min) val = ranges.rpm.min;
                if (val > ranges.rpm.max) val = ranges.rpm.max;
                state.rpm = val;
                inputRpm.value = val.toFixed(0);
                sliderRpm.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncRpmFromSlider() {
                state.rpm = parseFloat(sliderRpm.value);
                inputRpm.value = state.rpm.toFixed(0);
                calculateThirdVariable();
            }

            // 4. Update UI Outputs
            function updateUIValues() {
                // Update inputs
                if (state.mode !== 'P') {
                    inputPower.value = state.power.toFixed(2);
                    sliderPower.value = Math.round(state.power);
                }
                if (state.mode !== 'T') {
                    inputTorque.value = state.torque.toFixed(1);
                    sliderTorque.value = Math.round(state.torque);
                }
                if (state.mode !== 'N') {
                    inputRpm.value = state.rpm.toFixed(0);
                    sliderRpm.value = Math.round(state.rpm);
                }

                // Primary Output Readout Box
                if (state.mode === 'P') {
                    txtPrimaryOutput.innerText = state.power.toFixed(2) + ' kW';
                    const hp = state.power * 1.35962;
                    txtSecondaryOutput.innerText = hp.toFixed(2) + ' HP (마력)';
                } else if (state.mode === 'T') {
                    txtPrimaryOutput.innerText = state.torque.toFixed(1) + ' N·m';
                    const kgm = state.torque / 9.80665;
                    txtSecondaryOutput.innerText = kgm.toFixed(2) + ' kgf·m (킬로그램토크)';
                } else if (state.mode === 'N') {
                    txtPrimaryOutput.innerText = state.rpm.toFixed(0) + ' RPM';
                    const hz = state.rpm / 60;
                    txtSecondaryOutput.innerText = hz.toFixed(1) + ' Hz (초당 회전수)';
                }

                // Results Grid Card Values
                resPower.innerText = state.power.toFixed(2) + ' kW';
                resHp.innerText = (state.power * 1.35962).toFixed(2) + ' HP';
                resTorqueNm.innerText = state.torque.toFixed(1) + ' N·m';
                resTorqueKgm.innerText = (state.torque / 9.80665).toFixed(2) + ' kgf·m';

                // Mini metrics strip at the bottom
                const omega = (2 * Math.PI * state.rpm) / 60;
                valOmega.innerText = omega.toFixed(1) + ' rad/s';
                valWatts.innerText = (state.power * 1000).toLocaleString('ko-KR', { maximumFractionDigits: 0 }) + ' W';
                
                // Winch drum radius is 50mm (0.05m). Speed = r * omega
                const ropeSpeed = 0.05 * omega;
                valRopeSpeed.innerText = ropeSpeed.toFixed(2) + ' m/s';

                // Thermal Warning status
                if (state.power > 150) {
                    overlayRunStatus.innerText = '고부하 운전 (과열 주의)';
                    overlayRunStatus.style.color = '#f59e0b';
                } else {
                    overlayRunStatus.innerText = '정상 운전 중';
                    overlayRunStatus.style.color = '#10b981';
                }
            }

            // 5. Calculate mode toggle
            function setMode(newMode) {
                state.mode = newMode;
                
                // Active button styling
                btnModeP.classList.remove('active');
                btnModeT.classList.remove('active');
                btnModeN.classList.remove('active');

                // Enable/disable slider blocks
                groupPower.classList.remove('disabled');
                groupTorque.classList.remove('disabled');
                groupRpm.classList.remove('disabled');

                if (newMode === 'P') {
                    btnModeP.classList.add('active');
                    groupPower.classList.add('disabled');
                } else if (newMode === 'T') {
                    btnModeT.classList.add('active');
                    groupTorque.classList.add('disabled');
                } else if (newMode === 'N') {
                    btnModeN.classList.add('active');
                    groupRpm.classList.add('disabled');
                }
                calculateThirdVariable();
            }

            // Presets selector
            const presetBtns = document.querySelectorAll('.preset-btn');
            presetBtns.forEach(btn => {
                btn.addEventListener('click', function() {
                    presetBtns.forEach(b => b.classList.remove('active'));
                    this.classList.add('active');

                    const preset = this.getAttribute('data-preset');
                    if (preset === 'standard') {
                        state.torque = 70.0;
                        state.rpm = 1500.0;
                        setMode('P');
                    } else if (preset === 'crane') {
                        state.torque = 650.0;
                        state.rpm = 160.0;
                        setMode('P');
                    } else if (preset === 'spindle') {
                        state.torque = 8.5;
                        state.rpm = 4500.0;
                        setMode('P');
                    }
                });
            });

            // 6. Interactive Canvas Physics Simulation
            const canvas = document.getElementById('physics-canvas');
            const ctx = canvas.getContext('2d');

            function getDPR() {
                return window.devicePixelRatio || 1;
            }

            function initCanvas() {
                const dpr = getDPR();
                const rect = canvas.getBoundingClientRect();
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.scale(dpr, dpr);
            }

            window.addEventListener('resize', initCanvas);
            initCanvas();
            // Polling backups to guarantee canvas is sized correctly after WordPress page loads
            setTimeout(initCanvas, 100);
            setTimeout(initCanvas, 300);
            setTimeout(initCanvas, 800);

            // Winch Rope limits will be calculated dynamically inside animation loop to prevent counterweight overlapping

            // Spark Particle Generator
            function addSparks(x, y, count) {
                for (let i = 0; i < count; i++) {
                    state.sparks.push({
                        x: x,
                        y: y,
                        vx: (Math.random() - 0.5) * 8 + (state.rpm > 0 ? 3 : -3),
                        vy: (Math.random() - 0.7) * 6 - 2,
                        life: 1.0,
                        decay: Math.random() * 0.05 + 0.03,
                        color: Math.random() > 0.4 ? '#0284c7' : '#db2777'
                    });
                }
            }

            function updateSparks() {
                for (let i = state.sparks.length - 1; i >= 0; i--) {
                    const p = state.sparks[i];
                    p.x += p.vx;
                    p.y += p.vy;
                    p.vy += 0.15; // gravity
                    p.life -= p.decay;
                    if (p.life <= 0) {
                        state.sparks.splice(i, 1);
                    }
                }
            }

            function drawDiameterLeader(c, x, y, r, valStr, labelPrefix, side) {
                c.save();
                c.strokeStyle = 'rgba(71, 85, 105, 0.4)';
                c.lineWidth = 1.2;
                
                // Angle of leader line (45 degrees up-left or up-right)
                const angle = side === -1 ? -Math.PI * 0.75 : -Math.PI * 0.25;
                const px = x + Math.cos(angle) * r;
                const py = y + Math.sin(angle) * r;
                
                // Draw a beautiful small dot on the circle boundary
                c.beginPath();
                c.arc(px, py, 2.5, 0, Math.PI * 2);
                c.fillStyle = side === -1 ? '#0284c7' : '#db2777';
                c.fill();
                
                // Draw leader lines: from circle boundary diagonal outwards, then horizontal
                const lx1 = px + Math.cos(angle) * 20;
                const ly1 = py + Math.sin(angle) * 20;
                const lx2 = lx1 + side * 45;
                const ly2 = ly1;
                
                c.beginPath();
                c.moveTo(px, py);
                c.lineTo(lx1, ly1);
                c.lineTo(lx2, ly2);
                c.stroke();
                
                // Draw text above the horizontal line
                const text = `${labelPrefix} ${valStr}`;
                c.font = 'bold 10px var(--font-body)';
                
                const tx = side === -1 ? lx1 - 4 : lx1 + 4;
                c.fillStyle = '#1e293b';
                c.textAlign = side === -1 ? 'right' : 'left';
                c.textBaseline = 'bottom';
                c.fillText(text, tx, ly2 - 1);
                c.restore();
            }

            // Main Animation loop
            let lastTime = 0;
            function animate(currentTime) {
                if (lastTime === 0) lastTime = currentTime;
                const dt = (currentTime - lastTime) / 1000;
                lastTime = currentTime;

                const dpr = getDPR();
                const width = canvas.width / dpr;
                const height = canvas.height / dpr;

                // Dynamically center the motor-coupling-winch assembly to fill the canvas beautifully
                const totalSystemWidth = 410;
                const startX = (width - totalSystemWidth) / 2;
                
                const mx = startX + 70;
                const my = height / 2;
                const cpx = mx + 120;
                const wx = cpx + 140;

                // Physics state updates
                // Angular step = omega * dt
                const omega = (2 * Math.PI * state.rpm) / 60;
                state.angle += omega * dt;

                // Winch lifting ropes simulation
                // Dynamic Y boundaries to prevent counterweight overlapping with the winch drum!
                // The top of the counterweight box (ropeY + 12) should stop at the bottom of the drum cylinder (my + 32).
                const minRopeY = my + 20; // safe clearance limit
                const maxRopeY = my + 130;
                
                // Rope Y coordinate moves in proportion to speed
                const speedCoeff = 0.015;
                state.ropeY += state.ropeDir * omega * speedCoeff;
                if (state.ropeY > maxRopeY) {
                    state.ropeY = maxRopeY;
                    state.ropeDir = -1.0;
                }
                if (state.ropeY < minRopeY) {
                    state.ropeY = minRopeY;
                    state.ropeDir = 1.0;
                }
                // Clamp Y bounds dynamically on screen resize or initial load
                if (state.ropeY < minRopeY) state.ropeY = minRopeY;
                if (state.ropeY > maxRopeY) state.ropeY = maxRopeY;

                // Sparks fly on coupling joint if spinning
                if (state.rpm > 50) {
                    const sparkRate = Math.min(state.power * 0.1 + 1, 6);
                    if (Math.random() < sparkRate * dt * 25) {
                        // Position of coupling joint is dynamic (cpx, my)
                        addSparks(cpx, my, 2);
                    }
                }
                updateSparks();

                // Clear &#038; draw
                ctx.clearRect(0, 0, width, height);

                // 1. Draw 모눈종이 Blueprint Grid
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.06)';
                ctx.lineWidth = 1;
                const gridSize = 25;
                for (let x = 0; x < width; x += gridSize) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += gridSize) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }

                // 2. Draw Motor Housing (Left side centered dynamically)
                ctx.save();
                ctx.shadowBlur = 10;
                ctx.shadowColor = 'rgba(0, 0, 0, 0.05)';
                ctx.fillStyle = '#e2e8f0';
                ctx.strokeStyle = '#94a3b8';
                ctx.lineWidth = 4;
                ctx.beginPath();
                ctx.roundRect(mx - 70, my - 60, 140, 120, 16);
                ctx.fill();
                ctx.stroke();

                // Draw heat cooling fins on motor
                ctx.fillStyle = '#cbd5e1';
                for (let fx = mx - 50; fx <= mx + 30; fx += 20) {
                    ctx.fillRect(fx, my - 68, 10, 8);
                    ctx.fillRect(fx, my + 60, 10, 8);
                }

                // Draw Electric Flow inside Motor Housing
                if (state.power > 1.0) {
                    ctx.save();
                    ctx.strokeStyle = '#0284c7'; // Cobalt blue
                    ctx.lineWidth = 2.5;
                    ctx.shadowBlur = 8;
                    ctx.shadowColor = '#0284c7';
                    
                    const flowRate = (state.rpm / 60) * 8;
                    ctx.setLineDash([12, 18]);
                    ctx.lineDashOffset = -flowRate * (currentTime / 100);
                    
                    ctx.beginPath();
                    ctx.roundRect(mx - 62, my - 52, 124, 104, 12);
                    ctx.stroke();
                    ctx.restore();
                }
                ctx.restore();

                // 2b. Draw Motor Text Label
                ctx.save();
                ctx.fillStyle = '#64748b'; // Slate grey
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('구동 모터 (MOTOR)', mx, my - 86);
                ctx.restore();

                // 3. Draw Rotating Coupling Shaft (Middle connection)
                ctx.save();
                ctx.fillStyle = '#475569';
                ctx.fillRect(mx + 70, my - 10, 130, 20); // steel shaft
                ctx.restore();

                // 3b. Draw Rotating Shaft Text Label
                ctx.save();
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('회전 커플링 (COUPLING)', cpx, my - 86);
                ctx.restore();

                // 4. Draw Coupling Joint (Center at 240, 200)
                ctx.save();
                ctx.translate(cpx, my);
                ctx.rotate(state.angle);
                
                // Coupling Flange Disc (Gray plate)
                ctx.fillStyle = '#475569';
                ctx.strokeStyle = '#334155';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.arc(0, 0, 26, 0, Math.PI * 2);
                ctx.fill();
                ctx.stroke();

                // Center Sleeve Hub (Darker gray)
                ctx.fillStyle = '#1e293b';
                ctx.beginPath();
                ctx.arc(0, 0, 12, 0, Math.PI * 2);
                ctx.fill();

                // 4 Bolt Holes / Pins (rotating with the flange)
                ctx.fillStyle = '#94a3b8';
                for (let i = 0; i < 4; i++) {
                    const bAngle = (i * Math.PI) / 2;
                    ctx.beginPath();
                    ctx.arc(Math.cos(bAngle) * 18, Math.sin(bAngle) * 18, 3.5, 0, Math.PI * 2);
                    ctx.fill();
                }
                ctx.restore();

                // 5. Draw Winch Drum &#038; Frame (Centered dynamically)
                ctx.save();
                ctx.translate(wx, my);
                
                // Winch base bracket
                ctx.fillStyle = '#94a3b8';
                ctx.fillRect(-10, 48, 80, 16);
                ctx.fillRect(60, -60, 10, 120); // vertical pillar
                ctx.restore();

                // 5b. Draw Winch Text Label
                ctx.save();
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('윈치 드럼 (WINCH DRUM)', wx, my - 86);
                ctx.restore();

                // Rotating Winch Drum &#038; Flanges
                ctx.save();
                ctx.translate(wx, my);
                ctx.rotate(state.angle);

                // Winch drum cylinder
                ctx.fillStyle = '#4b5563';
                ctx.beginPath();
                ctx.arc(0, 0, 32, 0, Math.PI * 2);
                ctx.fill();

                // Side flanges
                ctx.fillStyle = '#1f2937';
                ctx.fillRect(-8, -42, 16, 84);
                
                // Spoke lines on drum to visualize speed
                ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
                ctx.lineWidth = 3;
                for (let i = 0; i < 4; i++) {
                    const spAngle = (i * Math.PI) / 2;
                    ctx.beginPath();
                    ctx.moveTo(Math.cos(spAngle) * 8, Math.sin(spAngle) * 8);
                    ctx.lineTo(Math.cos(spAngle) * 28, Math.sin(spAngle) * 28);
                    ctx.stroke();
                }
                ctx.restore();

                // Draw Winch Drum Diameter Dimension Ø = 100 mm with professional CAD leader line
                // side=-1: leader goes upper-left to avoid overlap with LOAD text on right
                drawDiameterLeader(ctx, wx, my, 32, '100 mm', 'Ø =', -1);

                // 6. Draw Winch Rope &#038; Hanging Load Weight
                ctx.save();
                const ropeX = wx - 32;
                const ropeStartY = my; // rope exits from drum left side at center height
                
                // Rope line
                ctx.strokeStyle = state.torque > 350 ? '#db2777' : '#475569';
                ctx.lineWidth = state.torque > 350 ? 4 : 2.5;
                if (state.torque > 350) {
                    ctx.shadowBlur = 8;
                    ctx.shadowColor = '#db2777';
                }
                ctx.beginPath();
                ctx.moveTo(ropeX, ropeStartY);
                ctx.lineTo(ropeX, state.ropeY);
                ctx.stroke();
                ctx.shadowBlur = 0;

                // Hanging hook & mass weight block
                ctx.translate(ropeX, state.ropeY);
                
                // Load Mass Box
                const massWidth = 56;
                const massHeight = 44;
                ctx.fillStyle = '#334155';
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.roundRect(-massWidth / 2, 12, massWidth, massHeight, 6);
                ctx.fill();
                ctx.stroke();

                // Mass Text Label (Representing Torque)
                ctx.fillStyle = '#ffffff';
                ctx.font = 'bold 11px Inter';
                ctx.textAlign = 'center';
                const loadWeightKg = (state.torque / (9.80665 * 0.05)).toFixed(0); // T = F * r = m*g*r => m = T/(g*r)
                ctx.fillText(loadWeightKg + " kg", 0, 38);

                // Load Text Label
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'left';
                ctx.fillText('부하 중량 (LOAD)', massWidth / 2 + 8, 38);

                // Rope hooks/eyelet
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.arc(0, 4, 6, 0, Math.PI * 2);
                ctx.stroke();
                ctx.restore();

                // 7. Draw Torque Vector & Speed Vector (Arrows on Drum)
                ctx.save();
                ctx.translate(wx, my);
                
                // Torque vector (glowing circle arrow, representing loading torque)
                if (state.torque > 5.0) {
                    ctx.save();
                    ctx.strokeStyle = '#db2777'; // Pink/magenta
                    ctx.lineWidth = Math.min(state.torque * 0.015 + 2, 8);
                    ctx.shadowBlur = 10;
                    ctx.shadowColor = '#db2777';
                    
                    ctx.beginPath();
                    ctx.arc(0, 0, 56, 0.1, Math.PI * 1.5);
                    ctx.stroke();

                    // Arrowhead
                    ctx.fillStyle = '#db2777';
                    ctx.beginPath();
                    ctx.moveTo(0, -56 - 6);
                    ctx.lineTo(12, -56);
                    ctx.lineTo(0, -56 + 6);
                    ctx.closePath();
                    ctx.fill();
                    ctx.restore();

                    // Vector text label
                    ctx.fillStyle = '#db2777';
                    ctx.font = 'bold 10px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText(`토크 T = ${state.torque.toFixed(1)} N·m`, 0, -68);
                }
                ctx.restore();

                // 8. Draw Spark particles
                ctx.save();
                for (let i = 0; i < state.sparks.length; i++) {
                    const p = state.sparks[i];
                    ctx.fillStyle = p.color;
                    ctx.globalAlpha = p.life;
                    ctx.shadowBlur = 6;
                    ctx.shadowColor = p.color;
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, 2.5 * p.life, 0, Math.PI * 2);
                    ctx.fill();
                }
                ctx.restore();

                requestAnimationFrame(animate);
            }
            requestAnimationFrame(animate);

            // 7. Events &#038; Initialization
            btnModeP.addEventListener('click', () => setMode('P'));
            btnModeT.addEventListener('click', () => setMode('T'));
            btnModeN.addEventListener('click', () => setMode('N'));

            // Slider & Text inputs binding
            inputPower.addEventListener('change', syncPowerFromInput);
            sliderPower.addEventListener('input', syncPowerFromSlider);

            inputTorque.addEventListener('change', syncTorqueFromInput);
            sliderTorque.addEventListener('input', syncTorqueFromSlider);

            inputRpm.addEventListener('change', syncRpmFromInput);
            sliderRpm.addEventListener('input', syncRpmFromSlider);

            // Manual position reset
            btnResetPos.addEventListener('click', () => {
                state.angle = 0;
                const dpr = getDPR();
                const my = (canvas.height / dpr) / 2;
                state.ropeY = my + 60; // Safe middle position between my+20 and my+130
                state.ropeDir = 1.0;
                state.sparks = [];
            });

            // Set initial state
            setMode('P');

            // 8. Copy and Right-Click Protection Document level
            (function() {
                function blockEvents() {
                    // Block right click on document and show custom alert
                    document.addEventListener('contextmenu', function(e) {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }, { capture: true });
                    
                    // Block text selection on document
                    document.addEventListener('selectstart', function(e) {
                        e.preventDefault();
                        return false;
                    }, { capture: true });
                    
                    // Block developer tools and copy hotkeys
                    document.addEventListener('keydown', function(e) {
                        // nested if statements to avoid ampersands conversions in WordPress REST API
                        if (e.key === 'F12') {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                        if (e.ctrlKey) {
                            if (e.key === 'u' || e.key === 'c' || e.key === 's') {
                                e.preventDefault();
                                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                                return false;
                            }
                        }
                        if (e.ctrlKey) {
                            if (e.shiftKey) {
                                if (e.key === 'i' || e.key === 'I' || e.key === 'j' || e.key === 'J' || e.key === 'c' || e.key === 'C') {
                                    e.preventDefault();
                                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                                    return false;
                                }
                            }
                        }
                    }, { capture: true });
                }
                
                if (document.readyState === 'complete' || document.readyState === 'interactive') {
                    blockEvents();
                } else {
                    document.addEventListener('DOMContentLoaded', blockEvents);
                    window.addEventListener('load', blockEvents);
                }
            })();

        })();
    </script>

    </div>
</div>

<script>
        (function() {
            // Safe guard against duplicate initiation
            if (window.__torquepower_initialized) return;
            window.__torquepower_initialized = true;

            // 1. Core DOM queries
            const btnModeP = document.getElementById('btn-mode-p');
            const btnModeT = document.getElementById('btn-mode-t');
            const btnModeN = document.getElementById('btn-mode-n');

            const groupPower = document.getElementById('group-power');
            const groupTorque = document.getElementById('group-torque');
            const groupRpm = document.getElementById('group-rpm');

            const inputPower = document.getElementById('input-power');
            const sliderPower = document.getElementById('slider-power');
            const inputTorque = document.getElementById('input-torque');
            const sliderTorque = document.getElementById('slider-torque');
            const inputRpm = document.getElementById('input-rpm');
            const sliderRpm = document.getElementById('slider-rpm');

            const txtPrimaryOutput = document.getElementById('txt-primary-output');
            const txtSecondaryOutput = document.getElementById('txt-secondary-output');

            const resPower = document.getElementById('res-power');
            const resHp = document.getElementById('res-hp');
            const resTorqueNm = document.getElementById('res-torque-nm');
            const resTorqueKgm = document.getElementById('res-torque-kgm');

            const valOmega = document.getElementById('val-omega');
            const valWatts = document.getElementById('val-watts');
            const valRopeSpeed = document.getElementById('val-rope-speed');

            const overlayRunStatus = document.getElementById('overlay-run-status');
            const btnResetPos = document.getElementById('btn-reset-pos');

            // 2. Constants & Physics State
            const state = {
                mode: 'P', // 'P' (calc Power), 'T' (calc Torque), 'N' (calc RPM)
                power: 11.0,  // kW
                torque: 70.0, // N·m
                rpm: 1500.0,  // RPM
                angle: 0.0,
                ropeY: 180.0,
                ropeDir: 1.0,
                sparks: []
            };

            // Range definition (Min / Max)
            const ranges = {
                power: { min: 0.1, max: 250.0 },
                torque: { min: 1.0, max: 1000.0 },
                rpm: { min: 10.0, max: 5000.0 }
            };

            // 3. Mathematical Solvers
            function calculateThirdVariable() {
                if (state.mode === 'P') {
                    // P = T * N / 9549.3
                    state.power = (state.torque * state.rpm) / 9549.3;
                    // Apply bounds check
                    if (state.power < ranges.power.min) state.power = ranges.power.min;
                    if (state.power > ranges.power.max) state.power = ranges.power.max;
                } else if (state.mode === 'T') {
                    // T = 9549.3 * P / N
                    if (state.rpm > 0) {
                        state.torque = (9549.3 * state.power) / state.rpm;
                    } else {
                        state.torque = ranges.torque.min;
                    }
                    if (state.torque < ranges.torque.min) state.torque = ranges.torque.min;
                    if (state.torque > ranges.torque.max) state.torque = ranges.torque.max;
                } else if (state.mode === 'N') {
                    // N = 9549.3 * P / T
                    if (state.torque > 0) {
                        state.rpm = (9549.3 * state.power) / state.torque;
                    } else {
                        state.rpm = ranges.rpm.min;
                    }
                    if (state.rpm < ranges.rpm.min) state.rpm = ranges.rpm.min;
                    if (state.rpm > ranges.rpm.max) state.rpm = ranges.rpm.max;
                }
                updateUIValues();
            }

            // Sync text inputs and range sliders without aggressive clamping
            function syncPowerFromInput() {
                let val = parseFloat(inputPower.value);
                if (isNaN(val)) val = ranges.power.min;
                if (val < ranges.power.min) val = ranges.power.min;
                if (val > ranges.power.max) val = ranges.power.max;
                state.power = val;
                inputPower.value = val.toFixed(2);
                sliderPower.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncPowerFromSlider() {
                state.power = parseFloat(sliderPower.value);
                inputPower.value = state.power.toFixed(1);
                calculateThirdVariable();
            }

            function syncTorqueFromInput() {
                let val = parseFloat(inputTorque.value);
                if (isNaN(val)) val = ranges.torque.min;
                if (val < ranges.torque.min) val = ranges.torque.min;
                if (val > ranges.torque.max) val = ranges.torque.max;
                state.torque = val;
                inputTorque.value = val.toFixed(1);
                sliderTorque.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncTorqueFromSlider() {
                state.torque = parseFloat(sliderTorque.value);
                inputTorque.value = state.torque.toFixed(0);
                calculateThirdVariable();
            }

            function syncRpmFromInput() {
                let val = parseFloat(inputRpm.value);
                if (isNaN(val)) val = ranges.rpm.min;
                if (val < ranges.rpm.min) val = ranges.rpm.min;
                if (val > ranges.rpm.max) val = ranges.rpm.max;
                state.rpm = val;
                inputRpm.value = val.toFixed(0);
                sliderRpm.value = Math.round(val);
                calculateThirdVariable();
            }

            function syncRpmFromSlider() {
                state.rpm = parseFloat(sliderRpm.value);
                inputRpm.value = state.rpm.toFixed(0);
                calculateThirdVariable();
            }

            // 4. Update UI Outputs
            function updateUIValues() {
                // Update inputs
                if (state.mode !== 'P') {
                    inputPower.value = state.power.toFixed(2);
                    sliderPower.value = Math.round(state.power);
                }
                if (state.mode !== 'T') {
                    inputTorque.value = state.torque.toFixed(1);
                    sliderTorque.value = Math.round(state.torque);
                }
                if (state.mode !== 'N') {
                    inputRpm.value = state.rpm.toFixed(0);
                    sliderRpm.value = Math.round(state.rpm);
                }

                // Primary Output Readout Box
                if (state.mode === 'P') {
                    txtPrimaryOutput.innerText = state.power.toFixed(2) + ' kW';
                    const hp = state.power * 1.35962;
                    txtSecondaryOutput.innerText = hp.toFixed(2) + ' HP (마력)';
                } else if (state.mode === 'T') {
                    txtPrimaryOutput.innerText = state.torque.toFixed(1) + ' N·m';
                    const kgm = state.torque / 9.80665;
                    txtSecondaryOutput.innerText = kgm.toFixed(2) + ' kgf·m (킬로그램토크)';
                } else if (state.mode === 'N') {
                    txtPrimaryOutput.innerText = state.rpm.toFixed(0) + ' RPM';
                    const hz = state.rpm / 60;
                    txtSecondaryOutput.innerText = hz.toFixed(1) + ' Hz (초당 회전수)';
                }

                // Results Grid Card Values
                resPower.innerText = state.power.toFixed(2) + ' kW';
                resHp.innerText = (state.power * 1.35962).toFixed(2) + ' HP';
                resTorqueNm.innerText = state.torque.toFixed(1) + ' N·m';
                resTorqueKgm.innerText = (state.torque / 9.80665).toFixed(2) + ' kgf·m';

                // Mini metrics strip at the bottom
                const omega = (2 * Math.PI * state.rpm) / 60;
                valOmega.innerText = omega.toFixed(1) + ' rad/s';
                valWatts.innerText = (state.power * 1000).toLocaleString('ko-KR', { maximumFractionDigits: 0 }) + ' W';
                
                // Winch drum radius is 50mm (0.05m). Speed = r * omega
                const ropeSpeed = 0.05 * omega;
                valRopeSpeed.innerText = ropeSpeed.toFixed(2) + ' m/s';

                // Thermal Warning status
                if (state.power > 150) {
                    overlayRunStatus.innerText = '고부하 운전 (과열 주의)';
                    overlayRunStatus.style.color = '#f59e0b';
                } else {
                    overlayRunStatus.innerText = '정상 운전 중';
                    overlayRunStatus.style.color = '#10b981';
                }
            }

            // 5. Calculate mode toggle
            function setMode(newMode) {
                state.mode = newMode;
                
                // Active button styling
                btnModeP.classList.remove('active');
                btnModeT.classList.remove('active');
                btnModeN.classList.remove('active');

                // Enable/disable slider blocks
                groupPower.classList.remove('disabled');
                groupTorque.classList.remove('disabled');
                groupRpm.classList.remove('disabled');

                if (newMode === 'P') {
                    btnModeP.classList.add('active');
                    groupPower.classList.add('disabled');
                } else if (newMode === 'T') {
                    btnModeT.classList.add('active');
                    groupTorque.classList.add('disabled');
                } else if (newMode === 'N') {
                    btnModeN.classList.add('active');
                    groupRpm.classList.add('disabled');
                }
                calculateThirdVariable();
            }

            // Presets selector
            const presetBtns = document.querySelectorAll('.preset-btn');
            presetBtns.forEach(btn => {
                btn.addEventListener('click', function() {
                    presetBtns.forEach(b => b.classList.remove('active'));
                    this.classList.add('active');

                    const preset = this.getAttribute('data-preset');
                    if (preset === 'standard') {
                        state.torque = 70.0;
                        state.rpm = 1500.0;
                        setMode('P');
                    } else if (preset === 'crane') {
                        state.torque = 650.0;
                        state.rpm = 160.0;
                        setMode('P');
                    } else if (preset === 'spindle') {
                        state.torque = 8.5;
                        state.rpm = 4500.0;
                        setMode('P');
                    }
                });
            });

            // 6. Interactive Canvas Physics Simulation
            const canvas = document.getElementById('physics-canvas');
            const ctx = canvas.getContext('2d');

            function getDPR() {
                return window.devicePixelRatio || 1;
            }

            function initCanvas() {
                const dpr = getDPR();
                const rect = canvas.getBoundingClientRect();
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.scale(dpr, dpr);
            }

            window.addEventListener('resize', initCanvas);
            initCanvas();
            // Polling backups to guarantee canvas is sized correctly after WordPress page loads
            setTimeout(initCanvas, 100);
            setTimeout(initCanvas, 300);
            setTimeout(initCanvas, 800);

            // Winch Rope limits will be calculated dynamically inside animation loop to prevent counterweight overlapping

            // Spark Particle Generator
            function addSparks(x, y, count) {
                for (let i = 0; i < count; i++) {
                    state.sparks.push({
                        x: x,
                        y: y,
                        vx: (Math.random() - 0.5) * 8 + (state.rpm > 0 ? 3 : -3),
                        vy: (Math.random() - 0.7) * 6 - 2,
                        life: 1.0,
                        decay: Math.random() * 0.05 + 0.03,
                        color: Math.random() > 0.4 ? '#0284c7' : '#db2777'
                    });
                }
            }

            function updateSparks() {
                for (let i = state.sparks.length - 1; i >= 0; i--) {
                    const p = state.sparks[i];
                    p.x += p.vx;
                    p.y += p.vy;
                    p.vy += 0.15; // gravity
                    p.life -= p.decay;
                    if (p.life <= 0) {
                        state.sparks.splice(i, 1);
                    }
                }
            }

            function drawDiameterLeader(c, x, y, r, valStr, labelPrefix, side) {
                c.save();
                c.strokeStyle = 'rgba(71, 85, 105, 0.4)';
                c.lineWidth = 1.2;
                
                // Angle of leader line (45 degrees up-left or up-right)
                const angle = side === -1 ? -Math.PI * 0.75 : -Math.PI * 0.25;
                const px = x + Math.cos(angle) * r;
                const py = y + Math.sin(angle) * r;
                
                // Draw a beautiful small dot on the circle boundary
                c.beginPath();
                c.arc(px, py, 2.5, 0, Math.PI * 2);
                c.fillStyle = side === -1 ? '#0284c7' : '#db2777';
                c.fill();
                
                // Draw leader lines: from circle boundary diagonal outwards, then horizontal
                const lx1 = px + Math.cos(angle) * 20;
                const ly1 = py + Math.sin(angle) * 20;
                const lx2 = lx1 + side * 45;
                const ly2 = ly1;
                
                c.beginPath();
                c.moveTo(px, py);
                c.lineTo(lx1, ly1);
                c.lineTo(lx2, ly2);
                c.stroke();
                
                // Draw text above the horizontal line
                const text = `${labelPrefix} ${valStr}`;
                c.font = 'bold 10px var(--font-body)';
                
                const tx = side === -1 ? lx1 - 4 : lx1 + 4;
                c.fillStyle = '#1e293b';
                c.textAlign = side === -1 ? 'right' : 'left';
                c.textBaseline = 'bottom';
                c.fillText(text, tx, ly2 - 1);
                c.restore();
            }

            // Main Animation loop
            let lastTime = 0;
            function animate(currentTime) {
                if (lastTime === 0) lastTime = currentTime;
                const dt = (currentTime - lastTime) / 1000;
                lastTime = currentTime;

                const dpr = getDPR();
                const width = canvas.width / dpr;
                const height = canvas.height / dpr;

                // Dynamically center the motor-coupling-winch assembly to fill the canvas beautifully
                const totalSystemWidth = 410;
                const startX = (width - totalSystemWidth) / 2;
                
                const mx = startX + 70;
                const my = height / 2;
                const cpx = mx + 120;
                const wx = cpx + 140;

                // Physics state updates
                // Angular step = omega * dt
                const omega = (2 * Math.PI * state.rpm) / 60;
                state.angle += omega * dt;

                // Winch lifting ropes simulation
                // Dynamic Y boundaries to prevent counterweight overlapping with the winch drum!
                // The top of the counterweight box (ropeY + 12) should stop at the bottom of the drum cylinder (my + 32).
                const minRopeY = my + 20; // safe clearance limit
                const maxRopeY = my + 130;
                
                // Rope Y coordinate moves in proportion to speed
                const speedCoeff = 0.015;
                state.ropeY += state.ropeDir * omega * speedCoeff;
                if (state.ropeY > maxRopeY) {
                    state.ropeY = maxRopeY;
                    state.ropeDir = -1.0;
                }
                if (state.ropeY < minRopeY) {
                    state.ropeY = minRopeY;
                    state.ropeDir = 1.0;
                }
                // Clamp Y bounds dynamically on screen resize or initial load
                if (state.ropeY < minRopeY) state.ropeY = minRopeY;
                if (state.ropeY > maxRopeY) state.ropeY = maxRopeY;

                // Sparks fly on coupling joint if spinning
                if (state.rpm > 50) {
                    const sparkRate = Math.min(state.power * 0.1 + 1, 6);
                    if (Math.random() < sparkRate * dt * 25) {
                        // Position of coupling joint is dynamic (cpx, my)
                        addSparks(cpx, my, 2);
                    }
                }
                updateSparks();

                // Clear &#038; draw
                ctx.clearRect(0, 0, width, height);

                // 1. Draw 모눈종이 Blueprint Grid
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.06)';
                ctx.lineWidth = 1;
                const gridSize = 25;
                for (let x = 0; x < width; x += gridSize) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += gridSize) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }

                // 2. Draw Motor Housing (Left side centered dynamically)
                ctx.save();
                ctx.shadowBlur = 10;
                ctx.shadowColor = 'rgba(0, 0, 0, 0.05)';
                ctx.fillStyle = '#e2e8f0';
                ctx.strokeStyle = '#94a3b8';
                ctx.lineWidth = 4;
                ctx.beginPath();
                ctx.roundRect(mx - 70, my - 60, 140, 120, 16);
                ctx.fill();
                ctx.stroke();

                // Draw heat cooling fins on motor
                ctx.fillStyle = '#cbd5e1';
                for (let fx = mx - 50; fx <= mx + 30; fx += 20) {
                    ctx.fillRect(fx, my - 68, 10, 8);
                    ctx.fillRect(fx, my + 60, 10, 8);
                }

                // Draw Electric Flow inside Motor Housing
                if (state.power > 1.0) {
                    ctx.save();
                    ctx.strokeStyle = '#0284c7'; // Cobalt blue
                    ctx.lineWidth = 2.5;
                    ctx.shadowBlur = 8;
                    ctx.shadowColor = '#0284c7';
                    
                    const flowRate = (state.rpm / 60) * 8;
                    ctx.setLineDash([12, 18]);
                    ctx.lineDashOffset = -flowRate * (currentTime / 100);
                    
                    ctx.beginPath();
                    ctx.roundRect(mx - 62, my - 52, 124, 104, 12);
                    ctx.stroke();
                    ctx.restore();
                }
                ctx.restore();

                // 2b. Draw Motor Text Label
                ctx.save();
                ctx.fillStyle = '#64748b'; // Slate grey
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('구동 모터 (MOTOR)', mx, my - 86);
                ctx.restore();

                // 3. Draw Rotating Coupling Shaft (Middle connection)
                ctx.save();
                ctx.fillStyle = '#475569';
                ctx.fillRect(mx + 70, my - 10, 130, 20); // steel shaft
                ctx.restore();

                // 3b. Draw Rotating Shaft Text Label
                ctx.save();
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('회전 커플링 (COUPLING)', cpx, my - 86);
                ctx.restore();

                // 4. Draw Coupling Joint (Center at 240, 200)
                ctx.save();
                ctx.translate(cpx, my);
                ctx.rotate(state.angle);
                
                // Coupling Flange Disc (Gray plate)
                ctx.fillStyle = '#475569';
                ctx.strokeStyle = '#334155';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.arc(0, 0, 26, 0, Math.PI * 2);
                ctx.fill();
                ctx.stroke();

                // Center Sleeve Hub (Darker gray)
                ctx.fillStyle = '#1e293b';
                ctx.beginPath();
                ctx.arc(0, 0, 12, 0, Math.PI * 2);
                ctx.fill();

                // 4 Bolt Holes / Pins (rotating with the flange)
                ctx.fillStyle = '#94a3b8';
                for (let i = 0; i < 4; i++) {
                    const bAngle = (i * Math.PI) / 2;
                    ctx.beginPath();
                    ctx.arc(Math.cos(bAngle) * 18, Math.sin(bAngle) * 18, 3.5, 0, Math.PI * 2);
                    ctx.fill();
                }
                ctx.restore();

                // 5. Draw Winch Drum &#038; Frame (Centered dynamically)
                ctx.save();
                ctx.translate(wx, my);
                
                // Winch base bracket
                ctx.fillStyle = '#94a3b8';
                ctx.fillRect(-10, 48, 80, 16);
                ctx.fillRect(60, -60, 10, 120); // vertical pillar
                ctx.restore();

                // 5b. Draw Winch Text Label
                ctx.save();
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'center';
                ctx.fillText('윈치 드럼 (WINCH DRUM)', wx, my - 86);
                ctx.restore();

                // Rotating Winch Drum &#038; Flanges
                ctx.save();
                ctx.translate(wx, my);
                ctx.rotate(state.angle);

                // Winch drum cylinder
                ctx.fillStyle = '#4b5563';
                ctx.beginPath();
                ctx.arc(0, 0, 32, 0, Math.PI * 2);
                ctx.fill();

                // Side flanges
                ctx.fillStyle = '#1f2937';
                ctx.fillRect(-8, -42, 16, 84);
                
                // Spoke lines on drum to visualize speed
                ctx.strokeStyle = 'rgba(255, 255, 255, 0.2)';
                ctx.lineWidth = 3;
                for (let i = 0; i < 4; i++) {
                    const spAngle = (i * Math.PI) / 2;
                    ctx.beginPath();
                    ctx.moveTo(Math.cos(spAngle) * 8, Math.sin(spAngle) * 8);
                    ctx.lineTo(Math.cos(spAngle) * 28, Math.sin(spAngle) * 28);
                    ctx.stroke();
                }
                ctx.restore();

                // Draw Winch Drum Diameter Dimension Ø = 100 mm with professional CAD leader line
                // side=-1: leader goes upper-left to avoid overlap with LOAD text on right
                drawDiameterLeader(ctx, wx, my, 32, '100 mm', 'Ø =', -1);

                // 6. Draw Winch Rope &#038; Hanging Load Weight
                ctx.save();
                const ropeX = wx - 32;
                const ropeStartY = my; // rope exits from drum left side at center height
                
                // Rope line
                ctx.strokeStyle = state.torque > 350 ? '#db2777' : '#475569';
                ctx.lineWidth = state.torque > 350 ? 4 : 2.5;
                if (state.torque > 350) {
                    ctx.shadowBlur = 8;
                    ctx.shadowColor = '#db2777';
                }
                ctx.beginPath();
                ctx.moveTo(ropeX, ropeStartY);
                ctx.lineTo(ropeX, state.ropeY);
                ctx.stroke();
                ctx.shadowBlur = 0;

                // Hanging hook & mass weight block
                ctx.translate(ropeX, state.ropeY);
                
                // Load Mass Box
                const massWidth = 56;
                const massHeight = 44;
                ctx.fillStyle = '#334155';
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.roundRect(-massWidth / 2, 12, massWidth, massHeight, 6);
                ctx.fill();
                ctx.stroke();

                // Mass Text Label (Representing Torque)
                ctx.fillStyle = '#ffffff';
                ctx.font = 'bold 11px Inter';
                ctx.textAlign = 'center';
                const loadWeightKg = (state.torque / (9.80665 * 0.05)).toFixed(0); // T = F * r = m*g*r => m = T/(g*r)
                ctx.fillText(loadWeightKg + " kg", 0, 38);

                // Load Text Label
                ctx.fillStyle = '#64748b';
                ctx.font = 'bold 10px var(--font-body)';
                ctx.textAlign = 'left';
                ctx.fillText('부하 중량 (LOAD)', massWidth / 2 + 8, 38);

                // Rope hooks/eyelet
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 3;
                ctx.beginPath();
                ctx.arc(0, 4, 6, 0, Math.PI * 2);
                ctx.stroke();
                ctx.restore();

                // 7. Draw Torque Vector & Speed Vector (Arrows on Drum)
                ctx.save();
                ctx.translate(wx, my);
                
                // Torque vector (glowing circle arrow, representing loading torque)
                if (state.torque > 5.0) {
                    ctx.save();
                    ctx.strokeStyle = '#db2777'; // Pink/magenta
                    ctx.lineWidth = Math.min(state.torque * 0.015 + 2, 8);
                    ctx.shadowBlur = 10;
                    ctx.shadowColor = '#db2777';
                    
                    ctx.beginPath();
                    ctx.arc(0, 0, 56, 0.1, Math.PI * 1.5);
                    ctx.stroke();

                    // Arrowhead
                    ctx.fillStyle = '#db2777';
                    ctx.beginPath();
                    ctx.moveTo(0, -56 - 6);
                    ctx.lineTo(12, -56);
                    ctx.lineTo(0, -56 + 6);
                    ctx.closePath();
                    ctx.fill();
                    ctx.restore();

                    // Vector text label
                    ctx.fillStyle = '#db2777';
                    ctx.font = 'bold 10px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText(`토크 T = ${state.torque.toFixed(1)} N·m`, 0, -68);
                }
                ctx.restore();

                // 8. Draw Spark particles
                ctx.save();
                for (let i = 0; i < state.sparks.length; i++) {
                    const p = state.sparks[i];
                    ctx.fillStyle = p.color;
                    ctx.globalAlpha = p.life;
                    ctx.shadowBlur = 6;
                    ctx.shadowColor = p.color;
                    ctx.beginPath();
                    ctx.arc(p.x, p.y, 2.5 * p.life, 0, Math.PI * 2);
                    ctx.fill();
                }
                ctx.restore();

                requestAnimationFrame(animate);
            }
            requestAnimationFrame(animate);

            // 7. Events &#038; Initialization
            btnModeP.addEventListener('click', () => setMode('P'));
            btnModeT.addEventListener('click', () => setMode('T'));
            btnModeN.addEventListener('click', () => setMode('N'));

            // Slider & Text inputs binding
            inputPower.addEventListener('change', syncPowerFromInput);
            sliderPower.addEventListener('input', syncPowerFromSlider);

            inputTorque.addEventListener('change', syncTorqueFromInput);
            sliderTorque.addEventListener('input', syncTorqueFromSlider);

            inputRpm.addEventListener('change', syncRpmFromInput);
            sliderRpm.addEventListener('input', syncRpmFromSlider);

            // Manual position reset
            btnResetPos.addEventListener('click', () => {
                state.angle = 0;
                const dpr = getDPR();
                const my = (canvas.height / dpr) / 2;
                state.ropeY = my + 60; // Safe middle position between my+20 and my+130
                state.ropeDir = 1.0;
                state.sparks = [];
            });

            // Set initial state
            setMode('P');

            // 8. Copy and Right-Click Protection Document level
            (function() {
                function blockEvents() {
                    // Block right click on document and show custom alert
                    document.addEventListener('contextmenu', function(e) {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }, { capture: true });
                    
                    // Block text selection on document
                    document.addEventListener('selectstart', function(e) {
                        e.preventDefault();
                        return false;
                    }, { capture: true });
                    
                    // Block developer tools and copy hotkeys
                    document.addEventListener('keydown', function(e) {
                        // nested if statements to avoid ampersands conversions in WordPress REST API
                        if (e.key === 'F12') {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                        if (e.ctrlKey) {
                            if (e.key === 'u' || e.key === 'c' || e.key === 's') {
                                e.preventDefault();
                                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                                return false;
                            }
                        }
                        if (e.ctrlKey) {
                            if (e.shiftKey) {
                                if (e.key === 'i' || e.key === 'I' || e.key === 'j' || e.key === 'J' || e.key === 'c' || e.key === 'C') {
                                    e.preventDefault();
                                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                                    return false;
                                }
                            }
                        }
                    }, { capture: true });
                }
                
                if (document.readyState === 'complete' || document.readyState === 'interactive') {
                    blockEvents();
                } else {
                    document.addEventListener('DOMContentLoaded', blockEvents);
                    window.addEventListener('load', blockEvents);
                }
            })();

        })();
    </script>




<div style="background: linear-gradient(135deg, rgba(0,242,254,0.03), rgba(138,43,226,0.03)); border: 1px solid rgba(0,242,254,0.15); border-radius: 12px; padding: 18px 24px; margin: 25px auto 35px auto; font-size: 0.95em; color: #4b5563; line-height: 1.7; font-family: sans-serif;">
    <strong style="color: #1f2937; font-size: 1.05em; display: flex; align-items: center; gap: 8px;">
        <span style="font-size: 1.2em;"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4a1.png" alt="💡" class="wp-smiley" style="height: 1em; max-height: 1em;" /></span> 간편 사용 설명서
    </strong>
    <ol style="margin: 10px 0 0 0; padding-left: 20px;">
        <li style="margin-bottom: 6px;"><strong>입력 단위 설정: 동력(kW, HP), 토크(N·m, kgf·m, kgf·cm), 회전 속도(RPM) 중 기준이 될 두 가지 변수를 활성화하여 입력합니다.</strong></li>
<li style="margin-bottom: 6px;">슬라이더 또는 직접 입력: 활성화된 두 변수의 슬라이더를 조절하거나 직접 값을 입력합니다. (실시간으로 나머지 한 변수가 계산됩니다.)</li>
<li style="margin-bottom: 6px;">실시간 물리 작용 확인: 입력한 토크와 속도에 맞춰 2D 모터 샤프트와 드럼이 회전하며, 토크가 높을수록 드럼에 감기는 로프의 장력과 모터 하우징의 동력 플로우(Glowing power flux)가 강하게 시각화됩니다.</li>
<li style="margin-bottom: 6px;">사전 정의 프리셋 활용: 고토크-저속(크레인), 저토크-고속(스핀들), 표준 산업용 모터 프리셋을 선택하여 동작 차이를 비교해 볼 수 있습니다.</li>
    </ol>
</div>



<details class="premium-seo-accordion" style="border: 1px solid rgba(0,0,0,0.08); border-radius: 12px; background: #fbfbfc; padding: 0; margin: 30px auto; box-shadow: 0 4px 6px -1px rgba(0,0,0,0.01); font-family: sans-serif;">
    <summary style="display: flex; justify-content: space-between; align-items: center; padding: 20px 24px; font-size: 1.1em; font-weight: 700; color: #1f2937; cursor: pointer; user-select: none; outline: none; list-style: none;">
        <span><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f4da.png" alt="📚" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 상세 기계공학 해설 및 설계 규격 (KS/ISO) 확인하기</span>
        <span class="accordion-arrow" style="font-size: 0.9em; color: #9ca3af; transition: transform 0.2s ease;">▼</span>
    </summary>
    <div style="padding: 0 24px 24px 24px; border-top: 1px solid rgba(0,0,0,0.04); background: #ffffff; border-radius: 0 0 12px 12px; font-size: 0.98em; color: #374151; line-height: 1.8;">
        <div style="margin-top: 20px;">
            <h3>1. 회전 기계설계에서 토크(Torque)와 동력(Power)의 관계</h3>
<p>기계설계 및 산업 현장에서 <strong>동력(Power, P)</strong>과 <strong>토크(Torque, T)</strong>는 회전 운동 시스템을 정의하는 핵심 물리량입니다. 토크는 물체를 회전시키려는 물리적인 힘의 모멘트(Moment of Force)를 뜻하며, 동력은 단위 시간당 모터가 수행할 수 있는 일의 양을 나타냅니다. 따라서 동력은 단순히 힘(토크)의 크기뿐만 아니라 그 힘이 얼마나 빠른 속도(각속도, &omega;)로 작용하고 있는지를 종합적으로 정의하는 물리량입니다.</p><p>축 설계(Shaft Design) 및 감속기(Gearbox) 선정 시 이 둘의 유기적인 관계를 파악하는 것이 필수적입니다. 감속기를 사용하여 회전 속도를 낮추면, 동력은 마찰 손실을 제외하고 일정하게 유지되지만 토크는 감속비에 비례하여 증대됩니다. 이를 통해 작은 용량의 모터로도 무거운 하중을 들어 올리거나 기계를 구동하는 시스템 설계가 가능해집니다.</p>
<h3>2. 핵심 변환 공식 유도 및 단위 환산</h3>
<p>물리학적으로 동력 <code>P</code>는 토크 <code>T</code>와 각속도 <code>&omega;</code>의 곱으로 정의됩니다:</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">P = T &times; &omega; &nbsp;[W]</p><p>여기서 각속도 <code>&omega;</code>를 실무에서 널리 사용하는 분당 회전수 <code>N [RPM]</code>으로 변환하면 <code>&omega; = (2&pi; &times; N) / 60</code>이 됩니다. 이를 대입하면 다음과 같습니다:</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">P = T &times; (2&pi; &times; N) / 60 &nbsp;[W]</p><p>와트(W) 단위를 킬로와트(kW) 단위로 환산(1kW = 1000W)하고 정밀 상수를 정리하면 실무에서 가장 빈번하게 사용되는 유명한 <strong>9549 공학 상수 공식</strong>이 유도됩니다:</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.1em; color: #0369a1;">P [kW] = (T [N&middot;m] &times; N [RPM]) / 9,549.3 &nbsp; 또는 &nbsp; T [N&middot;m] = 9,549.3 &times; P [kW] / N [RPM]</p><p>또한 마력(HP) 단위를 환산할 때 주로 사용되는 공학 상수는 716.2와 726입니다. 영국식 마력(HP)과 미터법 토크(kgf&middot;m) 단위 환산 관계는 다음과 같습니다:</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">T [kgf&middot;m] = 716.2 &times; P [HP] / N [RPM]</p>
<h3>3. 모터 용량 선정 가이드 및 안전 계수 (KS B 6310 규격 표준)</h3>
<p>산업용 회전 전동기 선정 시, 필요한 기계적 소요 동력에 적절한 안전율과 환경 계수를 가산하여 모터 정격 출력을 최종 산정해야 합니다. 모터의 시동 시에는 정격 토크의 1.5 ~ 2.5배에 달하는 시동 토크(Starting Torque)가 요구되므로 극심한 시동 부하 또는 정지 마찰을 이겨낼 수 있는 부하 검토가 병행되어야 합니다.</p><ul><li><strong>관성 부하 검토 (GD²):</strong> 대형 팬, 블로워, 플라이휠 등 부하측의 회전 관성 모멘트가 큰 기계는 모터가 정격 속도에 도달하기까지의 시동 시간 동안 과도한 발열이 누적되므로 정격 토크 대비 2배 이상의 기동 안전율을 반영합니다.</li><li><strong>과부하 내량 (Overload Capacity):</strong> 급격한 충격 부하가 빈번한 파쇄기나 절단기 등은 정상 운전 상태의 소요 토크 대비 부하 안전계수 <code>S_f = 1.5 ~ 2.0</code>을 적용하여 샤프트 및 키 홈의 전단 응력을 안전하게 검토해야 합니다 (KS B ISO 1940 참조).</li></ul>

        </div>
    </div>
</details>
<style>
details.premium-seo-accordion[open] summary .accordion-arrow { transform: rotate(180deg); color: #00f2fe; }
details.premium-seo-accordion summary::-webkit-details-marker { display: none; }
details.premium-seo-accordion:hover { border-color: rgba(0,242,254,0.3); }
</style>

]]></content:encoded>
					
					<wfw:commentRss>https://myengnote.com/torque-power-converter-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
