<?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>MyEngNote</title>
	<atom:link href="https://myengnote.com/feed/" rel="self" type="application/rss+xml" />
	<link>https://myengnote.com</link>
	<description></description>
	<lastBuildDate>Thu, 11 Jun 2026 23:10:05 +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; 시뮬레이터</title>
		<link>https://myengnote.com/thin-walled-pressure-vessel-calculator-2/</link>
					<comments>https://myengnote.com/thin-walled-pressure-vessel-calculator-2/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Thu, 11 Jun 2026 23:05:44 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[압력용기]]></category>
		<category><![CDATA[원주응력]]></category>
		<category><![CDATA[재료역학]]></category>
		<category><![CDATA[축방향응력]]></category>
		<guid isPermaLink="false">https://myengnote.com/thin-walled-pressure-vessel-calculator-2/</guid>

					<description><![CDATA[실린더형 및 구형 압력용기의 내경, 벽면 두께, 작동 내압 및 재료 항복강도를 조절하여 박막 압력용기 공식에 입각한 원주 응력(Hoop), 축 방향 응력(Longitudinal), 최대 전단 응력 및 안전율과 파열 시뮬레이션을 제공하는 계산기입니다. <a href="https://myengnote.com/thin-walled-pressure-vessel-calculator-2/" style="text-decoration:none; color:#0073aa; font-weight:bold;">[본문 전체보기 >]</a>]]></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; 시뮬레이터</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 pressure-vessel-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .pressure-vessel-calculator-wrapper *, .pressure-vessel-calculator-wrapper *::before, .pressure-vessel-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .pressure-vessel-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; /* Cobalt blue */
            --color-cyan-glow: rgba(2, 132, 199, 0.15);
            --color-magenta: #db2777; /* Pink/magenta */
            --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);
        }
        .pressure-vessel-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%;
        }
        .app-background-glow::after {
            background: var(--color-purple);
            bottom: -10%;
            left: -5%;
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
        }
        .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;
        }
        .simulation-panel .panel-header {
            justify-content: space-between;
            flex-wrap: wrap;
        }
        .canvas-scale-indicator {
            font-size: 11px;
            font-weight: 600;
            padding: 4px 10px;
            border-radius: 12px;
            border: 1px solid transparent;
            white-space: nowrap;
            display: inline-flex;
            align-items: center;
            gap: 6px;
        }
        .text-cyan { color: var(--color-cyan); }
        .text-magenta { color: var(--color-magenta); }
        .text-purple { color: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .tab-group {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            background: rgba(0, 0, 0, 0.02);
            padding: 4px;
            border-radius: 10px;
            border: 1px solid var(--color-border);
        }
        .tab-btn {
            background: transparent;
            border: none;
            padding: 8px 12px;
            font-size: 12px;
            font-weight: 600;
            color: var(--color-text-muted);
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
        }
        .tab-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: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 8px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        .simulation-panel {
            grid-column: 2;
        }
        .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: rgba(255, 255, 255, 0.9);
            border: 1px solid var(--color-border);
            padding: 6px 12px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 11px;
        }
        .overlay-item .label {
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .overlay-item .value {
            font-weight: 700;
        }
        .btn-action-row {
            display: flex;
            gap: 12px;
            margin-top: 10px;
        }
        .primary-btn {
            background: var(--gradient-primary);
            border: none;
            color: #ffffff;
            font-weight: 700;
            font-size: 13px;
            padding: 10px 18px;
            border-radius: 10px;
            cursor: pointer;
            box-shadow: 0 4px 12px rgba(2, 132, 199, 0.2);
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .primary-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 16px rgba(2, 132, 199, 0.3);
        }
        .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;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(2, 132, 199, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .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);
            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);
            background: rgba(2, 132, 199, 0.02);
            border-color: rgba(2, 132, 199, 0.15);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: var(--color-cyan);
            border-color: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.05);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.pressure-vessel-calculator-wrapper .app-main-grid,
.pressure-vessel-calculator-wrapper .main-grid,
.pressure-vessel-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.pressure-vessel-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.pressure-vessel-calculator-wrapper .simulation-panel,
.pressure-vessel-calculator-wrapper .canvas-panel,
.pressure-vessel-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.pressure-vessel-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.pressure-vessel-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.pressure-vessel-calculator-wrapper .simulation-results-section .ratio-readout-box,
.pressure-vessel-calculator-wrapper .simulation-results-section .re-readout-box,
.pressure-vessel-calculator-wrapper .simulation-results-section .status-readout-box,
.pressure-vessel-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: auto !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.pressure-vessel-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: 1 / span 2 !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.pressure-vessel-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.pressure-vessel-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 !important;
    grid-row: 2 !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .pressure-vessel-calculator-wrapper .app-main-grid,
    .pressure-vessel-calculator-wrapper .main-grid,
    .pressure-vessel-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .pressure-vessel-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .pressure-vessel-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .pressure-vessel-calculator-wrapper .simulation-panel,
    .pressure-vessel-calculator-wrapper .canvas-panel,
    .pressure-vessel-calculator-wrapper .sim-panel,
    .pressure-vessel-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .pressure-vessel-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .pressure-vessel-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .pressure-vessel-calculator-wrapper .simulation-results-section .re-readout-box,
    .pressure-vessel-calculator-wrapper .simulation-results-section .status-readout-box,
    .pressure-vessel-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .pressure-vessel-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .pressure-vessel-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.pressure-vessel-calculator-wrapper .app-container,
.pressure-vessel-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="pressure-vessel-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-circle-notch"></i></div>
                <div>
                    <h1>PRESSURE VESSEL</h1>
                    <div class="subtitle">압력용기 응력 계산기 &#038; 실시간 2D 파열 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">VESSEL CORE 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>
                <!-- Vessel Shape Tab -->
                <div class="tab-group">
                    <button id="tab-cylindrical" class="tab-btn active">
                        <i class="fa-solid fa-capsules"></i> 실린더형 (Cylinder)
                    </button>
                    <button id="tab-spherical" class="tab-btn">
                        <i class="fa-solid fa-globe"></i> 구형 (Sphere)
                    </button>
                </div>
                <!-- Pressure p -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-gauge-simple-high text-magenta"></i> 내부 작동 압력 (p)</label>
                        <span class="helper-text">탱크 내 가스압 (0.0 ~ 10.0 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-p" class="custom-number-input" min="0" max="10" step="0.1" value="2.5">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-p" class="custom-slider" min="0" max="10" step="0.1" value="2.5">
                </div>
                <!-- Diameter D -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-arrows-alt text-cyan"></i> 용기 내부 지름 (D)</label>
                        <span class="helper-text">내경 (100 ~ 2000 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d" class="custom-number-input" min="100" max="2000" value="800">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-d" class="custom-slider" min="100" max="2000" value="800">
                </div>
                <!-- Thickness t -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-layer-group text-purple"></i> 벽면 외곽 두께 (t)</label>
                        <span class="helper-text">용기 철판 두께 (2 ~ 100 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-t" class="custom-number-input" min="2" max="100" value="16">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-t" class="custom-slider" min="2" max="100" value="16">
                </div>
                <!-- Yield Strength Sy -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-shield"></i> 재료 항복 강도 (Sy)</label>
                        <span class="helper-text">철판 한계 내력 (100 ~ 600 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sy" class="custom-number-input" min="100" max="600" value="250">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sy" class="custom-slider" min="100" max="600" value="250">
                </div>
            </section>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <section class="panel simulation-panel">
                                <div class="panel-header">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-chart-line text-cyan"></i>
                                        <h2>용기 팽창 거동 &#038; 고압 파열 가상 모사</h2>
                                    </div>
                                    <div id="txt-vessel-state" class="canvas-scale-indicator"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2705.png" alt="✅" class="wp-smiley" style="height: 1em; max-height: 1em;" /> 박막 압력용기 조건 만족 (Thin-walled active)</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas"></canvas>
                                    <!-- Overlay Floating Data -->
                                    <div class="canvas-overlay-data">
                                        <div class="overlay-item">
                                            <span class="label">원주 응력 (σh):</span>
                                            <span class="value text-cyan" id="txt-stress-overlay">0.0 MPa</span>
                                        </div>
                                    </div>
                                </div>
                                <!-- Reset Button -->
                                <div class="btn-action-row">
                                    <button id="btn-reset-vessel" class="primary-btn"><i class="fa-solid fa-rotate-left"></i> 파열 탱크 복원</button>
                                </div>
                                <!-- Mini Metrics -->
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <div class="label">두께 대 직경 비율 (t/D)</div>
                                        <div id="txt-td-ratio" class="value">0.020</div>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <div class="label">원주응력 대비 축방향 비율</div>
                                        <div id="txt-stress-ratio-mini" class="value">2 : 1</div>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="ratio-readout-box">
                                    <div class="ratio-title">현재 벽면 안전율 (F.S.)</div>
                                    <div id="txt-safety-factor" class="ratio-value">2.45</div>
                                    <div id="txt-safety-desc" class="ratio-type">탄성 거동 상태</div>
                                </div>
                                <div class="results-grid">
                                    <!-- Hoop stress -->
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-dot text-cyan"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">원주 / 호프 응력 (σh)</span>
                                            <span id="txt-hoop-stress" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                    <!-- Longitudinal stress -->
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-arrows-alt-h text-purple"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">축 방향 응력 (σl)</span>
                                            <span id="txt-long-stress" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                    <!-- Max Shear -->
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-scissors text-magenta"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">최대 전단 응력 (τmax)</span>
                                            <span id="txt-shear-stress" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                    <h4>박막 이론 팽창 응력식</h4>
                                    <div class="formula-equation" id="formula-txt-1">σh = p × D / (2 × t)</div>
                                    <div class="formula-equation" id="formula-txt-2">σl = p × D / (4 × t)</div>
                                </div>
                            </div>
                </section>
            </div>
        </main>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    <!-- Physics & Animation Engine -->
    </div>
</div>
<script>
        (function() {
        let isInit = false;
        function initSimulator() {
            if (isInit) return;
            const canvas = document.getElementById('physics-canvas');
            if (!canvas) return;
            const ctx = canvas.getContext('2d');
            if (!ctx) return;
            isInit = true;
            window.__vessel_initialized = true;
            // DOM Elements
            const tabCylindrical = document.getElementById('tab-cylindrical');
            const tabSpherical = document.getElementById('tab-spherical');
            const sliderP = document.getElementById('slider-p');
            const inputP = document.getElementById('input-p');
            const sliderD = document.getElementById('slider-d');
            const inputD = document.getElementById('input-d');
            const sliderT = document.getElementById('slider-t');
            const inputT = document.getElementById('input-t');
            const sliderSy = document.getElementById('slider-sy');
            const inputSy = document.getElementById('input-sy');
            const btnReset = document.getElementById('btn-reset-vessel');
            const txtVesselState = document.getElementById('txt-vessel-state');
            const txtStressOverlay = document.getElementById('txt-stress-overlay');
            const txtTdRatio = document.getElementById('txt-td-ratio');
            const txtStressRatioMini = document.getElementById('txt-stress-ratio-mini');
            const txtSafetyFactor = document.getElementById('txt-safety-factor');
            const txtSafetyDesc = document.getElementById('txt-safety-desc');
            const txtHoopStress = document.getElementById('txt-hoop-stress');
            const txtLongStress = document.getElementById('txt-long-stress');
            const txtShearStress = document.getElementById('txt-shear-stress');
            const formulaTxt1 = document.getElementById('formula-txt-1');
            const formulaTxt2 = document.getElementById('formula-txt-2');
            // State variables
            const state = {
                type: 'cylindrical', // cylindrical, spherical
                p: 2.5, // MPa
                D: 800.0, // mm
                t: 16.0, // mm
                Sy: 250.0, // MPa
                particles: [], // high pressure gas particles
                time: 0
            };
            // Reset burst particles & pressure
            btnReset.addEventListener('click', function() {
                state.p = 1.0;
                sliderP.value = 1.0;
                inputP.value = 1.0;
                state.particles = [];
                updateCalculations();
            });
            // Resizing
            function resizeCanvas() {
                const rect = canvas.getBoundingClientRect();
                const dpr = window.devicePixelRatio || 1;
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.setTransform(1, 0, 0, 1, 0, 0); // reset before re-scaling
                ctx.scale(dpr, dpr);
            }
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);
            setTimeout(resizeCanvas, 300);
            // Sync helper
            function makeSync(slider, input, key, isFloat) {
                slider.addEventListener('input', function() {
                    let val = parseFloat(slider.value);
                    if (!isFloat) { val = parseInt(slider.value); }
                    input.value = isFloat ? val.toFixed(1) : val;
                    state[key] = val;
                    updateCalculations();
                });
                input.addEventListener('change', function() {
                    let val = parseFloat(input.value);
                    const min = parseFloat(input.min);
                    const max = parseFloat(input.max);
                    if (isNaN(val)) { val = min; }
                    if (val < min) { val = min; }
                    if (val > max) { val = max; }
                    slider.value = val;
                    state[key] = val;
                    updateCalculations();
                });
            }
            makeSync(sliderP, inputP, 'p', true);
            makeSync(sliderD, inputD, 'D', false);
            makeSync(sliderT, inputT, 't', false);
            makeSync(sliderSy, inputSy, 'Sy', false);
            // Tabs
            tabCylindrical.addEventListener('click', function() {
                tabCylindrical.classList.add('active');
                tabSpherical.classList.remove('active');
                state.type = 'cylindrical';
                formulaTxt1.innerText = 'σh = p × D / (2 × t)';
                formulaTxt2.innerText = 'σl = p × D / (4 × t)';
                txtStressRatioMini.innerText = '2 : 1';
                updateCalculations();
            });
            tabSpherical.addEventListener('click', function() {
                tabSpherical.classList.add('active');
                tabCylindrical.classList.remove('active');
                state.type = 'spherical';
                formulaTxt1.innerText = 'σh = p × D / (4 × t)';
                formulaTxt2.innerText = 'σl = p × D / (4 × t)';
                txtStressRatioMini.innerText = '1 : 1';
                updateCalculations();
            });
            // Calculations
            function updateCalculations() {
                const td = state.t / state.D;
                txtTdRatio.innerText = td.toFixed(3);
                if (td > 0.1) {
                    txtVesselState.innerText = '&#x26a0; 두꺼운 압력용기 범주 (Thick-walled recommended)';
                    txtVesselState.style.color = 'var(--color-warning)';
                    txtVesselState.style.backgroundColor = 'rgba(245, 158, 11, 0.08)';
                    txtVesselState.style.borderColor = 'rgba(245, 158, 11, 0.2)';
                } else {
                    txtVesselState.innerText = '&#x2705; 박막 압력용기 조건 만족 (Thin-walled active)';
                    txtVesselState.style.color = 'var(--color-success)';
                    txtVesselState.style.backgroundColor = 'rgba(16, 185, 129, 0.08)';
                    txtVesselState.style.borderColor = 'rgba(16, 185, 129, 0.2)';
                }
                let sig_h = 0.0;
                let sig_l = 0.0;
                let tau_max = 0.0;
                if (state.type === 'cylindrical') {
                    sig_h = (state.p * state.D) / (2.0 * state.t);
                    sig_l = (state.p * state.D) / (4.0 * state.t);
                    tau_max = sig_h / 2.0;
                } else {
                    sig_h = (state.p * state.D) / (4.0 * state.t);
                    sig_l = (state.p * state.D) / (4.0 * state.t);
                    tau_max = sig_h / 2.0; // out-of-plane maximum shear stress is sig_h/2
                }
                txtHoopStress.innerText = sig_h.toFixed(1) + ' MPa';
                txtLongStress.innerText = sig_l.toFixed(1) + ' MPa';
                txtShearStress.innerText = tau_max.toFixed(1) + ' MPa';
                txtStressOverlay.innerText = sig_h.toFixed(1) + ' MPa';
                let fs = state.Sy / sig_h;
                if (sig_h === 0) { fs = 99.0; }
                if (fs > 99) {
                    txtSafetyFactor.innerText = '무한대';
                } else {
                    txtSafetyFactor.innerText = fs.toFixed(2);
                }
                if (fs >= 1.0) {
                    txtSafetyDesc.innerText = '탄성 안정 상태';
                    txtSafetyDesc.style.color = '#10b981';
                } else {
                    txtSafetyDesc.innerText = '고압 용기 파열 (BURST)';
                    txtSafetyDesc.style.color = '#db2777';
                }
            }
            // Animation Loop
            function animate() {
                requestAnimationFrame(animate);
                state.time += 0.05;
                const width = canvas.width / (window.devicePixelRatio || 1);
                const height = canvas.height / (window.devicePixelRatio || 1);
                ctx.clearRect(0, 0, width, height);
                // Draw blueprint grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Compute parameters
                let sig_h = 0.0;
                if (state.type === 'cylindrical') {
                    sig_h = (state.p * state.D) / (2.0 * state.t);
                } else {
                    sig_h = (state.p * state.D) / (4.0 * state.t);
                }
                const isFailed = sig_h > state.Sy;
                const cX = width / 2;
                const cY = height / 2;
                // Visual base radii mapping
                const baseRad = 70 + (state.D / 2000) * 35; 
                // Visual thickness scaling
                const visThickness = 3 + (state.t / 100) * 14;
                // Elastic expansion scale
                let expScale = 1.0;
                if (state.p > 0) {
                    expScale = 1.0 + (state.p / 10.0) * 0.04;
                }
                // Spawning pressurized steam particles if failed (burst!)
                const crackX = cX + baseRad * expScale;
                const crackY = cY;
                if (isFailed) {
                    // Spawn particles
                    for (let j = 0; j < 3; j++) {
                        state.particles.push({
                            x: crackX,
                            y: crackY,
                            vx: 3 + Math.random() * 5,
                            vy: (Math.random() - 0.5) * 4,
                            size: 2 + Math.random() * 6,
                            alpha: 1.0
                        });
                    }
                }
                // Render escaping particles
                ctx.save();
                for (let i = state.particles.length - 1; i >= 0; i--) {
                    const p = state.particles[i];
                    p.x += p.vx;
                    p.y += p.vy;
                    p.alpha -= 0.015;
                    if (p.alpha <= 0) {
                        state.particles.splice(i, 1);
                    } else {
                        ctx.fillStyle = `rgba(2, 132, 199, ${0.4 * p.alpha})`;
                        ctx.beginPath();
                        ctx.arc(p.x, p.y, p.size, 0, Math.PI*2);
                        ctx.fill();
                    }
                }
                ctx.restore();
                // Draw Pressure Vessel Body
                ctx.save();
                ctx.lineWidth = visThickness;
                if (isFailed) {
                    ctx.strokeStyle = '#db2777';
                    ctx.shadowBlur = 8;
                    ctx.shadowColor = 'rgba(219, 39, 119, 0.15)';
                } else {
                    ctx.strokeStyle = '#0284c7';
                }
                if (state.type === 'cylindrical') {
                    // Cylindrical capsule shape
                    const capLength = 65; 
                    const totalR = baseRad * expScale;
                    // Inner shell drawing
                    ctx.fillStyle = 'rgba(2, 132, 199, 0.02)';
                    ctx.beginPath();
                    ctx.arc(cX - capLength, cY, totalR, Math.PI/2, Math.PI * 1.5);
                    ctx.lineTo(cX + capLength, cY - totalR);
                    ctx.arc(cX + capLength, cY, totalR, Math.PI * 1.5, Math.PI/2);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // Radial outward pressure force vectors
                    ctx.strokeStyle = '#db2777';
                    ctx.fillStyle = '#db2777';
                    ctx.lineWidth = 1.8;
                    // Draw a set of outward arrows from center axis
                    for (let ang = 0; ang < Math.PI * 2; ang += Math.PI / 4) {
                        const startR = 10;
                        const endR = totalR - 10;
                        const sX = cX + Math.cos(ang) * startR;
                        const sY = cY + Math.sin(ang) * startR;
                        const eX = cX + Math.cos(ang) * endR;
                        const eY = cY + Math.sin(ang) * endR;
                        ctx.beginPath();
                        ctx.moveTo(sX, sY);
                        ctx.lineTo(eX, eY);
                        ctx.stroke();
                        // Arrowhead
                        ctx.beginPath();
                        ctx.moveTo(eX, eY);
                        ctx.lineTo(eX - 5 * Math.cos(ang - 0.3), eY - 5 * Math.sin(ang - 0.3));
                        ctx.lineTo(eX - 5 * Math.cos(ang + 0.3), eY - 5 * Math.sin(ang + 0.3));
                        ctx.closePath();
                        ctx.fill();
                    }
                    // Red crack marking if ruptured
                    if (isFailed) {
                        ctx.strokeStyle = 'red';
                        ctx.lineWidth = 3;
                        ctx.beginPath();
                        ctx.moveTo(cX + capLength, cY - 10);
                        ctx.lineTo(cX + capLength + totalR, cY);
                        ctx.lineTo(cX + capLength, cY + 10);
                        ctx.stroke();
                    }
                } else {
                    // Spherical shape (Perfect Circle)
                    const totalR = baseRad * expScale;
                    ctx.fillStyle = 'rgba(2, 132, 199, 0.02)';
                    ctx.beginPath();
                    ctx.arc(cX, cY, totalR, 0, Math.PI*2);
                    ctx.fill();
                    ctx.stroke();
                    // Radial pressure arrows
                    ctx.strokeStyle = '#db2777';
                    ctx.fillStyle = '#db2777';
                    ctx.lineWidth = 1.8;
                    for (let ang = 0; ang < Math.PI * 2; ang += Math.PI / 6) {
                        const startR = 10;
                        const endR = totalR - 10;
                        const sX = cX + Math.cos(ang) * startR;
                        const sY = cY + Math.sin(ang) * startR;
                        const eX = cX + Math.cos(ang) * endR;
                        const eY = cY + Math.sin(ang) * endR;
                        ctx.beginPath();
                        ctx.moveTo(sX, sY);
                        ctx.lineTo(eX, eY);
                        ctx.stroke();
                        // Arrowhead
                        ctx.beginPath();
                        ctx.moveTo(eX, eY);
                        ctx.lineTo(eX - 5 * Math.cos(ang - 0.3), eY - 5 * Math.sin(ang - 0.3));
                        ctx.lineTo(eX - 5 * Math.cos(ang + 0.3), eY - 5 * Math.sin(ang + 0.3));
                        ctx.closePath();
                        ctx.fill();
                    }
                    // Burst hole crack in Sphere
                    if (isFailed) {
                        ctx.strokeStyle = 'red';
                        ctx.lineWidth = 3.5;
                        ctx.beginPath();
                        ctx.moveTo(cX + totalR - 5, cY - 12);
                        ctx.lineTo(cX + totalR + 5, cY);
                        ctx.lineTo(cX + totalR - 5, cY + 12);
                        ctx.stroke();
                    }
                }
                ctx.restore();
            }
            // Document security
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            updateCalculations();
            animate();
        }
        let initAttempts = 0;
        function tryInit() {
            initAttempts++;
            initSimulator();
            if (!window.__vessel_initialized) {
                if (initAttempts < 50) {
                    setTimeout(tryInit, 100);
                }
            }
        }
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            tryInit();
        } else {
            document.addEventListener('DOMContentLoaded', tryInit);
            window.addEventListener('load', tryInit);
        }
        })();
</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>용기 형상 타입 선택: 상단 탭에서 가스 탱크에 쓰이는 실린더형 용기(Cylindrical Vessel) 또는 구형 저장 탱크(Spherical Vessel) 중 하나를 선택합니다.</strong></li>
<li style="margin-bottom: 6px;">용기 치수 및 허용 압력 입력: 슬라이더로 내부 지름(D), 외벽 두께(t), 내부 작용 가스/액체 압력(p)을 입력합니다.</li>
<li style="margin-bottom: 6px;">재료 한계치 조절: 압력 용기 철판의 고유 한계값인 항복 강도(Sy)를 세부 설정합니다.</li>
<li style="margin-bottom: 6px;">안전 진단 및 초고압 파열 관찰: 용기 두께 조건(t/D < 0.1 박막 이론 범위)을 진단받고, 내부 압력이 강도 한계를 초과하여 발생하는 국부 파열(Burst) 및 초고압 가스 분사 입자 애니메이션을 확인합니다.</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;" /> 박막 압력용기(Thin-Walled Pressure Vessel) 공학 이론 공식 확인하기</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. 박막 압력용기(Thin-Walled Pressure Vessel)의 전제 조건</h3>
<p>내부에 고압의 가스나 액체를 저장하는 용기를 <strong>압력용기(Pressure Vessel)</strong>라고 부릅니다. 이 용기 중 외벽 두께 <code>t</code>가 용기 내부 반지름 <code>r</code> 또는 내경 <code>D</code>에 비해 매우 얇은 부류를 <strong>박막 압력용기(Thin-Walled Pressure Vessel)</strong>로 정의합니다.</p><p>통상적으로 두께 대 내경 비율이 다음과 같은 조건을 만족할 때 박막 이론을 안전하게 적용합니다:</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">t / D &le; 0.1 &nbsp;(혹은&nbsp; t / r &le; 0.1)</p><p>이 조건 하에서는 벽 두께 전반에 걸친 응력 분포가 매우 균일하다고 가정할 수 있어 단순 대수방정식으로 벽면에 유도되는 평면 응력(Normal Stress)을 정밀 산출할 수 있습니다.</p>
<h3>2. 원주 응력(Hoop Stress)과 축방향 응력(Longitudinal Stress)</h3>
<p>내부 가스압에 의해 용기가 팽창하려 할 때, 내부 벽면에 수직하게 유도되는 주요 응력들은 다음과 같습니다.</p><p><strong>① 실린더형 용기 (Cylindrical Pressure Vessel):</strong></p><ul><li><strong>원주 응력/호프 응력 (Hoop Stress, &sigma;_h):</strong> 실린더 원주 방향으로 둥글게 찢어지려는 응력으로, 종방향 용접부 등에 직접 작용하는 지표입니다.<br><span style="font-weight:bold; color: #0284c7;">&sigma;_h = (p &times; D) / (2 &times; t) &nbsp;[MPa]</span></li><li><strong>축방향 응력 (Longitudinal Stress, &sigma;_l):</strong> 실린더 축 길이 방향으로 밀어내는 응력입니다.<br><span style="font-weight:bold; color: #purple;">&sigma;_l = (p &times; D) / (4 &times; t) &nbsp;[MPa]</span></li></ul><p>&sigma;_h가 &sigma;_l에 비해 정확히 <strong>2배</strong> 더 큽니다. 따라서 실린더 가스 용기가 터질 때는 항상 세로 방향으로 길게 갈라지면서 파단됩니다.</p><p><strong>② 구형 용기 (Spherical Pressure Vessel):</strong></p><p>모든 방향으로 완벽한 대칭을 이룸에 따라, 모든 접선 방향 응력이 축방향 응력 공식과 같아집니다.</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">&sigma;_h = &sigma;_l = (p &times; D) / (4 &times; t) &nbsp;[MPa]</p><p>따라서 구형 용기는 동일 지름과 압력 조건 하에서 실린더형 용기보다 두께를 절반으로 얇게 설계해도 동일한 안전 수준을 확보하므로 고압 대형 저장소에 매우 경제적인 형상입니다.</p>
<h3>3. 구조 파손 및 최대 전단 응력(Max Shear Stress) 계산</h3>
<p>압력용기 판재 내에서 45도 미끄러짐을 일으켜 면외(Out-of-Plane) 소성 유동을 야기하는 <strong>최대 전단 응력(&tau;_max)</strong>은 다음과 같이 모원의 역학 관계에서 정의됩니다.</p><p><strong>실린더형 용기 최대 전단 응력:</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">&tau;_max = &sigma;_h / 2 = (p &times; D) / (4 &times; t)</p><p>허용 응력 혹은 항복 강도(Sy)에 도달하면 용기 쉘 벽면의 국부 부위가 인장 파괴 한계에 직면하여 파열(Burst) 현상이 초래되므로 밸브 보호와 비파괴 가공 관리가 철저히 준수되어야 합니다.</p>
        </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/thin-walled-pressure-vessel-calculator-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>끼워맞춤 공차 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/fit-tolerance-iso286-calculator-simulator-2/</link>
					<comments>https://myengnote.com/fit-tolerance-iso286-calculator-simulator-2/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Wed, 10 Jun 2026 23:37:19 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[ISO286]]></category>
		<category><![CDATA[IT공차]]></category>
		<category><![CDATA[끼워맞춤]]></category>
		<category><![CDATA[죔새계산]]></category>
		<category><![CDATA[틈새계산]]></category>
		<guid isPermaLink="false">https://myengnote.com/fit-tolerance-iso286-calculator-simulator-2/</guid>

					<description><![CDATA[ISO 286 규격에 의거한 축과 구멍의 공차 등급(H7, g6 등)과 기준 치수에 따라 정밀 치수 허용 한계, 최대/최소 틈새 및 죔새를 실시간 계산하고 마이크로 공차 밴드의 맞물림 영역을 1000배율 확대 시각화하는 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; 시뮬레이터</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 fittolerance-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .fittolerance-calculator-wrapper *, .fittolerance-calculator-wrapper *::before, .fittolerance-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .fittolerance-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);
        }
        .fittolerance-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;
        }
        .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(2, 132, 199, 0.1); border: 1px solid rgba(2, 132, 199, 0.2); padding: 6px 14px; border-radius: 20px;
        }
        .pulse-dot {
            width: 8px; height: 8px; background-color: var(--color-cyan); border-radius: 50%; box-shadow: 0 0 10px var(--color-cyan); 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-cyan); 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: 18px; 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: var(--color-purple); }
        .control-panel { grid-column: 1; }
        .input-group { display: flex; flex-direction: column; gap: 8px; }
        .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);
        }
        .number-input-wrapper:focus-within { border-color: var(--color-cyan); box-shadow: 0 0 10px var(--color-cyan-glow); }
                .custom-number-input {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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;
        }
        .custom-number-input::-webkit-outer-spin-button, .custom-number-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
                .unit-badge {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none; width: 100%; height: 6px; border-radius: 3px; background: rgba(0, 0, 0, 0.05); outline: none; margin: 4px 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;
        }
        .slider-cyan::-webkit-slider-thumb { background: var(--color-cyan); box-shadow: 0 0 8px var(--color-cyan); }
        .slider-cyan::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .slider-magenta::-webkit-slider-thumb { background: var(--color-magenta); box-shadow: 0 0 8px var(--color-magenta); }
        .slider-magenta::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .slider-purple::-webkit-slider-thumb { background: var(--color-purple); box-shadow: 0 0 8px var(--color-purple); }
        .slider-purple::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .slider-cyan::-moz-range-thumb { width: 18px; height: 18px; border: none; border-radius: 50%; background: var(--color-cyan); }
        .slider-magenta::-moz-range-thumb { width: 18px; height: 18px; border: none; border-radius: 50%; background: var(--color-magenta); }
        .slider-purple::-moz-range-thumb { width: 18px; height: 18px; border: none; border-radius: 50%; background: var(--color-purple); }
        .select-wrapper { position: relative; display: flex; border-radius: 8px; overflow: hidden; border: 1px solid var(--color-border); background: #ffffff; }
        .custom-select {
            width: 100%; background: #f8fafc; border: none; outline: none; color: #0f172a; padding: 10px 14px; font-family: var(--font-body); font-size: 14px; font-weight: 600; appearance: none; cursor: pointer;
        }
        .select-arrow { position: absolute; right: 14px; top: 50%; transform: translateY(-50%); pointer-events: none; color: var(--color-text-muted); font-size: 12px; }
        .presets-section { display: flex; flex-direction: column; gap: 10px; border-top: 1px solid var(--color-border); padding-top: 16px; }
        .presets-section h3 { font-size: 13px; font-weight: 700; color: var(--color-text-main); display: flex; align-items: center; gap: 8px; }
        .presets-grid { display: flex; flex-direction: column; 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.04); 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);
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon { background: var(--gradient-primary); color: #fff; }
        .preset-name { font-size: 12px; font-weight: 600; }
        .simulation-panel { grid-column: 2; align-self: stretch; justify-content: space-between; }
        .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; display: flex; align-items: center; justify-content: center; 
        }
        #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: rgba(255, 255, 255, 0.9); color: var(--color-text-main); backdrop-filter: blur(4px); border: 1px solid var(--color-border); padding: 6px 12px; border-radius: 8px;
            display: flex; align-items: center; gap: 8px; 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: 24px; 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);
        }
        .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;
        }
        @container fit-container (max-width: 1024px) {
            .app-main-grid { grid-template-columns: 1fr; }
            .control-panel { grid-column: 1; }
            .simulation-panel { grid-column: 1; order: -1; }
            .results-panel { grid-column: 1; }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.fittolerance-calculator-wrapper .app-main-grid,
.fittolerance-calculator-wrapper .main-grid,
.fittolerance-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.fittolerance-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.fittolerance-calculator-wrapper .simulation-panel,
.fittolerance-calculator-wrapper .canvas-panel,
.fittolerance-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.fittolerance-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.fittolerance-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.fittolerance-calculator-wrapper .simulation-results-section .ratio-readout-box,
.fittolerance-calculator-wrapper .simulation-results-section .re-readout-box,
.fittolerance-calculator-wrapper .simulation-results-section .status-readout-box,
.fittolerance-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.fittolerance-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.fittolerance-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.fittolerance-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .fittolerance-calculator-wrapper .app-main-grid,
    .fittolerance-calculator-wrapper .main-grid,
    .fittolerance-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .fittolerance-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .fittolerance-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .fittolerance-calculator-wrapper .simulation-panel,
    .fittolerance-calculator-wrapper .canvas-panel,
    .fittolerance-calculator-wrapper .sim-panel,
    .fittolerance-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .fittolerance-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .fittolerance-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .fittolerance-calculator-wrapper .simulation-results-section .re-readout-box,
    .fittolerance-calculator-wrapper .simulation-results-section .status-readout-box,
    .fittolerance-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .fittolerance-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .fittolerance-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.fittolerance-calculator-wrapper .app-container,
.fittolerance-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="fittolerance-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-arrows-left-right"></i></div>
                <div>
                    <h1>FIT &#038; TOLERANCE</h1>
                    <div class="subtitle">축과 구멍의 ISO 286 공차 설계 계산기 &#038; 실시간 2D 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">ISO 286 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>
                <!-- Basic Size D -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-d"><i class="fa-solid fa-arrows-left-right text-cyan"></i> 기준 치수 (Basic Size)</label>
                        <span class="helper-text">(1.0 ~ 250.0 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d" class="custom-number-input" min="1" max="250" value="40" step="any">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-d" class="custom-slider slider-cyan" min="1" max="250" value="40">
                </div>
                <!-- Hole Class -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="select-hole"><i class="fa-solid fa-circle-notch text-cyan"></i> 구멍 공차 등급 (Hole)</label>
                    </div>
                    <div class="select-wrapper">
                        <select id="select-hole" class="custom-select">
                            <option value="H7" selected>H7 (IT7 등급, 기준 편차 0)</option>
                            <option value="H8">H8 (IT8 등급, 기준 편차 0)</option>
                            <option value="G7">G7 (IT7 등급, 틈새 슬라이드용)</option>
                            <option value="F7">F7 (IT7 등급, 회전 베어링용)</option>
                        </select>
                        <i class="fa-solid fa-chevron-down select-arrow"></i>
                    </div>
                </div>
                <!-- Shaft Class -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="select-shaft"><i class="fa-solid fa-circle text-magenta"></i> 축 공차 등급 (Shaft)</label>
                    </div>
                    <div class="select-wrapper">
                        <select id="select-shaft" class="custom-select">
                            <option value="g6" selected>g6 (정밀 회전/슬라이드 맞춤)</option>
                            <option value="h6">h6 (IT6 등급, 기준 축 맞춤)</option>
                            <option value="f6">f6 (일반 회전축 틈새 맞춤)</option>
                            <option value="js6">js6 (대칭 공차역 중간 맞춤)</option>
                            <option value="p6">p6 (경압입 조립 억지 맞춤)</option>
                            <option value="s6">s6 (강제 압입 결합 강억지 맞춤)</option>
                        </select>
                        <i class="fa-solid fa-chevron-down select-arrow"></i>
                    </div>
                </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" id="preset-clearance">
                            <div class="preset-icon"><i class="fa-solid fa-dharmachakra"></i></div>
                            <div class="preset-details"><span class="preset-name">H7/g6 정밀 기계 활주 회전 맞춤</span><span class="preset-spec">Clearance</span></div>
                        </button>
                        <button class="preset-btn" id="preset-transition">
                            <div class="preset-icon"><i class="fa-solid fa-gear"></i></div>
                            <div class="preset-details"><span class="preset-name">H7/js6 기어/풀리 정렬 중간 맞춤</span><span class="preset-spec">Transition</span></div>
                        </button>
                        <button class="preset-btn" id="preset-interference">
                            <div class="preset-icon"><i class="fa-solid fa-hammer"></i></div>
                            <div class="preset-details"><span class="preset-name">H7/p6 회전력 전달 압입 억지 맞춤</span><span class="preset-spec">Interference</span></div>
                        </button>
                    </div>
                </div>
            </section>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <section class="panel simulation-panel">
                                <div class="panel-header">
                                    <i class="fa-solid fa-dharmachakra text-magenta"></i>
                                    <h2>맞물림 마이크로 공차 밴드 가시화 (1000배율 돋보기 View)</h2>
                                </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">틈새 끼워맞춤</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <span class="label">구멍 치수 범위</span>
                                        <span class="value" id="val-hole-range">40.000 ~ 40.025 mm</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">축 치수 범위</span>
                                        <span class="value" id="val-shaft-range">39.975 ~ 39.991 mm</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">틈새 / 죔새 크기</span>
                                        <span class="value" id="val-clearance-limit">Min +9 / Max +34 &mu;m</span>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="ratio-readout-box">
                                    <div class="ratio-title" id="txt-readout-title">끼워맞춤 판정 결과</div>
                                    <div class="ratio-value" id="txt-readout-val">틈새 끼워맞춤 (Clearance)</div>
                                    <div class="ratio-type" id="txt-readout-sub">추천 사양: 정밀 공작기계 주축 미끄럼부</div>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-arrows-to-eye"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">구멍 공차 폭 (&mu;m)</span>
                                            <span class="card-value" id="res-hole-tol">+25 / 0 &mu;m</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-arrows-to-dot"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">축 공차 폭 (&mu;m)</span>
                                            <span class="card-value" id="res-shaft-tol">-9 / -25 &mu;m</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-exclamation"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">최종 틈새/죔새 설계량</span>
                                            <span class="card-value" id="res-fit-vals">최소 9 / 최대 50 &mu;m</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                    <h4><i class="fa-solid fa-info-circle text-cyan"></i> 공차 한계 정의 공식</h4>
                                    <div class="formula-equation">
                                        Clearance = Hole_Dia &#8211; Shaft_Dia &nbsp;&gt; 0
                                    </div>
                                    <div class="formula-equation">
                                        Interference = Shaft_Dia &#8211; Hole_Dia &nbsp;&gt; 0
                                    </div>
                                </div>
                            </div>
                </section>
            </div>
        </main>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
    </div>
    <!-- CORE INTERACTIVE ENGINE -->
    </div>
</div>
<script>
    // Polyfill for CanvasRenderingContext2D.roundRect for backward compatibility
    if (typeof CanvasRenderingContext2D !== 'undefined') {
        if (!CanvasRenderingContext2D.prototype.roundRect) {
            CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r) {
                if (typeof r === 'number') r = [r];
                if (!Array.isArray(r)) r = [0];
                const rad = r[0] || 0;
                this.beginPath();
                this.moveTo(x + rad, y);
                this.lineTo(x + w - rad, y);
                this.quadraticCurveTo(x + w, y, x + w, y + rad);
                this.lineTo(x + w, y + h - rad);
                this.quadraticCurveTo(x + w, y + h, x + w - rad, y + h);
                this.lineTo(x + rad, y + h);
                this.quadraticCurveTo(x, y + h, x, y + h - rad);
                this.lineTo(x, y + rad);
                this.quadraticCurveTo(x, y, x + rad, y);
                this.closePath();
                return this;
            };
        }
    }
        (function() {
            let isInit = false;
            function initSimulator() {
                if (isInit) return;
                const inputD = document.getElementById('input-d');
                const sliderD = document.getElementById('slider-d');
                const selectHole = document.getElementById('select-hole');
                const selectShaft = document.getElementById('select-shaft');
                const canvas = document.getElementById('physics-canvas');
                const txtReadoutTitle = document.getElementById('txt-readout-title');
                const txtReadoutVal = document.getElementById('txt-readout-val');
                const txtReadoutSub = document.getElementById('txt-readout-sub');
                const valHoleRange = document.getElementById('val-hole-range');
                const valShaftRange = document.getElementById('val-shaft-range');
                const valClearanceLimit = document.getElementById('val-clearance-limit');
                const resHoleTol = document.getElementById('res-hole-tol');
                const resShaftTol = document.getElementById('res-shaft-tol');
                const resFitVals = document.getElementById('res-fit-vals');
                const overlayRunStatus = document.getElementById('overlay-run-status');
                // 철벽 방어: 모든 조작 및 결과 출력 DOM 엘리먼트가 온전히 존재해야만 초기화 승인
                if (!inputD || !sliderD || !selectHole || !selectShaft || !canvas ||
                    !txtReadoutTitle || !txtReadoutVal || !txtReadoutSub ||
                    !valHoleRange || !valShaftRange || !valClearanceLimit ||
                    !resHoleTol || !resShaftTol || !resFitVals || !overlayRunStatus) {
                    return;
                }
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
                isInit = true;
                window.__fittolerance_initialized = true;
            const state = {
                d: 40.0, // basic size mm
                holeClass: 'H7',
                shaftClass: 'g6'
            };
            const ranges = {
                d: { min: 1.0, max: 250.0 }
            };
            // Standard ISO 286 Database for D ranges (values in micrometers)
            const holeDb = {
                'H7': [ [0, 10], [0, 12], [0, 15], [0, 18], [0, 21], [0, 25], [0, 30], [0, 35], [0, 40], [0, 46] ],
                'H8': [ [0, 14], [0, 18], [0, 22], [0, 27], [0, 33], [0, 39], [0, 46], [0, 54], [0, 63], [0, 72] ],
                'G7': [ [2, 12], [4, 16], [5, 20], [6, 24], [7, 28], [9, 34], [10, 40], [12, 47], [14, 54], [15, 61] ],
                'F7': [ [6, 16], [10, 22], [13, 28], [16, 34], [20, 41], [25, 50], [30, 60], [36, 71], [43, 83], [50, 96] ]
            };
            const shaftDb = {
                'h6': [ [-6, 0], [-8, 0], [-9, 0], [-11, 0], [-13, 0], [-16, 0], [-19, 0], [-22, 0], [-25, 0], [-29, 0] ],
                'g6': [ [-8, -2], [-12, -4], [-14, -5], [-17, -6], [-20, -7], [-25, -9], [-30, -10], [-34, -12], [-39, -14], [-44, -15] ],
                'f6': [ [-12, -6], [-18, -10], [-22, -13], [-27, -16], [-33, -20], [-41, -25], [-49, -30], [-56, -34], [-63, -39], [-72, -44] ],
                'js6': [ [-3, 3], [-4, 4], [-4.5, 4.5], [-5.5, 5.5], [-6.5, 6.5], [-8, 8], [-9.5, 9.5], [-11, 11], [-12.5, 12.5], [-14.5, 14.5] ],
                'p6': [ [6, 12], [12, 20], [15, 24], [18, 29], [22, 35], [26, 42], [32, 51], [37, 59], [43, 68], [50, 79] ],
                's6': [ [14, 20], [19, 27], [24, 33], [29, 40], [35, 48], [42, 58], [53, 72], [71, 93], [92, 117], [113, 142] ]
            };
            function getRangeIndex(val) {
                if (val <= 3.0) return 0;
                if (val <= 6.0) return 1;
                if (val <= 10.0) return 2;
                if (val <= 18.0) return 3;
                if (val <= 30.0) return 4;
                if (val <= 50.0) return 5;
                if (val <= 80.0) return 6;
                if (val <= 120.0) return 7;
                if (val <= 180.0) return 8;
                return 9; // up to 250
            }
            function calculateFit() {
                const idx = getRangeIndex(state.d);
                // Get Hole Tolerances
                const holeTol = holeDb[state.holeClass][idx];
                const EI = holeTol[0];
                const ES = holeTol[1];
                // Get Shaft Tolerances
                const shaftTol = shaftDb[state.shaftClass][idx];
                const ei = shaftTol[0];
                const es = shaftTol[1];
                state.EI = EI; state.ES = ES;
                state.ei = ei; state.es = es;
                // Max/Min diameters
                const maxHole = state.d + ES / 1000.0;
                const minHole = state.d + EI / 1000.0;
                const maxShaft = state.d + es / 1000.0;
                const minShaft = state.d + ei / 1000.0;
                state.maxHole = maxHole;
                state.minHole = minHole;
                state.maxShaft = maxShaft;
                state.minShaft = minShaft;
                // Clearances (틈새)
                const maxClearance = (ES - ei);
                const minClearance = (EI - es);
                // Interferences (죔새)
                const maxInterference = (es - EI);
                const minInterference = (ei - ES);
                state.maxClearance = maxClearance;
                state.minClearance = minClearance;
                state.maxInterference = maxInterference;
                state.minInterference = minInterference;
                // Determine classification
                let fitType = 'Clearance'; // Default
                if (minClearance >= 0.0) {
                    fitType = 'Clearance';
                } else {
                    if (maxInterference >= 0.0) {
                        if (minInterference >= 0.0) {
                            fitType = 'Interference';
                        } else {
                            fitType = 'Transition';
                        }
                    } else {
                        fitType = 'Transition';
                    }
                }
                state.fitType = fitType;
                updateUIValues();
            }
            function syncDFromInput() {
                let val = parseFloat(inputD.value);
                if (isNaN(val)) { val = ranges.d.min; }
                if (val < ranges.d.min) { val = ranges.d.min; }
                if (val > ranges.d.max) { val = ranges.d.max; }
                state.d = val;
                inputD.value = val.toFixed(1);
                sliderD.value = Math.round(val);
                calculateFit();
            }
            function syncDFromSlider() {
                state.d = parseFloat(sliderD.value);
                inputD.value = state.d.toFixed(1);
                calculateFit();
            }
            function updateUIValues() {
                valHoleRange.innerText = state.minHole.toFixed(3) + ' ~ ' + state.maxHole.toFixed(3) + ' mm';
                valShaftRange.innerText = state.minShaft.toFixed(3) + ' ~ ' + state.maxShaft.toFixed(3) + ' mm';
                // Formatting results card values
                resHoleTol.innerText = (state.EI >= 0 ? '+' : '') + state.EI + ' / ' + (state.ES >= 0 ? '+' : '') + state.ES + ' \u03bcm';
                resShaftTol.innerText = (state.ei >= 0 ? '+' : '') + state.ei + ' / ' + (state.es >= 0 ? '+' : '') + state.es + ' \u03bcm';
                if (state.fitType === 'Clearance') {
                    valClearanceLimit.innerText = 'Min +' + state.minClearance + ' / Max +' + state.maxClearance + ' \u03bcm';
                    resFitVals.innerText = '최소 ' + state.minClearance + ' / 최대 ' + state.maxClearance + ' \u03bcm';
                    txtReadoutVal.innerText = '틈새 끼워맞춤 (Clearance)';
                    txtReadoutVal.style.color = '#10b981';
                    overlayRunStatus.innerText = '틈새 끼워맞춤 (Clearance Fit)';
                    overlayRunStatus.style.color = '#10b981';
                    txtReadoutSub.innerText = '가동 결합: 미끄럼 샤프트 베어링부 적합';
                } else {
                    if (state.fitType === 'Interference') {
                        valClearanceLimit.innerText = 'Min -' + Math.abs(state.minInterference) + ' / Max -' + Math.abs(state.maxInterference) + ' \u03bcm';
                        resFitVals.innerText = '최소 ' + Math.abs(state.minInterference) + ' / 최대 ' + Math.abs(state.maxInterference) + ' \u03bcm';
                        txtReadoutVal.innerText = '억지 끼워맞춤 (Interference)';
                        txtReadoutVal.style.color = '#db2777';
                        overlayRunStatus.innerText = '억지 끼워맞춤 (Interference Fit)';
                        overlayRunStatus.style.color = '#db2777';
                        txtReadoutSub.innerText = '고정 결합: 축 가압 압입 및 열박음부 적합';
                    } else {
                        // Transition
                        valClearanceLimit.innerText = 'Max 틈새 +' + state.maxClearance + ' / Max 죔새 +' + state.maxInterference + ' \u03bcm';
                        resFitVals.innerText = '최대 틈새 ' + state.maxClearance + ' / 최대 죔새 ' + state.maxInterference + ' \u03bcm';
                        txtReadoutVal.innerText = '중간 끼워맞춤 (Transition)';
                        txtReadoutVal.style.color = '#f59e0b';
                        overlayRunStatus.innerText = '중간 끼워맞춤 (Transition Fit)';
                        overlayRunStatus.style.color = '#f59e0b';
                        txtReadoutSub.innerText = '정밀 위치 결합: 탈착식 기어 및 키부 적합';
                    }
                }
            }
            // Element listeners
            inputD.addEventListener('change', syncDFromInput);
            sliderD.addEventListener('input', syncDFromSlider);
            selectHole.addEventListener('change', function() {
                state.holeClass = selectHole.value;
                calculateFit();
            });
            selectShaft.addEventListener('change', function() {
                state.shaftClass = selectShaft.value;
                calculateFit();
            });
            // Presets
            document.getElementById('preset-clearance').addEventListener('click', function() {
                setActivePreset('preset-clearance');
                state.d = 40;
                state.holeClass = 'H7';
                state.shaftClass = 'g6';
                syncControlsToState();
                calculateFit();
            });
            document.getElementById('preset-transition').addEventListener('click', function() {
                setActivePreset('preset-transition');
                state.d = 40;
                state.holeClass = 'H7';
                state.shaftClass = 'js6';
                syncControlsToState();
                calculateFit();
            });
            document.getElementById('preset-interference').addEventListener('click', function() {
                setActivePreset('preset-interference');
                state.d = 40;
                state.holeClass = 'H7';
                state.shaftClass = 'p6';
                syncControlsToState();
                calculateFit();
            });
            function setActivePreset(id) {
                document.getElementById('preset-clearance').classList.remove('active');
                document.getElementById('preset-transition').classList.remove('active');
                document.getElementById('preset-interference').classList.remove('active');
                document.getElementById(id).classList.add('active');
            }
            function syncControlsToState() {
                inputD.value = state.d;
                sliderD.value = state.d;
                selectHole.value = state.holeClass;
                selectShaft.value = state.shaftClass;
            }
            // Canvas drawing
            // (canvas and ctx are already declared at the top of initSimulator)
            function getDPR() { return window.devicePixelRatio || 1; }
            function initCanvas() {
                const dpr = getDPR();
                const rect = canvas.getBoundingClientRect();
                if (rect.width > 0) {
                    if (rect.height > 0) {
                        canvas.width = rect.width * dpr;
                        canvas.height = rect.height * dpr;
                    } else {
                        canvas.width = 640 * dpr;
                        canvas.height = 400 * dpr;
                    }
                } else {
                    canvas.width = 640 * dpr;
                    canvas.height = 400 * dpr;
                }
                ctx.setTransform(1, 0, 0, 1, 0, 0);
                ctx.scale(dpr, dpr);
            }
            window.addEventListener('resize', initCanvas);
            initCanvas();
            setTimeout(initCanvas, 300);
            function animate(timestamp) {
                const dpr = getDPR();
                const rect = canvas.getBoundingClientRect();
                if (rect.width > 0) {
                    if (rect.height > 0) {
                        const targetW = Math.round(rect.width * dpr);
                        const targetH = Math.round(rect.height * dpr);
                        if (canvas.width !== targetW || canvas.height !== targetH) {
                            initCanvas();
                        }
                    }
                }
                const width = canvas.width / dpr;
                const height = canvas.height / dpr;
                ctx.clearRect(0, 0, width, height);
                // Grid
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                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();
                }
                // 2D Split Screen Drawing
                const cx1 = width * 0.28;
                const cx2 = width * 0.72;
                const cy = height / 2;
                // --- 1. Left Assembly Drawing: Standard Shaft and Hole ---
                ctx.save();
                // Clip to left section only — prevents shaft overflowing into right tolerance diagram
                ctx.beginPath();
                ctx.rect(0, 0, width * 0.49, height);
                ctx.clip();
                // Animation logic: slide in and out of the hole
                if (!state.animT) state.animT = 0;
                state.animT += 0.025; // Continuous time step
                const phase = (state.animT % 6); // 6-second loop
                let slideOffset = 0; // 0: fully inserted, 1: fully retracted
                if (phase < 1.5) {
                    // Sliding in
                    const t = phase / 1.5;
                    slideOffset = 1 - (3 * t * t - 2 * t * t * t);
                } else if (phase < 3.5) {
                    // Paused inside
                    slideOffset = 0;
                } else if (phase < 5.0) {
                    // Sliding out
                    const t = (phase - 3.5) / 1.5;
                    slideOffset = 3 * t * t - 2 * t * t * t;
                } else {
                    // Paused outside
                    slideOffset = 1;
                }
                // Set dynamic widths based on fit type
                const holeW = 72;
                let shaftW = 68; // Default
                if (state.fitType === 'Clearance') {
                    const avgC = (state.maxClearance + state.minClearance) / 2;
                    const visGap = Math.max(3, Math.min(10, 2 + avgC * 0.15));
                    shaftW = holeW - visGap * 2;
                } else if (state.fitType === 'Interference') {
                    const avgI = (Math.abs(state.minInterference) + Math.abs(state.maxInterference)) / 2;
                    const visOverlap = Math.max(3, Math.min(8, 2 + avgI * 0.1));
                    shaftW = holeW + visOverlap * 2;
                } else {
                    // Transition
                    shaftW = holeW;
                }
                // Draw Hole outer block (grey section with cutout) - Horizontal
                ctx.fillStyle = 'rgba(203,213,225,0.7)';
                ctx.strokeStyle = '#475569';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.rect(cx1 - 100, cy - 80, 200, 160);
                ctx.fill();
                ctx.stroke();
                // Cutout representation (draw background color inside cutout) - Horizontal
                ctx.fillStyle = '#f8fafc';
                ctx.beginPath();
                ctx.rect(cx1 - 101, cy - holeW / 2, 202, holeW);
                ctx.fill();
                ctx.strokeStyle = '#cbd5e1';
                ctx.stroke();
                // Draw Shaft: slides LEFT (exits left side) to avoid crossing the center divider
                const shaftX = cx1 - 130 * slideOffset;
                // Draw interference warning glow if overlapping
                if (state.fitType === 'Interference') {
                    if (slideOffset < 0.95) {
                        ctx.save();
                        ctx.shadowBlur = 15;
                        ctx.shadowColor = '#db2777';
                        ctx.strokeStyle = 'rgba(219, 39, 119, 0.4)';
                        ctx.lineWidth = 5;
                        ctx.beginPath();
                        ctx.roundRect(shaftX - 120, cy - shaftW / 2, 240, shaftW, 4);
                        ctx.stroke();
                        ctx.restore();
                    }
                }
                ctx.fillStyle = 'rgba(219,39,119,0.85)';
                ctx.strokeStyle = '#334155';
                ctx.lineWidth = 2.5;
                ctx.beginPath();
                ctx.roundRect(shaftX - 120, cy - shaftW / 2, 240, shaftW, 4);
                ctx.fill();
                ctx.stroke();
                // Anchor block walls — drawn AFTER shaft so they clip the shaft ends cleanly
                ctx.fillStyle = '#8497a8';  // slightly lighter for 3D feel
                ctx.strokeStyle = '#475569';
                ctx.lineWidth = 1.5;
                // Top wall
                ctx.beginPath();
                ctx.rect(cx1 - 100, cy - 80, 200, 80 - holeW / 2);
                ctx.fill();
                ctx.stroke();
                // Bottom wall
                ctx.beginPath();
                ctx.rect(cx1 - 100, cy + holeW / 2, 200, 80 - holeW / 2);
                ctx.fill();
                ctx.stroke();
                // Draw assembly status text
                ctx.fillStyle = '#475569';
                ctx.font = 'bold 11px sans-serif';
                ctx.textAlign = 'center';
                let statusText = "대기 중...";
                if (slideOffset === 0) {
                    if (state.fitType === 'Clearance') {
                        statusText = "조립 완료 (틈새 존재)";
                    } else if (state.fitType === 'Interference') {
                        statusText = "압입 조립 완료 (죔새 강결합)";
                    } else {
                        statusText = "조립 완료 (중간 끼워맞춤)";
                    }
                } else if (slideOffset === 1) {
                    statusText = "분해 상태 (축 분리)";
                } else {
                    statusText = "조립 진행 중...";
                }
                ctx.fillText(statusText, cx1, cy - 95);
                ctx.restore();
                // Divider line (centered at width * 0.5 to leave 40px gap from axis)
                ctx.strokeStyle = '#cbd5e1';
                ctx.lineWidth = 1.5;
                ctx.beginPath(); ctx.moveTo(width * 0.50, 20); ctx.lineTo(width * 0.50, height - 20); ctx.stroke();
                // --- 2. Right 1000x Magnified boundary diagram (Horizontal Zero Line) ---
                ctx.save();
                // Zero nominal line (Nominal dimension reference) - drawn horizontally
                const zeroY = cy;
                ctx.strokeStyle = '#94a3b8';
                ctx.lineWidth = 2;
                ctx.setLineDash([4, 4]);
                ctx.beginPath();
                ctx.moveTo(cx2 - 110, zeroY);
                ctx.lineTo(cx2 + 110, zeroY);
                ctx.stroke();
                ctx.setLineDash([]);
                // Legend for Zero Line (placed at the top right to avoid clipping and overlap)
                ctx.fillStyle = '#64748b';
                ctx.font = '500 10px Inter';
                ctx.textAlign = 'right';
                ctx.fillText('기본 치수 기준선 (Zero Line)', width - 20, 30);
                ctx.strokeStyle = '#94a3b8';
                ctx.lineWidth = 1.5;
                ctx.setLineDash([3, 3]);
                ctx.beginPath();
                ctx.moveTo(width - 200, 26);
                ctx.lineTo(width - 165, 26);
                ctx.stroke();
                ctx.setLineDash([]);
                // Tolerance scaling (dynamic scale factor to ensure graph always fits beautifully)
                const maxAbsDev = Math.max(
                    Math.abs(state.EI),
                    Math.abs(state.ES),
                    Math.abs(state.ei),
                    Math.abs(state.es)
                );
                const scalePx = maxAbsDev > 0 ? Math.min(3.5, 120 / maxAbsDev) : 1.2;
                // Vertical Y-axis for deviation scale
                const axisX = cx2 - 95;
                ctx.strokeStyle = '#cbd5e1';
                ctx.lineWidth = 1;
                ctx.beginPath();
                ctx.moveTo(axisX, cy - 130);
                ctx.lineTo(axisX, cy + 130);
                ctx.stroke();
                // Draw Y-axis ticks
                let tickInterval = 50;
                if (maxAbsDev <= 20) tickInterval = 5;
                else if (maxAbsDev <= 50) tickInterval = 10;
                else if (maxAbsDev <= 100) tickInterval = 25;
                ctx.fillStyle = '#94a3b8';
                ctx.font = '9px Inter';
                ctx.textAlign = 'right';
                ctx.textBaseline = 'middle';
                const limit = Math.ceil(130 / (tickInterval * scalePx));
                for (let t = -limit; t <= limit; t++) {
                    const val = t * tickInterval;
                    const ty = cy - val * scalePx;
                    if (ty >= cy - 130) {
                        if (ty <= cy + 130) {
                            ctx.beginPath();
                            ctx.moveTo(axisX - 4, ty);
                            ctx.lineTo(axisX, ty);
                            ctx.stroke();
                            let label = (val > 0 ? '+' : '') + val;
                            if (val === 0) label = '0';
                            ctx.fillText(label, axisX - 8, ty);
                        }
                    }
                }
                // Hole Tolerance Zone (Hole is on the left side of right section)
                // Y range represents: [zeroY - ES*scalePx, zeroY - EI*scalePx]
                const hTop = zeroY - state.ES * scalePx;
                const hHeight = (state.ES - state.EI) * scalePx;
                ctx.fillStyle = 'rgba(2, 132, 199, 0.15)';
                ctx.strokeStyle = '#0284c7';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.rect(cx2 - 65, hTop, 40, hHeight);
                ctx.fill();
                ctx.stroke();
                // Shaft Tolerance Zone (Shaft is on the right side of right section)
                // Y range represents: [zeroY - es*scalePx, zeroY - ei*scalePx]
                const sTop = zeroY - state.es * scalePx;
                const sHeight = (state.es - state.ei) * scalePx;
                ctx.fillStyle = 'rgba(219, 39, 119, 0.15)';
                ctx.strokeStyle = '#db2777';
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.rect(cx2 + 20, sTop, 40, sHeight);
                ctx.fill();
                ctx.stroke();
                // Draw texts on the bands - positioned to avoid overlapping with Zero Line
                ctx.textBaseline = 'alphabetic';
                const hCenter = (state.EI + state.ES) / 2;
                const sCenter = (state.ei + state.es) / 2;
                if (hCenter >= sCenter) {
                    // Hole is higher or equal (Clearance or Transition where Hole is higher)
                    // Draw Hole text ABOVE the Hole box
                    ctx.fillStyle = '#0284c7';
                    ctx.font = 'bold 9.5px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText('Hole ' + state.holeClass, cx2 - 45, hTop - 18);
                    ctx.fillText('[' + (state.EI >= 0 ? '+' : '') + state.EI + ', ' + (state.ES >= 0 ? '+' : '') + state.ES + '] \u03bcm', cx2 - 45, hTop - 6);
                    // Draw Shaft text BELOW the Shaft box
                    ctx.fillStyle = '#db2777';
                    ctx.font = 'bold 9.5px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText('Shaft ' + state.shaftClass, cx2 + 40, sTop + sHeight + 14);
                    ctx.fillText('[' + (state.ei >= 0 ? '+' : '') + state.ei + ', ' + (state.es >= 0 ? '+' : '') + state.es + '] \u03bcm', cx2 + 40, sTop + sHeight + 26);
                } else {
                    // Shaft is higher (Interference or Transition where Shaft is higher)
                    // Draw Hole text BELOW the Hole box
                    ctx.fillStyle = '#0284c7';
                    ctx.font = 'bold 9.5px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText('Hole ' + state.holeClass, cx2 - 45, hTop + hHeight + 14);
                    ctx.fillText('[' + (state.EI >= 0 ? '+' : '') + state.EI + ', ' + (state.ES >= 0 ? '+' : '') + state.ES + '] \u03bcm', cx2 - 45, hTop + hHeight + 26);
                    // Draw Shaft text ABOVE the Shaft box
                    ctx.fillStyle = '#db2777';
                    ctx.font = 'bold 9.5px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText('Shaft ' + state.shaftClass, cx2 + 40, sTop - 18);
                    ctx.fillText('[' + (state.ei >= 0 ? '+' : '') + state.ei + ', ' + (state.es >= 0 ? '+' : '') + state.es + '] \u03bcm', cx2 + 40, sTop - 6);
                }
                // Show clearance/interference gap visualization
                ctx.lineWidth = 1.5;
                ctx.textAlign = 'left';
                if (state.fitType === 'Clearance') {
                    // Clearance Gap Arrow (Vertical between bottom of hole and top of shaft)
                    const arrowY1 = zeroY - state.EI * scalePx;
                    const arrowY2 = zeroY - state.es * scalePx;
                    ctx.strokeStyle = '#10b981';
                    ctx.fillStyle = '#10b981';
                    ctx.beginPath();
                    ctx.moveTo(cx2 - 10, arrowY1);
                    ctx.lineTo(cx2 - 10, arrowY2);
                    ctx.stroke();
                    // Arrow heads (only if gap is large enough, otherwise draw small dots)
                    if (Math.abs(arrowY1 - arrowY2) >= 15) {
                        ctx.beginPath();
                        ctx.moveTo(cx2 - 10, arrowY1); ctx.lineTo(cx2 - 14, arrowY1 + 5); ctx.lineTo(cx2 - 6, arrowY1 + 5); ctx.closePath(); ctx.fill();
                        ctx.beginPath();
                        ctx.moveTo(cx2 - 10, arrowY2); ctx.lineTo(cx2 - 14, arrowY2 - 5); ctx.lineTo(cx2 - 6, arrowY2 - 5); ctx.closePath(); ctx.fill();
                    } else {
                        ctx.beginPath();
                        ctx.arc(cx2 - 10, arrowY1, 2.5, 0, Math.PI * 2);
                        ctx.arc(cx2 - 10, arrowY2, 2.5, 0, Math.PI * 2);
                        ctx.fill();
                    }
                    // Draw text with white background box to clear zero line behind it
                    const txt = '최소 틈새: ' + state.minClearance + ' \u03bcm';
                    ctx.font = 'bold 9px Inter';
                    const txtWidth = ctx.measureText(txt).width;
                    const ty = (arrowY1 + arrowY2) / 2 + 3.5;
                    ctx.fillStyle = '#ffffff';
                    ctx.fillRect(cx2 - txtWidth / 2 - 3, ty - 8, txtWidth + 6, 11);
                    ctx.fillStyle = '#10b981';
                    ctx.textAlign = 'center';
                    ctx.fillText(txt, cx2, ty);
                } else if (state.fitType === 'Interference') {
                    // Interference Overlap Arrow (Vertical between top of hole and bottom of shaft)
                    const arrowY1 = zeroY - state.ES * scalePx;
                    const arrowY2 = zeroY - state.ei * scalePx;
                    ctx.strokeStyle = '#db2777';
                    ctx.fillStyle = '#db2777';
                    ctx.beginPath();
                    ctx.moveTo(cx2 - 10, arrowY1);
                    ctx.lineTo(cx2 - 10, arrowY2);
                    ctx.stroke();
                    // Arrow heads (only if gap is large enough, otherwise draw small dots)
                    if (Math.abs(arrowY1 - arrowY2) >= 15) {
                        ctx.beginPath();
                        ctx.moveTo(cx2 - 10, arrowY1); ctx.lineTo(cx2 - 14, arrowY1 + 5); ctx.lineTo(cx2 - 6, arrowY1 + 5); ctx.closePath(); ctx.fill();
                        ctx.beginPath();
                        ctx.moveTo(cx2 - 10, arrowY2); ctx.lineTo(cx2 - 14, arrowY2 - 5); ctx.lineTo(cx2 - 6, arrowY2 - 5); ctx.closePath(); ctx.fill();
                    } else {
                        ctx.beginPath();
                        ctx.arc(cx2 - 10, arrowY1, 2.5, 0, Math.PI * 2);
                        ctx.arc(cx2 - 10, arrowY2, 2.5, 0, Math.PI * 2);
                        ctx.fill();
                    }
                    // Draw text with white background box to clear zero line behind it
                    const txt = '최소 죔새: ' + Math.abs(state.minInterference) + ' \u03bcm';
                    ctx.font = 'bold 9px Inter';
                    const txtWidth = ctx.measureText(txt).width;
                    const ty = (arrowY1 + arrowY2) / 2 + 3.5;
                    ctx.fillStyle = '#ffffff';
                    ctx.fillRect(cx2 - txtWidth / 2 - 3, ty - 8, txtWidth + 6, 11);
                    ctx.fillStyle = '#db2777';
                    ctx.textAlign = 'center';
                    ctx.fillText(txt, cx2, ty);
                } else {
                    // Transition
                    ctx.strokeStyle = '#f59e0b';
                    ctx.fillStyle = '#f59e0b';
                    ctx.font = 'bold 10px Inter';
                    ctx.textAlign = 'center';
                    const txt = '공차 중첩 (중간 끼워맞춤)';
                    const txtWidth = ctx.measureText(txt).width;
                    ctx.fillStyle = '#ffffff';
                    ctx.fillRect(cx2 - txtWidth / 2 - 4, cy - 10, txtWidth + 8, 14);
                    ctx.fillStyle = '#f59e0b';
                    ctx.fillText(txt, cx2, cy + 1);
                }
                ctx.restore();
                requestAnimationFrame(animate);
            }
            requestAnimationFrame(animate);
            // Initial calculation
            calculateFit();
            // Right click / Copy Protection
            (function() {
                function blockEvents() {
                    document.addEventListener('contextmenu', function(e) {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }, { capture: true });
                    document.addEventListener('selectstart', function(e) {
                        e.preventDefault();
                        return false;
                    }, { capture: true });
                }
                if (document.readyState === 'complete' || document.readyState === 'interactive') {
                    blockEvents();
                } else {
                    document.addEventListener('DOMContentLoaded', blockEvents);
                }
            })();
            }
            // Visual debug logger for WordPress page
            function showVisualError(err) {
                let div = document.getElementById('fit-tol-debug-log');
                if (!div) {
                    div = document.createElement('div');
                    div.id = 'fit-tol-debug-log';
                    div.style.position = 'fixed';
                    div.style.bottom = '10px';
                    div.style.right = '10px';
                    div.style.background = 'rgba(255, 0, 0, 0.9)';
                    div.style.color = '#fff';
                    div.style.padding = '15px';
                    div.style.borderRadius = '8px';
                    div.style.zIndex = '99999';
                    div.style.fontFamily = 'monospace';
                    div.style.fontSize = '12px';
                    div.style.maxWidth = '400px';
                    div.style.maxHeight = '200px';
                    div.style.overflowY = 'auto';
                    div.style.boxShadow = '0 4px 12px rgba(0,0,0,0.3)';
                    document.body.appendChild(div);
                }
                div.innerHTML += "<div>" + err + "</div>";
            }
            // Retry loader sequence
            let loadAttempts = 0;
            function tryLoad() {
                loadAttempts++;
                try {
                    initSimulator();
                    if (window.__fittolerance_initialized) {
                        let div = document.getElementById('fit-tol-debug-log');
                        if (div) div.style.background = 'rgba(0, 200, 0, 0.9)';
                    }
                } catch (e) {
                    console.error("FitTolerance init error on attempt " + loadAttempts + ":", e);
                    showVisualError("Attempt " + loadAttempts + " Error: " + (e.stack || e.message || e));
                }
                if (!window.__fittolerance_initialized) {
                    if (loadAttempts < 50) {
                        setTimeout(tryLoad, 100);
                    } else {
                        showVisualError("Attempts exceeded 50. Key DOM elements not found!");
                    }
                }
            }
            if (document.readyState === 'complete' || document.readyState === 'interactive') {
                tryLoad();
            } else {
                document.addEventListener('DOMContentLoaded', tryLoad);
                window.addEventListener('load', tryLoad);
            }
        })();
</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>기준 치수(Basic Size) 설정: 끼워맞춤 부하를 설계할 기준 치수(1.0 ~ 200.0 mm)를 제어합니다.</strong></li>
<li style="margin-bottom: 6px;">구멍(Hole) 및 축(Shaft) 공차 등급 선택: 구멍의 기호(H7, H8, G7, F7) 및 축의 기호(h6, g6, f6, js6, p6, s6)를 각각 드롭다운에서 매칭합니다.</li>
<li style="margin-bottom: 6px;">끼워맞춤 성격 분석: 틈새 끼워맞춤(Clearance), 중간 끼워맞춤(Transition), 억지 끼워맞춤(Interference) 중 어떠한 성격으로 판정되었는지 결과를 확인합니다.</li>
<li style="margin-bottom: 6px;">공차 밴드(Tolerance Zone) 확대 분석: 2D 시뮬레이션 영역에서 구멍 공차(Hole: Blue Band)와 축 공차(Shaft: Pink Band)가 결합 치수 경계 속에서 어떻게 밀착, 중첩 혹은 이격되는지 1000배율 확대 그래픽으로 관찰합니다.</li>
<li style="margin-bottom: 6px;">최대/최소 틈새 및 죔새 수치 획득: 조립 후 부품 간의 틈새(Clearance) 크기 또는 열팽창 압입 조립 시 요구되는 죔새(Interference) 수치들을 명확한 도면 한계 치수 카탈로그로 추출합니다.</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;" /> 상세 기계공학 해설 및 ISO 286 공차 설계 규격 확인하기</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. ISO 공차 시스템 (ISO 286) 및 끼워맞춤의 기초 정의</h3>
<p>기계 조립 공정에서 완벽한 호환성(Interchangeability)을 확보하기 위해 구멍(Hole)과 축(Shaft)의 치수 한계를 국제 규격으로 정의한 것이 <strong>ISO 286 공차 시스템 (ISO Limits and Fits)</strong>입니다. 모든 부품은 가공 오차로 인해 완벽한 치수로 만들 수 없으므로, 허용되는 한계 오차 범위인 <strong>공차역 (Tolerance Zone)</strong>을 설정합니다.</p><p>끼워맞춤은 두 부품이 조립된 상태의 조밀한 성격에 따라 다음과 같이 세 분류로 구분됩니다:</p><ul><li><strong>틈새 끼워맞춤 (Clearance Fit):</strong> 구멍의 최소 허용 치수가 축의 최대 허용 치수보다 항상 커서 조립 후 항상 **틈새(Clearance)**가 발생하는 상태입니다. 윤활 회전축(g6, f7 등) 조립에 적용됩니다.</li><li><strong>억지 끼워맞춤 (Interference Fit):</strong> 구멍의 최대 허용 치수가 축의 최소 허용 치수보다 항상 작아 조립 전 축의 지름이 더 크며 조립 시 열박음이나 강한 압력이 필요하여 항상 **죔새(Interference)**가 발생하는 상태입니다. 고정용 키레스 결합(p6, s6 등)에 적용됩니다.</li><li><strong>중간 끼워맞춤 (Transition Fit):</strong> 가공 치수 산포에 따라 조립 후 틈새가 발생할 수도 있고 죔새가 발생할 수도 있는 기하학적 중간 점착 영역(js6, k6 등)입니다. 정밀 가이드 기구부나 풀리의 축 정렬 조립에 쓰입니다.</li></ul>
<h3>2. 기초공차(IT Grade)와 기본 치수 허용 한계선</h3>
<p>공차 기호 <code>H7 / g6</code>에서 문자는 공차역의 기준선 편차 위치인 **기초선 편차(Fundamental Deviation)**를 나타내며, 대문자는 구멍, 소문자는 축을 뜻합니다. 뒤에 붙는 숫자(6, 7, 8 등)는 공차의 절대적인 크기 수준을 나타내는 **IT 공차 등급 (Standard Tolerance Grade)**을 지칭하며, 숫자가 작을수록 고도의 정밀 가공(연삭, 정밀 브로칭 등)이 필요함을 뜻합니다.</p><ul><li><strong>구멍 H 기호:</strong> 아래 치수 허용차가 0 (<code>EI = 0</code>)인 기준 구멍 시스템의 표준입니다.</li><li><strong>축 h 기호:</strong> 위 치수 허용차가 0 (<code>es = 0</code>)인 기준 축 시스템의 표준입니다.</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/fit-tolerance-iso286-calculator-simulator-2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>열전달 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/heat-transfer-conduction-calculator/</link>
					<comments>https://myengnote.com/heat-transfer-conduction-calculator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 22:00:09 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[단열설계]]></category>
		<category><![CDATA[대류전열]]></category>
		<category><![CDATA[복합벽]]></category>
		<category><![CDATA[열전달]]></category>
		<category><![CDATA[열전도율]]></category>
		<guid isPermaLink="false">https://myengnote.com/heat-transfer-conduction-calculator/</guid>

					<description><![CDATA[전도, 대류 및 단열재 3중 복합 벽체 구조를 통과하는 열유동을 실시간 유한차분 모델링으로 해석하여 온도 구배 선도와 총 열손실을 정밀하게 계산하는 공학 시뮬레이터입니다.]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><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;" /> 열전달 계산기 &amp; 시뮬레이터</h2></p>



<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&amp;family=Outfit:wght@400;500;600;700;800&amp;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 heattransfer-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .heattransfer-calculator-wrapper *, .heattransfer-calculator-wrapper *::before, .heattransfer-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .heattransfer-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;
            --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);
        }
        .heattransfer-calculator-wrapper {
            background-color: var(--color-bg-dark);
            color: var(--color-text-main);
            font-family: var(--font-body);
            min-height: 100vh;
            display: block; 
            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.1; }
            100% { transform: scale(1.18) translate(40px, 40px); opacity: 0.18; }
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
            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: #0284c7; }
        .text-magenta { color: #db2777; }
        .text-purple { color: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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;
        }
        .range-helper {
            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);
        }
        .number-input-wrapper:focus-within {
            border-color: #0284c7;
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-select {
            width: 100%;
            background: #f8fafc;
            border: 1px solid var(--color-border);
            outline: none;
            color: #0f172a;
            padding: 8px 12px;
            font-family: var(--font-body);
            font-size: 13px;
            font-weight: 600;
            border-radius: 8px;
            cursor: pointer;
        }
        .custom-number-input {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 6px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
        }
        .presets-section {
            border-top: 1px solid var(--color-border);
            padding-top: 18px;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .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: #0284c7;
            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: #0284c7;
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch; 
            width: 100%; 
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 9;
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(2, 132, 199, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .ratio-title {
            font-size: 11px;
            font-weight: 700;
            color: #0284c7;
            letter-spacing: 1px;
            text-transform: uppercase;
        }
        .ratio-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 28px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-cyan));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }
        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .result-card {
            background: rgba(255, 255, 255, 0.85);
            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: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.03);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: #0284c7;
            border-color: rgba(2, 132, 199, 0.3);
            background: rgba(2, 132, 199, 0.1);
        }
        .card-content {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .card-unit {
            font-size: 10px;
            font-weight: 600;
            color: var(--color-text-muted);
            text-transform: uppercase;
        }
        .card-value {
            font-family: var(--font-heading);
            font-weight: 700;
            font-size: 15px;
            color: var(--color-text-main);
        }
        .formula-card {
            background: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: #0284c7;
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        @container transfer-container (max-width: 1024px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel {
                grid-column: 1;
                max-height: none;
            }
            .simulation-panel {
                grid-column: 1;
                order: -1;
            }
            .results-panel {
                grid-column: 1;
            }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }

/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.heattransfer-calculator-wrapper .app-main-grid,
.heattransfer-calculator-wrapper .main-grid,
.heattransfer-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}

.heattransfer-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
    width: 100% !important; 
}

.heattransfer-calculator-wrapper .simulation-panel,
.heattransfer-calculator-wrapper .canvas-panel,
.heattransfer-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
    align-self: stretch !important; 
    width: 100% !important; 
}

.heattransfer-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}

/* 2열 통합 결과 분석 영역 레이아웃 (공식카드를 좌측 박스 밑으로 이동) */
.heattransfer-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    grid-template-rows: 1fr auto !important; /* 위아래 2행 분할 (1행: 파란박스 자동늘어남 / 2행: 공식카드) */
    gap: 16px 20px !important; /* 세로간격 16px, 가로간격 20px */
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}

/* 좌측 상단: 파란색 열유속 박스 */
.heattransfer-calculator-wrapper .simulation-results-section .ratio-readout-box,
.heattransfer-calculator-wrapper .simulation-results-section .re-readout-box,
.heattransfer-calculator-wrapper .simulation-results-section .status-readout-box,
.heattransfer-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important; /* 1행 배치 */
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}

/* 좌측 하단: 다층 벽체 공식 카드 */
.heattransfer-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 !important; /* 1열로 이동 */
    grid-row: 2 !important;    /* 파란박스 바로 밑 2행에 배치 */
    margin: 0 !important;
    height: auto !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}

/* 우측: 4개 결과 카드 그리드 */
.heattransfer-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: 1 / span 2 !important; /* 1~2행 전체 높이를 차지하여 좌측과 우측 박스 높이 맞춤 */
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.heattransfer-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}

/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 */
@media (max-width: 800px) {
    .heattransfer-calculator-wrapper .app-main-grid,
    .heattransfer-calculator-wrapper .main-grid,
    .heattransfer-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .heattransfer-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .heattransfer-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .heattransfer-calculator-wrapper .simulation-panel,
    .heattransfer-calculator-wrapper .canvas-panel,
    .heattransfer-calculator-wrapper .sim-panel,
    .heattransfer-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .heattransfer-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
        grid-template-rows: auto auto auto !important; /* 모바일에서는 3단으로 펼침 */
    }
    .heattransfer-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .heattransfer-calculator-wrapper .simulation-results-section .re-readout-box,
    .heattransfer-calculator-wrapper .simulation-results-section .status-readout-box,
    .heattransfer-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: 1 !important; /* 첫번째: 파란박스 */
        height: auto !important;
    }
    .heattransfer-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: 2 !important; /* 두번째: 공식카드 */
    }
    .heattransfer-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: 3 !important; /* 세번째: 4개 결과 카드 */
    }
}
.heattransfer-calculator-wrapper .app-container,
.heattransfer-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="heattransfer-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 class="app-header">
            <div class="logo-area">
                <div class="logo-icon"><i class="fa-solid fa-arrows-left-right-to-line"></i></div>
                <div>
                    <h1>HEAT TRANSFER</h1>
                    <div class="subtitle">다층 복합벽 전열 계산기 &amp; 실시간 1D 열전달 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">전열 수치해석 실시간 계산중</div>
            </div>
        </header>
        <div class="app-main-grid">
            <div class="panel control-panel">
                <div class="panel-header">
                    <i class="fa-solid fa-sliders text-cyan"></i>
                    <h2>벽체 및 경계인자 제어</h2>
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-temperature-arrow-up text-magenta"></i> 고온 유체 온도 (T_f,h)</label>
                        <span class="range-helper">Min: 50°C / Max: 600°C</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-tfh" class="custom-number-input" value="400" step="1" min="50" max="600">
                        <span class="unit-badge">°C</span>
                    </div>
                    <input type="range" id="slider-tfh" class="custom-slider" value="400" min="50" max="600" step="1">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-temperature-arrow-down text-cyan"></i> 저온 유체 온도 (T_f,c)</label>
                        <span class="range-helper">Min: -20°C / Max: 50°C</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-tfc" class="custom-number-input" value="15" step="1" min="-20" max="50">
                        <span class="unit-badge">°C</span>
                    </div>
                    <input type="range" id="slider-tfc" class="custom-slider" value="15" min="-20" max="50" step="1">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label>Layer 1 (고온측 재질/두께)</label>
                    </div>
                    <select id="select-k1" class="custom-select">
                        <option value="brick" selected="">적벽돌 (Brick &#8211; 0.7 W/m·K)</option>
                        <option value="steel">강철 (Steel &#8211; 50.0 W/m·K)</option>
                        <option value="concrete">콘크리트 (Concrete &#8211; 1.4 W/m·K)</option>
                        <option value="glasswool">글라스울 (Glasswool &#8211; 0.04 W/m·K)</option>
                    </select>
                    <div class="number-input-wrapper" style="margin-top: 4px;">
                        <input type="number" id="input-l1" class="custom-number-input" value="120" step="10" min="10" max="300">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-l1" class="custom-slider" value="120" min="10" max="300" step="10">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label>Layer 2 (중간층 재질/두께)</label>
                    </div>
                    <select id="select-k2" class="custom-select">
                        <option value="glasswool" selected="">글라스울 (Glasswool &#8211; 0.04 W/m·K)</option>
                        <option value="steel">강철 (Steel &#8211; 50.0 W/m·K)</option>
                        <option value="concrete">콘크리트 (Concrete &#8211; 1.4 W/m·K)</option>
                        <option value="brick">적벽돌 (Brick &#8211; 0.7 W/m·K)</option>
                    </select>
                    <div class="number-input-wrapper" style="margin-top: 4px;">
                        <input type="number" id="input-l2" class="custom-number-input" value="50" step="10" min="10" max="300">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-l2" class="custom-slider" value="50" min="10" max="300" step="10">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label>Layer 3 (저온측 재질/두께)</label>
                    </div>
                    <select id="select-k3" class="custom-select">
                        <option value="concrete" selected="">콘크리트 (Concrete &#8211; 1.4 W/m·K)</option>
                        <option value="steel">강철 (Steel &#8211; 50.0 W/m·K)</option>
                        <option value="brick">적벽돌 (Brick &#8211; 0.7 W/m·K)</option>
                        <option value="glasswool">글라스울 (Glasswool &#8211; 0.04 W/m·K)</option>
                    </select>
                    <div class="number-input-wrapper" style="margin-top: 4px;">
                        <input type="number" id="input-l3" class="custom-number-input" value="120" step="10" min="10" max="300">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-l3" class="custom-slider" value="120" min="10" max="300" step="10">
                </div>
                <div class="presets-section">
                    <h3><i class="fa-solid fa-hotel text-purple"></i> 벽체 구성 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn" data-preset="insulated">
                            <div class="preset-icon"><i class="fa-solid fa-shield-halved"></i></div>
                            <div class="preset-details"><span class="preset-name">고성능 표준 단열벽 시나리오</span></div>
                        </button>
                        <button class="preset-btn" data-preset="non_insulated">
                            <div class="preset-icon"><i class="fa-solid fa-triangle-exclamation"></i></div>
                            <div class="preset-name">미단열 콘크리트벽<span class="preset-spec">열교현상</span></div>
                        </button>
                        <button class="preset-btn" data-preset="extreme">
                            <div class="preset-icon"><i class="fa-solid fa-snowflake"></i></div>
                            <div class="preset-details"><span class="preset-name">극저온 혹한 외부 노출 전열</span></div>
                        </button>
                    </div>
                </div>
            </div>
            <div class="right-column">
                <div class="panel simulation-panel">
                                <div class="panel-header">
                                    <div>
                                        <i class="fa-solid fa-chart-line text-cyan"></i>
                                        <h2>실시간 다층벽 온도 구배 (Temperature Gradient Line)</h2>
                                    </div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas"></canvas>
                                </div>
                            <div class="simulation-results-section">
                                <div class="ratio-readout-box">
                                    <div class="ratio-title">통과 열유속<span class="preset-spec">Heat Flux &#8211; q&#8221;</span></div>
                                    <div id="txt-heat-flux" class="ratio-value">256.4 W/m²</div>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-wave-square"></i></div>
                                        <div class="card-content">
                                            <div class="card-unit">총 열저항 (R_tot)</div>
                                            <div id="txt-rtot" class="card-value">1.500 m²·K/W</div>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-bolt"></i></div>
                                        <div class="card-content">
                                            <div class="card-unit">총괄 열전달 계수 (U-Value)</div>
                                            <div id="txt-uvalue" class="card-value">0.667 W/m²·K</div>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-down"></i></div>
                                        <div class="card-content">
                                            <div class="card-unit">내부 표면 온도 (T_s,c)</div>
                                            <div id="txt-tsc" class="card-value">20.4 °C</div>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-up"></i></div>
                                        <div class="card-content">
                                            <div class="card-unit">외부 표면 온도 (T_s,h)</div>
                                            <div id="txt-tsh" class="card-value">374.5 °C</div>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                    <h4>다층 벽체 총 열저항 식</h4>
                                    <div class="formula-equation">R = 1/h_h + Σ(L_i/k_i) + 1/h_c</div>
                                </div>
                            </div>
                </div>
            </div>
        </div>
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    </div>
</div>
<script>
        (function() {
            let isInit = false;
            function initSimulator() {
                if (isInit) return;
                const canvas = document.getElementById('physics-canvas');
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
                isInit = true;
                window.__heattransfer_initialized = true;
                // DOM controls
                const sliderTfh = document.getElementById('slider-tfh');
                const inputTfh = document.getElementById('input-tfh');
                const sliderTfc = document.getElementById('slider-tfc');
                const inputTfc = document.getElementById('input-tfc');
                const selectK1 = document.getElementById('select-k1');
                const sliderL1 = document.getElementById('slider-l1');
                const inputL1 = document.getElementById('input-l1');
                const selectK2 = document.getElementById('select-k2');
                const sliderL2 = document.getElementById('slider-l2');
                const inputL2 = document.getElementById('input-l2');
                const selectK3 = document.getElementById('select-k3');
                const sliderL3 = document.getElementById('slider-l3');
                const inputL3 = document.getElementById('input-l3');
                const presetBtns = document.querySelectorAll('.preset-btn');
                // Readouts
                const txtHeatFlux = document.getElementById('txt-heat-flux');
                const txtRtot = document.getElementById('txt-rtot');
                const txtUvalue = document.getElementById('txt-uvalue');
                const txtTsc = document.getElementById('txt-tsc');
                const txtTsh = document.getElementById('txt-tsh');
                // Material conductivity table [W/m·K]
                const conductivities = {
                    brick: 0.7,
                    steel: 50.0,
                    concrete: 1.4,
                    glasswool: 0.04
                };
                // State
                const state = {
                    tfh: 400.0,
                    tfc: 15.0,
                    l1: 0.12,  // in meters
                    l2: 0.05,
                    l3: 0.12,
                    k1: 0.7,   // brick
                    k2: 0.04,  // glasswool
                    k3: 1.4,   // concrete
                    hh: 100.0, // convection hot W/m2K
                    hc: 25.0,  // convection cold W/m2K
                    particles: [],
                    fluxOffset: 0
                };
                function resizeCanvas() {
                    const rect = canvas.getBoundingClientRect();
                    const dpr = window.devicePixelRatio || 1;
                    canvas.width = rect.width * dpr;
                    canvas.height = rect.height * dpr;
                    ctx.scale(dpr, dpr);
                    state.particles = [];
                }
                resizeCanvas();
                window.addEventListener('resize', resizeCanvas);
                setTimeout(resizeCanvas, 300);
                // Input Synchronization with strict "clamping on change only"
                function syncTfhFromSlider() {
                    state.tfh = parseFloat(sliderTfh.value);
                    inputTfh.value = Math.round(state.tfh);
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncTfhFromInput() {
                    let val = parseFloat(inputTfh.value);
                    if (isNaN(val)) {
                        val = 400;
                    }
                    if (val < 50) {
                        val = 50;
                    }
                    if (val > 600) {
                        val = 600;
                    }
                    inputTfh.value = Math.round(val);
                    sliderTfh.value = val;
                    state.tfh = val;
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncTfcFromSlider() {
                    state.tfc = parseFloat(sliderTfc.value);
                    inputTfc.value = Math.round(state.tfc);
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncTfcFromInput() {
                    let val = parseFloat(inputTfc.value);
                    if (isNaN(val)) {
                        val = 15;
                    }
                    if (val < -20) {
                        val = -20;
                    }
                    if (val > 50) {
                        val = 50;
                    }
                    inputTfc.value = Math.round(val);
                    sliderTfc.value = val;
                    state.tfc = val;
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL1FromSlider() {
                    state.l1 = parseFloat(sliderL1.value) / 1000;
                    inputL1.value = Math.round(sliderL1.value);
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL1FromInput() {
                    let val = parseFloat(inputL1.value);
                    if (isNaN(val)) {
                        val = 120;
                    }
                    if (val < 10) {
                        val = 10;
                    }
                    if (val > 300) {
                        val = 300;
                    }
                    inputL1.value = Math.round(val);
                    sliderL1.value = val;
                    state.l1 = val / 1000;
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL2FromSlider() {
                    state.l2 = parseFloat(sliderL2.value) / 1000;
                    inputL2.value = Math.round(sliderL2.value);
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL2FromInput() {
                    let val = parseFloat(inputL2.value);
                    if (isNaN(val)) {
                        val = 50;
                    }
                    if (val < 10) {
                        val = 10;
                    }
                    if (val > 300) {
                        val = 300;
                    }
                    inputL2.value = Math.round(val);
                    sliderL2.value = val;
                    state.l2 = val / 1000;
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL3FromSlider() {
                    state.l3 = parseFloat(sliderL3.value) / 1000;
                    inputL3.value = Math.round(sliderL3.value);
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncL3FromInput() {
                    let val = parseFloat(inputL3.value);
                    if (isNaN(val)) {
                        val = 120;
                    }
                    if (val < 10) {
                        val = 10;
                    }
                    if (val > 300) {
                        val = 300;
                    }
                    inputL3.value = Math.round(val);
                    sliderL3.value = val;
                    state.l3 = val / 1000;
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncSelectK1() {
                    state.k1 = conductivities[selectK1.value];
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncSelectK2() {
                    state.k2 = conductivities[selectK2.value];
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                function syncSelectK3() {
                    state.k3 = conductivities[selectK3.value];
                    clearPresets();
                    updateHeatTransferCalculations();
                }
                // Preset scripts
                function loadPreset(key) {
                    clearPresets();
                    presetBtns.forEach(btn => {
                        if (btn.dataset.preset === key) {
                            btn.classList.add('active');
                        }
                    });
                    if (key === 'insulated') {
                        state.tfh = 450;
                        state.tfc = 15;
                        state.l1 = 0.12;
                        state.l2 = 0.08;
                        state.l3 = 0.12;
                        selectK1.value = 'brick';
                        selectK2.value = 'glasswool';
                        selectK3.value = 'concrete';
                    }
                    if (key === 'non_insulated') {
                        state.tfh = 300;
                        state.tfc = 15;
                        state.l1 = 0.10;
                        state.l2 = 0.15;
                        state.l3 = 0.10;
                        selectK1.value = 'concrete';
                        selectK2.value = 'steel';
                        selectK3.value = 'concrete';
                    }
                    if (key === 'extreme') {
                        state.tfh = 550;
                        state.tfc = -20;
                        state.l1 = 0.15;
                        state.l2 = 0.12;
                        state.l3 = 0.08;
                        selectK1.value = 'brick';
                        selectK2.value = 'glasswool';
                        selectK3.value = 'brick';
                    }
                    state.k1 = conductivities[selectK1.value];
                    state.k2 = conductivities[selectK2.value];
                    state.k3 = conductivities[selectK3.value];
                    sliderTfh.value = state.tfh;
                    inputTfh.value = state.tfh;
                    sliderTfc.value = state.tfc;
                    inputTfc.value = state.tfc;
                    sliderL1.value = state.l1 * 1000;
                    inputL1.value = state.l1 * 1000;
                    sliderL2.value = state.l2 * 1000;
                    inputL2.value = state.l2 * 1000;
                    sliderL3.value = state.l3 * 1000;
                    inputL3.value = state.l3 * 1000;
                    updateHeatTransferCalculations();
                }
                function clearPresets() {
                    presetBtns.forEach(btn => btn.classList.remove('active'));
                }
                // Math Engine
                const nodalTemps = {
                    tfh: 0,
                    tsh: 0,
                    t12: 0,
                    t23: 0,
                    tsc: 0,
                    tfc: 0,
                    heatFlux: 0
                };
                function updateHeatTransferCalculations() {
                    // Thermal resistances
                    const r_conv_h = 1.0 / state.hh;
                    const r_cond_1 = state.l1 / state.k1;
                    const r_cond_2 = state.l2 / state.k2;
                    const r_cond_3 = state.l3 / state.k3;
                    const r_conv_c = 1.0 / state.hc;
                    const R_tot = r_conv_h + r_cond_1 + r_cond_2 + r_cond_3 + r_conv_c;
                    const uValue = 1.0 / R_tot;
                    const heatFlux = (state.tfh - state.tfc) / R_tot; // W/m²
                    // Calculate nodal interface temperatures
                    nodalTemps.tfh = state.tfh;
                    nodalTemps.tsh = state.tfh - heatFlux * r_conv_h;
                    nodalTemps.t12 = nodalTemps.tsh - heatFlux * r_cond_1;
                    nodalTemps.t23 = nodalTemps.t12 - heatFlux * r_cond_2;
                    nodalTemps.tsc = nodalTemps.t23 - heatFlux * r_cond_3;
                    nodalTemps.tfc = state.tfc;
                    nodalTemps.heatFlux = heatFlux;
                    // Update DOM metrics display
                    txtHeatFlux.innerText = `${heatFlux.toFixed(1)} W/m²`;
                    txtRtot.innerText = `${R_tot.toFixed(4)} m²·K/W`;
                    txtUvalue.innerText = `${uValue.toFixed(3)} W/m²·K`;
                    txtTsh.innerText = `${nodalTemps.tsh.toFixed(1)} °C`;
                    txtTsc.innerText = `${nodalTemps.tsc.toFixed(1)} °C`;
                }
                // Fluid Flow Animation Particle
                class FluidFlowParticle {
                    constructor(xRange, type) {
                        this.xMin = xRange[0];
                        this.xMax = xRange[1];
                        this.x = this.xMin + Math.random() * (this.xMax - this.xMin);
                        this.y = 0; // Will be initialized by dynamic setupY
                        this.type = type; // 'hot' or 'cold'
                        this.size = Math.random() * 3 + 1.5;
                        this.vy = (Math.random() - 0.5) * 0.8;
                        this.vx = (Math.random() - 0.5) * 0.4;
                        if (type === 'hot') {
                            this.color = `rgba(239, 68, 68, ${Math.random() * 0.4 + 0.25})`;
                        }
                        if (type === 'cold') {
                            this.color = `rgba(56, 189, 248, ${Math.random() * 0.4 + 0.25})`;
                        }
                    }
                    setupY(wY, wH) {
                        this.y = wY + Math.random() * wH;
                    }
                    update(wY, wH) {
                        this.x += this.vx;
                        this.y += this.vy;
                        // Boundary bounds check
                        if (this.x < this.xMin) {
                            this.x = this.xMax;
                        }
                        if (this.x > this.xMax) {
                            this.x = this.xMin;
                        }
                        if (this.y < wY) {
                            this.y = wY + wH;
                        }
                        if (this.y > wY + wH) {
                            this.y = wY;
                        }
                    }
                    draw(c) {
                        c.save();
                        c.fillStyle = this.color;
                        c.beginPath();
                        c.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                        c.fill();
                        c.restore();
                    }
                }
                // Populate particles
                function initParticles(width, wY, wH) {
                    state.particles = [];
                    // Hot fluid left
                    for (let i = 0; i < 40; i++) {
                        const p = new FluidFlowParticle([10, 100], 'hot');
                        p.setupY(wY, wH);
                        state.particles.push(p);
                    }
                    // Cold fluid right
                    for (let i = 0; i < 40; i++) {
                        const p = new FluidFlowParticle([width - 100, width - 10], 'cold');
                        p.setupY(wY, wH);
                        state.particles.push(p);
                    }
                }
                // Animation main loop
                function animate(timestamp) {
                    requestAnimationFrame(animate);
                    // Check and auto-fit canvas resolution to layout changes (prevents vertical stretch &#038; empty gaps)
                    const dpr = window.devicePixelRatio || 1;
                    const rect = canvas.getBoundingClientRect();
                    if (Math.abs(canvas.width - rect.width * dpr) > 1.5 || Math.abs(canvas.height - rect.height * dpr) > 1.5) {
                        resizeCanvas();
                    }
                    const width = canvas.width / dpr;
                    const height = canvas.height / dpr;
                    ctx.clearRect(0, 0, width, height);
                    // Blueprint grid
                    ctx.save();
                    ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                    ctx.lineWidth = 1;
                    for (let x = 0; x < width; x += 24) {
                        ctx.beginPath();
                        ctx.moveTo(x, 0);
                        ctx.lineTo(x, height);
                        ctx.stroke();
                    }
                    for (let y = 0; y < height; y += 24) {
                        ctx.beginPath();
                        ctx.moveTo(0, y);
                        ctx.lineTo(width, y);
                        ctx.stroke();
                    }
                    ctx.restore();
                    // Wall Drawing Layout Coordinates
                    const wallLeftX = 115;
                    const wallRightX = width - 115;
                    const wallWidth = wallRightX - wallLeftX;
                    const wallHeight = Math.max(100, Math.min(150, height * 0.42));
                    // Golden Vertical Centering: mathematically centers the composite walls and graph lines
                    // Shift wall slightly up by 15px to give more room at the bottom for labels
                    const wallY = (height - wallHeight) / 2 - 15;
                    // Initialize particles on canvas resize
                    if (state.particles.length === 0) {
                        initParticles(width, wallY, wallHeight);
                    }
                    // Thickness proportions in pixels
                    const totalL = state.l1 + state.l2 + state.l3;
                    const w1 = (state.l1 / totalL) * wallWidth;
                    const w2 = (state.l2 / totalL) * wallWidth;
                    const w3 = (state.l3 / totalL) * wallWidth;
                    const x0 = wallLeftX;
                    const x1 = x0 + w1;
                    const x2 = x1 + w2;
                    const x3 = x2 + w3;
                    // Draw fluids on left/right edges
                    // Hot Fluid Left Block
                    ctx.fillStyle = 'rgba(239, 68, 68, 0.03)';
                    ctx.fillRect(10, wallY, wallLeftX - 10, wallHeight);
                    // Cold Fluid Right Block
                    ctx.fillStyle = 'rgba(56, 189, 248, 0.03)';
                    ctx.fillRect(wallRightX, wallY, width - wallRightX - 10, wallHeight);
                    // Update &#038; draw fluid flows
                    state.particles.forEach(p => {
                        p.update(wallY, wallHeight);
                        p.draw(ctx);
                    });
                    // Draw Composite Wall Layers
                    // Layer 1
                    ctx.save();
                    drawLayerBox(x0, wallY, w1, wallHeight, selectK1.value);
                    // Layer 2
                    drawLayerBox(x1, wallY, w2, wallHeight, selectK2.value);
                    // Layer 3
                    drawLayerBox(x2, wallY, w3, wallHeight, selectK3.value);
                    ctx.restore();
                    // Temperature axis y-mapping
                    // Maps temperature from state.tfh to state.tfc onto wall height [wallY+20, wallY+wallHeight-20]
                    const maxTemp = Math.max(state.tfh, 100);
                    const minTemp = Math.min(state.tfc, 0);
                    const tempSpan = maxTemp - minTemp;
                    function mapTempToY(temp) {
                        const ratio = (temp - minTemp) / tempSpan;
                        // Invert so high temperatures are at the top, leaving a comfortable 20px padding at the borders
                        return wallY + wallHeight - 20 - ratio * (wallHeight - 40);
                    }
                    // Compute node mapping positions on x-axis
                    const px_fh = 70;
                    const px_sh = x0;
                    const px_12 = x1;
                    const px_23 = x2;
                    const px_sc = x3;
                    const px_fc = width - 70;
                    const py_fh = mapTempToY(nodalTemps.tfh);
                    const py_sh = mapTempToY(nodalTemps.tsh);
                    const py_12 = mapTempToY(nodalTemps.t12);
                    const py_23 = mapTempToY(nodalTemps.t23);
                    const py_sc = mapTempToY(nodalTemps.tsc);
                    const py_fc = mapTempToY(nodalTemps.tfc);
                    // Draw Temperature Gradient Line Curve
                    ctx.save();
                    ctx.strokeStyle = '#db2777';
                    ctx.lineWidth = 4.5;
                    ctx.shadowBlur = 10;
                    ctx.shadowColor = 'rgba(219, 39, 119, 0.4)';
                    ctx.lineCap = 'round';
                    ctx.lineJoin = 'round';
                    ctx.beginPath();
                    ctx.moveTo(px_fh, py_fh);
                    ctx.lineTo(px_sh, py_sh);
                    ctx.lineTo(px_12, py_12);
                    ctx.lineTo(px_23, py_23);
                    ctx.lineTo(px_sc, py_sc);
                    ctx.lineTo(px_fc, py_fc);
                    ctx.stroke();
                    ctx.shadowBlur = 0;
                    ctx.restore();
                    // Draw connection points (circles) & labels
                    const nodes = [
                        { x: px_fh, y: py_fh, label: `T_fh: ${Math.round(nodalTemps.tfh)}°C`, col: '#ef4444', align: 'right', ox: -8, oy: -10 },
                        { x: px_sh, y: py_sh, label: `T_sh: ${nodalTemps.tsh.toFixed(1)}°C`, col: '#ea580c', align: 'left', ox: 8, oy: -10 },
                        { x: px_12, y: py_12, label: `${nodalTemps.t12.toFixed(1)}°C`, col: '#7c3aed', align: 'center', ox: 0, oy: -10 },
                        { x: px_23, y: py_23, label: `${nodalTemps.t23.toFixed(1)}°C`, col: '#7c3aed', align: 'center', ox: 0, oy: -10 },
                        { x: px_sc, y: py_sc, label: `T_sc: ${nodalTemps.tsc.toFixed(1)}°C`, col: '#2563eb', align: 'right', ox: -8, oy: -10 },
                        { x: px_fc, y: py_fc, label: `T_fc: ${Math.round(nodalTemps.tfc)}°C`, col: '#0284c7', align: 'left', ox: 8, oy: -10 }
                    ];
                    nodes.forEach(node => {
                        ctx.beginPath();
                        ctx.arc(node.x, node.y, 5, 0, Math.PI * 2);
                        ctx.fillStyle = '#ffffff';
                        ctx.fill();
                        ctx.strokeStyle = node.col;
                        ctx.lineWidth = 2.5;
                        ctx.stroke();
                        ctx.fillStyle = '#0f172a';
                        ctx.font = 'bold 9.5px sans-serif';
                        ctx.textAlign = node.align;
                        ctx.fillText(node.label, node.x + node.ox, node.y + node.oy);
                    });
                    // Draw convective film layers visually
                    ctx.save();
                    ctx.strokeStyle = 'rgba(239, 68, 68, 0.25)';
                    ctx.lineWidth = 2;
                    ctx.setLineDash([3, 3]);
                    ctx.beginPath();
                    ctx.moveTo(x0 - 15, wallY);
                    ctx.lineTo(x0 - 15, wallY + wallHeight);
                    ctx.stroke();
                    ctx.strokeStyle = 'rgba(56, 189, 248, 0.25)';
                    ctx.beginPath();
                    ctx.moveTo(x3 + 15, wallY);
                    ctx.lineTo(x3 + 15, wallY + wallHeight);
                    ctx.stroke();
                    ctx.restore();
                    // Heat Flux Flow indicator Arrow at bottom
                    ctx.save();
                    const arrowY = height - 25;
                    ctx.fillStyle = '#e2e8f0';
                    ctx.fillRect(wallLeftX - 10, arrowY - 6, wallWidth + 20, 12);
                    const arrowGrad = ctx.createLinearGradient(wallLeftX - 10, 0, wallRightX + 10, 0);
                    arrowGrad.addColorStop(0, '#f97316');
                    arrowGrad.addColorStop(1, '#0284c7');
                    ctx.fillStyle = arrowGrad;
                    const normalizedFluxSize = Math.min(nodalTemps.heatFlux / 1200, 1.0);
                    const flowBarHeight = Math.max(normalizedFluxSize * 10, 2);
                    ctx.fillRect(wallLeftX - 10, arrowY - flowBarHeight / 2, wallWidth + 20, flowBarHeight);
                    // Moving energy packets along heat path
                    state.fluxOffset += nodalTemps.heatFlux * 0.003;
                    if (state.fluxOffset > 40) {
                        state.fluxOffset = 0;
                    }
                    ctx.fillStyle = '#ffffff';
                    for (let ax = wallLeftX + state.fluxOffset; ax < wallRightX; ax += 30) {
                        ctx.beginPath();
                        ctx.arc(ax, arrowY, 2.5, 0, Math.PI * 2);
                        ctx.fill();
                    }
                    ctx.fillStyle = '#475569';
                    ctx.font = 'bold 10px Outfit';
                    ctx.fillText('전송 열에너지 흐름 방향 (Heat Energy Flux Path)', wallLeftX, arrowY - 14);
                    ctx.restore();
                    // Add material names below layers
                    ctx.save();
                    ctx.fillStyle = '#475569';
                    ctx.font = 'bold 9.5px sans-serif';
                    ctx.textAlign = 'center';
                    ctx.fillText(getMaterialLabel(selectK1.value) + ` (${Math.round(state.l1 * 1000)}mm)`, x0 + w1 / 2, wallY + wallHeight + 16);
                    ctx.fillText(getMaterialLabel(selectK2.value) + ` (${Math.round(state.l2 * 1000)}mm)`, x1 + w2 / 2, wallY + wallHeight + 16);
                    ctx.fillText(getMaterialLabel(selectK3.value) + ` (${Math.round(state.l3 * 1000)}mm)`, x2 + w3 / 2, wallY + wallHeight + 16);
                    ctx.restore();
                }
                // Helper to color/pattern wall materials
                function drawLayerBox(x, y, w, h, mat) {
                    if (mat === 'steel') {
                        const g = ctx.createLinearGradient(x, y, x + w, y);
                        g.addColorStop(0, '#cbd5e1');
                        g.addColorStop(1, '#94a3b8');
                        ctx.fillStyle = g;
                        ctx.fillRect(x, y, w, h);
                        ctx.strokeStyle = '#475569';
                        ctx.strokeRect(x, y, w, h);
                    }
                    if (mat === 'concrete') {
                        ctx.fillStyle = '#e2e8f0';
                        ctx.fillRect(x, y, w, h);
                        // Speckles (constrained inside boundaries with 2px padding)
                        ctx.fillStyle = 'rgba(0,0,0,0.15)';
                        for (let s = 0; s < 12; s++) {
                            const sx = x + 2 + (Math.sin(s * 10) * 0.5 + 0.5) * (w - 4);
                            const sy = y + 2 + (Math.cos(s * 7) * 0.5 + 0.5) * (h - 4);
                            ctx.fillRect(sx, sy, 2, 2);
                        }
                        ctx.strokeStyle = '#64748b';
                        ctx.strokeRect(x, y, w, h);
                    }
                    if (mat === 'brick') {
                        ctx.fillStyle = '#fca5a5';
                        ctx.fillRect(x, y, w, h);
                        // Brick joints lines
                        ctx.strokeStyle = 'rgba(255,255,255,0.45)';
                        ctx.lineWidth = 1.5;
                        for (let bx = x + 10; bx < x + w; bx += 14) {
                            ctx.beginPath();
                            ctx.moveTo(bx, y);
                            ctx.lineTo(bx, y + h);
                            ctx.stroke();
                        }
                        for (let by = y + 15; by < y + h; by += 20) {
                            ctx.beginPath();
                            ctx.moveTo(x, by);
                            ctx.lineTo(x + w, by);
                            ctx.stroke();
                        }
                        ctx.strokeStyle = '#b91c1c';
                        ctx.strokeRect(x, y, w, h);
                    }
                    if (mat === 'glasswool') {
                        ctx.fillStyle = '#fef08a';
                        ctx.fillRect(x, y, w, h);
                        // Swirls fibers (dynamically spaced and bound to box height h)
                        ctx.strokeStyle = '#eab308';
                        ctx.lineWidth = 1;
                        const spacing = 24;
                        const r = Math.min(w * 0.35, 10);
                        const startY = y + r + 5;
                        const maxY = y + h - r - 5;
                        let f = 0;
                        while (true) {
                            const cy = startY + f * spacing;
                            if (cy > maxY) {
                                break;
                            }
                            ctx.beginPath();
                            ctx.arc(x + w / 2, cy, r, 0, Math.PI, f % 2 === 0);
                            ctx.stroke();
                            f++;
                        }
                        ctx.strokeStyle = '#ca8a04';
                        ctx.strokeRect(x, y, w, h);
                    }
                }
                function getMaterialLabel(mat) {
                    if (mat === 'brick') return '적벽돌';
                    if (mat === 'steel') return '강철';
                    if (mat === 'concrete') return '콘크리트';
                    if (mat === 'glasswool') return '글라스울';
                    return '';
                }
                // Event Listeners Mapping
                sliderTfh.addEventListener('input', syncTfhFromSlider);
                inputTfh.addEventListener('change', syncTfhFromInput);
                sliderTfc.addEventListener('input', syncTfcFromSlider);
                inputTfc.addEventListener('change', syncTfcFromInput);
                sliderL1.addEventListener('input', syncL1FromSlider);
                inputL1.addEventListener('change', syncL1FromInput);
                sliderL2.addEventListener('input', syncL2FromSlider);
                inputL2.addEventListener('change', syncL2FromInput);
                sliderL3.addEventListener('input', syncL3FromSlider);
                inputL3.addEventListener('change', syncL3FromInput);
                selectK1.addEventListener('change', syncSelectK1);
                selectK2.addEventListener('change', syncSelectK2);
                selectK3.addEventListener('change', syncSelectK3);
                presetBtns.forEach(btn => {
                    btn.addEventListener('click', function() {
                        loadPreset(btn.dataset.preset);
                    });
                });
                // Load Initial configuration
                loadPreset('insulated');
                requestAnimationFrame(animate);
                // Anti-copying / Context protection code
                document.addEventListener('contextmenu', function(e) {
                    e.preventDefault();
                    alert("이 시뮬레이터 프로그램의 지식재산권은 보호받고 있습니다.");
                    return false;
                }, { capture: true });
                document.addEventListener('selectstart', function(e) {
                    e.preventDefault();
                    return false;
                }, { capture: true });
            }
            // Retry loader sequence
            let loadAttempts = 0;
            function tryLoad() {
                loadAttempts++;
                initSimulator();
                if (!window.__heattransfer_initialized) {
                    if (loadAttempts < 50) {
                        setTimeout(tryLoad, 100);
                    }
                }
            }
            if (document.readyState === 'complete' || document.readyState === 'interactive') {
                tryLoad();
            } else {
                document.addEventListener('DOMContentLoaded', tryLoad);
                window.addEventListener('load', tryLoad);
            }
        })();
</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>유체 경계 온도 설정: 고온측 유체 온도(Tf,h)와 저온측 유체 온도(Tf,c)를 설정합니다.</strong></li>
<li style="margin-bottom: 6px;">벽체 레이어 설계: 복합벽을 구성하는 3개 레이어의 재질(강철, 콘크리트, 적벽돌, 글라스울 단열재 등)과 각각의 두께(L)를 입력합니다.</li>
<li style="margin-bottom: 6px;">대류 열전달 계수 조절: 좌측 고온측과 우측 저온측의 대류 경계막 계수(h)를 설정하여 대류 저항을 모델링합니다.</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;" /> 다층 벽체 전도 및 대류 열저항 이론 식 확인하기</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. 1차원 정상상태 열전달 (Steady-State Heat Transfer)의 이론</h3>
<p>공학적 단열 설계나 보일러 벽체, 열교환기 튜브 등은 다층 구조의 고체를 통과하는 1차원 열유동으로 열적 거동을 근사할 수 있습니다. 정상상태(Steady-State) 조건이란 시간에 따라 모든 지점의 온도가 변하지 않는 가상의 평형 상태를 뜻하며, 이 상태에서는 고온 유체에서 벽체 내부를 거쳐 저온 유체로 흐르는 열량(Heat Rate)이 전 구간에서 보존됩니다.</p><ul><li><strong>대류 열전달 (Convective Heat Transfer):</strong> 유체와 인접한 고체 계면 사이에서 분자 진동과 거시적 유체 유동의 복합 작용으로 열이 전달되는 메커니즘입니다 (Q = h·A·ΔT).</li><li><strong>전도 열전달 (Conductive Heat Transfer):</strong> 유체의 이동 없이 물질 내부의 원자/전자의 격자 진동에 의해 열이 순차적으로 전달됩니다 (푸리에 법칙: q'' = -k·dT/dx).</li></ul>
<h3>2. 열회로망(Thermal Network) 분석 및 총괄 전열계수</h3>
<p>복합적인 전도와 대류가 직렬로 연계된 경우, 전기 회로의 저항 개념을 도입하여 **열저항망(Thermal Resistance Network)**으로 손쉽게 해석할 수 있습니다.</p><p><strong>① 개별 열저항 계산 공식 (Area A = 1 m² 기준):</strong></p><ul><li>대류 경계 열저항: <code>R_conv = 1 / h</code> &nbsp;[K/W]</li><li>고체 전도 열저항: <code>R_cond = L / k</code> &nbsp;[K/W] (L: 두께 m, k: 열전도율 W/m·K)</li></ul><p><strong>② 총 열저항 및 열유속 (Heat Flux, q''):</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">R_tot = (1 / h_h) + (L1 / k1) + (L2 / k2) + (L3 / k3) + (1 / h_c) &nbsp;[m²·K/W]</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">q'' = Q / A = (T_f,h - T_f,c) / R_tot &nbsp;[W/m²]</p><p><strong>③ 노드별 표면 온도 강하 계산:</strong> 열저항을 통과할 때마다 전위차와 같이 온도 강하가 정비례하여 일어납니다:</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">T_s,h = T_f,h - q'' × (1 / h_h) &nbsp;[°C]</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">T_12 = T_s,h - q'' × (L1 / k1) &nbsp;[°C]</p><p>이 공식을 통해 복합 벽체의 계면 온도들을 구하여 재료가 열적으로 견딜 수 있는 안전 한계를 모니터링할 수 있습니다.</p>
<h3>3. 주요 공학 단열재의 특징 및 열전도성</h3>
<p>효율적인 열 관리를 위해 벽체 중간층에 열전도율이 대단히 낮은 단열재를 배치합니다.</p><ul><li><strong>글라스울/미네랄울 (Glass/Mineral Wool):</strong> 대략 k ≈ 0.035~0.045 W/m·K 로 고온 단열 성능이 탁월하며 미세 기공 내 공기의 열전도를 최소화합니다.</li><li><strong>붉은 벽돌 (Brick):</strong> k ≈ 0.7~0.8 W/m·K 로 구조적 내구성은 있으나 열손실 차단 성능은 단열재 대비 20배 이하로 떨어집니다.</li><li><strong>콘크리트 (Concrete):</strong> k ≈ 1.3~1.5 W/m·K 로 매우 높은 열전도를 가져 겨울철 결로 및 열교(Thermal Bridge) 현상의 주원인이 됩니다.</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/heat-transfer-conduction-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>열팽창 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/thermal-expansion-calculator-simulator/</link>
					<comments>https://myengnote.com/thermal-expansion-calculator-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 19:52:51 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[기계설계]]></category>
		<category><![CDATA[선팽창계수]]></category>
		<category><![CDATA[열역학]]></category>
		<category><![CDATA[열팽창]]></category>
		<category><![CDATA[재료역학]]></category>
		<guid isPermaLink="false">https://myengnote.com/thermal-expansion-calculator-simulator/</guid>

					<description><![CDATA[온도 변화에 따른 다양한 금속 봉의 길이 팽창을 실시간 시각화하고, 선팽창 공식과 마이크로 미터 단위의 변위를 물리 모델링과 아토믹 진동으로 보여주는 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; 시뮬레이터</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 thermalexpansion-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .thermalexpansion-calculator-wrapper *, .thermalexpansion-calculator-wrapper *::before, .thermalexpansion-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .thermalexpansion-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;
            --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);
        }
        .thermalexpansion-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.1; }
            100% { transform: scale(1.18) translate(40px, 40px); opacity: 0.18; }
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
            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: #0284c7; }
        .text-magenta { color: #db2777; }
        .text-purple { color: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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;
        }
        .range-helper {
            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);
        }
        .number-input-wrapper:focus-within {
            border-color: #0284c7;
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-select {
            width: 100%;
            background: #f8fafc;
            border: 1px solid var(--color-border);
            outline: none;
            color: #0f172a;
            padding: 10px 14px;
            font-family: var(--font-body);
            font-size: 14px;
            font-weight: 600;
            border-radius: 8px;
            cursor: pointer;
            transition: all 0.2s ease;
        }
        .custom-select:focus {
            border-color: #0284c7;
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
                .custom-number-input {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 6px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
            transition: transform 0.1s ease;
        }
        .custom-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        .presets-section {
            border-top: 1px solid var(--color-border);
            padding-top: 18px;
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .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: 10px 14px;
            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: #0284c7;
            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: #0284c7;
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: start;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 8;
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(219, 39, 119, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(219, 39, 119, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .ratio-title {
            font-size: 11px;
            font-weight: 700;
            color: #db2777;
            letter-spacing: 1px;
            text-transform: uppercase;
        }
        .ratio-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 28px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-magenta));
            -webkit-background-clip: text;
            -webkit-text-fill-color: transparent;
        }
        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .result-card {
            background: rgba(255, 255, 255, 0.85);
            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: rgba(219, 39, 119, 0.2);
            background: rgba(219, 39, 119, 0.03);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: #db2777;
            border-color: rgba(219, 39, 119, 0.3);
            background: rgba(219, 39, 119, 0.1);
        }
        .card-content {
            display: flex;
            flex-direction: column;
            gap: 2px;
        }
        .card-unit {
            font-size: 10px;
            font-weight: 600;
            color: var(--color-text-muted);
            text-transform: uppercase;
        }
        .card-value {
            font-family: var(--font-heading);
            font-weight: 700;
            font-size: 16px;
            color: var(--color-text-main);
        }
        .formula-card {
            background: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: #0284c7;
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        @container expansion-container (max-width: 1024px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel {
                grid-column: 1;
            }
            .simulation-panel {
                grid-column: 1;
                order: -1;
            }
            .simulation-results-section {
                grid-template-columns: 1fr !important;
            }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.thermalexpansion-calculator-wrapper .app-main-grid,
.thermalexpansion-calculator-wrapper .main-grid,
.thermalexpansion-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.thermalexpansion-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.thermalexpansion-calculator-wrapper .simulation-panel,
.thermalexpansion-calculator-wrapper .canvas-panel,
.thermalexpansion-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.thermalexpansion-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.thermalexpansion-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.thermalexpansion-calculator-wrapper .simulation-results-section .ratio-readout-box,
.thermalexpansion-calculator-wrapper .simulation-results-section .re-readout-box,
.thermalexpansion-calculator-wrapper .simulation-results-section .status-readout-box,
.thermalexpansion-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.thermalexpansion-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.thermalexpansion-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.thermalexpansion-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .thermalexpansion-calculator-wrapper .app-main-grid,
    .thermalexpansion-calculator-wrapper .main-grid,
    .thermalexpansion-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .thermalexpansion-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .thermalexpansion-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .thermalexpansion-calculator-wrapper .simulation-panel,
    .thermalexpansion-calculator-wrapper .canvas-panel,
    .thermalexpansion-calculator-wrapper .sim-panel,
    .thermalexpansion-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .thermalexpansion-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .thermalexpansion-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .thermalexpansion-calculator-wrapper .simulation-results-section .re-readout-box,
    .thermalexpansion-calculator-wrapper .simulation-results-section .status-readout-box,
    .thermalexpansion-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .thermalexpansion-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .thermalexpansion-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.thermalexpansion-calculator-wrapper .app-container,
.thermalexpansion-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="thermalexpansion-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-temperature-three-quarters"></i></div>
                <div>
                    <h1>THERMAL EXPANSION</h1>
                    <div class="subtitle">금속 열팽창 계수 계산기 &#038; 실시간 2D 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">물리 연산 엔진 작동중</div>
            </div>
        </header>
        <div class="app-main-grid">
            <!-- Left Column: Controls -->
            <div class="panel control-panel">
                <div class="panel-header">
                    <i class="fa-solid fa-sliders text-cyan"></i>
                    <h2>시뮬레이션 제어</h2>
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-cubes text-cyan"></i> 대상 금속 재질</label>
                    </div>
                    <select id="select-material" class="custom-select">
                        <option value="steel">탄소강 (Steel)</option>
                        <option value="copper">구리 (Copper)</option>
                        <option value="aluminum">알루미늄 (Aluminum)</option>
                        <option value="brass">황동 (Brass)</option>
                    </select>
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-ruler-horizontal text-cyan"></i> 초기 길이 (L₀)</label>
                        <span class="range-helper">Min: 1.0m / Max: 10.0m</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-l0" class="custom-number-input" value="5.0" step="0.1" min="1.0" max="10.0">
                        <span class="unit-badge">m</span>
                    </div>
                    <input type="range" id="slider-l0" class="custom-slider" value="5.0" min="1.0" max="10.0" step="0.1">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-temperature-low text-cyan"></i> 초기 온도 (T₁)</label>
                        <span class="range-helper">Min: -50°C / Max: 100°C</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-t1" class="custom-number-input" value="20" step="1" min="-50" max="100">
                        <span class="unit-badge">°C</span>
                    </div>
                    <input type="range" id="slider-t1" class="custom-slider" value="20" min="-50" max="100" step="1">
                </div>
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-temperature-high text-magenta"></i> 최종 온도 (T₂)</label>
                        <span class="range-helper">Min: -50°C / Max: 500°C</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-t2" class="custom-number-input" value="200" step="1" min="-50" max="500">
                        <span class="unit-badge">°C</span>
                    </div>
                    <input type="range" id="slider-t2" class="custom-slider" value="200" min="-50" max="500" step="1">
                </div>
                <div class="presets-section">
                    <h3><i class="fa-solid fa-fire-burner text-purple"></i> 급속 시나리오 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn" data-preset="heat">
                            <div class="preset-icon"><i class="fa-solid fa-fire"></i></div>
                            <div class="preset-details"><span class="preset-name">고온 가열 시험</span><span class="preset-spec">500°C</span></div>
                        </button>
                        <button class="preset-btn" data-preset="normal">
                            <div class="preset-icon"><i class="fa-solid fa-leaf"></i></div>
                            <div class="preset-details"><span class="preset-name">표준 대기온 상태</span><span class="preset-spec">20°C</span></div>
                        </button>
                        <button class="preset-btn" data-preset="cold">
                            <div class="preset-icon"><i class="fa-solid fa-snowflake"></i></div>
                            <div class="preset-details"><span class="preset-name">극저온 빙결 시험</span><span class="preset-spec">-50°C</span></div>
                        </button>
                    </div>
                </div>
            </div>
            <!-- Middle Column: Live Canvas Visualization & Integrated Results -->
            <div class="panel simulation-panel">
                <div class="panel-header">
                    <div>
                        <i class="fa-solid fa-chart-line text-cyan"></i>
                        <h2>실시간 거시/미시 변형 시뮬레이터</h2>
                    </div>
                    <span id="canvas-scale-indicator" class="canvas-scale-indicator">배율: 100배 과장</span>
                </div>
                <div class="canvas-wrapper">
                    <canvas id="physics-canvas"></canvas>
                </div>
                <!-- Integrated Results Analysis Section -->
                <div class="simulation-results-section" style="display: grid; grid-template-columns: 1.1fr 1.3fr; 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; background: linear-gradient(135deg, rgba(219, 39, 119, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%); border: 1px solid rgba(219, 39, 119, 0.25); border-radius: 16px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; box-shadow: 0 0 15px rgba(219, 39, 119, 0.05);">
                            <div class="ratio-title" style="font-size: 11px; font-weight: 700; color: #db2777; letter-spacing: 1px; text-transform: uppercase;">길이 변화량 (ΔL)</div>
                            <div id="txt-delta-l" class="ratio-value" style="font-family: var(--font-heading); font-weight: 800; font-size: 26px; background: linear-gradient(90deg, var(--color-text-main), var(--color-magenta)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;">+15.30 mm</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-circle-info text-cyan"></i> 선팽창 계산 공식</h4>
                            <div class="formula-equation" style="font-size: 11px; padding: 6px;">
                                ΔL = α × L₀ × ΔT
                            </div>
                        </div>
                    </div>
                    <!-- Right Column: Quantitative Results Cards -->
                    <div class="results-grid" style="display: flex; flex-direction: column; gap: 10px; justify-content: center;">
                        <!-- Temperature Diff -->
                        <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-temperature-half text-cyan"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">온도 변화폭 (ΔT)</span>
                                <span id="txt-delta-t" class="card-value" style="font-size: 15px;">180 °C</span>
                            </div>
                        </div>
                        <!-- Final Length -->
                        <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-ruler-horizontal text-purple"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">최종 전체 길이 (L)</span>
                                <span id="txt-final-l" class="card-value" style="font-size: 15px;">5.0153 m</span>
                            </div>
                        </div>
                        <!-- Thermal Strain -->
                        <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-percent text-magenta"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">열변형률 (Thermal Strain)</span>
                                <span id="txt-strain" class="card-value" style="font-size: 15px;">0.306 %</span>
                            </div>
                        </div>
                        <!-- Thermal Stress -->
                        <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-compress text-success"></i></div>
                            <div class="card-content">
                                <span class="card-unit" style="font-size: 10px;">구속 시 온도응력 (Thermal Stress)</span>
                                <span id="txt-stress" class="card-value" style="font-size: 15px;">414.0 MPa</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    <!-- JavaScript Core Simulator Engine -->
    </div>
</div>
<script>
        (function() {
            let isInit = false;
            function initSimulator() {
                if (isInit) return;
                const canvas = document.getElementById('physics-canvas');
                if (!canvas) return;
                const ctx = canvas.getContext('2d');
                if (!ctx) return;
                isInit = true;
                window.__thermalexpansion_initialized = true;
                // DOM elements
                const selectMaterial = document.getElementById('select-material');
                const sliderL0 = document.getElementById('slider-l0');
                const inputL0 = document.getElementById('input-l0');
                const sliderT1 = document.getElementById('slider-t1');
                const inputT1 = document.getElementById('input-t1');
                const sliderT2 = document.getElementById('slider-t2');
                const inputT2 = document.getElementById('input-t2');
                const presetBtns = document.querySelectorAll('.preset-btn');
                const txtDeltaL = document.getElementById('txt-delta-l');
                const txtDeltaT = document.getElementById('txt-delta-t');
                const txtFinalL = document.getElementById('txt-final-l');
                const txtStrain = document.getElementById('txt-strain');
                const txtStress = document.getElementById('txt-stress');
                // Simulation State
                const state = {
                    material: 'steel',
                    l0: 5.0,
                    t1: 20,
                    t2: 200,
                    alpha: 11.5e-6,
                    youngsModulus: 200e9, // Pa
                    density: 7850,
                    colorGradStart: '#94a3b8',
                    colorGradEnd: '#475569',
                    particles: []
                };
                const materialProperties = {
                    steel: {
                        alpha: 11.5e-6,
                        E: 200e9,
                        density: 7850,
                        c1: '#a1a1aa',
                        c2: '#52525b'
                    },
                    copper: {
                        alpha: 17.0e-6,
                        E: 110e9,
                        density: 8960,
                        c1: '#fb923c',
                        c2: '#c2410c'
                    },
                    aluminum: {
                        alpha: 23.0e-6,
                        E: 70e9,
                        density: 2700,
                        c1: '#cbd5e1',
                        c2: '#64748b'
                    },
                    brass: {
                        alpha: 19.0e-6,
                        E: 90e9,
                        density: 8500,
                        c1: '#fde047',
                        c2: '#ca8a04'
                    }
                };
                // Retina Display Canvas adaptation
                function resizeCanvas() {
                    const rect = canvas.getBoundingClientRect();
                    const dpr = window.devicePixelRatio || 1;
                    canvas.width = rect.width * dpr;
                    canvas.height = rect.height * dpr;
                    ctx.scale(dpr, dpr);
                }
                resizeCanvas();
                window.addEventListener('resize', resizeCanvas);
                setTimeout(resizeCanvas, 300);
                // Input sync handlers
                function syncL0FromSlider() {
                    const val = parseFloat(sliderL0.value);
                    inputL0.value = val.toFixed(1);
                    state.l0 = val;
                    clearPresets();
                    updateCalculations();
                }
                function syncL0FromInput() {
                    let val = parseFloat(inputL0.value);
                    if (isNaN(val)) {
                        val = 5.0;
                    }
                    if (val < 1.0) {
                        val = 1.0;
                    }
                    if (val > 10.0) {
                        val = 10.0;
                    }
                    sliderL0.value = val;
                    state.l0 = val;
                    clearPresets();
                    updateCalculations();
                }
                function syncT1FromSlider() {
                    const val = parseInt(sliderT1.value);
                    inputT1.value = val;
                    state.t1 = val;
                    clearPresets();
                    updateCalculations();
                }
                function syncT1FromInput() {
                    let val = parseInt(inputT1.value);
                    if (isNaN(val)) {
                        val = 20;
                    }
                    if (val < -50) {
                        val = -50;
                    }
                    if (val > 100) {
                        val = 100;
                    }
                    sliderT1.value = val;
                    state.t1 = val;
                    clearPresets();
                    updateCalculations();
                }
                function syncT2FromSlider() {
                    const val = parseInt(sliderT2.value);
                    inputT2.value = val;
                    state.t2 = val;
                    clearPresets();
                    updateCalculations();
                }
                function syncT2FromInput() {
                    let val = parseInt(inputT2.value);
                    if (isNaN(val)) {
                        val = 200;
                    }
                    if (val < -50) {
                        val = -50;
                    }
                    if (val > 500) {
                        val = 500;
                    }
                    sliderT2.value = val;
                    state.t2 = val;
                    clearPresets();
                    updateCalculations();
                }
                function handleMaterialChange() {
                    const selected = selectMaterial.value;
                    const props = materialProperties[selected];
                    if (props) {
                        state.material = selected;
                        state.alpha = props.alpha;
                        state.youngsModulus = props.E;
                        state.density = props.density;
                        state.colorGradStart = props.c1;
                        state.colorGradEnd = props.c2;
                    }
                    updateCalculations();
                }
                function loadPreset(presetKey) {
                    clearPresets();
                    presetBtns.forEach(btn => {
                        if (btn.dataset.preset === presetKey) {
                            btn.classList.add('active');
                        }
                    });
                    if (presetKey === 'heat') {
                        state.t1 = 20;
                        state.t2 = 500;
                    }
                    if (presetKey === 'normal') {
                        state.t1 = 20;
                        state.t2 = 20;
                    }
                    if (presetKey === 'cold') {
                        state.t1 = 20;
                        state.t2 = -50;
                    }
                    sliderT1.value = state.t1;
                    inputT1.value = state.t1;
                    sliderT2.value = state.t2;
                    inputT2.value = state.t2;
                    updateCalculations();
                }
                function clearPresets() {
                    presetBtns.forEach(btn => btn.classList.remove('active'));
                }
                // Math Model update
                function updateCalculations() {
                    const deltaT = state.t2 - state.t1;
                    const deltaL = state.alpha * state.l0 * deltaT * 1000; // mm
                    const finalL = state.l0 + (deltaL / 1000);
                    const strain = (state.alpha * deltaT) * 100; // %
                    const stress = state.youngsModulus * state.alpha * deltaT / 1e6; // MPa
                    txtDeltaT.innerText = `${deltaT} °C`;
                    if (deltaL >= 0) {
                        txtDeltaL.innerText = `+${deltaL.toFixed(3)} mm`;
                        txtDeltaL.style.color = '#db2777';
                    }
                    if (deltaL < 0) {
                        txtDeltaL.innerText = `${deltaL.toFixed(3)} mm`;
                        txtDeltaL.style.color = '#0284c7';
                    }
                    txtFinalL.innerText = `${finalL.toFixed(6)} m`;
                    txtStrain.innerText = `${strain.toFixed(4)} %`;
                    txtStress.innerText = `${stress.toFixed(1)} MPa`;
                }
                // Particle systems (Fire/Ice)
                class EffectParticle {
                    constructor(x, y, type) {
                        this.x = x;
                        this.y = y;
                        this.type = type; // 'fire' or 'cold'
                        this.size = Math.random() * 4 + 2;
                        this.vx = (Math.random() - 0.5) * 1.0;
                        if (type === 'fire') {
                            this.vy = -(Math.random() * 1.5 + 0.5);
                            this.life = Math.random() * 30 + 20;
                            const colors = ['#f97316', '#ef4444', '#f59e0b', '#ef4444'];
                            this.color = colors[Math.floor(Math.random() * colors.length)];
                        }
                        if (type === 'cold') {
                            this.vy = Math.random() * 1.2 + 0.3;
                            this.life = Math.random() * 40 + 30;
                            const colors = ['#38bdf8', '#0284c7', '#ffffff', '#e0f2fe'];
                            this.color = colors[Math.floor(Math.random() * colors.length)];
                        }
                        this.maxLife = this.life;
                    }
                    update() {
                        this.x += this.vx;
                        this.y += this.vy;
                        this.life--;
                    }
                    draw(c) {
                        const alpha = this.life / this.maxLife;
                        c.save();
                        c.globalAlpha = alpha;
                        c.fillStyle = this.color;
                        c.beginPath();
                        if (this.type === 'cold') {
                            c.arc(this.x, this.y, this.size, 0, Math.PI * 2);
                            c.fill();
                        }
                        if (this.type === 'fire') {
                            c.beginPath();
                            c.moveTo(this.x, this.y - this.size);
                            c.lineTo(this.x + this.size, this.y + this.size);
                            c.lineTo(this.x - this.size, this.y + this.size);
                            c.closePath();
                            c.fill();
                        }
                        c.restore();
                    }
                }
                // Helper to draw dimensional arrowheads
                function drawArrowhead(c, x, y, angle) {
                    c.save();
                    c.translate(x, y);
                    c.rotate(angle);
                    c.beginPath();
                    c.moveTo(0, 0);
                    c.lineTo(-6, -3.5);
                    c.lineTo(-6, 3.5);
                    c.closePath();
                    c.fillStyle = c.strokeStyle;
                    c.fill();
                    c.restore();
                }
                // Render main loop
                function animate(timestamp) {
                    requestAnimationFrame(animate);
                    // Check and auto-fit canvas resolution to layout changes (prevents vertical stretch &#038; empty gaps)
                    const dpr = window.devicePixelRatio || 1;
                    const rect = canvas.getBoundingClientRect();
                    if (Math.abs(canvas.width - rect.width * dpr) > 1.5 || Math.abs(canvas.height - rect.height * dpr) > 1.5) {
                        resizeCanvas();
                    }
                    const width = canvas.width / dpr;
                    const height = canvas.height / dpr;
                    ctx.clearRect(0, 0, width, height);
                    // Blueprint Grid Lines
                    ctx.save();
                    ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                    ctx.lineWidth = 1;
                    for (let x = 0; x < width; x += 24) {
                        ctx.beginPath();
                        ctx.moveTo(x, 0);
                        ctx.lineTo(x, height);
                        ctx.stroke();
                    }
                    for (let y = 0; y < height; y += 24) {
                        ctx.beginPath();
                        ctx.moveTo(0, y);
                        ctx.lineTo(width, y);
                        ctx.stroke();
                    }
                    ctx.restore();
                    // Physics Expansion calculations
                    const deltaT = state.t2 - state.t1;
                    const deltaL = state.alpha * state.l0 * deltaT * 1000; // in mm
                    const finalL = state.l0 + (deltaL / 1000);
                    // Layout positions - dynamic auto-zoom based on canvas size
                    const startX = 60;
                    // Leave 240px for dial gauge and magnifier to prevent boundary overflow
                    const availWidth = (width - startX - 240); 
                    // Map l0 (1.0m to 10.0m) to baseWidth (35% to 65% of availWidth)
                    const maxBaseWidth = availWidth * 0.65;
                    const minBaseWidth = availWidth * 0.35;
                    const baseWidth = minBaseWidth + (state.l0 - 1.0) / 9.0 * (maxBaseWidth - minBaseWidth);
                    // Dynamic visual expansion scale: max 15% of availWidth at 1.5% thermal strain
                    const maxExpansionWidth = availWidth * 0.15;
                    const strainPct = state.alpha * deltaT;
                    const visualExpansion = (strainPct / 0.015) * maxExpansionWidth; 
                    const rodHeight = Math.max(25, Math.min(45, height * 0.105));
                    // Golden Vertical Centering: mathematically centers the rod, dimension lines, and particle overlays
                    const totalDrawHeight = rodHeight + 84;
                    const rodY = (height - totalDrawHeight) / 2 + 10;
                    const currentRodWidth = baseWidth + visualExpansion;
                    // Draw Left Anchor Bracket
                    ctx.save();
                    ctx.fillStyle = '#64748b';
                    ctx.fillRect(startX - 20, rodY - 15, 20, rodHeight + 30);
                    ctx.strokeStyle = '#334155';
                    ctx.lineWidth = 2;
                    ctx.strokeRect(startX - 20, rodY - 15, 20, rodHeight + 30);
                    // Hatch pattern on anchor
                    ctx.strokeStyle = 'rgba(255,255,255,0.2)';
                    ctx.lineWidth = 1.5;
                    for (let h = rodY - 10; h < rodY + rodHeight + 15; h += 8) {
                        ctx.beginPath();
                        ctx.moveTo(startX - 18, h);
                        ctx.lineTo(startX - 2, h - 8);
                        ctx.stroke();
                    }
                    ctx.restore();
                    // Particles spawn (Flame underneath, or ice falling from top)
                    if (state.t2 > state.t1) {
                        // Spawn burner flame
                        const heatIntensity = Math.min((state.t2 - state.t1) / 300, 1.5);
                        if (Math.random() < 0.25 * heatIntensity) {
                            const px = startX + Math.random() * currentRodWidth;
                            state.particles.push(new EffectParticle(px, rodY + rodHeight + 15, 'fire'));
                        }
                    }
                    if (state.t2 < state.t1) {
                        // Spawn cold snowflake drop
                        const coldIntensity = Math.min((state.t1 - state.t2) / 100, 1.5);
                        if (Math.random() < 0.25 * coldIntensity) {
                            const px = startX + Math.random() * currentRodWidth;
                            state.particles.push(new EffectParticle(px, rodY - 20, 'cold'));
                        }
                    }
                    // Update &#038; Draw Particles
                    state.particles = state.particles.filter(p => p.life > 0);
                    state.particles.forEach(p => {
                        p.update();
                        p.draw(ctx);
                    });
                    // Draw heating element or cooling block visualizers
                    if (state.t2 > state.t1) {
                        ctx.fillStyle = 'rgba(239, 68, 68, 0.05)';
                        ctx.fillRect(startX, rodY + rodHeight + 2, currentRodWidth, 12);
                    }
                    if (state.t2 < state.t1) {
                        ctx.fillStyle = 'rgba(56, 189, 248, 0.05)';
                        ctx.fillRect(startX, rodY - 14, currentRodWidth, 12);
                    }
                    // Draw the Metal Rod itself
                    ctx.save();
                    // 1. Draw base initial length segment
                    const metalGrad = ctx.createLinearGradient(startX, rodY, startX, rodY + rodHeight);
                    metalGrad.addColorStop(0, state.colorGradStart);
                    metalGrad.addColorStop(0.3, '#ffffff');
                    metalGrad.addColorStop(1, state.colorGradEnd);
                    ctx.fillStyle = metalGrad;
                    // Draw base width (L0 portion)
                    if (visualExpansion >= 0) {
                        ctx.fillRect(startX, rodY, baseWidth, rodHeight);
                        // 2. Draw expanded segment (with pinkish/red overlay + stripes for clarity)
                        ctx.fillStyle = 'rgba(219, 39, 119, 0.2)';
                        ctx.fillRect(startX + baseWidth, rodY, visualExpansion, rodHeight);
                        ctx.strokeStyle = 'rgba(219, 39, 119, 0.4)';
                        ctx.lineWidth = 1.5;
                        for (let bx = startX + baseWidth; bx < startX + baseWidth + visualExpansion; bx += 8) {
                            ctx.beginPath();
                            ctx.moveTo(bx, rodY);
                            ctx.lineTo(bx + 6, rodY + rodHeight);
                            ctx.stroke();
                        }
                    } else {
                        // Rod has contracted (visualExpansion is negative)
                        ctx.fillRect(startX, rodY, currentRodWidth, rodHeight);
                        // Show contracted missing region as dotted translucent blue box
                        ctx.strokeStyle = 'rgba(2, 132, 199, 0.4)';
                        ctx.lineWidth = 1.5;
                        ctx.setLineDash([3, 3]);
                        ctx.strokeRect(startX + currentRodWidth, rodY, -visualExpansion, rodHeight);
                        ctx.setLineDash([]);
                        ctx.fillStyle = 'rgba(2, 132, 199, 0.08)';
                        ctx.fillRect(startX + currentRodWidth, rodY, -visualExpansion, rodHeight);
                    }
                    // External border for the actual physical rod
                    ctx.strokeStyle = state.colorGradEnd;
                    ctx.lineWidth = 2.5;
                    ctx.strokeRect(startX, rodY, currentRodWidth, rodHeight);
                    ctx.restore();
                    // Microscopic View Zoom Callout Circle
                    const zoomCx = width - 85;
                    const zoomCy = 75;
                    const zoomR = 45;
                    ctx.save();
                    // Draw outer border ring of magnifier
                    ctx.beginPath();
                    ctx.arc(zoomCx, zoomCy, zoomR, 0, Math.PI * 2);
                    ctx.fillStyle = '#ffffff';
                    ctx.fill();
                    ctx.strokeStyle = '#94a3b8';
                    ctx.lineWidth = 3;
                    ctx.stroke();
                    // Microscope glass reflection / grid lines
                    ctx.beginPath();
                    ctx.arc(zoomCx, zoomCy, zoomR, 0, Math.PI * 2);
                    ctx.clip();
                    ctx.fillStyle = '#f8fafc';
                    ctx.fill();
                    // Microscopic grid background
                    ctx.strokeStyle = 'rgba(0,0,0,0.02)';
                    ctx.lineWidth = 1;
                    for (let gx = zoomCx - zoomR; gx < zoomCx + zoomR; gx += 12) {
                        ctx.beginPath();
                        ctx.moveTo(gx, zoomCy - zoomR);
                        ctx.lineTo(gx, zoomCy + zoomR);
                        ctx.stroke();
                    }
                    for (let gy = zoomCy - zoomR; gy < zoomCy + zoomR; gy += 12) {
                        ctx.beginPath();
                        ctx.moveTo(zoomCx - zoomR, gy);
                        ctx.lineTo(zoomCx + zoomR, gy);
                        ctx.stroke();
                    }
                    // Atom lattice visualization inside zoom circle
                    const temperatureInfluence = Math.max((state.t2 + 100) / 100, 0.1);
                    const latticeSpacing = 16 + (deltaT * 0.005);
                    const vibrationAmp = temperatureInfluence * 0.45;
                    ctx.fillStyle = state.colorGradEnd;
                    ctx.strokeStyle = 'rgba(0,0,0,0.12)';
                    ctx.lineWidth = 1;
                    for (let lx = -3; lx <= 3; lx++) {
                        for (let ly = -3; ly <= 3; ly++) {
                            const vx = Math.sin(timestamp * 0.015 + lx) * vibrationAmp;
                            const vy = Math.cos(timestamp * 0.012 + ly) * vibrationAmp;
                            const atomX = zoomCx + lx * latticeSpacing + vx;
                            const atomY = zoomCy + ly * latticeSpacing + vy;
                            // Draw bond connections to right and bottom neighbors
                            if (lx < 3) {
                                const nextAtomX = zoomCx + (lx + 1) * latticeSpacing + Math.sin(timestamp * 0.015 + lx + 1) * vibrationAmp;
                                const nextAtomY = zoomCy + ly * latticeSpacing + Math.cos(timestamp * 0.012 + ly) * vibrationAmp;
                                ctx.beginPath();
                                ctx.moveTo(atomX, atomY);
                                ctx.lineTo(nextAtomX, nextAtomY);
                                ctx.stroke();
                            }
                            if (ly < 3) {
                                const nextAtomX = zoomCx + lx * latticeSpacing + Math.sin(timestamp * 0.015 + lx) * vibrationAmp;
                                const nextAtomY = zoomCy + (ly + 1) * latticeSpacing + Math.cos(timestamp * 0.012 + ly + 1) * vibrationAmp;
                                ctx.beginPath();
                                ctx.moveTo(atomX, atomY);
                                ctx.lineTo(nextAtomX, nextAtomY);
                                ctx.stroke();
                            }
                            // Draw atom node
                            ctx.beginPath();
                            ctx.arc(atomX, atomY, 3.5, 0, Math.PI * 2);
                            ctx.fill();
                        }
                    }
                    // Lens reflection overlay
                    const glassGrad = ctx.createLinearGradient(zoomCx - zoomR, zoomCy - zoomR, zoomCx + zoomR, zoomCy + zoomR);
                    glassGrad.addColorStop(0, 'rgba(255,255,255,0.4)');
                    glassGrad.addColorStop(0.5, 'rgba(255,255,255,0.05)');
                    glassGrad.addColorStop(1, 'rgba(2,132,199,0.05)');
                    ctx.fillStyle = glassGrad;
                    ctx.beginPath();
                    ctx.arc(zoomCx, zoomCy, zoomR, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.restore();
                    // Magnifier link lines from rod end to callout
                    ctx.save();
                    ctx.strokeStyle = 'rgba(148, 163, 184, 0.4)';
                    ctx.lineWidth = 1;
                    ctx.setLineDash([4, 4]);
                    ctx.beginPath();
                    ctx.moveTo(startX + currentRodWidth, rodY);
                    ctx.lineTo(zoomCx - zoomR + 5, zoomCy - 20);
                    ctx.moveTo(startX + currentRodWidth, rodY + rodHeight);
                    ctx.lineTo(zoomCx - zoomR + 5, zoomCy + 20);
                    ctx.stroke();
                    ctx.restore();
                    // Microscope tag text
                    ctx.fillStyle = '#64748b';
                    ctx.font = 'bold 9px sans-serif';
                    ctx.textAlign = 'center';
                    ctx.fillText('미시 원자 격자 뷰', zoomCx, zoomCy + zoomR + 13);
                    // Micro Dial gauge representation below rod right end
                    const gaugeX = startX + currentRodWidth + 10;
                    const gaugeY = rodY + rodHeight / 2;
                    ctx.save();
                    // Draw contact plunger pin pressed against rod end
                    ctx.fillStyle = '#94a3b8';
                    ctx.fillRect(startX + currentRodWidth, gaugeY - 3, 22, 6);
                    ctx.strokeStyle = '#475569';
                    ctx.lineWidth = 1.5;
                    ctx.strokeRect(startX + currentRodWidth, gaugeY - 3, 22, 6);
                    // Dial body
                    const dialR = 24;
                    const dialCx = gaugeX + dialR + 10;
                    const dialCy = gaugeY;
                    ctx.beginPath();
                    ctx.arc(dialCx, dialCy, dialR, 0, Math.PI * 2);
                    ctx.fillStyle = '#ffffff';
                    ctx.fill();
                    ctx.strokeStyle = '#334155';
                    ctx.lineWidth = 2.5;
                    ctx.stroke();
                    // Dial grad ticks
                    ctx.strokeStyle = '#475569';
                    ctx.lineWidth = 1;
                    for (let ti = 0; ti < 12; ti++) {
                        const tickAng = (ti * Math.PI * 2) / 12;
                        ctx.beginPath();
                        ctx.moveTo(dialCx + dialR * Math.cos(tickAng), dialCy + dialR * Math.sin(tickAng));
                        ctx.lineTo(dialCx + (dialR - 4) * Math.cos(tickAng), dialCy + (dialR - 4) * Math.sin(tickAng));
                        ctx.stroke();
                    }
                    // Rotating indicator hand
                    // Rotates 360deg for every 10mm of expansion
                    const handAngle = (deltaL / 10.0) * Math.PI * 2;
                    ctx.strokeStyle = '#ef4444';
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(dialCx, dialCy);
                    ctx.lineTo(dialCx + (dialR - 6) * Math.cos(handAngle), dialCy + (dialR - 6) * Math.sin(handAngle));
                    ctx.stroke();
                    // Dial hub pin
                    ctx.fillStyle = '#0f172a';
                    ctx.beginPath();
                    ctx.arc(dialCx, dialCy, 3, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.restore();
                    ctx.fillStyle = '#64748b';
                    ctx.font = 'bold 9px sans-serif';
                    ctx.textAlign = 'center';
                    ctx.fillText('마이크로미터 변위계', dialCx, dialCy + dialR + 13);
                    // Visual Temperature Gradient Coloring bar (Repositioned to top-left corner)
                    ctx.save();
                    ctx.font = 'bold 11px Outfit';
                    ctx.fillStyle = '#64748b';
                    ctx.textAlign = 'left';
                    const tempBarX = 24;
                    const tempBarY = 40;
                    const tempBarW = 130;
                    const tempBarH = 6;
                    ctx.fillText(`현재 온도: ${state.t2}°C`, tempBarX, tempBarY - 10);
                    ctx.fillStyle = '#e2e8f0';
                    ctx.fillRect(tempBarX, tempBarY, tempBarW, tempBarH);
                    const barGrad = ctx.createLinearGradient(tempBarX, tempBarY, tempBarX + tempBarW, tempBarY);
                    barGrad.addColorStop(0, '#38bdf8');
                    barGrad.addColorStop(0.5, '#e2e8f0');
                    barGrad.addColorStop(1, '#ef4444');
                    ctx.fillStyle = barGrad;
                    ctx.fillRect(tempBarX, tempBarY, tempBarW, tempBarH);
                    // Draw marker pointer for current temperature T2
                    const normalizedT2 = (state.t2 - (-50)) / 550; // -50 to 500 scale
                    const markerX = tempBarX + Math.min(Math.max(normalizedT2, 0), 1.0) * tempBarW;
                    ctx.fillStyle = '#0f172a';
                    ctx.beginPath();
                    ctx.moveTo(markerX, tempBarY - 2);
                    ctx.lineTo(markerX - 4, tempBarY - 7);
                    ctx.lineTo(markerX + 4, tempBarY - 7);
                    ctx.closePath();
                    ctx.fill();
                    ctx.restore();
                    // Rod Dimensions scale line underneath (L0, Delta L, and Final L)
                    ctx.save();
                    // 1. Vertical guide lines (dashed)
                    ctx.strokeStyle = 'rgba(148, 163, 184, 0.35)';
                    ctx.lineWidth = 1;
                    ctx.setLineDash([3, 3]);
                    // Start guide line (startX)
                    ctx.beginPath();
                    ctx.moveTo(startX, rodY + rodHeight);
                    ctx.lineTo(startX, rodY + rodHeight + 63);
                    ctx.stroke();
                    // Initial length guide line (startX + baseWidth)
                    ctx.beginPath();
                    ctx.moveTo(startX + baseWidth, rodY);
                    ctx.lineTo(startX + baseWidth, rodY + rodHeight + 43);
                    ctx.stroke();
                    // Final end-point guide line (startX + currentRodWidth)
                    ctx.beginPath();
                    ctx.moveTo(startX + currentRodWidth, rodY);
                    ctx.lineTo(startX + currentRodWidth, rodY + rodHeight + 63);
                    ctx.stroke();
                    ctx.setLineDash([]);
                    // 2. Initial Length dimension line (L₀) - Level 1 (dimY1)
                    const dimY1 = rodY + rodHeight + 20;
                    ctx.strokeStyle = '#64748b';
                    ctx.lineWidth = 1;
                    ctx.beginPath();
                    ctx.moveTo(startX, dimY1);
                    ctx.lineTo(startX + baseWidth, dimY1);
                    ctx.stroke();
                    // Arrowheads
                    drawArrowhead(ctx, startX, dimY1, Math.PI);
                    drawArrowhead(ctx, startX + baseWidth, dimY1, 0);
                    // L₀ text label
                    ctx.fillStyle = '#475569';
                    ctx.font = '500 10px Inter';
                    ctx.textAlign = 'center';
                    ctx.fillText(`초기 L₀ = ${state.l0.toFixed(1)} m`, startX + baseWidth / 2, dimY1 - 4);
                    // 3. Changed/Increased Length dimension line (ΔL) - Level 2 (dimY2)
                    if (Math.abs(visualExpansion) > 3) {
                        const dimY2 = rodY + rodHeight + 40;
                        const isExpansion = deltaL >= 0;
                        ctx.strokeStyle = isExpansion ? '#db2777' : '#0284c7';
                        ctx.lineWidth = 1.2;
                        const expStartX = isExpansion ? startX + baseWidth : startX + currentRodWidth;
                        const expEndX = isExpansion ? startX + currentRodWidth : startX + baseWidth;
                        ctx.beginPath();
                        ctx.moveTo(expStartX, dimY2);
                        ctx.lineTo(expEndX, dimY2);
                        ctx.stroke();
                        drawArrowhead(ctx, expStartX, dimY2, Math.PI);
                        drawArrowhead(ctx, expEndX, dimY2, 0);
                        ctx.fillStyle = isExpansion ? '#db2777' : '#0284c7';
                        ctx.font = 'bold 10px Inter';
                        ctx.textAlign = 'center';
                        const sign = isExpansion ? '+' : '';
                        ctx.fillText(`ΔL = ${sign}${deltaL.toFixed(2)} mm`, expStartX + Math.abs(visualExpansion) / 2, dimY2 - 4);
                    }
                    // 4. Total Final Length dimension line (L) - Level 3 (dimY3)
                    const dimY3 = rodY + rodHeight + 60;
                    ctx.strokeStyle = '#334155';
                    ctx.lineWidth = 1.2;
                    ctx.beginPath();
                    ctx.moveTo(startX, dimY3);
                    ctx.lineTo(startX + currentRodWidth, dimY3);
                    ctx.stroke();
                    // vertical ticks
                    ctx.beginPath();
                    ctx.moveTo(startX, dimY3 - 4); ctx.lineTo(startX, dimY3 + 4);
                    ctx.moveTo(startX + currentRodWidth, dimY3 - 4); ctx.lineTo(startX + currentRodWidth, dimY3 + 4);
                    ctx.stroke();
                    // Final L text
                    ctx.fillStyle = '#0f172a';
                    ctx.font = 'bold 11px monospace';
                    ctx.textAlign = 'center';
                    ctx.fillText(`최종 L = ${finalL.toFixed(5)} m`, startX + currentRodWidth / 2, dimY3 + 14);
                    ctx.restore();
                }
                // Event Listeners mapping
                sliderL0.addEventListener('input', syncL0FromSlider);
                inputL0.addEventListener('change', syncL0FromInput);
                sliderT1.addEventListener('input', syncT1FromSlider);
                inputT1.addEventListener('change', syncT1FromInput);
                sliderT2.addEventListener('input', syncT2FromSlider);
                inputT2.addEventListener('change', syncT2FromInput);
                selectMaterial.addEventListener('change', handleMaterialChange);
                presetBtns.forEach(btn => {
                    btn.addEventListener('click', function() {
                        loadPreset(btn.dataset.preset);
                    });
                });
                // Initial run
                handleMaterialChange();
                requestAnimationFrame(animate);
                // Anti-copying / Context protection code
                document.addEventListener('contextmenu', function(e) {
                    e.preventDefault();
                    alert("이 시뮬레이터 프로그램의 지식재산권은 보호받고 있습니다.");
                    return false;
                }, { capture: true });
                document.addEventListener('selectstart', function(e) {
                    e.preventDefault();
                    return false;
                }, { capture: true });
                document.addEventListener('keydown', function(e) {
                    if (e.key === 'F12') {
                        e.preventDefault();
                        alert("이 시뮬레이터 프로그램의 지식재산권은 보호받고 있습니다.");
                        return false;
                    }
                    if (e.ctrlKey) {
                        if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                            e.preventDefault();
                            alert("이 시뮬레이터 프로그램의 지식재산권은 보호받고 있습니다.");
                            return false;
                        }
                    }
                }, { capture: true });
            }
            // Retry loader sequence
            let loadAttempts = 0;
            function tryLoad() {
                loadAttempts++;
                initSimulator();
                if (!window.__thermalexpansion_initialized) {
                    if (loadAttempts < 50) {
                        setTimeout(tryLoad, 100);
                    }
                }
            }
            if (document.readyState === 'complete' || document.readyState === 'interactive') {
                tryLoad();
            } else {
                document.addEventListener('DOMContentLoaded', tryLoad);
                window.addEventListener('load', tryLoad);
            }
        })();
</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>재질 선택: 강철, 구리, 알루미늄, 황동 등 분석할 금속을 선택하여 고유 선팽창계수(α)를 적용합니다.</strong></li>
<li style="margin-bottom: 6px;">초기 조건 설정: 봉의 초기 길이(L₀)와 초기 온도(T₁)를 슬라이더 또는 텍스트 입력으로 설정합니다.</li>
<li style="margin-bottom: 6px;">대상 온도 제어: 가열 또는 냉각할 최종 온도(T₂)를 조절하면 봉 아래에 가스 버너 불꽃이나 얼음 결정이 렌더링됩니다.</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;" /> 선팽창계수 공식 및 재료별 열팽창 특성 해설 보기</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. 고체 열팽창(Thermal Expansion)의 물리적 근원</h3>
<p>모든 고체 물질은 온도가 상승함에 따라 부피가 팽창하는 성질을 가집니다. 이는 미시적 관점에서 격자 진동(Lattice Vibration)으로 설명됩니다. 고체 내부의 원자들은 원자 간 결합력에 의해 평형 상태의 에너지를 유지하며 진동하고 있습니다. 에너지가 가해져 온도가 올라가면 원자들의 진동 진폭이 커지게 되는데, 원자 간 비선형 포텐셜 에너지 곡선의 비대칭성 때문에 진동 중심 사이의 평균 거리가 멀어지게 됩니다. 이것이 거시적으로 열팽창으로 관찰되는 물리적 원리입니다.</p><ul><li><strong>선팽창 (Linear Expansion):</strong> 막대나 봉과 같이 길이 방향 차원이 지배적인 물체에서 일어나는 팽창입니다.</li><li><strong>체적팽창 (Volume Expansion):</strong> 3차원 입체 형상 전체의 부피가 팽창하는 것으로, 등방성 고체의 경우 체적팽창계수는 선팽창계수의 약 3배(β ≈ 3α)입니다.</li></ul>
<h3>2. 열팽창 설계 방정식과 변위 유도</h3>
<p>고체의 선팽창 관계식은 온도 변화폭이 아주 크지 않은 경우 다음과 같이 선형 근사로 고도로 정밀하게 예측할 수 있습니다.</p><p><strong>① 선팽창 기본 공식:</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">ΔL = α × L₀ × ΔT &nbsp;[mm]</p><p>여기서 <code>α</code>는 선팽창계수(Coefficient of Linear Expansion, K⁻¹ 또는 °C⁻¹), <code>L₀</code>는 초기 온도에서의 길이(m), <code>ΔT</code>는 온도 변화량(T₂ - T₁, °C)입니다.</p><p><strong>② 최종 길이 계산:</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">L = L₀ + ΔL = L₀ × (1 + α × ΔT) &nbsp;[m]</p><p><strong>③ 온도 응력 (Thermal Stress):</strong> 만약 양단이 강체 벽으로 구속되어 팽창이 불가능한 상태에서 가열된다면, 팽창하려는 변위만큼 압축 변형률(Strain)이 발생하여 매우 강력한 내력이 발생합니다. 양단 구속 시 발생하는 열응력 공식은 다음과 같습니다:</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">σ_t = E × α × ΔT &nbsp;[MPa]</p><p>여기서 <code>E</code>는 금속의 세로탄성계수(Young's Modulus)입니다. 기계 부품 설계 시 이러한 열응력으로 인한 파손을 방지하기 위해 팽창 여유 갭(Thermal Gap)이나 신축 이음(Expansion Joint) 설치가 필수적입니다.</p>
<h3>3. 주요 금속 재료별 열팽창 특성 및 설계 가이드</h3>
<p>다양한 공업용 금속들은 결정 구조와 결합력의 차이로 인해 상이한 선팽창계수를 가집니다. 설계 시 이종 금속 접합 구조물이 온도 변화를 겪을 때 휠 가능성(바이메탈 효과)을 신중히 고려해야 합니다.</p><ul><li><strong>알루미늄 (Aluminum):</strong> 약 23.0 × 10⁻⁶ /°C 로 열팽창이 매우 크며, 경량화 설계 시 갭 설계가 중요합니다.</li><li><strong>황동 (Brass):</strong> 약 19.0 × 10⁻⁶ /°C 로 동합금 특유의 높은 팽창률을 가집니다.</li><li><strong>구리 (Copper):</strong> 약 17.0 × 10⁻⁶ /°C 로 전선 설계 및 배관 매설 시 길이 방향 수축팽창을 고려해야 합니다.</li><li><strong>탄소강 (Steel):</strong> 약 11.5 × 10⁻⁶ /°C 로 상대적으로 낮고 안정적이며 콘크리트의 팽창계수와 유사하여 철근콘크리트 구조가 성립되는 기반이 됩니다.</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/thermal-expansion-calculator-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>베르누이 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/bernoulli-venturi-effect-calculator-simulator/</link>
					<comments>https://myengnote.com/bernoulli-venturi-effect-calculator-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Tue, 09 Jun 2026 03:21:10 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[공학설계]]></category>
		<category><![CDATA[동압정압]]></category>
		<category><![CDATA[베르누이]]></category>
		<category><![CDATA[벤투리관]]></category>
		<category><![CDATA[유체역학]]></category>
		<category><![CDATA[진공발생]]></category>
		<guid isPermaLink="false">https://myengnote.com/bernoulli-venturi-effect-calculator-simulator/</guid>

					<description><![CDATA[벤투리 관(Venturi Tube)의 입구와 목(Throat) 직경, 유속, 정압을 입력하여 베르누이 정리(Bernoulli's Theorem)에 의한 속도 상승 및 정압 강하(Venturi Effect)를 실시간 해석하고 압력계를 시각화하는 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; 시뮬레이터</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 bernoulli-venturi-wrapper */
        /* Modern Reset and Design Tokens */
        .bernoulli-venturi-wrapper *, .bernoulli-venturi-wrapper *::before, .bernoulli-venturi-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .bernoulli-venturi-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);
        }
        .bernoulli-venturi-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.15;
        }
        .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.12; }
            100% { transform: scale(1.2) translate(50px, 50px); opacity: 0.22; }
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-purple));
            -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: 1px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-text-muted));
            -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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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);
        }
        .number-input-wrapper:focus-within {
            border-color: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
                .custom-number-input {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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;
        }
        .custom-number-input::-webkit-outer-spin-button,
        .custom-number-input::-webkit-inner-spin-button {
            -webkit-appearance: none;
            margin: 0;
        }
                .unit-badge {
            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;
            flex-shrink: 0;
        }
        .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;
        }
        #slider-d1::-webkit-slider-thumb {
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
        }
        #slider-d1::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-d2::-webkit-slider-thumb {
            background: var(--color-magenta);
            box-shadow: 0 0 10px var(--color-magenta);
        }
        #slider-d2::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-v1::-webkit-slider-thumb {
            background: var(--color-purple);
            box-shadow: 0 0 10px var(--color-purple);
        }
        #slider-v1::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-p1::-webkit-slider-thumb {
            background: var(--color-success);
            box-shadow: 0 0 10px var(--color-success);
        }
        #slider-p1::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .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: 10px 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.04);
            transform: translateX(4px);
        }
        .preset-btn.active {
            background: linear-gradient(90deg, var(--color-cyan-glow), var(--color-purple-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);
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 10;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .press-readout-box {
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
            transition: all 0.3s ease;
        }
        .press-readout-box.normal {
            background: rgba(2, 132, 199, 0.08);
            border: 1px solid rgba(2, 132, 199, 0.25);
            box-shadow: 0 0 15px rgba(2, 132, 199, 0.05);
        }
        .press-readout-box.vacuum {
            background: rgba(219, 39, 119, 0.08);
            border: 1px solid rgba(219, 39, 119, 0.25);
            box-shadow: 0 0 15px rgba(219, 39, 119, 0.1);
            animation: glow-pulse-vac 2s infinite alternate;
        }
        @keyframes glow-pulse-vac {
            0% { border-color: rgba(219, 39, 119, 0.25); box-shadow: 0 0 10px rgba(219, 39, 119, 0.05); }
            100% { border-color: rgba(219, 39, 119, 0.6); box-shadow: 0 0 20px rgba(219, 39, 119, 0.2); }
        }
        .press-title {
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 1px;
            text-transform: uppercase;
        }
        .press-readout-box.normal .press-title { color: var(--color-cyan); }
        .press-readout-box.vacuum .press-title { color: var(--color-magenta); }
        .press-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 26px;
            color: var(--color-text-main);
        }
        .press-type {
            font-size: 11px;
            font-weight: 700;
            padding: 4px 12px;
            border-radius: 20px;
            color: #fff;
        }
        .press-readout-box.normal .press-type { background: var(--color-cyan); }
        .press-readout-box.vacuum .press-type { background: var(--color-magenta); }
        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .result-card {
            background: rgba(255, 255, 255, 0.85);
            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: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.03);
        }
        .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);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 8px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        /* Container Query */
        @container bernoulli-container (max-width: 1024px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel {
                grid-column: 1;
            }
            .simulation-panel {
                grid-column: 1;
                order: -1;
            }
            .results-panel {
                grid-column: 1;
            }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.bernoulli-venturi-wrapper .app-main-grid,
.bernoulli-venturi-wrapper .main-grid,
.bernoulli-venturi-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.bernoulli-venturi-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.bernoulli-venturi-wrapper .simulation-panel,
.bernoulli-venturi-wrapper .canvas-panel,
.bernoulli-venturi-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.bernoulli-venturi-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.bernoulli-venturi-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.bernoulli-venturi-wrapper .simulation-results-section .ratio-readout-box,
.bernoulli-venturi-wrapper .simulation-results-section .re-readout-box,
.bernoulli-venturi-wrapper .simulation-results-section .status-readout-box,
.bernoulli-venturi-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.bernoulli-venturi-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.bernoulli-venturi-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.bernoulli-venturi-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .bernoulli-venturi-wrapper .app-main-grid,
    .bernoulli-venturi-wrapper .main-grid,
    .bernoulli-venturi-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .bernoulli-venturi-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .bernoulli-venturi-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .bernoulli-venturi-wrapper .simulation-panel,
    .bernoulli-venturi-wrapper .canvas-panel,
    .bernoulli-venturi-wrapper .sim-panel,
    .bernoulli-venturi-wrapper .canvas-section {
        order: -1 !important;
    }
    .bernoulli-venturi-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .bernoulli-venturi-wrapper .simulation-results-section .ratio-readout-box,
    .bernoulli-venturi-wrapper .simulation-results-section .re-readout-box,
    .bernoulli-venturi-wrapper .simulation-results-section .status-readout-box,
    .bernoulli-venturi-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .bernoulli-venturi-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .bernoulli-venturi-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.bernoulli-venturi-wrapper .app-container,
.bernoulli-venturi-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="bernoulli-venturi-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 class="app-header">
            <div class="logo-area">
                <div class="logo-icon"><i class="fa-solid fa-arrows-split-up-and-left"></i></div>
                <div>
                    <h1>BERNOULLI VENTURI</h1>
                    <div class="subtitle">베르누이 정리 &#038; 벤투리 유동 수치 계산기</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <span class="badge-text">BERNOULLI LAW</span>
            </div>
        </header>
        <div class="app-main-grid">
            <!-- Controls -->
            <div class="panel control-panel">
                <div class="panel-header text-cyan">
                    <i class="fa-solid fa-sliders"></i>
                    <h2>유체 벤투리 물성 입력</h2>
                </div>
                <!-- D1 Inlet Diameter -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-d1"><i class="fa-solid fa-circle text-cyan"></i> 입구 지름 (D₁)</label>
                        <span class="helper-text">범위: 50 ~ 250 mm</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d1" class="custom-number-input" min="50" max="250" value="150">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-d1" class="custom-slider" min="50" max="250" value="150">
                </div>
                <!-- D2 Throat Diameter -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-d2"><i class="fa-solid fa-circle text-magenta"></i> 수축부 목 지름 (D₂)</label>
                        <span class="helper-text">범위: 20 ~ 140 mm</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d2" class="custom-number-input" min="20" max="140" value="60">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-d2" class="custom-slider" min="20" max="140" value="60">
                </div>
                <!-- v1 Inlet Velocity -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-v1"><i class="fa-solid fa-gauge-high text-purple"></i> 입구 흐름 속도 (v₁)</label>
                        <span class="helper-text">범위: 0.1 ~ 5.0 m/s</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-v1" class="custom-number-input" min="0.1" max="5.0" step="0.1" value="1.5">
                        <span class="unit-badge">m/s</span>
                    </div>
                    <input type="range" id="slider-v1" class="custom-slider" min="1" max="50" value="15">
                </div>
                <!-- P1 Inlet Pressure -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-p1"><i class="fa-solid fa-compress text-success"></i> 입구 정적 압력 (P₁)</label>
                        <span class="helper-text">범위: 100 ~ 500 kPa</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-p1" class="custom-number-input" min="100" max="500" value="250">
                        <span class="unit-badge">kPa</span>
                    </div>
                    <input type="range" id="slider-p1" class="custom-slider" min="100" max="500" value="250">
                </div>
                <!-- Presets -->
                <div class="presets-section">
                    <h3><i class="fa-solid fa-vial"></i> 벤투리 설계 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn" data-preset="standard">
                            <div class="preset-icon"><i class="fa-solid fa-gauge-simple"></i></div>
                            <div class="preset-details"><span class="preset-name">표준 감압 설계</span><span class="preset-spec">150-60A, 1.5m/s</span></div>
                        </button>
                        <button class="preset-btn" data-preset="vacuumJet">
                            <div class="preset-icon"><i class="fa-solid fa-wind"></i></div>
                            <div class="preset-details"><span class="preset-name">이젝터 진공 흡입</span><span class="preset-spec">200-50A, 2.5m/s</span></div>
                        </button>
                        <button class="preset-btn" data-preset="lowDiff">
                            <div class="preset-icon"><i class="fa-solid fa-equals"></i></div>
                            <div class="preset-details"><span class="preset-name">미소 수축 유량계</span><span class="preset-spec">120-100A, 1.0m/s</span></div>
                        </button>
                    </div>
                </div>
            </div>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <div class="panel simulation-panel">
                                <div class="panel-header text-purple">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-display"></i>
                                        <h2>실시간 벤투리 액주계 압력 강하 시각화</h2>
                                    </div>
                                    <div class="canvas-scale-indicator" id="txt-canvas-scale">직경 수축비: 2.50배</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas" width="640" height="400"></canvas>
                                </div>
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <span class="label">차압 (&Delta;P)</span>
                                        <span class="value" id="mini-delta-p">120 kPa</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">입구 동압 (Pd1)</span>
                                        <span class="value" id="mini-pd1">1.13 kPa</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">목부 동압 (Pd2)</span>
                                        <span class="value" id="mini-pd2">44.1 kPa</span>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="press-readout-box normal" id="press-box">
                                    <span class="press-title">THROAT STATIC PRESSURE (P₂)</span>
                                    <span class="press-value" id="txt-p2">156.2 kPa</span>
                                    <span class="press-type" id="txt-press-status">정압 안심 상태</span>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon text-cyan"><i class="fa-solid fa-bolt-lightning"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">수축 목부 유속 (v₂)</span>
                                            <span class="card-value" id="txt-v2">9.38 m/s</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon text-purple"><i class="fa-solid fa-ratio"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">유량 보존 체적 유량 (Q)</span>
                                            <span class="card-value" id="txt-q">95.4 m³/h</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                        <h4>베르누이 방정식 적용 공식</h4>
                                        <div class="formula-equation">P₂ = P₁ &#8211; &frac12;&rho;(v₂² &#8211; v₁²)</div>
                                        <p>
                                            * 유체 밀도는 1000 kg/m³(물 기준) 상수로 산정하여 수축 가속 유속에 의한 저압 발생치를 도출합니다.
                                        </p>
                                    </div>
                            </div>
                </div>
            </div>
        </div>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    </div>
</div>
<script>
        (function() {
            // State
            const state = {
                d1: 150,     // mm
                d2: 60,      // mm
                v1: 1.5,     // m/s
                p1: 250,     // kPa
                rho: 1000,   // kg/m³
                particles: [],
                lastTime: 0
            };
            // DOM Elements
            const inputD1 = document.getElementById('input-d1');
            const sliderD1 = document.getElementById('slider-d1');
            const inputD2 = document.getElementById('input-d2');
            const sliderD2 = document.getElementById('slider-d2');
            const inputV1 = document.getElementById('input-v1');
            const sliderV1 = document.getElementById('slider-v1');
            const inputP1 = document.getElementById('input-p1');
            const sliderP1 = document.getElementById('slider-p1');
            const txtP2 = document.getElementById('txt-p2');
            const txtPressStatus = document.getElementById('txt-press-status');
            const pressBox = document.getElementById('press-box');
            const txtV2 = document.getElementById('txt-v2');
            const txtQ = document.getElementById('txt-q');
            const miniDeltaP = document.getElementById('mini-delta-p');
            const miniPd1 = document.getElementById('mini-pd1');
            const miniPd2 = document.getElementById('mini-pd2');
            const presetButtons = document.querySelectorAll('.preset-btn');
            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();
            setTimeout(initCanvas, 300);
            // Initialize Particles
            const maxParticles = 90;
            for (let i = 0; i < maxParticles; i++) {
                state.particles.push({
                    x: Math.random() * 640,
                    yFraction: 0.1 + Math.random() * 0.8
                });
            }
            // Clamping rules to prevent Throat Diameter >= Inlet Diameter
            function validateThroatDiameter() {
                const maxAllowedD2 = state.d1 - 10;
                if (state.d2 > maxAllowedD2) {
                    state.d2 = maxAllowedD2;
                    inputD2.value = state.d2;
                    sliderD2.value = state.d2;
                }
                sliderD2.max = maxAllowedD2;
            }
            // Sync Listeners
            function syncD1FromSlider() {
                state.d1 = parseInt(sliderD1.value);
                inputD1.value = state.d1;
                validateThroatDiameter();
                clearPresets();
                updateCalculations();
            }
            function syncD1FromInput() {
                let val = parseInt(inputD1.value);
                if (isNaN(val)) { val = 150; }
                if (val < 50) { val = 50; }
                if (val > 250) { val = 250; }
                state.d1 = val;
                inputD1.value = val;
                sliderD1.value = val;
                validateThroatDiameter();
                clearPresets();
                updateCalculations();
            }
            function syncD2FromSlider() {
                state.d2 = parseInt(sliderD2.value);
                inputD2.value = state.d2;
                validateThroatDiameter();
                clearPresets();
                updateCalculations();
            }
            function syncD2FromInput() {
                let val = parseInt(inputD2.value);
                if (isNaN(val)) { val = 60; }
                if (val < 20) { val = 20; }
                if (val > state.d1 - 10) { val = state.d1 - 10; }
                state.d2 = val;
                inputD2.value = val;
                sliderD2.value = val;
                validateThroatDiameter();
                clearPresets();
                updateCalculations();
            }
            function syncV1FromSlider() {
                state.v1 = parseFloat(sliderV1.value) / 10;
                inputV1.value = state.v1.toFixed(1);
                clearPresets();
                updateCalculations();
            }
            function syncV1FromInput() {
                let val = parseFloat(inputV1.value);
                if (isNaN(val)) { val = 1.5; }
                if (val < 0.1) { val = 0.1; }
                if (val > 5.0) { val = 5.0; }
                state.v1 = val;
                inputV1.value = val.toFixed(1);
                sliderV1.value = Math.round(val * 10);
                clearPresets();
                updateCalculations();
            }
            function syncP1FromSlider() {
                state.p1 = parseInt(sliderP1.value);
                inputP1.value = state.p1;
                clearPresets();
                updateCalculations();
            }
            function syncP1FromInput() {
                let val = parseInt(inputP1.value);
                if (isNaN(val)) { val = 250; }
                if (val < 100) { val = 100; }
                if (val > 500) { val = 500; }
                state.p1 = val;
                inputP1.value = val;
                sliderP1.value = val;
                clearPresets();
                updateCalculations();
            }
            sliderD1.addEventListener('input', syncD1FromSlider);
            inputD1.addEventListener('change', syncD1FromInput);
            sliderD2.addEventListener('input', syncD2FromSlider);
            inputD2.addEventListener('change', syncD2FromInput);
            sliderV1.addEventListener('input', syncV1FromSlider);
            inputV1.addEventListener('change', syncV1FromInput);
            sliderP1.addEventListener('input', syncP1FromSlider);
            inputP1.addEventListener('change', syncP1FromInput);
            // Presets
            function loadPreset(presetKey) {
                clearPresets();
                presetButtons.forEach(function(btn) {
                    if (btn.dataset.preset === presetKey) {
                        btn.classList.add('active');
                    }
                });
                if (presetKey === 'standard') {
                    state.d1 = 150;
                    state.d2 = 60;
                    state.v1 = 1.5;
                    state.p1 = 250;
                } else if (presetKey === 'vacuumJet') {
                    state.d1 = 200;
                    state.d2 = 45;
                    state.v1 = 2.5;
                    state.p1 = 150;
                } else if (presetKey === 'lowDiff') {
                    state.d1 = 120;
                    state.d2 = 100;
                    state.v1 = 1.0;
                    state.p1 = 300;
                }
                inputD1.value = state.d1;
                sliderD1.value = state.d1;
                inputD2.value = state.d2;
                sliderD2.value = state.d2;
                inputV1.value = state.v1.toFixed(1);
                sliderV1.value = Math.round(state.v1 * 10);
                inputP1.value = state.p1;
                sliderP1.value = state.p1;
                validateThroatDiameter();
                updateCalculations();
            }
            function clearPresets() {
                presetButtons.forEach(function(btn) {
                    btn.classList.remove('active');
                });
            }
            presetButtons.forEach(function(btn) {
                btn.addEventListener('click', function() {
                    loadPreset(btn.dataset.preset);
                });
            });
            // Physics Calcs
            function updateCalculations() {
                const ratio = state.d1 / state.d2;
                const areaRatio = Math.pow(ratio, 2);
                // Throat velocity v2
                const v2 = state.v1 * areaRatio;
                txtV2.innerText = v2.toFixed(2) + ' m/s';
                // Flow rate Q = A1 * v1 * 3600 (m³/h)
                const d1M = state.d1 / 1000;
                const area1 = (Math.PI * Math.pow(d1M, 2)) / 4;
                const qM3h = area1 * state.v1 * 3600;
                txtQ.innerText = qM3h.toFixed(1) + ' m³/h';
                // Pressures
                // Pd1 = 0.5 * rho * v1^2 / 1000 in kPa
                const pd1 = (0.5 * state.rho * Math.pow(state.v1, 2)) / 1000;
                // Pd2 = 0.5 * rho * v2^2 / 1000 in kPa
                const pd2 = (0.5 * state.rho * Math.pow(v2, 2)) / 1000;
                miniPd1.innerText = pd1.toFixed(2) + ' kPa';
                miniPd2.innerText = pd2.toFixed(1) + ' kPa';
                // P2 = P1 - (Pd2 - Pd1)
                const p2 = state.p1 - (pd2 - pd1);
                txtP2.innerText = p2.toFixed(1) + ' kPa';
                const deltaPVal = state.p1 - p2;
                miniDeltaP.innerText = deltaPVal.toFixed(1) + ' kPa';
                // Eval warning for vacuums/cavitations
                pressBox.className = 'press-readout-box';
                if (p2 < 0) {
                    pressBox.classList.add('vacuum');
                    txtPressStatus.innerText = '진공 흡착 유도 상태 (Vacuum Jet)';
                } else if (p2 < 15.0) {
                    // Water boiling cap is low at room temp (around 3 kPa absolute)
                    pressBox.classList.add('vacuum');
                    txtPressStatus.innerText = '공동현상(Cavitation) 극도 주의!';
                } else {
                    pressBox.classList.add('normal');
                    txtPressStatus.innerText = '안정적 압력 상태';
                }
                const txtCanvasScale = document.getElementById('txt-canvas-scale');
                if (txtCanvasScale) {
                    txtCanvasScale.innerText = `직경 비율: ${(state.d1 / state.d2).toFixed(2)}배 수축`;
                }
            }
            // Draw Venturi
            function draw(currentTime) {
                requestAnimationFrame(draw);
                let dt = (currentTime - state.lastTime) / 1000;
                if (isNaN(dt)) { dt = 0.016; }
                if (dt < 0) { dt = 0.016; }
                if (dt > 0.1) { dt = 0.016; }
                state.lastTime = currentTime;
                const dpr = getDPR();
                const width = canvas.width / dpr;
                const height = canvas.height / dpr;
                const scale = Math.min(width / 640, height / 400);
                ctx.clearRect(0, 0, width, height);
                // Grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.04)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30 * scale) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30 * scale) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Venturi pipe coordinates mapping
                // Pipe sections along x: 
                // Left (straight, Inlet): x=0 to x=180
                // Constriction: x=180 to x=320 (Throat center at x=320)
                // Expansion: x=320 to x=460
                // Right (straight): x=460 to x=640
                // Mapped pixel heights
                const maxPixH = 150 * scale;
                const minPixH = 30 * scale;
                // Inlet height (D1) scaled
                const h1 = 60 * scale + ((state.d1 - 50) / 200) * (maxPixH - 60 * scale);
                // Throat height (D2) scaled
                const h2 = minPixH + ((state.d2 - 20) / 120) * (h1 - 20 * scale - minPixH);
                const cy = height / 2 + 30 * scale; // lower center to make room for glass columns on top
                // Helper to get pipe height at any given x
                function getPipeHalfHeightAt(x) {
                    const sx = x / scale;
                    if (sx < 180) {
                        return h1 / 2;
                    }
                    if (sx < 320) {
                        // linear interpolation or cosine easing for smooth transition
                        const ratio = (sx - 180) / 140;
                        const smoothRatio = (1 - Math.cos(ratio * Math.PI)) / 2;
                        return (h1 / 2) * (1 - smoothRatio) + (h2 / 2) * smoothRatio;
                    }
                    if (sx < 460) {
                        const ratio = (sx - 320) / 140;
                        const smoothRatio = (1 - Math.cos(ratio * Math.PI)) / 2;
                        return (h2 / 2) * (1 - smoothRatio) + (h1 / 2) * smoothRatio;
                    }
                    return h1 / 2;
                }
                // 1. Draw Venturi Glass contours
                ctx.save();
                ctx.fillStyle = 'rgba(2, 132, 199, 0.12)';
                ctx.beginPath();
                ctx.moveTo(0, cy - getPipeHalfHeightAt(0));
                for (let x = 0; x <= width; x += 5 * scale) {
                    ctx.lineTo(x, cy - getPipeHalfHeightAt(x));
                }
                ctx.lineTo(width, cy + getPipeHalfHeightAt(width));
                for (let x = width; x >= 0; x -= 5 * scale) {
                    ctx.lineTo(x, cy + getPipeHalfHeightAt(x));
                }
                ctx.closePath();
                ctx.fill();
                // Draw Pipe boundary lines
                ctx.strokeStyle = '#475569';
                ctx.lineWidth = 3.5 * scale;
                ctx.beginPath();
                ctx.moveTo(0, cy - getPipeHalfHeightAt(0));
                for (let x = 0; x <= width; x += 5 * scale) {
                    ctx.lineTo(x, cy - getPipeHalfHeightAt(x));
                }
                ctx.stroke();
                ctx.beginPath();
                ctx.moveTo(0, cy + getPipeHalfHeightAt(0));
                for (let x = 0; x <= width; x += 5 * scale) {
                    ctx.lineTo(x, cy + getPipeHalfHeightAt(x));
                }
                ctx.stroke();
                ctx.restore();
                // 2. Draw vertical manometer glass tubes
                // Tube 1 (Inlet, center at x = 90)
                // Tube 2 (Throat, center at x = 320)
                const tube1X = 90 * scale;
                const tube2X = 320 * scale;
                const tubeWidth = 14 * scale;
                // Mapped static water height: P1 [100 ~ 500] -> [40 ~ 160] pixels high
                const hFluid1 = (40 + ((state.p1 - 100) / 400) * 120) * scale;
                // Calculate P2
                const ratio = state.d1 / state.d2;
                const v2 = state.v1 * Math.pow(ratio, 2);
                const pd1 = (0.5 * state.rho * Math.pow(state.v1, 2)) / 1000;
                const pd2 = (0.5 * state.rho * Math.pow(v2, 2)) / 1000;
                const p2 = state.p1 - (pd2 - pd1);
                // Water column 2 height scaled
                let hFluid2 = (40 + ((p2 - 100) / 400) * 120) * scale;
                if (p2 < 0) { hFluid2 = 5 * scale; } // Vacuum drops to minimum visible line
                // Draw Glass columns
                ctx.save();
                ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
                ctx.strokeStyle = '#64748b';
                ctx.lineWidth = 2 * scale;
                // Column 1
                const colTopY1 = cy - getPipeHalfHeightAt(tube1X);
                ctx.fillRect(tube1X - tubeWidth / 2, colTopY1 - 180 * scale, tubeWidth, 180 * scale);
                ctx.beginPath();
                ctx.moveTo(tube1X - tubeWidth / 2, colTopY1 - 180 * scale);
                ctx.lineTo(tube1X - tubeWidth / 2, colTopY1);
                ctx.moveTo(tube1X + tubeWidth / 2, colTopY1 - 180 * scale);
                ctx.lineTo(tube1X + tubeWidth / 2, colTopY1);
                ctx.stroke();
                // Liquid in Column 1
                ctx.fillStyle = 'rgba(2, 132, 199, 0.6)';
                ctx.fillRect(tube1X - tubeWidth / 2 + 1, colTopY1 - hFluid1, tubeWidth - 2, hFluid1);
                // Column 2
                const colTopY2 = cy - getPipeHalfHeightAt(tube2X);
                ctx.fillStyle = 'rgba(255, 255, 255, 0.4)';
                ctx.fillRect(tube2X - tubeWidth / 2, colTopY2 - 180 * scale, tubeWidth, 180 * scale);
                ctx.beginPath();
                ctx.moveTo(tube2X - tubeWidth / 2, colTopY2 - 180 * scale);
                ctx.lineTo(tube2X - tubeWidth / 2, colTopY2);
                ctx.moveTo(tube2X + tubeWidth / 2, colTopY2 - 180 * scale);
                ctx.lineTo(tube2X + tubeWidth / 2, colTopY2);
                ctx.stroke();
                // Liquid in Column 2
                if (p2 < 0) {
                    // Show a blinking negative pressure indicator or vacuum bubbles
                    ctx.fillStyle = 'rgba(219, 39, 119, 0.7)';
                    ctx.fillRect(tube2X - tubeWidth / 2 + 1, colTopY2 - hFluid2, tubeWidth - 2, hFluid2);
                } else {
                    ctx.fillStyle = 'rgba(2, 132, 199, 0.6)';
                    ctx.fillRect(tube2X - tubeWidth / 2 + 1, colTopY2 - hFluid2, tubeWidth - 2, hFluid2);
                }
                ctx.restore();
                // 3. Animate Particles flowing through Venturi
                ctx.save();
                ctx.fillStyle = '#0284c7';
                state.particles.forEach(function(p) {
                    // Particle local velocity depends on coordinates
                    const halfH = getPipeHalfHeightAt(p.x);
                    const pipeMaxHalfH = h1 / 2;
                    // Continuity speed scaling
                    const areaScale = pipeMaxHalfH / halfH;
                    const speed = state.v1 * 120 * areaScale * scale;
                    p.x += speed * dt;
                    // Bound wrapping
                    if (p.x > width + 10 * scale) {
                        p.x = -10 * scale;
                        p.yFraction = 0.1 + Math.random() * 0.8;
                    }
                    // Compute vertical pixel position
                    const yPos = cy + (p.yFraction - 0.5) * 2 * halfH;
                    // Color shifts based on velocity
                    if (areaScale > 1.8) {
                        ctx.fillStyle = '#db2777';
                    } else if (areaScale > 1.2) {
                        ctx.fillStyle = '#7c3aed';
                    } else {
                        ctx.fillStyle = '#0284c7';
                    }
                    ctx.beginPath();
                    ctx.arc(p.x, yPos, 2.5 * scale, 0, Math.PI * 2);
                    ctx.fill();
                });
                ctx.restore();
            }
            // Right click/Copy block with nested if
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            loadPreset('standard');
            requestAnimationFrame(draw);
        })();
</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>배관 형상 입구(D1) 및 수축부 목(D2) 직경 설정: 슬라이더를 당겨 입구 구경과 가열 통로인 수축부(Throat) 구경을 지정합니다. (목 직경은 항상 입구 직경보다 작아야 합니다.)</strong></li>
<li style="margin-bottom: 6px;">입구 유속 및 정적 압력(P1) 설정: 유체가 벤투리 유입구로 들어올 때의 평균 유속(m/s)과 시작 정압(kPa)을 조절합니다.</li>
<li style="margin-bottom: 6px;">실시간 벤투리 감압 관찰: 수축부로 빨려 들어가는 파티클이 급격히 가속되고, 수축 지점에서 압력 게이지 액주계(Manometer) 높이가 크게 줄어드는 &#8216;벤투리 효과&#8217;를 목격합니다.</li>
<li style="margin-bottom: 6px;">정압/동압 분석 보고서 활용: 수축 단면에서의 최종 압력(P2)이 음압(진공) 상태에 진입하여 인젝터 흡입 구동력을 발생시킬 수 있는지 실시간 모듈에서 확인합니다.</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;" /> 베르누이 방정식(Bernoulli&#8217;s Equation)과 벤투리 효과(Venturi Effect) 수학적 이론 유도</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. 베르누이 정리(Bernoulli&#8217;s Theorem)의 기본 원리 및 물리적 정의</h3>
<p>유체역학의 가장 기본이 되는 **베르누이 방정식(Bernoulli&#8217;s Equation)**은 흐르는 유체에 대한 **에너지 보존 법칙**의 다른 표현입니다. 마찰이 없고 압축되지 않는 완전유체(Ideal Fluid)가 일정한 유선을 따라 흐를 때, 흐름 안의 어느 점에서도 단위 체적당 유체가 가진 **정압(Static Pressure)**, **동압(Dynamic Pressure)**, **위치 수두 에너지(Potential Pressure)**의 합은 항상 일정하게 유지됩니다.</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">P + &frac12; &times; &rho; &times; v&sup2; + &rho; &times; g &times; z = Constant</p><p>여기서 <code>P</code>는 유체의 정압(Pa), <code>&rho;</code>는 유체 밀도(kg/m³), <code>v</code>는 흐름 속도(m/s), <code>g</code>는 중력가속도, <code>z</code>는 기준면으로부터의 높이(m)입니다. 수평으로 놓인 배관(<code>z1 = z2</code>)에서는 위치 에너지가 상쇄되므로, 유속이 빨라지면 반드시 정압이 하락하며 반대로 유속이 느려지면 정압이 상승하게 됩니다.</p>
<h3>2. 연속방정식과 연계한 벤투리 수축부 속도 및 압력 거동 계산</h3>
<p>유체가 단면적이 좁아지는 벤투리관 수축부를 지나갈 때, 통과 질량이 보존되어야 하므로 유동의 단면 속도는 관내 축소비율에 반비례하여 급상승합니다(연속방정식: <code>A1v1 = A2v2</code>).</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">v2 = v1 &times; (A1 / A2) = v1 &times; (D1 / D2)&sup2;</p><p>상승한 가속 유속 <code>v2</code>를 베르누이 방정식에 대입하면 수축부(Throat)에서의 정압 <code>P2</code>를 도출할 수 있습니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">P2 = P1 &#8211; &frac12; &times; &rho; &times; (v2&sup2; &#8211; v1&sup2;)</p><p>만약 수축 비율이 극도로 커서 가속 유속이 한계치를 초과할 경우, 계산식 상의 <code>P2</code>가 대기압 이하로 감소하는 **진공 영역(Vacuum Pressure)**에 도달하게 됩니다. 이 저압 진공력에 의해 다른 부근의 액체나 가스를 스스로 빨아들이는 흡인 작용이 발생하며, 이는 스프레이 건, 기화기, 수류 이젝터 설계의 핵심 응용 물리로 활용됩니다.</p>
<h3>3. 실제 유체 해석(Loss and Cavitation)에서의 제한 요인</h3>
<p>이상 유체 계산식과 달리, 실제 점성 유체가 흐를 때는 내벽 마찰과 확산부에서의 와류 발생으로 인해 수축 단면 전후에 **마찰 압력 손실(Head Loss)**이 일어납니다. 또한 정압 <code>P2</code>가 이송 유체의 그 온도에 따른 **포화증기압(Vapor Pressure)** 이하로 떨어질 경우 유체가 관 내에서 비등 기화하여 **기포(Cavitation Bubble)**를 형성하는 공동현상이 일어날 수 있으며, 이는 배관 파손을 야기하므로 설계 시 임계 압력 이하로 떨어지지 않도록 철저히 차단 제어해야 합니다.</p>
        </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/bernoulli-venturi-effect-calculator-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>배관 유량 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/pipe-flow-velocity-calculator-simulator/</link>
					<comments>https://myengnote.com/pipe-flow-velocity-calculator-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Sun, 07 Jun 2026 22:00:07 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[공학설계]]></category>
		<category><![CDATA[배관경선정]]></category>
		<category><![CDATA[배관유량]]></category>
		<category><![CDATA[유속계산]]></category>
		<category><![CDATA[유체역학]]></category>
		<guid isPermaLink="false">https://myengnote.com/pipe-flow-velocity-calculator-simulator/</guid>

					<description><![CDATA[배관의 내경(D)과 설계 유량(Q)을 입력하여 단면 유속(v)을 실시간 산출하고, 배관 내 유속에 따른 권장 유속 조건 만족 여부 판정과 유체 흐름을 물리 엔진으로 시각화하는 고정밀 시뮬레이터입니다.]]></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; 시뮬레이터</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 pipe-flow-wrapper */
        /* Modern Reset and Design Tokens */
        .pipe-flow-wrapper *, .pipe-flow-wrapper *::before, .pipe-flow-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .pipe-flow-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);
        }
        .pipe-flow-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.15;
        }
        .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.12; }
            100% { transform: scale(1.2) translate(50px, 50px); opacity: 0.22; }
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-purple));
            -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: 1px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-text-muted));
            -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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            background: rgba(0, 0, 0, 0.03);
            color: var(--color-text-muted);
            font-size: 11px;
            font-weight: 700;
            padding: 0 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            border-left: 1px solid var(--color-border);
            pointer-events: none;
            letter-spacing: 0.5px;
            flex-shrink: 0;
        }
        .unit-badge-select-wrapper {
            position: relative;
            background: rgba(0, 0, 0, 0.03);
            border-left: 1px solid var(--color-border);
            display: flex;
            align-items: center;
            flex-shrink: 0;
        }
        .unit-select {
            background: transparent;
            border: none;
            outline: none;
            color: var(--color-text-muted);
            font-size: 11px;
            font-weight: 700;
            padding: 10px 24px 10px 14px;
            cursor: pointer;
            letter-spacing: 0.5px;
            -webkit-appearance: none;
            -moz-appearance: none;
            appearance: none;
            font-family: var(--font-body);
        }
        .unit-badge-select-wrapper::after {
            content: '\f0d7';
            font-family: 'Font Awesome 6 Free';
            font-weight: 900;
            position: absolute;
            right: 10px;
            font-size: 9px;
            color: var(--color-text-muted);
            pointer-events: none;
        }
        .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;
        }
        #slider-d::-webkit-slider-thumb {
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
        }
        #slider-d::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-q::-webkit-slider-thumb {
            background: var(--color-purple);
            box-shadow: 0 0 10px var(--color-purple);
        }
        #slider-q::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .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) !important;
            border: 1px solid var(--color-border) !important;
            border-radius: 10px !important;
            padding: 10px 12px !important;
            cursor: pointer !important;
            color: var(--color-text-main) !important;
            display: flex !important;
            align-items: center !important;
            gap: 12px !important;
            text-align: left !important;
            transition: all 0.2s ease !important;
            box-shadow: none !important;
        }
        .preset-btn:hover {
            background: rgba(0, 0, 0, 0.04) !important;
            transform: translateX(4px) !important;
            color: var(--color-text-main) !important;
        }
        .preset-btn.active {
            background: linear-gradient(90deg, var(--color-cyan-glow), var(--color-purple-glow)) !important;
            border-color: var(--color-cyan) !important;
            box-shadow: 0 0 10px var(--color-cyan-glow) !important;
            color: var(--color-text-main) !important;
        }
        .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);
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 10;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .status-readout-box {
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
            transition: all 0.3s ease;
        }
        .status-readout-box.optimal {
            background: rgba(16, 185, 129, 0.08);
            border: 1px solid rgba(16, 185, 129, 0.25);
            box-shadow: 0 0 15px rgba(16, 185, 129, 0.05);
        }
        .status-readout-box.warning {
            background: rgba(245, 158, 11, 0.08);
            border: 1px solid rgba(245, 158, 11, 0.25);
            box-shadow: 0 0 15px rgba(245, 158, 11, 0.05);
        }
        .status-readout-box.danger {
            background: rgba(219, 39, 119, 0.08);
            border: 1px solid rgba(219, 39, 119, 0.25);
            box-shadow: 0 0 15px rgba(219, 39, 119, 0.05);
        }
        .status-title {
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 1px;
            text-transform: uppercase;
        }
        .status-readout-box.optimal .status-title { color: var(--color-success); }
        .status-readout-box.warning .status-title { color: var(--color-warning); }
        .status-readout-box.danger .status-title { color: var(--color-magenta); }
        .status-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 26px;
            color: var(--color-text-main);
        }
        .status-badge {
            font-size: 12px;
            font-weight: 700;
            padding: 4px 12px;
            border-radius: 20px;
            color: #fff;
        }
        .status-readout-box.optimal .status-badge { background: var(--color-success); }
        .status-readout-box.warning .status-badge { background: var(--color-warning); }
        .status-readout-box.danger .status-badge { background: var(--color-magenta); }
        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .result-card {
            background: rgba(255, 255, 255, 0.85);
            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: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.03);
        }
        .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);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 8px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        .fluid-type-select {
            display: grid;
            grid-template-columns: 1fr 1fr 1fr;
            gap: 8px;
            border-top: 1px solid var(--color-border);
            padding-top: 14px;
        }
        .fluid-tab-btn {
            background: rgba(0, 0, 0, 0.03) !important;
            border: 1px solid rgba(0, 0, 0, 0.12) !important;
            padding: 6px 10px !important;
            border-radius: 8px !important;
            font-size: 11px !important;
            font-weight: 600 !important;
            cursor: pointer !important;
            text-align: center !important;
            transition: all 0.2s ease !important;
            color: var(--color-text-main) !important;
            box-shadow: none !important;
        }
        .fluid-tab-btn:hover {
            background: rgba(0, 0, 0, 0.06) !important;
            color: var(--color-text-main) !important;
        }
        .fluid-tab-btn.active {
            background: var(--color-cyan) !important;
            border-color: var(--color-cyan) !important;
            color: #fff !important;
        }
        /* Container Query */
        @container pipeflow-container (max-width: 1024px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel {
                grid-column: 1;
            }
            .simulation-panel {
                grid-column: 1;
                order: -1;
            }
            .results-panel {
                grid-column: 1;
            }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.pipe-flow-wrapper .app-main-grid,
.pipe-flow-wrapper .main-grid,
.pipe-flow-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.pipe-flow-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.pipe-flow-wrapper .simulation-panel,
.pipe-flow-wrapper .canvas-panel,
.pipe-flow-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.pipe-flow-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.pipe-flow-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.pipe-flow-wrapper .simulation-results-section .ratio-readout-box,
.pipe-flow-wrapper .simulation-results-section .re-readout-box,
.pipe-flow-wrapper .simulation-results-section .status-readout-box,
.pipe-flow-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.pipe-flow-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.pipe-flow-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.pipe-flow-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .pipe-flow-wrapper .app-main-grid,
    .pipe-flow-wrapper .main-grid,
    .pipe-flow-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .pipe-flow-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .pipe-flow-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .pipe-flow-wrapper .simulation-panel,
    .pipe-flow-wrapper .canvas-panel,
    .pipe-flow-wrapper .sim-panel,
    .pipe-flow-wrapper .canvas-section {
        order: -1 !important;
    }
    .pipe-flow-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .pipe-flow-wrapper .simulation-results-section .ratio-readout-box,
    .pipe-flow-wrapper .simulation-results-section .re-readout-box,
    .pipe-flow-wrapper .simulation-results-section .status-readout-box,
    .pipe-flow-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .pipe-flow-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .pipe-flow-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.pipe-flow-wrapper .app-container,
.pipe-flow-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="pipe-flow-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 class="app-header">
            <div class="logo-area">
                <div class="logo-icon"><i class="fa-solid fa-arrows-left-right-to-line"></i></div>
                <div>
                    <h1>PIPE FLOW VELOCITY</h1>
                    <div class="subtitle">배관 유량 계산기 &#038; 유속 권장 설계 분석기</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <span class="badge-text">REALTIME MONITORING</span>
            </div>
        </header>
        <div class="app-main-grid">
            <!-- Controls -->
            <div class="panel control-panel">
                <div class="panel-header text-cyan">
                    <i class="fa-solid fa-sliders"></i>
                    <h2>배관 및 유동 입력</h2>
                </div>
                <!-- D Inner Diameter -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-d"><i class="fa-solid fa-circle-dot text-cyan"></i> 배관 내경 (D)</label>
                        <span class="helper-text">범위: 10 ~ 1000 mm</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d" class="custom-number-input" min="10" max="1000" value="80">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-d" class="custom-slider" min="10" max="1000" value="80">
                </div>
                <!-- Q Flow Rate -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-q"><i class="fa-solid fa-gauge text-purple"></i> 설계 유량 (Q)</label>
                        <span class="helper-text">범위: 0.1 ~ 3600.0 m³/h</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-q" class="custom-number-input" min="0.1" max="3600" step="0.1" value="45.0">
                        <div class="unit-badge-select-wrapper">
                            <select id="unit-q" class="unit-select">
                                <option value="m3_h">m³/h</option>
                                <option value="m3_s">m³/s</option>
                                <option value="lpm">LPM</option>
                            </select>
                        </div>
                    </div>
                    <input type="range" id="slider-q" class="custom-slider" min="1" max="36000" value="450">
                </div>
                <!-- Fluid Type Selector -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-droplet text-cyan"></i> 수송 유체 타입</label>
                    </div>
                    <div class="fluid-type-select">
                        <button class="fluid-tab-btn active" data-fluid="water">물<br>(Water)</button>
                        <button class="fluid-tab-btn" data-fluid="oil">오일<br>(Light Oil)</button>
                        <button class="fluid-tab-btn" data-fluid="air">공기<br>(Air/Gas)</button>
                    </div>
                </div>
                <!-- Presets -->
                <div class="presets-section">
                    <h3><i class="fa-solid fa-circle-nodes"></i> 실무 배관 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn" data-preset="smallWater">
                            <div class="preset-icon"><i class="fa-solid fa-filter"></i></div>
                            <div class="preset-details"><span class="preset-name">소구경 물배관</span><span class="preset-spec">25A, 3 m³/h</span></div>
                        </button>
                        <button class="preset-btn" data-preset="midWater">
                            <div class="preset-icon"><i class="fa-solid fa-faucet-drip"></i></div>
                            <div class="preset-details"><span class="preset-name">중구경 메인관</span><span class="preset-spec">100A, 80 m³/h</span></div>
                        </button>
                        <button class="preset-btn" data-preset="hydraulic">
                            <div class="preset-icon"><i class="fa-solid fa-bolt"></i></div>
                            <div class="preset-details"><span class="preset-name">고압 유압배관</span><span class="preset-spec">20A, 250 LPM</span></div>
                        </button>
                    </div>
                </div>
            </div>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <div class="panel simulation-panel">
                                <div class="panel-header text-purple">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-display"></i>
                                        <h2>배관 내부 유속 분포 시뮬레이션</h2>
                                    </div>
                                    <div class="canvas-scale-indicator" id="txt-canvas-scale">배관 내경: 80 mm</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas" width="640" height="400"></canvas>
                                </div>
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <span class="label">배관 단면적 (A)</span>
                                        <span class="value" id="mini-area">50.27 cm²</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">설계 유량 (Q_lpm)</span>
                                        <span class="value" id="mini-q-lpm">750 LPM</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">동압 (Dynamic P)</span>
                                        <span class="value" id="mini-dp">1.25 kPa</span>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="status-readout-box optimal" id="status-box">
                                    <span class="status-title">FLOW VELOCITY</span>
                                    <span class="status-value" id="txt-v">2.49 m/s</span>
                                    <span class="status-badge" id="txt-status-eval">적정 속도 (Optimal)</span>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon text-cyan"><i class="fa-solid fa-circle-check"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">흡입 배관 적합도 (Suction)</span>
                                            <span class="card-value" id="txt-suction-eval">유속 빠름 (Cavitation 우려)</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon text-purple"><i class="fa-solid fa-circle-check"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">송출 배관 적합도 (Delivery)</span>
                                            <span class="card-value" id="txt-delivery-eval">적정 유속 범위</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                        <h4>기본 설계 공식</h4>
                                        <div class="formula-equation">v = Q / A</div>
                                        <p>
                                            * 유량과 단면적 간의 단위 계수를 맞춰 자동으로 미터법 평균 유속을 도출합니다.
                                        </p>
                                    </div>
                            </div>
                </div>
            </div>
        </div>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    </div>
</div>
<script>
        (function() {
            // State
            const state = {
                d: 80,      // mm
                q: 45.0,    // Q value in selected unit
                qUnit: 'm3_h',
                fluid: 'water',
                particles: [],
                lastTime: 0
            };
            const qUnitConfigs = {
                m3_h: {
                    min: 0.1,
                    max: 3600.0,
                    step: 0.1,
                    helperText: "범위: 0.1 ~ 3600.0 m³/h",
                    format: (v) => v.toFixed(1),
                    toSlider: (v) => Math.round(v * 10),
                    fromSlider: (sv) => sv / 10,
                    sliderMin: 1,
                    sliderMax: 36000,
                    defaultVal: 45.0
                },
                m3_s: {
                    min: 0.0001,
                    max: 1.0000,
                    step: 0.0001,
                    helperText: "범위: 0.0001 ~ 1.0000 m³/s",
                    format: (v) => v.toFixed(4),
                    toSlider: (v) => Math.round(v * 10000),
                    fromSlider: (sv) => sv / 10000,
                    sliderMin: 1,
                    sliderMax: 10000,
                    defaultVal: 0.0125
                },
                lpm: {
                    min: 1,
                    max: 60000,
                    step: 1,
                    helperText: "범위: 1 ~ 60,000 LPM",
                    format: (v) => Math.round(v).toString(),
                    toSlider: (v) => Math.round(v),
                    fromSlider: (sv) => sv,
                    sliderMin: 1,
                    sliderMax: 60000,
                    defaultVal: 750
                }
            };
            function getQInM3h() {
                if (state.qUnit === 'm3_s') {
                    return state.q * 3600;
                }
                if (state.qUnit === 'lpm') {
                    return state.q * 0.06;
                }
                return state.q;
            }
            function convertQ(val, fromUnit, toUnit) {
                let valM3h = val;
                if (fromUnit === 'm3_s') {
                    valM3h = val * 3600;
                } else if (fromUnit === 'lpm') {
                    valM3h = val * 0.06;
                }
                let targetVal = valM3h;
                if (toUnit === 'm3_s') {
                    targetVal = valM3h / 3600;
                } else if (toUnit === 'lpm') {
                    targetVal = valM3h / 0.06;
                }
                const cfg = qUnitConfigs[toUnit];
                if (targetVal < cfg.min) { targetVal = cfg.min; }
                if (targetVal > cfg.max) { targetVal = cfg.max; }
                return targetVal;
            }
            function updateQUnitUI() {
                const cfg = qUnitConfigs[state.qUnit];
                const labelQ = document.querySelector('#input-q');
                if (labelQ) {
                    const group = labelQ.closest('.input-group');
                    if (group) {
                        const helper = group.querySelector('.helper-text');
                        if (helper) {
                            helper.innerText = cfg.helperText;
                        }
                    }
                }
                inputQ.min = cfg.min;
                inputQ.max = cfg.max;
                inputQ.step = cfg.step;
                sliderQ.min = cfg.sliderMin;
                sliderQ.max = cfg.sliderMax;
                const formatted = cfg.format(state.q);
                if (inputQ.value !== formatted) {
                    inputQ.value = formatted;
                }
                const sliderValStr = cfg.toSlider(state.q).toString();
                if (sliderQ.value !== sliderValStr) {
                    sliderQ.value = sliderValStr;
                }
            }
            // DOM Elements
            const inputD = document.getElementById('input-d');
            const sliderD = document.getElementById('slider-d');
            const inputQ = document.getElementById('input-q');
            const sliderQ = document.getElementById('slider-q');
            const txtV = document.getElementById('txt-v');
            const txtStatusEval = document.getElementById('txt-status-eval');
            const statusBox = document.getElementById('status-box');
            const txtSuctionEval = document.getElementById('txt-suction-eval');
            const txtDeliveryEval = document.getElementById('txt-delivery-eval');
            const miniArea = document.getElementById('mini-area');
            const miniQLpm = document.getElementById('mini-q-lpm');
            const miniDp = document.getElementById('mini-dp');
            const fluidTabButtons = document.querySelectorAll('.fluid-tab-btn');
            const presetButtons = document.querySelectorAll('.preset-btn');
            const canvas = document.getElementById('physics-canvas');
            const ctx = canvas.getContext('2d');
            // Spawn Particles
            const maxParticles = 120;
            for (let i = 0; i < maxParticles; i++) {
                state.particles.push({
                    x: Math.random() * 640,
                    y: 0.05 + Math.random() * 0.90, // vertical fraction
                    size: 1 + Math.random() * 3
                });
            }
            // Sync
            function syncDFromSlider() {
                const newD = parseInt(sliderD.value);
                state.d = newD;
                const newDStr = newD.toString();
                if (inputD.value !== newDStr) {
                    inputD.value = newDStr;
                }
                clearPresets();
                updateCalculations();
            }
            function syncDFromInput() {
                let val = parseInt(inputD.value);
                if (isNaN(val)) { val = 80; }
                if (val < 10) { val = 10; }
                if (val > 1000) { val = 1000; }
                state.d = val;
                const newDStr = val.toString();
                if (inputD.value !== newDStr) {
                    inputD.value = newDStr;
                }
                if (sliderD.value !== newDStr) {
                    sliderD.value = newDStr;
                }
                clearPresets();
                updateCalculations();
            }
            function syncQFromSlider() {
                const cfg = qUnitConfigs[state.qUnit];
                state.q = cfg.fromSlider(parseFloat(sliderQ.value));
                const formatted = cfg.format(state.q);
                if (inputQ.value !== formatted) {
                    inputQ.value = formatted;
                }
                clearPresets();
                updateCalculations();
            }
            function syncQFromInput() {
                const cfg = qUnitConfigs[state.qUnit];
                let val = parseFloat(inputQ.value);
                if (isNaN(val)) { val = cfg.defaultVal; }
                if (val < cfg.min) { val = cfg.min; }
                if (val > cfg.max) { val = cfg.max; }
                state.q = val;
                const formatted = cfg.format(val);
                if (inputQ.value !== formatted) {
                    inputQ.value = formatted;
                }
                const sliderValStr = cfg.toSlider(val).toString();
                if (sliderQ.value !== sliderValStr) {
                    sliderQ.value = sliderValStr;
                }
                clearPresets();
                updateCalculations();
            }
            sliderD.addEventListener('input', syncDFromSlider);
            inputD.addEventListener('change', syncDFromInput);
            sliderQ.addEventListener('input', syncQFromSlider);
            inputQ.addEventListener('change', syncQFromInput);
            const selectUnitQ = document.getElementById('unit-q');
            if (selectUnitQ) {
                selectUnitQ.addEventListener('change', function() {
                    const newUnit = selectUnitQ.value;
                    state.q = convertQ(state.q, state.qUnit, newUnit);
                    state.qUnit = newUnit;
                    updateQUnitUI();
                    updateCalculations();
                });
            }
            // Tabs
            fluidTabButtons.forEach(function(btn) {
                btn.addEventListener('click', function() {
                    fluidTabButtons.forEach(function(b) { b.classList.remove('active'); });
                    btn.classList.add('active');
                    state.fluid = btn.dataset.fluid;
                    updateCalculations();
                });
            });
            // Presets
            function loadPreset(presetKey) {
                clearPresets();
                presetButtons.forEach(function(btn) {
                    if (btn.dataset.preset === presetKey) {
                        btn.classList.add('active');
                    }
                });
                if (presetKey === 'smallWater') {
                    state.d = 25;
                    state.q = 3.0;
                    state.fluid = 'water';
                } else if (presetKey === 'midWater') {
                    state.d = 100;
                    state.q = 80.0;
                    state.fluid = 'water';
                } else if (presetKey === 'hydraulic') {
                    state.d = 20;
                    state.q = 15.0; // 250 LPM is 15 m3/h
                    state.fluid = 'oil';
                }
                // Sync Tab
                fluidTabButtons.forEach(function(btn) {
                    btn.classList.remove('active');
                    if (btn.dataset.fluid === state.fluid) {
                        btn.classList.add('active');
                    }
                });
                // Reset flow rate unit to m³/h for presets
                state.qUnit = 'm3_h';
                const unitQ = document.getElementById('unit-q');
                if (unitQ) {
                    unitQ.value = 'm3_h';
                }
                updateQUnitUI();
                // Sync Input Values
                inputD.value = state.d;
                sliderD.value = state.d;
                updateCalculations();
            }
            function clearPresets() {
                presetButtons.forEach(function(btn) {
                    btn.classList.remove('active');
                });
            }
            presetButtons.forEach(function(btn) {
                btn.addEventListener('click', function() {
                    loadPreset(btn.dataset.preset);
                });
            });
            // Core Calculations
            function updateCalculations() {
                const D_m = state.d / 1000;
                const area = (Math.PI * Math.pow(D_m, 2)) / 4; // m²
                const qM3h = getQInM3h();
                // v = Q / (3600 * A)
                const v = qM3h / (3600 * area); // m/s
                txtV.innerText = v.toFixed(2) + ' m/s';
                miniArea.innerText = (area * 10000).toFixed(2) + ' cm²';
                const qLpm = (qM3h * 1000) / 60;
                miniQLpm.innerText = Math.round(qLpm).toLocaleString() + ' LPM';
                // Density by fluid type
                let rho = 1000; // water
                if (state.fluid === 'oil') { rho = 850; }
                if (state.fluid === 'air') { rho = 1.2; }
                // Dynamic pressure = 0.5 * rho * v^2 in kPa
                const dpKpa = (0.5 * rho * Math.pow(v, 2)) / 1000;
                miniDp.innerText = dpKpa.toFixed(3) + ' kPa';
                // Evaluation
                statusBox.className = 'status-readout-box';
                // Set defaults
                let overallEval = '적정 유속 범위';
                let isOptimal = true;
                let isWarning = false;
                let isDanger = false;
                // Suction Pipe eval (0.5 ~ 1.5 m/s)
                if (v < 0.5) {
                    txtSuctionEval.innerText = '유속 낮음 (침전 위험)';
                    txtSuctionEval.style.color = '#f59e0b';
                } else if (v <= 1.5) {
                    txtSuctionEval.innerText = '아주 적정 (권장)';
                    txtSuctionEval.style.color = '#10b981';
                } else {
                    txtSuctionEval.innerText = '유속 빠름 (흡입 불리)';
                    txtSuctionEval.style.color = '#db2777';
                }
                // Delivery Pipe eval (1.5 ~ 3.0 m/s)
                if (v < 1.0) {
                    txtDeliveryEval.innerText = '유속 다소 낮음';
                    txtDeliveryEval.style.color = '#475569';
                } else if (v <= 3.0) {
                    txtDeliveryEval.innerText = '최적 유속 설계';
                    txtDeliveryEval.style.color = '#10b981';
                } else {
                    txtDeliveryEval.innerText = '유속 다소 높음 (손실 증대)';
                    txtDeliveryEval.style.color = '#f59e0b';
                }
                // Overall status categorization based on fluid type
                if (state.fluid === 'water' || state.fluid === 'oil') {
                    if (v > 4.5) {
                        overallEval = '유속 과다 (배관 마모 위험)';
                        isDanger = true;
                    } else if (v > 3.0) {
                        overallEval = '유속 높음 (압력손실 증가)';
                        isWarning = true;
                    } else if (v < 0.5) {
                        overallEval = '유속 과소 (침전 우려)';
                        isWarning = true;
                    } else {
                        overallEval = '적정 설계 범위';
                        isOptimal = true;
                    }
                } else {
                    // Gas/Air allows much higher velocity (up to 15-20 m/s)
                    if (v > 20.0) {
                        overallEval = '가스 속도 과다';
                        isDanger = true;
                    } else if (v > 15.0) {
                        overallEval = '유속 높음';
                        isWarning = true;
                    } else if (v < 2.0) {
                        overallEval = '유속 낮음';
                        isWarning = true;
                    } else {
                        overallEval = '적정 가스 배관 속도';
                        isOptimal = true;
                    }
                }
                if (isOptimal) {
                    statusBox.classList.add('optimal');
                    txtStatusEval.innerText = overallEval;
                } else if (isWarning) {
                    statusBox.classList.add('warning');
                    txtStatusEval.innerText = overallEval;
                } else if (isDanger) {
                    statusBox.classList.add('danger');
                    txtStatusEval.innerText = overallEval;
                }
                const txtCanvasScale = document.getElementById('txt-canvas-scale');
                if (txtCanvasScale) {
                    txtCanvasScale.innerText = `배관 직경: ${state.d} mm`;
                }
            }
            // Draw
            function draw(currentTime) {
                requestAnimationFrame(draw);
                let dt = (currentTime - state.lastTime) / 1000;
                if (isNaN(dt)) { dt = 0.016; }
                if (dt < 0) { dt = 0.016; }
                if (dt > 0.1) { dt = 0.016; }
                state.lastTime = currentTime;
                const width = canvas.width;
                const height = canvas.height;
                ctx.clearRect(0, 0, width, height);
                // Grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.04)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Pipe mapping: 10 ~ 1000 mm mapped to 50 ~ 290 px
                const minScaleD = 10;
                const maxScaleD = 1000;
                const minPixH = 50;
                const maxPixH = 290;
                const pipeHeight = minPixH + ((state.d - minScaleD) / (maxScaleD - minScaleD)) * (maxPixH - minPixH);
                const pipeTop = (height - pipeHeight) / 2;
                const pipeBottom = pipeTop + pipeHeight;
                const pipeCenter = height / 2;
                // Flow physics variables
                const D_m = state.d / 1000;
                const area = (Math.PI * Math.pow(D_m, 2)) / 4;
                const qM3h = getQInM3h();
                const v = qM3h / (3600 * area);
                // Fluid properties for visual styling
                let particleColor = '#0284c7';
                let pipeFillColor = 'rgba(224, 242, 254, 0.3)';
                if (state.fluid === 'oil') {
                    particleColor = '#f59e0b';
                    pipeFillColor = 'rgba(254, 243, 199, 0.3)';
                }
                if (state.fluid === 'air') {
                    particleColor = '#94a3b8';
                    pipeFillColor = 'rgba(241, 245, 249, 0.3)';
                }
                // Draw Pipe Wall
                ctx.save();
                ctx.fillStyle = pipeFillColor;
                ctx.fillRect(0, pipeTop, width, pipeHeight);
                ctx.strokeStyle = '#64748b';
                ctx.lineWidth = 5;
                ctx.beginPath();
                ctx.moveTo(0, pipeTop);
                ctx.lineTo(width, pipeTop);
                ctx.moveTo(0, pipeBottom);
                ctx.lineTo(width, pipeBottom);
                ctx.stroke();
                ctx.restore();
                // Center dashed guideline
                ctx.save();
                ctx.strokeStyle = 'rgba(0,0,0,0.06)';
                ctx.lineWidth = 1.5;
                ctx.setLineDash([6, 6]);
                ctx.beginPath();
                ctx.moveTo(0, pipeCenter);
                ctx.lineTo(width, pipeCenter);
                ctx.stroke();
                ctx.restore();
                // Animate and draw particles
                ctx.save();
                ctx.fillStyle = particleColor;
                state.particles.forEach(function(p) {
                    const yReal = pipeTop + pipeHeight * p.y;
                    const rFrac = (p.y - 0.5) * 2; // range -1 to 1
                    // Parabolic velocity factor (high in center, low at walls)
                    let vProfile = 1.5 * (1 - rFrac * rFrac);
                    if (state.fluid === 'air') {
                        vProfile = 1.2 * Math.pow(1 - Math.abs(rFrac), 1/7); // flatter gas profile
                    }
                    // Move particles
                    const velocityFactor = 70; // visual scaling
                    const speed = v * velocityFactor * vProfile;
                    p.x += speed * dt;
                    // Boundaries
                    if (p.x > width + 10) {
                        p.x = -10;
                        p.y = 0.05 + Math.random() * 0.90;
                    }
                    // Clamp to make sure particles stay inside the pipe outlines (taking outline thickness into account)
                    const finalY = Math.max(pipeTop + 2.5 + p.size, Math.min(pipeBottom - 2.5 - p.size, yReal));
                    ctx.beginPath();
                    ctx.arc(p.x, finalY, p.size, 0, Math.PI * 2);
                    ctx.fill();
                });
                ctx.restore();
            }
            // Copy & right click block with nested if
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            loadPreset('midWater');
            requestAnimationFrame(draw);
        })();
</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>배관 지름(내경) 설정: 슬라이더나 직접 입력창을 통해 배관의 순수 내측 지름(mm)을 입력합니다.</strong></li>
<li style="margin-bottom: 6px;">설계 타겟 유량 설정: 단위 선택 기능과 함께 시간당 혹은 분당 요구되는 배관의 체적 유량을 입력합니다.</li>
<li style="margin-bottom: 6px;">용도별 권장 배관 속도 기준 확인: 계산된 유동 속도가 흡입관(Suction), 송출관(Delivery), 또는 고압 유압배관의 물리적 적정 속도 가이드라인(0.5 ~ 6 m/s)을 만족하는지 평가표를 확인합니다.</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;" /> 배관 내경, 유량, 유속 간 연속방정식 공식 및 설계 가이드</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. 유체의 연속방정식(Continuity Equation)과 유동 계산 공식</h3>
<p>배관 시스템을 흐르는 유체의 유량(Flow Rate)과 유속(Velocity) 및 배관 단면적(Area) 사이에는 유도되는 물리법칙인 <strong>연속방정식(Continuity Equation)</strong>이 성립합니다. 비압축성 유체가 꽉 차서 흐르는 정상 유동에서 시간당 배관을 통과하는 유체의 체적 유량은 항상 일정하며 단면적과 속도의 곱으로 결정됩니다.</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">Q = A &times; v = (&pi; &times; D² / 4) &times; v</p><p>여기서 <code>Q</code>는 체적 유량(m³/s), <code>A</code>는 배관 내부 단면적(m²), <code>v</code>는 평균 흐름 유속(m/s), <code>D</code>는 배관 내경(m)입니다. 실무 공학 계산에서는 대개 구경 단위로 mm를, 유량 단위로 m³/h 또는 LPM(L/min)을 사용하므로 다음과 같은 유효 환산 공식이 적용됩니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">v = (4 &times; Q_m³/h) / (&pi; &times; (D_mm / 1000)&sup2; &times; 3600) &approx; 353.68 &times; Q_m³/h / D_mm&sup2; &nbsp;[m/s]</p>
<h3>2. 엔지니어링 실무 배관 용도별 권장 설계 유속 기준 (Water/Oil)</h3>
<p>배관 설계 시 유속의 설정은 배관의 내구성 및 펌프 소요 에너지 효율에 직접적인 영향을 미칩니다. 유속이 너무 빠르면 마찰 손실이 기하급수적으로 늘어나고 <strong>공동현상(Cavitation)이나 배관 마모/부식(Erosion)</strong>을 야기할 수 있으며, 반대로 유속이 너무 느리면 동일 유량을 보내기 위해 지나치게 굵은 배관을 써야 하므로 설비 투자 단가가 늘어납니다.</p><ul><li><strong>펌프 흡입 배관 (Suction Line, Water):</strong> <code>0.5 ~ 1.5 m/s</code> (흡입 압력 손실 및 공동현상을 방지하기 위해 가장 느리게 설계함)</li><li><strong>펌프 송출 배관 (Delivery Line, Water):</strong> <code>1.5 ~ 3.0 m/s</code> (마찰 저항과 배관 경량화 사이의 경제적 최적 범위)</li><li><strong>유압 동력 전달 배관 (Hydraulic Pressure Line, Oil):</strong> <code>2.0 ~ 6.0 m/s</code> (고압 전달용 배관으로 상대적으로 속도 설계 허용 한계가 높음)</li></ul>
<h3>3. 경제적 배관 직경 선정 (Economic Pipe Diameter) 접근론</h3>
<p>배관 설계를 최종 결정할 때, 초기 설치 비용(배관 본체 및 피팅류 가격)과 운전 에너지 비용(마찰 압력 손실을 극복하기 위한 펌프 구동 전력비)을 모두 더한 <strong>총생애주기 비용(LCC)이 최소가 되는 지점</strong>이 바로 최적의 경제적 배관 직경입니다. 다양한 산업용 유속 기준치 계산 기능이 본 시뮬레이터에 실시간 모니터링 모듈로 내장되어 유동 구경 설계의 오차를 원천 차단해 줍니다.</p>
        </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/pipe-flow-velocity-calculator-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>레이놀즈 수 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/reynolds-number-calculator-simulator/</link>
					<comments>https://myengnote.com/reynolds-number-calculator-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Sat, 06 Jun 2026 21:59:07 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[난류]]></category>
		<category><![CDATA[레이놀즈수]]></category>
		<category><![CDATA[물리시뮬레이터]]></category>
		<category><![CDATA[배관설계]]></category>
		<category><![CDATA[유체역학]]></category>
		<category><![CDATA[층류]]></category>
		<guid isPermaLink="false">https://myengnote.com/reynolds-number-calculator-simulator/</guid>

					<description><![CDATA[유체의 밀도, 점도, 배관 직경 및 유속을 입력하여 레이놀즈 수를 실시간 계산하고, 층류(Laminar), 천이(Transition), 난류(Turbulent) 구역의 유동 흐름을 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; 시뮬레이터</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 reynolds-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .reynolds-calculator-wrapper *, .reynolds-calculator-wrapper *::before, .reynolds-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .reynolds-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);
        }
        .reynolds-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.15;
        }
        .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.12; }
            100% { transform: scale(1.2) translate(50px, 50px); opacity: 0.22; }
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-purple));
            -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: 1px;
            background: linear-gradient(90deg, var(--color-text-main), var(--color-text-muted));
            -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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .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;
        }
        #slider-d::-webkit-slider-thumb {
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan);
        }
        #slider-d::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-v::-webkit-slider-thumb {
            background: var(--color-purple);
            box-shadow: 0 0 10px var(--color-purple);
        }
        #slider-v::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-rho::-webkit-slider-thumb {
            background: var(--color-magenta);
            box-shadow: 0 0 10px var(--color-magenta);
        }
        #slider-rho::-webkit-slider-thumb:hover { transform: scale(1.2); }
        #slider-mu::-webkit-slider-thumb {
            background: #f59e0b;
            box-shadow: 0 0 10px #f59e0b;
        }
        #slider-mu::-webkit-slider-thumb:hover { transform: scale(1.2); }
        .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) !important;
            border: 1px solid var(--color-border) !important;
            border-radius: 10px !important;
            padding: 10px 12px !important;
            cursor: pointer !important;
            color: var(--color-text-main) !important;
            display: flex !important;
            align-items: center !important;
            gap: 12px !important;
            text-align: left !important;
            transition: all 0.2s ease !important;
            box-shadow: none !important;
        }
        .preset-btn:hover {
            background: rgba(0, 0, 0, 0.04) !important;
            transform: translateX(4px) !important;
            color: var(--color-text-main) !important;
        }
        .preset-btn.active {
            background: linear-gradient(90deg, var(--color-cyan-glow), var(--color-purple-glow)) !important;
            border-color: var(--color-cyan) !important;
            box-shadow: 0 0 10px var(--color-cyan-glow) !important;
            color: var(--color-text-main) !important;
        }
        .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);
        }
        .preset-btn:hover .preset-icon, .preset-btn.active .preset-icon {
            background: var(--gradient-primary);
            color: #fff;
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #physics-canvas {
            display: block;
            width: 100%;
            height: auto;
            aspect-ratio: 16 / 10;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .re-readout-box {
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
            transition: all 0.3s ease;
        }
        .re-readout-box.laminar {
            background: rgba(2, 132, 199, 0.08);
            border: 1px solid rgba(2, 132, 199, 0.25);
            box-shadow: 0 0 15px rgba(2, 132, 199, 0.05);
        }
        .re-readout-box.transition {
            background: rgba(124, 58, 237, 0.08);
            border: 1px solid rgba(124, 58, 237, 0.25);
            box-shadow: 0 0 15px rgba(124, 58, 237, 0.05);
        }
        .re-readout-box.turbulent {
            background: rgba(219, 39, 119, 0.08);
            border: 1px solid rgba(219, 39, 119, 0.25);
            box-shadow: 0 0 15px rgba(219, 39, 119, 0.05);
        }
        .re-title {
            font-size: 11px;
            font-weight: 700;
            letter-spacing: 1px;
            text-transform: uppercase;
        }
        .re-readout-box.laminar .re-title { color: var(--color-cyan); }
        .re-readout-box.transition .re-title { color: var(--color-purple); }
        .re-readout-box.turbulent .re-title { color: var(--color-magenta); }
        .re-value {
            font-family: var(--font-heading);
            font-weight: 800;
            font-size: 28px;
            color: var(--color-text-main);
        }
        .re-type {
            font-size: 12px;
            font-weight: 700;
            padding: 4px 12px;
            border-radius: 20px;
            color: #fff;
        }
        .re-readout-box.laminar .re-type { background: var(--color-cyan); }
        .re-readout-box.transition .re-type { background: var(--color-purple); }
        .re-readout-box.turbulent .re-type { background: var(--color-magenta); }
        .results-grid {
            display: flex;
            flex-direction: column;
            gap: 12px;
        }
        .result-card {
            background: rgba(255, 255, 255, 0.85);
            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: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.03);
        }
        .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);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 8px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        /* Container Query */
        @container reynolds-container (max-width: 1024px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel {
                grid-column: 1;
            }
            .simulation-panel {
                grid-column: 1;
                order: -1;
            }
            .results-panel {
                grid-column: 1;
            }
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.reynolds-calculator-wrapper .app-main-grid,
.reynolds-calculator-wrapper .main-grid,
.reynolds-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.reynolds-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.reynolds-calculator-wrapper .simulation-panel,
.reynolds-calculator-wrapper .canvas-panel,
.reynolds-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.reynolds-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.reynolds-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.reynolds-calculator-wrapper .simulation-results-section .ratio-readout-box,
.reynolds-calculator-wrapper .simulation-results-section .re-readout-box,
.reynolds-calculator-wrapper .simulation-results-section .status-readout-box,
.reynolds-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.reynolds-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.reynolds-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.reynolds-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .reynolds-calculator-wrapper .app-main-grid,
    .reynolds-calculator-wrapper .main-grid,
    .reynolds-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .reynolds-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .reynolds-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .reynolds-calculator-wrapper .simulation-panel,
    .reynolds-calculator-wrapper .canvas-panel,
    .reynolds-calculator-wrapper .sim-panel,
    .reynolds-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .reynolds-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .reynolds-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .reynolds-calculator-wrapper .simulation-results-section .re-readout-box,
    .reynolds-calculator-wrapper .simulation-results-section .status-readout-box,
    .reynolds-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .reynolds-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .reynolds-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.reynolds-calculator-wrapper .app-container,
.reynolds-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="reynolds-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 class="app-header">
            <div class="logo-area">
                <div class="logo-icon"><i class="fa-solid fa-water"></i></div>
                <div>
                    <h1>REYNOLDS NUMBER</h1>
                    <div class="subtitle">레이놀즈 수 계산기 &#038; 유체 가시화 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <span class="badge-text">REALTIME PHYSICS</span>
            </div>
        </header>
        <div class="app-main-grid">
            <!-- Controls -->
            <div class="panel control-panel">
                <div class="panel-header text-cyan">
                    <i class="fa-solid fa-sliders"></i>
                    <h2>제어 변수 설정</h2>
                </div>
                <!-- D Inner Diameter -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-d"><i class="fa-solid fa-ruler-horizontal text-cyan"></i> 배관 내경 (D)</label>
                        <span class="helper-text">범위: 10 ~ 200 mm</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d" class="custom-number-input" min="10" max="200" value="50">
                        <span class="unit-badge">mm</span>
                    </div>
                    <input type="range" id="slider-d" class="custom-slider" min="10" max="200" value="50">
                </div>
                <!-- v Velocity -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-v"><i class="fa-solid fa-gauge-high text-purple"></i> 평균 유속 (v)</label>
                        <span class="helper-text">범위: 0.01 ~ 5.00 m/s</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-v" class="custom-number-input" min="0.01" max="5" step="0.01" value="0.50">
                        <span class="unit-badge">m/s</span>
                    </div>
                    <input type="range" id="slider-v" class="custom-slider" min="1" max="500" value="50">
                </div>
                <!-- rho Density -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-rho"><i class="fa-solid fa-weight-hanging text-magenta"></i> 유체 밀도 (&rho;)</label>
                        <span class="helper-text">범위: 500 ~ 2000 kg/m³</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-rho" class="custom-number-input" min="500" max="2000" value="1000">
                        <span class="unit-badge">kg/m³</span>
                    </div>
                    <input type="range" id="slider-rho" class="custom-slider" min="500" max="2000" value="1000">
                </div>
                <!-- mu Viscosity -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label for="input-mu"><i class="fa-solid fa-droplet text-warning"></i> 절대 점도 (&mu;)</label>
                        <span class="helper-text">범위: 0.1 ~ 1000.0 cP</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-mu" class="custom-number-input" min="0.1" max="1000" step="0.1" value="1.0">
                        <span class="unit-badge">cP</span>
                    </div>
                    <input type="range" id="slider-mu" class="custom-slider" min="1" max="10000" value="10">
                </div>
                <!-- Presets -->
                <div class="presets-section">
                    <h3><i class="fa-solid fa-flask"></i> 유체 프리셋</h3>
                    <div class="presets-grid">
                        <button class="preset-btn" data-preset="water20">
                            <div class="preset-icon"><i class="fa-solid fa-glass-water"></i></div>
                            <div class="preset-details"><span class="preset-name">물</span><span class="preset-spec">20°C, 저점도</span></div>
                        </button>
                        <button class="preset-btn" data-preset="oil46">
                            <div class="preset-icon"><i class="fa-solid fa-oil-can"></i></div>
                            <div class="preset-details"><span class="preset-name">작동유</span><span class="preset-spec">ISO VG 46, 중점도</span></div>
                        </button>
                        <button class="preset-btn" data-preset="glycerin">
                            <div class="preset-icon"><i class="fa-solid fa-bottle-droplet"></i></div>
                            <div class="preset-details"><span class="preset-name">글리세린</span><span class="preset-spec">고점도 유체</span></div>
                        </button>
                    </div>
                </div>
            </div>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <div class="panel simulation-panel">
                                <div class="panel-header text-purple">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-display"></i>
                                        <h2>실시간 유동 비주얼 시뮬레이션</h2>
                                    </div>
                                    <div class="canvas-scale-indicator" id="txt-canvas-scale">배관 내경 스케일: 1:1</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas" width="640" height="400"></canvas>
                                </div>
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <span class="label">유량 (Q)</span>
                                        <span class="value" id="mini-q">0.00 m³/h</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">동점도 (&nu;)</span>
                                        <span class="value" id="mini-nu">1.00 cSt</span>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <span class="label">동압 (Dynamic P)</span>
                                        <span class="value" id="mini-dp">125 Pa</span>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="re-readout-box laminar" id="re-box">
                                    <span class="re-title">REYNOLDS NUMBER</span>
                                    <span class="re-value" id="txt-re">25,000</span>
                                    <span class="re-type" id="txt-flow-type">층류 유동 (Laminar)</span>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon text-cyan"><i class="fa-solid fa-water"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">흐름 지배력</span>
                                            <span class="card-value" id="txt-force-balance">점성력 지배</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon text-purple"><i class="fa-solid fa-circle-nodes"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">임계 속도 (Re=2100 기준)</span>
                                            <span class="card-value" id="txt-vc">0.04 m/s</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                        <h4>계산 유도 공식</h4>
                                        <div class="formula-equation">Re = (&rho; &times; v &times; D) / &mu;</div>
                                        <p>
                                            * 점도 &mu;는 cP 단위를 Pa&middot;s로 자동 변환하여 계산됩니다. (1 cP = 10⁻³ Pa&middot;s)
                                        </p>
                                    </div>
                            </div>
                </div>
            </div>
        </div>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    </div>
</div>
<script>
        (function() {
            // State variables
            const state = {
                d: 50,      // mm
                v: 0.50,    // m/s
                rho: 1000,  // kg/m³
                mu: 1.0,    // cP
                particles: [],
                lastTime: 0,
                flowWidth: 640,
                flowHeight: 400,
                angleOffset: 0
            };
            // DOM Elements
            const inputD = document.getElementById('input-d');
            const sliderD = document.getElementById('slider-d');
            const inputV = document.getElementById('input-v');
            const sliderV = document.getElementById('slider-v');
            const inputRho = document.getElementById('input-rho');
            const sliderRho = document.getElementById('slider-rho');
            const inputMu = document.getElementById('input-mu');
            const sliderMu = document.getElementById('slider-mu');
            const txtRe = document.getElementById('txt-re');
            const txtFlowType = document.getElementById('txt-flow-type');
            const reBox = document.getElementById('re-box');
            const txtForceBalance = document.getElementById('txt-force-balance');
            const txtVc = document.getElementById('txt-vc');
            const miniQ = document.getElementById('mini-q');
            const miniNu = document.getElementById('mini-nu');
            const miniDp = document.getElementById('mini-dp');
            const presetButtons = document.querySelectorAll('.preset-btn');
            const canvas = document.getElementById('physics-canvas');
            const ctx = canvas.getContext('2d');
            // Setup particles array
            const numParticles = 120;
            for (let i = 0; i < numParticles; i++) {
                state.particles.push({
                    x: Math.random() * 640,
                    y: 0.1 + Math.random() * 0.8, // fractional height inside the channel
                    size: 1.5 + Math.random() * 2,
                    offsetY: (Math.random() - 0.5) * 5,
                    phase: Math.random() * Math.PI * 2
                });
            }
            // Sync routines for inputs
            function syncDFromSlider() {
                state.d = parseInt(sliderD.value);
                inputD.value = state.d;
                clearPresets();
                updateCalculations();
            }
            function syncDFromInput() {
                let val = parseInt(inputD.value);
                if (isNaN(val)) { val = 50; }
                if (val < 10) { val = 10; }
                if (val > 200) { val = 200; }
                state.d = val;
                inputD.value = val;
                sliderD.value = val;
                clearPresets();
                updateCalculations();
            }
            function syncVFromSlider() {
                state.v = parseFloat(sliderV.value) / 100;
                inputV.value = state.v.toFixed(2);
                clearPresets();
                updateCalculations();
            }
            function syncVFromInput() {
                let val = parseFloat(inputV.value);
                if (isNaN(val)) { val = 0.5; }
                if (val < 0.01) { val = 0.01; }
                if (val > 5.0) { val = 5.0; }
                state.v = val;
                inputV.value = val.toFixed(2);
                sliderV.value = Math.round(val * 100);
                clearPresets();
                updateCalculations();
            }
            function syncRhoFromSlider() {
                state.rho = parseInt(sliderRho.value);
                inputRho.value = state.rho;
                clearPresets();
                updateCalculations();
            }
            function syncRhoFromInput() {
                let val = parseInt(inputRho.value);
                if (isNaN(val)) { val = 1000; }
                if (val < 500) { val = 500; }
                if (val > 2000) { val = 2000; }
                state.rho = val;
                inputRho.value = val;
                sliderRho.value = val;
                clearPresets();
                updateCalculations();
            }
            function syncMuFromSlider() {
                state.mu = parseFloat(sliderMu.value) / 10;
                inputMu.value = state.mu.toFixed(1);
                clearPresets();
                updateCalculations();
            }
            function syncMuFromInput() {
                let val = parseFloat(inputMu.value);
                if (isNaN(val)) { val = 1.0; }
                if (val < 0.1) { val = 0.1; }
                if (val > 1000.0) { val = 1000.0; }
                state.mu = val;
                inputMu.value = val.toFixed(1);
                sliderMu.value = Math.round(val * 10);
                clearPresets();
                updateCalculations();
            }
            // Bind Events
            sliderD.addEventListener('input', syncDFromSlider);
            inputD.addEventListener('change', syncDFromInput);
            sliderV.addEventListener('input', syncVFromSlider);
            inputV.addEventListener('change', syncVFromInput);
            sliderRho.addEventListener('input', syncRhoFromSlider);
            inputRho.addEventListener('change', syncRhoFromInput);
            sliderMu.addEventListener('input', syncMuFromSlider);
            inputMu.addEventListener('change', syncMuFromInput);
            // Preset handler
            function loadPreset(presetKey) {
                clearPresets();
                presetButtons.forEach(function(btn) {
                    if (btn.dataset.preset === presetKey) {
                        btn.classList.add('active');
                    }
                });
                if (presetKey === 'water20') {
                    state.d = 50;
                    state.v = 0.50;
                    state.rho = 1000;
                    state.mu = 1.0;
                } else if (presetKey === 'oil46') {
                    state.d = 40;
                    state.v = 1.20;
                    state.rho = 870;
                    state.mu = 40.0;
                } else if (presetKey === 'glycerin') {
                    state.d = 80;
                    state.v = 2.50;
                    state.rho = 1260;
                    state.mu = 950.0;
                }
                // Sync UI
                inputD.value = state.d;
                sliderD.value = state.d;
                inputV.value = state.v.toFixed(2);
                sliderV.value = Math.round(state.v * 100);
                inputRho.value = state.rho;
                sliderRho.value = state.rho;
                inputMu.value = state.mu.toFixed(1);
                sliderMu.value = Math.round(state.mu * 10);
                updateCalculations();
            }
            function clearPresets() {
                presetButtons.forEach(function(btn) {
                    btn.classList.remove('active');
                });
            }
            presetButtons.forEach(function(btn) {
                btn.addEventListener('click', function() {
                    loadPreset(btn.dataset.preset);
                });
            });
            // Math calculations
            function updateCalculations() {
                // Re = rho * v * D_m / mu_Pa.s
                // D in mm -> D_m = D/1000
                // mu in cP -> mu_Pa.s = mu / 1000
                // Factors of 1000 cancel out completely: Re = rho * v * d / mu
                const dM = state.d / 1000;
                const muPa = state.mu / 1000;
                const re = (state.rho * state.v * dM) / muPa;
                txtRe.innerText = Math.round(re).toLocaleString();
                // Dynamic pressure Pd = 0.5 * rho * v^2
                const dynPress = 0.5 * state.rho * Math.pow(state.v, 2);
                miniDp.innerText = Math.round(dynPress).toLocaleString() + ' Pa';
                // Flow rate Q = A * v * 3600 (m³/h)
                const area = (Math.PI * Math.pow(dM, 2)) / 4;
                const flowRate = area * state.v * 3600;
                miniQ.innerText = flowRate.toFixed(2) + ' m³/h';
                // Kinematic viscosity nu = mu_Pa.s / rho * 10^6 (cSt)
                const nuCst = (muPa / state.rho) * 1e6;
                miniNu.innerText = nuCst.toFixed(2) + ' cSt';
                // Critical Velocity for Re = 2100
                const vc = (2100 * muPa) / (state.rho * dM);
                txtVc.innerText = vc.toFixed(3) + ' m/s';
                // Update styling based on regime
                reBox.className = 're-readout-box';
                if (re < 2100) {
                    reBox.classList.add('laminar');
                    txtFlowType.innerText = '층류 유동 (Laminar)';
                    txtForceBalance.innerText = '점성력(Viscous) 지배';
                } else if (re <= 4000) {
                    reBox.classList.add('transition');
                    txtFlowType.innerText = '천이 유동 (Transition)';
                    txtForceBalance.innerText = '임계 평형 상태';
                } else {
                    reBox.classList.add('turbulent');
                    txtFlowType.innerText = '난류 유동 (Turbulent)';
                    txtForceBalance.innerText = '관성력(Inertial) 지배';
                }
                // Update canvas scale text
                const txtCanvasScale = document.getElementById('txt-canvas-scale');
                if (txtCanvasScale) {
                    txtCanvasScale.innerText = `배관 내경: ${state.d} mm (실물 비례)`;
                }
            }
            // Canvas Animation loop
            function draw(currentTime) {
                requestAnimationFrame(draw);
                let dt = (currentTime - state.lastTime) / 1000;
                if (isNaN(dt)) { dt = 0.016; }
                if (dt < 0) { dt = 0.016; }
                if (dt > 0.1) { dt = 0.016; }
                state.lastTime = currentTime;
                const width = canvas.width;
                const height = canvas.height;
                ctx.clearRect(0, 0, width, height);
                // Grid background
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.04)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Geometry mapping
                // Map pipe diameter to canvas height: D: 10 ~ 200 mm mapped to 50 ~ 280 pixels
                const minScaleD = 10;
                const maxScaleD = 200;
                const minPixH = 60;
                const maxPixH = 280;
                const pipeHeight = minPixH + ((state.d - minScaleD) / (maxScaleD - minScaleD)) * (maxPixH - minPixH);
                const pipeTop = (height - pipeHeight) / 2;
                const pipeBottom = pipeTop + pipeHeight;
                const pipeCenter = height / 2;
                // Re calculation for animation speeds and styles
                const dM = state.d / 1000;
                const muPa = state.mu / 1000;
                const re = (state.rho * state.v * dM) / muPa;
                // Draw Pipe Wall
                ctx.save();
                ctx.fillStyle = 'rgba(241, 245, 249, 0.9)';
                ctx.fillRect(0, pipeTop, width, pipeHeight);
                // Pipe boundary outlines
                ctx.strokeStyle = '#94a3b8';
                ctx.lineWidth = 4;
                ctx.beginPath();
                ctx.moveTo(0, pipeTop);
                ctx.lineTo(width, pipeTop);
                ctx.moveTo(0, pipeBottom);
                ctx.lineTo(width, pipeBottom);
                ctx.stroke();
                ctx.restore();
                // Parabolic or Flat profile shape visualization overlay
                ctx.save();
                ctx.strokeStyle = 'rgba(0,0,0,0.08)';
                ctx.lineWidth = 2;
                ctx.setLineDash([4, 4]);
                ctx.beginPath();
                ctx.moveTo(0, pipeCenter);
                ctx.lineTo(width, pipeCenter);
                ctx.stroke();
                ctx.restore();
                // Streamlines
                ctx.save();
                let flowColor = '#0284c7';
                if (re > 2100) {
                    if (re <= 4000) {
                        flowColor = '#7c3aed';
                    } else {
                        flowColor = '#db2777';
                    }
                }
                // Draw 5 main streamline paths
                const numPaths = 5;
                for (let p = 0; p < numPaths; p++) {
                    const fracY = (p + 1) / (numPaths + 1); // 0.16 to 0.83
                    const yPos = pipeTop + pipeHeight * fracY;
                    // Velocity at this fractional depth (parabolic distribution factor)
                    // center velocity is highest
                    const rFrac = (fracY - 0.5) * 2; // -1.0 to 1.0
                    let vLocal = 1.0;
                    if (re < 2100) {
                        // Laminar parabolic
                        vLocal = 2 * (1 - rFrac * rFrac);
                    } else if (re >= 4000) {
                        // Turbulent flatter profile
                        vLocal = 1.2 * Math.pow(1 - Math.abs(rFrac), 1/7);
                    } else {
                        // Transition blended profile
                        const tRatio = (re - 2100) / 1900;
                        const vLam = 2 * (1 - rFrac * rFrac);
                        const vTurb = 1.2 * Math.pow(1 - Math.abs(rFrac), 1/7);
                        vLocal = vLam * (1 - tRatio) + vTurb * tRatio;
                    }
                    ctx.beginPath();
                    ctx.strokeStyle = flowColor;
                    ctx.globalAlpha = 0.25;
                    ctx.lineWidth = 2;
                    state.angleOffset += state.v * dt * 0.1;
                    for (let x = 0; x <= width; x += 10) {
                        let yOffset = 0;
                        if (re > 2100) {
                            if (re <= 4000) {
                                // Transition wave
                                const amp = pipeHeight * 0.03 * ((re - 2100)/1900);
                                yOffset = Math.sin(x * 0.05 - state.angleOffset * 40) * amp;
                            } else {
                                // Turbulent chaos waves
                                const amp = pipeHeight * 0.05;
                                yOffset = (Math.sin(x * 0.08 - state.angleOffset * 80) + Math.cos(x * 0.03 - state.angleOffset * 40)) * amp * 0.5;
                            }
                        }
                        if (x === 0) {
                            ctx.moveTo(x, yPos + yOffset);
                        } else {
                            ctx.lineTo(x, yPos + yOffset);
                        }
                    }
                    ctx.stroke();
                }
                ctx.restore();
                // Draw and animate particles
                ctx.save();
                ctx.fillStyle = flowColor;
                state.particles.forEach(function(p) {
                    // Fractional vertical position mapped to current animated tube height
                    const yReal = pipeTop + pipeHeight * p.y;
                    const rFrac = (p.y - 0.5) * 2; // -1 to 1
                    // Local velocity factor based on fluid physics profile
                    let vLocal = 1.0;
                    if (re < 2100) {
                        vLocal = 2 * (1 - rFrac * rFrac);
                    } else if (re >= 4000) {
                        vLocal = 1.2 * Math.pow(1 - Math.abs(rFrac), 1/7);
                    } else {
                        const tRatio = (re - 2100) / 1900;
                        const vLam = 2 * (1 - rFrac * rFrac);
                        const vTurb = 1.2 * Math.pow(1 - Math.abs(rFrac), 1/7);
                        vLocal = vLam * (1 - tRatio) + vTurb * tRatio;
                    }
                    // Base speed proportional to state velocity
                    const speedMultiplier = 150; 
                    const speed = state.v * speedMultiplier * vLocal;
                    p.x += speed * dt;
                    // Add lateral dispersion/agitation for non-laminar
                    let yDispersion = 0;
                    if (re > 2100) {
                        if (re <= 4000) {
                            const intensity = (re - 2100) / 1900;
                            yDispersion = Math.sin(p.x * 0.08 + p.phase) * intensity * 2;
                        } else {
                            // Turbulent highly chaotic agitation
                            const intensity = Math.min((re - 4000) / 20000, 1.0) + 1.0;
                            yDispersion = (Math.sin(p.x * 0.12 + p.phase) + Math.cos(p.x * 0.23 + p.phase * 2)) * intensity * 6;
                        }
                        // Damp lateral dispersion near the walls to follow fluid dynamics boundary layer
                        yDispersion = yDispersion * (1 - rFrac * rFrac);
                    }
                    // Wrap boundaries
                    if (p.x > width + 10) {
                        p.x = -10;
                        p.y = 0.1 + Math.random() * 0.8;
                    }
                    // Clamp to make sure particles stay inside the pipe outlines (taking outline thickness into account)
                    const finalY = Math.max(pipeTop + 2 + p.size, Math.min(pipeBottom - 2 - p.size, yReal + yDispersion));
                    ctx.beginPath();
                    ctx.arc(p.x, finalY, p.size, 0, Math.PI * 2);
                    ctx.fill();
                });
                ctx.restore();
            }
            // Copy & right-click protection with nested if statements (no logical AND)
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Init state
            loadPreset('water20');
            requestAnimationFrame(draw);
        })();
</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>유체 사양 및 물성치 선택: 프리셋 버튼을 클릭하거나 온도/밀도/점도 입력창을 통해 분석할 유체의 마찰적 특성을 설정합니다.</strong></li>
<li style="margin-bottom: 6px;">배관 직경 및 평균 유속 조정: 슬라이더나 정밀 입력 칸을 통해 배관의 내경(D)과 단면에서의 평균 유속(v)을 변경합니다.</li>
<li style="margin-bottom: 6px;">실시간 유동 가시화 관찰: 계산된 레이놀즈 수에 따라 캔버스 화면에서 층류(파란색 평행선), 천이(흔들리는 유선), 난류(붉은색 소용돌이 입자)의 실제 움직임을 눈으로 확인합니다.</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;" /> 레이놀즈 수 공식 유도 및 유공압 배관 층류/난류 판정 기준</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. 레이놀즈 수(Reynolds Number, Re)의 정의와 물리학적 의미</h3>
<p>레이놀즈 수(Reynolds Number, <code>Re</code>)는 유체역학에서 유체의 흐름 특성을 규정하는 가장 대표적인 무차원 수(Dimensionless Number)입니다. 1883년 영국의 물리·공학자 오스본 레이놀즈(Osborne Reynolds)가 발견하였으며, 유체의 **관성력(Inertial Force)**과 **점성력(Viscous Force)**의 상대적인 비를 나타냅니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">Re = 관성력 / 점성력 = (&rho; &times; v &times; D) / &mu; = (v &times; D) / &nu;</p><p>여기서 <code>&rho;</code>는 유체 밀도(kg/m³), <code>v</code>는 평균 유속(m/s), <code>D</code>는 대표 길이(배관 내경, m), <code>&mu;</code>는 유체의 절대 점도(Pa&middot;s), <code>&nu;</code>는 동점도(m²/s)를 뜻합니다. 점성력이 지배적인 흐름은 유선이 흐트러지지 않는 안정된 유동을 형성하지만, 관성력이 압도적으로 커지면 불규칙한 소용돌이와 강한 혼합이 발생하는 불안정한 유동으로 전이됩니다.</p>
<h3>2. 원관 유동에서의 유동 영역 구분 (Laminar, Transition, Turbulent)</h3>
<p>원형 배관 내부를 흐르는 정상 유동의 경우, 임계 레이놀즈 수(Critical Reynolds Number)를 경계로 유동 영역이 다음과 같이 3가지 영역으로 명확히 구분됩니다.</p><ul><li><strong>층류 영역 (Laminar Flow, Re &le; 2,100):</strong> 유체가 층을 이루어 서로 섞이지 않고 미끄러지듯 평행하게 흐르는 질서 정연한 상태입니다. 유로 중심부의 유속이 외곽부보다 빠른 포물선형(Parabolic) 속도 분포를 보입니다.</li><li><strong>천이 영역 (Transition Flow, 2,100 &lt; Re &lt; 4,000):</strong> 층류에서 난류로 바뀌는 불안정한 전이 구간입니다. 유속이나 압력의 작은 흔들림에도 유선이 물결처럼 요동치며 불안정한 대류가 시작됩니다.</li><li><strong>난류 영역 (Turbulent Flow, Re &ge; 4,000):</strong> 유체 입자가 고도로 무작위하고 불규칙하게 요동치며 소용돌이(Eddy)를 형성하고 흐르는 고에너지 유동 상태입니다. 유체 마찰 저항이 격격히 증가하며 단면의 유속 분포가 층류에 비해 비교적 편평해(Flat Profile)집니다.</li></ul>
<h3>3. 유체 온도에 따른 밀도 및 점도 변화 (온도 보정의 필요성)</h3>
<p>일반적으로 물이나 기름 같은 액체의 절대 점도는 온도가 올라감에 따라 크게 감소합니다. 예를 들어 20&deg;C 물의 절대 점도는 약 1.0 cP 이지만, 80&deg;C가 되면 약 0.35 cP 수준으로 3분의 1 이하로 급감합니다. 점도가 줄어들면 동일한 속도와 구경에서도 점성력 대비 관성력이 상대적으로 강해지므로, <strong>레이놀즈 수가 크게 증가하여 난류로 쉽게 전환</strong>됩니다. 따라서 배관 압력 손실 및 열교환 설계를 진행할 때는 단순 상온 기준의 물성치가 아닌 유체의 정확한 실시간 설계 온도를 반영한 동적 계산이 필수적입니다.</p>
        </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/reynolds-number-calculator-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>단면 2차 모멘트 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/moment-of-inertia-cross-sections-calculator/</link>
					<comments>https://myengnote.com/moment-of-inertia-cross-sections-calculator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Fri, 05 Jun 2026 22:50:07 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[단면2차모멘트]]></category>
		<category><![CDATA[단면계수]]></category>
		<category><![CDATA[도형면적]]></category>
		<category><![CDATA[재료역학]]></category>
		<guid isPermaLink="false">https://myengnote.com/moment-of-inertia-cross-sections-calculator/</guid>

					<description><![CDATA[사각형, 중공 사각형, I형강, 원형 단면의 폭, 높이, 두께를 실시간 설정하여 단면적, 도심축, 단면 2차 모멘트(Ix, Iy) 및 단면회전반경을 실시간 유도 분석하는 시뮬레이터입니다.]]></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;" /> 단면 2차 모멘트 계산기 &#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 moment-of-inertia-calculator-wrapper-v2 */
        /* Modern Reset and Design Tokens */
        .moment-of-inertia-calculator-wrapper-v2 *, .moment-of-inertia-calculator-wrapper-v2 *::before, .moment-of-inertia-calculator-wrapper-v2 *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .moment-of-inertia-calculator-wrapper-v2 {
            --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; /* Cobalt blue */
            --color-cyan-glow: rgba(2, 132, 199, 0.15);
            --color-magenta: #db2777; /* Pink/magenta */
            --color-magenta-glow: rgba(219, 39, 119, 0.15);
            --color-purple: #7c3aed;
            --color-purple-glow: rgba(124, 58, 237, 0.12);
            --color-success: #10b981;
            --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);
        }
        .moment-of-inertia-calculator-wrapper-v2 {
            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%;
        }
        .app-background-glow::after {
            background: var(--color-purple);
            bottom: -10%;
            left: -5%;
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
        }
        @media (max-width: 1200px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel, .simulation-panel, .results-panel {
                grid-column: auto;
            }
        }
        .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;
        }
        .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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .tab-group {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 6px;
            background: rgba(0, 0, 0, 0.02);
            padding: 4px;
            border-radius: 10px;
            border: 1px solid var(--color-border);
        }
        .tab-btn {
            background: transparent !important;
            border: none !important;
            padding: 8px 6px !important;
            font-size: 11px !important;
            font-weight: 700 !important;
            color: var(--color-text-muted) !important;
            border-radius: 6px !important;
            cursor: pointer !important;
            transition: all 0.2s ease !important;
            display: flex !important;
            align-items: center !important;
            justify-content: center !important;
            gap: 4px !important;
            box-shadow: none !important;
        }
        .tab-btn.active {
            background: #ffffff !important;
            color: var(--color-cyan) !important;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05) !important;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 8px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #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: rgba(255, 255, 255, 0.9);
            border: 1px solid var(--color-border);
            padding: 6px 12px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
            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;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(2, 132, 199, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .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);
            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);
            background: rgba(2, 132, 199, 0.02);
            border-color: rgba(2, 132, 199, 0.15);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: var(--color-cyan);
            border-color: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.05);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.moment-of-inertia-calculator-wrapper-v2 .app-main-grid,
.moment-of-inertia-calculator-wrapper-v2 .main-grid,
.moment-of-inertia-calculator-wrapper-v2 .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.moment-of-inertia-calculator-wrapper-v2 .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.moment-of-inertia-calculator-wrapper-v2 .simulation-panel,
.moment-of-inertia-calculator-wrapper-v2 .canvas-panel,
.moment-of-inertia-calculator-wrapper-v2 .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.moment-of-inertia-calculator-wrapper-v2 .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .ratio-readout-box,
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .re-readout-box,
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .status-readout-box,
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: auto !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: 1 / span 2 !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .formula-card {
    grid-column: 1 !important;
    grid-row: 2 !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .moment-of-inertia-calculator-wrapper-v2 .app-main-grid,
    .moment-of-inertia-calculator-wrapper-v2 .main-grid,
    .moment-of-inertia-calculator-wrapper-v2 .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .simulation-panel,
    .moment-of-inertia-calculator-wrapper-v2 .canvas-panel,
    .moment-of-inertia-calculator-wrapper-v2 .sim-panel,
    .moment-of-inertia-calculator-wrapper-v2 .canvas-section {
        order: -1 !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .ratio-readout-box,
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .re-readout-box,
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .status-readout-box,
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .moment-of-inertia-calculator-wrapper-v2 .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.moment-of-inertia-calculator-wrapper-v2 .app-container,
.moment-of-inertia-calculator-wrapper-v2 .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="moment-of-inertia-calculator-wrapper-v2" 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-shapes"></i></div>
                <div>
                    <h1>MOMENT OF INERTIA</h1>
                    <div class="subtitle">단면 2차 모멘트 계산기 &#038; 2D 기하 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">GEOMETRIC ENGINE 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>
                <!-- Unit Selector Group -->
                <div class="input-group" style="margin-bottom: 8px;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-ruler text-cyan"></i> 해석/입력 단위 (Unit)</label>
                    </div>
                    <div class="number-input-wrapper">
                        <select id="select-unit" style="width: 100%; border: none; outline: none; padding: 10px 14px; font-family: var(--font-body); font-size: 13px; font-weight: 700; background: #f8fafc; color: #0f172a; cursor: pointer;">
                            <option value="mm" selected>Milimeter (mm)</option>
                            <option value="cm">Centimeter (cm)</option>
                            <option value="m">Meter (m)</option>
                            <option value="in">Inch (in)</option>
                        </select>
                    </div>
                </div>
                <!-- Shape Selector Tabs -->
                <div class="tab-group" style="grid-template-columns: 1fr 1fr 1fr 1fr;">
                    <button id="tab-rect" class="tab-btn active">사각형</button>
                    <button id="tab-hollow" class="tab-btn">중공형</button>
                    <button id="tab-ibeam" class="tab-btn">I형강</button>
                    <button id="tab-circle" class="tab-btn">원형</button>
                </div>
                <!-- Dimension Input Groups (Dynamically displayed) -->
                <!-- Width b -->
                <div class="input-group" id="group-b">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-arrows-left-right text-cyan"></i> 단면 폭 (b)</label>
                        <span class="helper-text">가로 폭 (10 ~ 250 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-b" class="custom-number-input" min="10" max="250" value="120">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-b" class="custom-slider" min="10" max="250" value="120">
                </div>
                <!-- Height h -->
                <div class="input-group" id="group-h">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-arrows-up-down text-magenta"></i> 단면 높이 (h)</label>
                        <span class="helper-text">세로 높이 (10 ~ 250 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-h" class="custom-number-input" min="10" max="250" value="160">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-h" class="custom-slider" min="10" max="250" value="160">
                </div>
                <!-- Thickness t (Hollow Box) -->
                <div class="input-group" id="group-t" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-border-all text-purple"></i> 벽면 두께 (t)</label>
                        <span class="helper-text">외벽 (2 ~ 35 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-t" class="custom-number-input" min="2" max="35" value="12">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-t" class="custom-slider" min="2" max="35" value="12">
                </div>
                <!-- Flange Thickness tf (I-Beam) -->
                <div class="input-group" id="group-tf" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-grip-lines text-purple"></i> 플랜지 두께 (tf)</label>
                        <span class="helper-text">상하 두께 (2 ~ 30 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-tf" class="custom-number-input" min="2" max="30" value="15">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-tf" class="custom-slider" min="2" max="30" value="15">
                </div>
                <!-- Web Thickness tw (I-Beam) -->
                <div class="input-group" id="group-tw" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-grip-lines-vertical text-success"></i> 웨브 두께 (tw)</label>
                        <span class="helper-text">수직 두께 (2 ~ 30 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-tw" class="custom-number-input" min="2" max="30" value="10">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-tw" class="custom-slider" min="2" max="30" value="10">
                </div>
                <!-- Outer Diameter D (Circle) -->
                <div class="input-group" id="group-D" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle text-cyan"></i> 외경 지름 (D)</label>
                        <span class="helper-text">외곽 원 지름 (20 ~ 250 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-D-val" class="custom-number-input" min="20" max="250" value="180">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-D-val" class="custom-slider" min="20" max="250" value="180">
                </div>
                <!-- Inner Diameter d (Circle) -->
                <div class="input-group" id="group-d-val" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle-notch text-magenta"></i> 내경 지름 (d)</label>
                        <span class="helper-text">내부 홀 지름 (0 ~ 220 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d-val" class="custom-number-input" min="0" max="220" value="100">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-d-val" class="custom-slider" min="0" max="220" value="100">
                </div>
            </section>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <section class="panel simulation-panel">
                                <div class="panel-header">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-drafting-compass text-cyan"></i>
                                        <h2>실시간 단면 도면 &#038; 도심 축 맵</h2>
                                    </div>
                                    <div id="txt-centroid" class="canvas-scale-indicator">도심 좌표: X=0, Y=0</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas"></canvas>
                                    <!-- Overlay Floating Data -->
                                    <div class="canvas-overlay-data">
                                        <div class="overlay-item">
                                            <span class="label">도형 면적 (A):</span>
                                            <span class="value text-cyan" id="txt-area-overlay">0.0 mm²</span>
                                        </div>
                                    </div>
                                </div>
                                <!-- Mini Metrics -->
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <div class="label">단면회전반경 (rx)</div>
                                        <div id="txt-rx" class="value">0.0 mm</div>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <div class="label">단면회전반경 (ry)</div>
                                        <div id="txt-ry" class="value">0.0 mm</div>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="ratio-readout-box">
                                    <div class="ratio-title">강성축 단면2차모멘트 (Ix)</div>
                                    <div id="txt-ix" class="ratio-value">0.0 cm⁴</div>
                                    <div id="txt-ix-mm" class="ratio-type">0.0 mm⁴</div>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-ruler-combined text-cyan"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">약축 단면2차모멘트 (Iy)</span>
                                            <span id="txt-iy" class="card-value">0.0 cm⁴</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-border-top-left text-magenta"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">전체 단면적 (A)</span>
                                            <span id="txt-area" class="card-value">0.0 mm²</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-gauge-simple-high text-purple"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">단면 계수 (Zx = Ix / (h/2))</span>
                                            <span id="txt-zx" class="card-value">0.0 cm³</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-percentage text-success"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">단면 효율비 (Ix / Area)</span>
                                            <span id="txt-ratio" class="card-value">0.00 mm²</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                    <h4>단면 관성모멘트 공식</h4>
                                    <div class="formula-equation" id="formula-txt">Ix = bh³ / 12 <br> Iy = hb³ / 12</div>
                                </div>
                            </div>
                </section>
            </div>
        </main>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    <!-- Physics & Animation Engine -->
    </div>
</div>
<script>
        (function() {
        let isInit = false;
        function initSimulator() {
            if (isInit) return;
            const canvas = document.getElementById('physics-canvas');
            if (!canvas) return;
            const ctx = canvas.getContext('2d');
            if (!ctx) return;
            isInit = true;
            window.__moment_initialized = true;
            // DOM Elements
            const tabRect = document.getElementById('tab-rect');
            const tabHollow = document.getElementById('tab-hollow');
            const tabIbeam = document.getElementById('tab-ibeam');
            const tabCircle = document.getElementById('tab-circle');
            const groupB = document.getElementById('group-b');
            const groupH = document.getElementById('group-h');
            const groupT = document.getElementById('group-t');
            const groupTf = document.getElementById('group-tf');
            const groupTw = document.getElementById('group-tw');
            const groupD = document.getElementById('group-D');
            const groupDval = document.getElementById('group-d-val');
            const sliderB = document.getElementById('slider-b');
            const inputB = document.getElementById('input-b');
            const sliderH = document.getElementById('slider-h');
            const inputH = document.getElementById('input-h');
            const sliderT = document.getElementById('slider-t');
            const inputT = document.getElementById('input-t');
            const sliderTf = document.getElementById('slider-tf');
            const inputTf = document.getElementById('input-tf');
            const sliderTw = document.getElementById('slider-tw');
            const inputTw = document.getElementById('input-tw');
            const sliderD = document.getElementById('slider-D-val');
            const inputD = document.getElementById('input-D-val');
            const sliderDval = document.getElementById('slider-d-val');
            const inputDval = document.getElementById('input-d-val');
            const txtCentroid = document.getElementById('txt-centroid');
            const txtAreaOverlay = document.getElementById('txt-area-overlay');
            const txtRx = document.getElementById('txt-rx');
            const txtRy = document.getElementById('txt-ry');
            const txtIx = document.getElementById('txt-ix');
            const txtIxMm = document.getElementById('txt-ix-mm');
            const txtIy = document.getElementById('txt-iy');
            const txtArea = document.getElementById('txt-area');
            const txtZx = document.getElementById('txt-zx');
            const txtRatio = document.getElementById('txt-ratio');
            const formulaTxt = document.getElementById('formula-txt');
            // State variables
            const state = {
                type: 'rect', // rect, hollow, ibeam, circle
                unit: 'mm',
                b: 120,
                h: 160,
                t: 12,
                tf: 15,
                tw: 10,
                D: 180,
                d: 100,
                time: 0
            };
            const unitScales = {
                mm: { factor: 1.0, label: 'mm', label2: 'mm²', label3: 'mm³', label4: 'mm⁴', decimals: 0, step: 1, range: { b: [10, 250], h: [10, 250], t: [2, 35], tf: [2, 30], tw: [2, 30], D: [20, 250], d: [0, 220] } },
                cm: { factor: 10.0, label: 'cm', label2: 'cm²', label3: 'cm³', label4: 'cm⁴', decimals: 1, step: 0.1, range: { b: [1.0, 25.0], h: [1.0, 25.0], t: [0.2, 3.5], tf: [0.2, 3.0], tw: [0.2, 3.0], D: [2.0, 25.0], d: [0.0, 22.0] } },
                m: { factor: 1000.0, label: 'm', label2: 'm²', label3: 'm³', label4: 'm⁴', decimals: 3, step: 0.001, range: { b: [0.01, 0.25], h: [0.01, 0.25], t: [0.002, 0.035], tf: [0.002, 0.030], tw: [0.002, 0.030], D: [0.02, 0.25], d: [0.0, 0.22] } },
                in: { factor: 25.4, label: 'in', label2: 'in²', label3: 'in³', label4: 'in⁴', decimals: 2, step: 0.05, range: { b: [0.4, 10.0], h: [0.4, 10.0], t: [0.08, 1.4], tf: [0.08, 1.2], tw: [0.08, 1.2], D: [0.8, 10.0], d: [0.0, 8.8] } }
            };
            const selectUnit = document.getElementById('select-unit');
            // Canvas Resizing
            function resizeCanvas() {
                const rect = canvas.getBoundingClientRect();
                const dpr = window.devicePixelRatio || 1;
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.scale(dpr, dpr);
            }
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);
            setTimeout(resizeCanvas, 300);
            // Unit change handler
            function onUnitChange(newUnit, prevUnit) {
                const prevScale = unitScales[prevUnit];
                const newScale = unitScales[newUnit];
                const ratio = prevScale.factor / newScale.factor;
                // 1. Convert state values
                const keys = ['b', 'h', 't', 'tf', 'tw', 'D', 'd'];
                keys.forEach(k => {
                    state[k] = state[k] * ratio;
                });
                // 2. Refresh input min/max/step/value properties
                const updateInputProps = (slider, input, key) => {
                    const range = newScale.range[key];
                    slider.min = range[0];
                    slider.max = range[1];
                    slider.step = newScale.step;
                    input.min = range[0];
                    input.max = range[1];
                    input.step = newScale.step;
                    slider.value = state[key];
                    input.value = state[key].toFixed(newScale.decimals);
                };
                updateInputProps(sliderB, inputB, 'b');
                updateInputProps(sliderH, inputH, 'h');
                updateInputProps(sliderT, inputT, 't');
                updateInputProps(sliderTf, inputTf, 'tf');
                updateInputProps(sliderTw, inputTw, 'tw');
                updateInputProps(sliderD, inputD, 'D');
                updateInputProps(sliderDval, inputDval, 'd');
                // 3. Update input unit badges
                document.querySelectorAll('.unit-badge').forEach(badge => {
                    badge.innerText = newScale.label;
                });
                // 4. Update slider helper text descriptions
                const helperB = groupB.querySelector('.helper-text');
                if (helperB) helperB.innerText = `가로 폭 (${newScale.range.b[0]} ~ ${newScale.range.b[1]} ${newScale.label})`;
                const helperH = groupH.querySelector('.helper-text');
                if (helperH) helperH.innerText = `세로 높이 (${newScale.range.h[0]} ~ ${newScale.range.h[1]} ${newScale.label})`;
                const helperT = groupT.querySelector('.helper-text');
                if (helperT) helperT.innerText = `외벽 (${newScale.range.t[0]} ~ ${newScale.range.t[1]} ${newScale.label})`;
                const helperTf = groupTf.querySelector('.helper-text');
                if (helperTf) helperTf.innerText = `상하 두께 (${newScale.range.tf[0]} ~ ${newScale.range.tf[1]} ${newScale.label})`;
                const helperTw = groupTw.querySelector('.helper-text');
                if (helperTw) helperTw.innerText = `수직 두께 (${newScale.range.tw[0]} ~ ${newScale.range.tw[1]} ${newScale.label})`;
                const helperD = groupD.querySelector('.helper-text');
                if (helperD) helperD.innerText = `외곽 원 지름 (${newScale.range.D[0]} ~ ${newScale.range.D[1]} ${newScale.label})`;
                const helperd = groupDval.querySelector('.helper-text');
                if (helperd) helperd.innerText = `내부 홀 지름 (${newScale.range.d[0]} ~ ${newScale.range.d[1]} ${newScale.label})`;
                state.unit = newUnit;
                updateCalculations();
            }
            selectUnit.addEventListener('change', function() {
                const prevUnit = state.unit;
                const newUnit = selectUnit.value;
                if (prevUnit !== newUnit) {
                    onUnitChange(newUnit, prevUnit);
                }
            });
            // Sync methods
            function makeSync(slider, input, key, callback) {
                slider.addEventListener('input', function() {
                    let val = parseFloat(slider.value);
                    const scale = unitScales[state.unit];
                    input.value = val.toFixed(scale.decimals);
                    state[key] = val;
                    if (callback) callback();
                    updateCalculations();
                });
                input.addEventListener('change', function() {
                    let val = parseFloat(input.value);
                    const min = parseFloat(input.min);
                    const max = parseFloat(input.max);
                    if (isNaN(val)) { val = min; }
                    if (val < min) { val = min; }
                    if (val > max) { val = max; }
                    const scale = unitScales[state.unit];
                    slider.value = val;
                    input.value = val.toFixed(scale.decimals);
                    state[key] = val;
                    if (callback) callback();
                    updateCalculations();
                });
            }
            // Sync all inputs
            makeSync(sliderB, inputB, 'b');
            makeSync(sliderH, inputH, 'h');
            makeSync(sliderT, inputT, 't', validateHollow);
            makeSync(sliderTf, inputTf, 'tf', validateIBeam);
            makeSync(sliderTw, inputTw, 'tw', validateIBeam);
            makeSync(sliderD, inputD, 'D', validateCircle);
            makeSync(sliderDval, inputDval, 'd', validateCircle);
            function validateHollow() {
                // Ensure 2t < b and 2t < h to avoid overlap
                const maxT = Math.min(state.b, state.h) / 2 - 2;
                if (state.t > maxT) {
                    state.t = Math.floor(maxT);
                    sliderT.value = state.t;
                    inputT.value = state.t;
                }
            }
            function validateIBeam() {
                // Ensure 2tf < h
                const maxTf = state.h / 2 - 5;
                if (state.tf > maxTf) {
                    state.tf = Math.floor(maxTf);
                    sliderTf.value = state.tf;
                    inputTf.value = state.tf;
                }
                // Ensure tw < b
                const maxTw = state.b - 10;
                if (state.tw > maxTw) {
                    state.tw = Math.floor(maxTw);
                    sliderTw.value = state.tw;
                    inputTw.value = state.tw;
                }
            }
            function validateCircle() {
                // Ensure d < D
                const maxD = state.D - 10;
                sliderDval.max = maxD;
                inputDval.max = maxD;
                if (state.d > maxD) {
                    state.d = maxD;
                    sliderDval.value = state.d;
                    inputDval.value = state.d;
                }
            }
            // Tab Handlers
            function hideAllControls() {
                groupB.style.display = 'none';
                groupH.style.display = 'none';
                groupT.style.display = 'none';
                groupTf.style.display = 'none';
                groupTw.style.display = 'none';
                groupD.style.display = 'none';
                groupDval.style.display = 'none';
            }
            tabRect.addEventListener('click', function() {
                hideAllControls();
                tabRect.classList.add('active');
                tabHollow.classList.remove('active');
                tabIbeam.classList.remove('active');
                tabCircle.classList.remove('active');
                groupB.style.display = 'flex';
                groupH.style.display = 'flex';
                state.type = 'rect';
                updateCalculations();
            });
            tabHollow.addEventListener('click', function() {
                hideAllControls();
                tabHollow.classList.add('active');
                tabRect.classList.remove('active');
                tabIbeam.classList.remove('active');
                tabCircle.classList.remove('active');
                groupB.style.display = 'flex';
                groupH.style.display = 'flex';
                groupT.style.display = 'flex';
                state.type = 'hollow';
                validateHollow();
                updateCalculations();
            });
            tabIbeam.addEventListener('click', function() {
                hideAllControls();
                tabIbeam.classList.add('active');
                tabRect.classList.remove('active');
                tabHollow.classList.remove('active');
                tabCircle.classList.remove('active');
                groupB.style.display = 'flex';
                groupH.style.display = 'flex';
                groupTf.style.display = 'flex';
                groupTw.style.display = 'flex';
                state.type = 'ibeam';
                validateIBeam();
                updateCalculations();
            });
            tabCircle.addEventListener('click', function() {
                hideAllControls();
                tabCircle.classList.add('active');
                tabRect.classList.remove('active');
                tabHollow.classList.remove('active');
                tabIbeam.classList.remove('active');
                groupD.style.display = 'flex';
                groupDval.style.display = 'flex';
                state.type = 'circle';
                validateCircle();
                updateCalculations();
            });
            // Calculations
            function updateCalculations() {
                const scale = unitScales[state.unit];
                const factor = scale.factor; // 1 selected unit = factor mm
                // Convert inputs to base mm for physical equations
                const b_mm = state.b * factor;
                const h_mm = state.h * factor;
                const t_mm = state.t * factor;
                const tf_mm = state.tf * factor;
                const tw_mm = state.tw * factor;
                const D_mm = state.D * factor;
                const d_mm = state.d * factor;
                let A_mm = 0.0;
                let Ix_mm = 0.0;
                let Iy_mm = 0.0;
                let cY_mm = 0.0;
                let cX_mm = 0.0;
                if (state.type === 'rect') {
                    A_mm = b_mm * h_mm;
                    Ix_mm = (b_mm * Math.pow(h_mm, 3)) / 12;
                    Iy_mm = (h_mm * Math.pow(b_mm, 3)) / 12;
                    cY_mm = h_mm / 2;
                    cX_mm = b_mm / 2;
                    formulaTxt.innerHTML = 'Ix = bh³ / 12 <br> Iy = hb³ / 12';
                } else if (state.type === 'hollow') {
                    const bi = b_mm - 2 * t_mm;
                    const hi = h_mm - 2 * t_mm;
                    A_mm = b_mm * h_mm - bi * hi;
                    Ix_mm = (b_mm * Math.pow(h_mm, 3) - bi * Math.pow(hi, 3)) / 12;
                    Iy_mm = (h_mm * Math.pow(b_mm, 3) - hi * Math.pow(bi, 3)) / 12;
                    cY_mm = h_mm / 2;
                    cX_mm = b_mm / 2;
                    formulaTxt.innerText = 'Ix = (bh³ - b_i h_i³) / 12 (감산 모델)';
                } else if (state.type === 'ibeam') {
                    A_mm = 2 * b_mm * tf_mm + (h_mm - 2 * tf_mm) * tw_mm;
                    Ix_mm = (b_mm * Math.pow(h_mm, 3)) / 12 - ((b_mm - tw_mm) * Math.pow(h_mm - 2 * tf_mm, 3)) / 12;
                    Iy_mm = (2 * tf_mm * Math.pow(b_mm, 3)) / 12 + ((h_mm - 2 * tf_mm) * Math.pow(tw_mm, 3)) / 12;
                    cY_mm = h_mm / 2;
                    cX_mm = b_mm / 2;
                    formulaTxt.innerText = 'Ix = [bh³ - (b-tw)(h-2tf)³] / 12';
                } else if (state.type === 'circle') {
                    A_mm = Math.PI * (Math.pow(D_mm, 2) - Math.pow(d_mm, 2)) / 4;
                    Ix_mm = Math.PI * (Math.pow(D_mm, 4) - Math.pow(d_mm, 4)) / 64;
                    Iy_mm = Ix_mm;
                    cY_mm = D_mm / 2;
                    cX_mm = D_mm / 2;
                    formulaTxt.innerText = 'Ix = Iy = π(D⁴ - d⁴) / 64';
                }
                // Centroid coordinates
                const cX_out = cX_mm / factor;
                const cY_out = cY_mm / factor;
                // Area in selected unit^2
                const A_out = A_mm / Math.pow(factor, 2);
                // Section properties
                let rx_out = 0.0;
                let ry_out = 0.0;
                if (A_mm > 0) {
                    rx_out = Math.sqrt(Ix_mm / A_mm) / factor;
                    ry_out = Math.sqrt(Iy_mm / A_mm) / factor;
                }
                const maxH_mm = state.type === 'circle' ? D_mm : h_mm;
                const Zx_mm = Ix_mm / (maxH_mm / 2);
                const Zx_out = Zx_mm / Math.pow(factor, 3);
                const ratio_out = (Ix_mm / A_mm) / Math.pow(factor, 2);
                // Display texts
                txtCentroid.innerText = `도심 중립점: X_bar = ${cX_out.toFixed(1)} ${scale.label}, Y_bar = ${cY_out.toFixed(1)} ${scale.label}`;
                txtAreaOverlay.innerText = A_out.toLocaleString(undefined, {maximumFractionDigits: scale.decimals + 1}) + ` ${scale.label2}`;
                txtArea.innerText = A_out.toLocaleString(undefined, {maximumFractionDigits: scale.decimals + 1}) + ` ${scale.label2}`;
                txtRx.innerText = rx_out.toFixed(2) + ` ${scale.label}`;
                txtRy.innerText = ry_out.toFixed(2) + ` ${scale.label}`;
                txtZx.innerText = Zx_out.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 3}) + ` ${scale.label3}`;
                txtRatio.innerText = ratio_out.toFixed(1) + ` ${scale.label2}`;
                // Update Zx formula card label dynamically
                const zxCard = txtZx.closest('.result-card');
                if (zxCard) {
                    const zxUnit = zxCard.querySelector('.card-unit');
                    if (zxUnit) {
                        zxUnit.innerText = state.type === 'circle' ? '단면 계수 (Zx = Ix / (D/2))' : '단면 계수 (Zx = Ix / (h/2))';
                    }
                }
                if (state.unit === 'mm') {
                    // Maintain standard cm4 and mm4 split representation for mm inputs
                    const Ix_cm4 = Ix_mm / 10000;
                    const Iy_cm4 = Iy_mm / 10000;
                    const ratioReadoutBox = txtIx.closest('.ratio-readout-box');
                    if (ratioReadoutBox) {
                        const ratioTitle = ratioReadoutBox.querySelector('.ratio-title');
                        if (ratioTitle) ratioTitle.innerText = '강성축 단면2차모멘트 (Ix)';
                    }
                    txtIx.innerText = Ix_cm4.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' cm⁴';
                    txtIxMm.innerText = Ix_mm.toLocaleString(undefined, {maximumFractionDigits: 0}) + ' mm⁴';
                    txtIxMm.style.display = 'block';
                    const iyCard = txtIy.closest('.result-card');
                    if (iyCard) {
                        const iyUnit = iyCard.querySelector('.card-unit');
                        if (iyUnit) iyUnit.innerText = '약축 단면2차모멘트 (Iy)';
                    }
                    txtIy.innerText = Iy_cm4.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 2}) + ' cm⁴';
                } else {
                    // For other scales, show outputs in selected unit^4 and hide secondary labels
                    const Ix_out = Ix_mm / Math.pow(factor, 4);
                    const Iy_out = Iy_mm / Math.pow(factor, 4);
                    const ratioReadoutBox = txtIx.closest('.ratio-readout-box');
                    if (ratioReadoutBox) {
                        const ratioTitle = ratioReadoutBox.querySelector('.ratio-title');
                        if (ratioTitle) ratioTitle.innerText = `강성축 단면2차모멘트 (Ix, ${scale.label4})`;
                    }
                    txtIx.innerText = Ix_out.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 4}) + ` ${scale.label4}`;
                    txtIxMm.style.display = 'none';
                    const iyCard = txtIy.closest('.result-card');
                    if (iyCard) {
                        const iyUnit = iyCard.querySelector('.card-unit');
                        if (iyUnit) iyUnit.innerText = `약축 단면2차모멘트 (Iy, ${scale.label4})`;
                    }
                    txtIy.innerText = Iy_out.toLocaleString(undefined, {minimumFractionDigits: 2, maximumFractionDigits: 4}) + ` ${scale.label4}`;
                }
            }
            // Animation Loop
            function animate() {
                requestAnimationFrame(animate);
                state.time += 0.05;
                const width = canvas.width / (window.devicePixelRatio || 1);
                const height = canvas.height / (window.devicePixelRatio || 1);
                ctx.clearRect(0, 0, width, height);
                // 1. Draw snow white blueprint grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // 2. Cross section sketch scaling
                // Scale shape so it fills ~65% of canvas area
                const canvasCX = width / 2;
                const canvasCY = height / 2;
                const b = state.b;
                const h = state.h;
                // Dynamic auto-zoom: fit shape to canvas size
                const maxDrawDim = Math.min(width, height) * 0.65;
                let scale = 1.0;
                if (state.type === 'circle') {
                    scale = maxDrawDim / state.D;
                } else {
                    scale = Math.min(maxDrawDim / b, maxDrawDim / h);
                }
                ctx.save();
                ctx.translate(canvasCX, canvasCY);
                // Shading styles
                ctx.fillStyle = 'rgba(2, 132, 199, 0.15)'; // primary transparent cyan
                ctx.strokeStyle = '#0284c7';
                ctx.lineWidth = 2.5;
                if (state.type === 'rect') {
                    const dw = b * scale;
                    const dh = h * scale;
                    ctx.beginPath();
                    ctx.rect(-dw/2, -dh/2, dw, dh);
                    ctx.fill();
                    ctx.stroke();
                } else {
                    if (state.type === 'hollow') {
                        const dw = b * scale;
                        const dh = h * scale;
                        const dwInner = (b - 2*state.t) * scale;
                        const dhInner = (h - 2*state.t) * scale;
                        // Draw outer rectangular path clockwise
                        ctx.beginPath();
                        ctx.rect(-dw/2, -dh/2, dw, dh);
                        // Draw inner rectangular path counter-clockwise to subtract
                        ctx.rect(dwInner/2, -dhInner/2, -dwInner, dhInner);
                        ctx.fill();
                        // Outer border stroke
                        ctx.beginPath();
                        ctx.rect(-dw/2, -dh/2, dw, dh);
                        ctx.stroke();
                        // Inner border stroke
                        ctx.strokeStyle = '#db2777';
                        ctx.lineWidth = 1.5;
                        ctx.beginPath();
                        ctx.rect(-dwInner/2, -dhInner/2, dwInner, dhInner);
                        ctx.stroke();
                    } else {
                        if (state.type === 'ibeam') {
                            const dw = b * scale;
                            const dh = h * scale;
                            const dtf = state.tf * scale;
                            const dtw = state.tw * scale;
                            ctx.beginPath();
                            // Trace H shape clockwise
                            ctx.moveTo(-dw/2, -dh/2); // Top flange left
                            ctx.lineTo(dw/2, -dh/2);  // Top flange right
                            ctx.lineTo(dw/2, -dh/2 + dtf);
                            ctx.lineTo(dtw/2, -dh/2 + dtf);
                            ctx.lineTo(dtw/2, dh/2 - dtf);
                            ctx.lineTo(dw/2, dh/2 - dtf);
                            ctx.lineTo(dw/2, dh/2);
                            ctx.lineTo(-dw/2, dh/2);
                            ctx.lineTo(-dw/2, dh/2 - dtf);
                            ctx.lineTo(-dtw/2, dh/2 - dtf);
                            ctx.lineTo(-dtw/2, -dh/2 + dtf);
                            ctx.lineTo(-dw/2, -dh/2 + dtf);
                            ctx.closePath();
                            ctx.fill();
                            ctx.stroke();
                        } else {
                            if (state.type === 'circle') {
                                const dOut = state.D * scale / 2;
                                const dIn = state.d * scale / 2;
                                ctx.beginPath();
                                ctx.arc(0, 0, dOut, 0, Math.PI*2, false);
                                if (dIn > 0) {
                                    ctx.arc(0, 0, dIn, 0, Math.PI*2, true); // inner cut
                                }
                                ctx.fill();
                                ctx.beginPath();
                                ctx.arc(0, 0, dOut, 0, Math.PI*2);
                                ctx.stroke();
                                if (dIn > 0) {
                                    ctx.strokeStyle = '#db2777';
                                    ctx.lineWidth = 1.5;
                                    ctx.beginPath();
                                    ctx.arc(0, 0, dIn, 0, Math.PI*2);
                                    ctx.stroke();
                                }
                            }
                        }
                    }
                }
                // 3. Draw Centroid axes
                ctx.save();
                ctx.setLineDash([4, 4]);
                // X-X Axis (horizontal)
                ctx.strokeStyle = '#0284c7';
                ctx.lineWidth = 1.2;
                ctx.beginPath();
                ctx.moveTo(-150, 0);
                ctx.lineTo(150, 0);
                ctx.stroke();
                // Y-Y Axis (vertical)
                ctx.strokeStyle = '#7c3aed';
                ctx.lineWidth = 1.2;
                ctx.beginPath();
                ctx.moveTo(0, -150);
                ctx.lineTo(0, 150);
                ctx.stroke();
                // Centroid central cross dot
                ctx.setLineDash([]);
                ctx.fillStyle = '#db2777';
                ctx.beginPath();
                ctx.arc(0, 0, 4, 0, Math.PI * 2);
                ctx.fill();
                ctx.restore();
                // 4. Labeling dimension cues
                const scaleObj = unitScales[state.unit];
                const formatVal = (val) => val.toFixed(scaleObj.decimals);
                ctx.fillStyle = '#94a3b8';
                ctx.font = '500 10px Inter, sans-serif';
                ctx.textAlign = 'center';
                if (state.type !== 'circle') {
                    // Width label
                    ctx.fillText('b = ' + formatVal(state.b) + ' ' + scaleObj.label, 0, (h*scale/2) + 20);
                    // Height label
                    ctx.save();
                    ctx.translate((-b*scale/2) - 18, 0);
                    ctx.rotate(-Math.PI/2);
                    ctx.fillText('h = ' + formatVal(state.h) + ' ' + scaleObj.label, 0, 0);
                    ctx.restore();
                } else {
                    ctx.fillText('D = ' + formatVal(state.D) + ' ' + scaleObj.label, 0, (state.D*scale/2) + 20);
                }
                ctx.restore();
            }
            // Document security
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            updateCalculations();
            animate();
        }
        let initAttempts = 0;
        function tryInit() {
            initAttempts++;
            initSimulator();
            if (!window.__moment_initialized) {
                if (initAttempts < 50) {
                    setTimeout(tryInit, 100);
                }
            }
        }
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            tryInit();
        } else {
            document.addEventListener('DOMContentLoaded', tryInit);
            window.addEventListener('load', tryInit);
        }
        })();
</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>단면 형상 선택: 상단 탭에서 사각형(Rectangle), 중공 사각형(Hollow Box), I형강(I-Beam), 원형(Circle) 중 원하는 형상을 선택합니다.</strong></li>
<li style="margin-bottom: 6px;">기하학적 수치 가변: 단면 폭(b), 높이(h), 플랜지/웨브 두께(tf, tw) 혹은 원형 지름(D, d) 슬라이더를 부드럽게 이동하여 치수를 조절합니다.</li>
<li style="margin-bottom: 6px;">도심 및 관성축 실시간 변동 관찰: 단면 크기가 변할 때, 2D 도면에 투영되는 도심(Centroid) 위치 및 중립축(Neutral Axis X-X, Y-Y)이 연동해서 움직이는 모습을 실시간으로 관찰합니다.</li>
<li style="margin-bottom: 6px;">단면 강성 지표 분석: 최종 연산 영역에서 단면적(A), 중립축 기준의 단면 2차 모멘트(Ix, Iy) 및 회전반경(rx, ry) 계측치를 기반으로 휨 강성을 평가합니다.</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;" /> 도형별 단면 2차 모멘트 상세 공식 및 도심축 물리적 해설 확인하기</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. 단면 2차 모멘트(Moment of Inertia)의 물리학적 정의와 역할</h3>
<p>구조재의 역학적 거동에서 <strong>단면 2차 모멘트(Moment of Inertia, I)</strong>는 보에 휨 모멘트(Bending Moment)가 작용할 때 부재의 단면이 변형에 대해 저항하려는 강성을 대변하는 순수 기하학적 척도입니다. 즉, 재료 고유의 강도(탄성계수)가 동일하더라도 형상을 다르게 하면 휨 저항성(강성)을 수십 배 이상 늘릴 수 있으며, 이때 단면 형상이 기여하는 핵심 상수가 바로 단면 2차 모멘트입니다.</p><p>수학적으로 단면 2차 모멘트는 미소 면적 <code>dA</code>에 도심 축으로부터의 거리의 제곱(<code>y²</code>)을 가중하여 단면 전체에 대해 적분한 값입니다:</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">I_x = &int; y&sup2; dA &nbsp;,&nbsp; I_y = &int; x&sup2; dA &nbsp;[mm⁴]</p>
<h3>2. 엔지니어링 대표 도형별 단면 2차 모멘트 핵심 공식</h3>
<p>실무 설계에서 쓰이는 표준 단면들의 중립축 기준 관성 모멘트 공식은 다음과 같습니다.</p><p><strong>① 직사각형 단면 (Rectangle - 폭 b, 높이 h):</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">I_x = (b &times; h&sup3;) / 12 &nbsp;,&nbsp; I_y = (h &times; b&sup3;) / 12</p><p><strong>② 원형 단면 (Solid Circle - 외경 D):</strong></p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">I_x = I_y = (&pi; &times; D⁴) / 64</p><p><strong>③ I형강 단면 (I-Beam - 플랜지 폭 b, 전체 높이 h, 웨브 두께 tw, 플랜지 두께 tf):</strong></p><p>외부 큰 사각형 영역(폭 b, 높이 h)에서 좌우로 비어있는 두 직사각형 공간[폭 <code>(b - tw)</code>, 높이 <code>(h - 2tf)</code>]을 감산하는 방식으로 고정 관성을 신속하게 유도합니다.</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">I_x = (b &times; h&sup3;) / 12 - (b - t_w) &times; (h - 2t_f)&sup3; / 12 &nbsp;[mm⁴]</p>
<h3>3. 평행축 정리(Parallel Axis Theorem) 및 단면 성능 확보 전략</h3>
<p>임의의 복합 단면의 관성 모멘트를 구할 때는 도심 축이 일치하지 않는 구성 부재들의 영향을 환산하기 위해 <strong>평행축 정리(Parallel Axis Theorem)</strong>를 필수적으로 적용합니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">I = I_c + A &times; d&sup2;</p><p>여기서 <code>I_c</code>는 부재 자체 도심에서의 단면 2차 모멘트이며, <code>A</code>는 단면적, <code>d</code>는 부재 도심에서 전체 복합 단면 도심 축까지의 수직 거리입니다. 이 정리에 따라, 자재의 양(단면적 A)을 높이지 않더라도 중립축에서 먼 거리에 플랜지 등 질량을 의도적으로 이격시키면(<code>d</code> 극대화) H형강이나 I형강처럼 매우 가볍고도 극도의 휨 저항 강성을 구현하는 최적화 보 설계가 가능해집니다.</p>
        </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/moment-of-inertia-cross-sections-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>안전계수 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/safety-factor-failure-theories-calculator/</link>
					<comments>https://myengnote.com/safety-factor-failure-theories-calculator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Thu, 04 Jun 2026 23:51:52 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[본미세스]]></category>
		<category><![CDATA[안전율]]></category>
		<category><![CDATA[재료역학]]></category>
		<category><![CDATA[주응력]]></category>
		<category><![CDATA[파괴이론]]></category>
		<guid isPermaLink="false">https://myengnote.com/safety-factor-failure-theories-calculator/</guid>

					<description><![CDATA[항복강도, 인장/압축강도 및 2차원 응력 상태(σx, σy, τxy)를 실시간 조절하여 주응력 및 본 미세스 등가응력을 산출하고, 연성(Von Mises 타원) 및 취성(Coulomb-Mohr) 파괴 모형 엔벨로프와 안전율을 시각화 분석하는 시뮬레이터입니다.]]></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; 시뮬레이터</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 safety-factor-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .safety-factor-calculator-wrapper *, .safety-factor-calculator-wrapper *::before, .safety-factor-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .safety-factor-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; /* Cobalt blue */
            --color-cyan-glow: rgba(2, 132, 199, 0.15);
            --color-magenta: #db2777; /* Pink/magenta */
            --color-magenta-glow: rgba(219, 39, 119, 0.15);
            --color-purple: #7c3aed;
            --color-purple-glow: rgba(124, 58, 237, 0.12);
            --color-success: #10b981;
            --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);
        }
        .safety-factor-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%;
        }
        .app-background-glow::after {
            background: var(--color-purple);
            bottom: -10%;
            left: -5%;
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
        }
        @media (max-width: 1200px) {
            .app-main-grid {
                grid-template-columns: 1fr;
            }
            .control-panel, .simulation-panel, .results-panel {
                grid-column: auto;
            }
        }
        .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;
        }
        .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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .tab-group {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 8px;
            background: rgba(0, 0, 0, 0.02);
            padding: 4px;
            border-radius: 10px;
            border: 1px solid var(--color-border);
        }
        .tab-btn {
            background: transparent;
            border: none;
            padding: 8px 12px;
            font-size: 12px;
            font-weight: 600;
            color: var(--color-text-muted);
            border-radius: 6px;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 6px;
        }
        .tab-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: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 8px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        .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: flex;
            flex-direction: column;
            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;
            transition: all 0.2s ease;
        }
        .preset-btn:hover {
            background: rgba(0, 0, 0, 0.04);
            transform: translateX(4px);
        }
        .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);
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: stretch;
            justify-content: space-between;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #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: rgba(255, 255, 255, 0.9);
            border: 1px solid var(--color-border);
            padding: 6px 12px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
            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;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(2, 132, 199, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .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);
            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);
            background: rgba(2, 132, 199, 0.02);
            border-color: rgba(2, 132, 199, 0.15);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: var(--color-cyan);
            border-color: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.05);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.safety-factor-calculator-wrapper .app-main-grid,
.safety-factor-calculator-wrapper .main-grid,
.safety-factor-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.safety-factor-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.safety-factor-calculator-wrapper .simulation-panel,
.safety-factor-calculator-wrapper .canvas-panel,
.safety-factor-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.safety-factor-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.safety-factor-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.safety-factor-calculator-wrapper .simulation-results-section .ratio-readout-box,
.safety-factor-calculator-wrapper .simulation-results-section .re-readout-box,
.safety-factor-calculator-wrapper .simulation-results-section .status-readout-box,
.safety-factor-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.safety-factor-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.safety-factor-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.safety-factor-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .safety-factor-calculator-wrapper .app-main-grid,
    .safety-factor-calculator-wrapper .main-grid,
    .safety-factor-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .safety-factor-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .safety-factor-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .safety-factor-calculator-wrapper .simulation-panel,
    .safety-factor-calculator-wrapper .canvas-panel,
    .safety-factor-calculator-wrapper .sim-panel,
    .safety-factor-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .safety-factor-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .safety-factor-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .safety-factor-calculator-wrapper .simulation-results-section .re-readout-box,
    .safety-factor-calculator-wrapper .simulation-results-section .status-readout-box,
    .safety-factor-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .safety-factor-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .safety-factor-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.safety-factor-calculator-wrapper .app-container,
.safety-factor-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="safety-factor-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-shield-halved"></i></div>
                <div>
                    <h1>SAFETY FACTOR</h1>
                    <div class="subtitle">다축 파괴이론 &#038; 실시간 안전율 계산 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">FAILURE MODEL ENGINE 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>
                <!-- Tab Select -->
                <div class="tab-group">
                    <button id="tab-ductile" class="tab-btn active">
                        <i class="fa-solid fa-gem"></i> 연성 (von Mises)
                    </button>
                    <button id="tab-brittle" class="tab-btn">
                        <i class="fa-solid fa-dice-d6"></i> 취성 (Mohr)
                    </button>
                </div>
                <!-- Normal Stress X sigX -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-arrows-left-right text-cyan"></i> 수직 응력 (σx)</label>
                        <span class="helper-text">X축 방향 (-400 ~ 400 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sigx" class="custom-number-input" min="-400" max="400" step="10" value="120">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sigx" class="custom-slider" min="-400" max="400" step="10" value="120">
                </div>
                <!-- Normal Stress Y sigY -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-arrows-up-down text-magenta"></i> 수직 응력 (σy)</label>
                        <span class="helper-text">Y축 방향 (-400 ~ 400 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sigy" class="custom-number-input" min="-400" max="400" step="10" value="80">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sigy" class="custom-slider" min="-400" max="400" step="10" value="80">
                </div>
                <!-- Shear Stress XY tauXY -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-rotate-left text-purple"></i> 전단 응력 (τxy)</label>
                        <span class="helper-text">면내 전단 (0 ~ 250 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-tau" class="custom-number-input" min="0" max="250" step="5" value="45">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-tau" class="custom-slider" min="0" max="250" step="5" value="45">
                </div>
                <!-- Strength Parameters depending on Tab -->
                <div class="input-group" id="strength-container-1">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-bolt"></i> 항복 강도 (Sy)</label>
                        <span class="helper-text">연성 허용치 (100 ~ 600 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sy" class="custom-number-input" min="100" max="600" value="250">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sy" class="custom-slider" min="100" max="600" value="250">
                </div>
                <div class="input-group" id="strength-container-2" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle-arrow-up text-cyan"></i> 인장 강도 (Sut)</label>
                        <span class="helper-text">취성 인장 (100 ~ 600 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sut" class="custom-number-input" min="100" max="600" value="150">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sut" class="custom-slider" min="100" max="600" value="150">
                </div>
                <div class="input-group" id="strength-container-3" style="display:none;">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle-arrow-down text-magenta"></i> 압축 강도 (Suc)</label>
                        <span class="helper-text">취성 압축 (100 ~ 1200 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-suc" class="custom-number-input" min="100" max="1200" value="450">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-suc" class="custom-slider" min="100" max="1200" value="450">
                </div>
            </section>
            <!-- Auto Injected Right Column wrapper for Design 1 -->
            <div class="right-column">
                <section class="panel simulation-panel">
                                <div class="panel-header">
                                    <div style="display: flex; align-items: center; gap: 12px;">
                                        <i class="fa-solid fa-chart-line text-cyan"></i>
                                        <h2>실시간 2D 응력 요소 &#038; 파괴 포락선 맵</h2>
                                    </div>
                                    <div id="txt-envelope-scale" class="canvas-scale-indicator">연성: Von Mises 휨 타원</div>
                                </div>
                                <div class="canvas-wrapper">
                                    <canvas id="physics-canvas"></canvas>
                                    <!-- Overlay Floating Data -->
                                    <div class="canvas-overlay-data">
                                        <div class="overlay-item">
                                            <span class="label">등가 응력 (σ_eq):</span>
                                            <span class="value text-cyan" id="txt-eq-stress-overlay">0.0 MPa</span>
                                        </div>
                                    </div>
                                </div>
                                <!-- Mini Metrics -->
                                <div class="simulation-metrics-strip">
                                    <div class="mini-metric">
                                        <div class="label">제1주응력 (σ₁)</div>
                                        <div id="txt-sig1" class="value">0.0 MPa</div>
                                    </div>
                                    <div class="mini-divider"></div>
                                    <div class="mini-metric">
                                        <div class="label">제2주응력 (σ₂)</div>
                                        <div id="txt-sig2" class="value">0.0 MPa</div>
                                    </div>
                                </div>
                            <div class="simulation-results-section">
                                <div class="ratio-readout-box">
                                    <div class="ratio-title">계측 안전율 (F.S.)</div>
                                    <div id="txt-safety-factor" class="ratio-value">2.45</div>
                                    <div id="txt-safety-desc" class="ratio-type">구조적 파괴 위험 안전 영역</div>
                                </div>
                                <div class="results-grid">
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-cube text-cyan"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit" id="txt-eq-title">본 미세스 등가응력 (σv)</span>
                                            <span id="txt-eq-stress" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-rotate-left text-magenta"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">최대 전단응력 (τmax)</span>
                                            <span id="txt-taumax" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-notch text-purple"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">평균 응력 (σ_avg)</span>
                                            <span id="txt-avg-stress" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                    <div class="result-card">
                                        <div class="card-icon"><i class="fa-solid fa-circle-nodes text-success"></i></div>
                                        <div class="card-content">
                                            <span class="card-unit">모르원 반지름 (R)</span>
                                            <span id="txt-mohr-r" class="card-value">0.0 MPa</span>
                                        </div>
                                    </div>
                                </div>
                                <div class="formula-card">
                                    <h4>선택한 기계 파괴식</h4>
                                    <div class="formula-equation" id="formula-txt-1">σv = √(σx²-σxσy+σy²+3τxy²)</div>
                                    <div class="formula-equation" id="formula-txt-2">F.S. = Sy / σv</div>
                                </div>
                            </div>
                </section>
            </div>
        </main>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    <!-- Physics & Animation Engine -->
    </div>
</div>
<script>
        (function() {
        let isInit = false;
        function initSimulator() {
            if (isInit) return;
            const canvas = document.getElementById('physics-canvas');
            if (!canvas) return;
            const ctx = canvas.getContext('2d');
            if (!ctx) return;
            isInit = true;
            window.__safety_initialized = true;
            // DOM Elements
            const tabDuctile = document.getElementById('tab-ductile');
            const tabBrittle = document.getElementById('tab-brittle');
            const sliderSigX = document.getElementById('slider-sigx');
            const inputSigX = document.getElementById('input-sigx');
            const sliderSigY = document.getElementById('slider-sigy');
            const inputSigY = document.getElementById('input-sigy');
            const sliderTau = document.getElementById('slider-tau');
            const inputTau = document.getElementById('input-tau');
            const strength1 = document.getElementById('strength-container-1');
            const strength2 = document.getElementById('strength-container-2');
            const strength3 = document.getElementById('strength-container-3');
            const sliderSy = document.getElementById('slider-sy');
            const inputSy = document.getElementById('input-sy');
            const sliderSut = document.getElementById('slider-sut');
            const inputSut = document.getElementById('input-sut');
            const sliderSuc = document.getElementById('slider-suc');
            const inputSuc = document.getElementById('input-suc');
            const txtEnvelopeScale = document.getElementById('txt-envelope-scale');
            const txtEqStressOverlay = document.getElementById('txt-eq-stress-overlay');
            const txtSig1 = document.getElementById('txt-sig1');
            const txtSig2 = document.getElementById('txt-sig2');
            const txtSafetyFactor = document.getElementById('txt-safety-factor');
            const txtSafetyDesc = document.getElementById('txt-safety-desc');
            const txtEqTitle = document.getElementById('txt-eq-title');
            const txtEqStress = document.getElementById('txt-eq-stress');
            const txtTauMax = document.getElementById('txt-taumax');
            const txtAvgStress = document.getElementById('txt-avg-stress');
            const txtMohrR = document.getElementById('txt-mohr-r');
            const formulaTxt1 = document.getElementById('formula-txt-1');
            const formulaTxt2 = document.getElementById('formula-txt-2');
            // State variables
            const state = {
                type: 'ductile', // ductile or brittle
                sigX: 120.0,
                sigY: 80.0,
                tauXY: 45.0,
                Sy: 250.0,
                Sut: 150.0,
                Suc: 450.0,
                time: 0
            };
            // Canvas Resizing
            function resizeCanvas() {
                const rect = canvas.getBoundingClientRect();
                const dpr = window.devicePixelRatio || 1;
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.scale(dpr, dpr);
            }
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);
            setTimeout(resizeCanvas, 300);
            // Synchronize control inputs
            function syncSigXFromSlider() {
                const val = parseFloat(sliderSigX.value);
                inputSigX.value = val.toFixed(0);
                state.sigX = val;
                updateCalculations();
            }
            function syncSigXFromInput() {
                let val = parseFloat(inputSigX.value);
                if (isNaN(val)) { val = -400; }
                if (val < -400) { val = -400; }
                if (val > 400) { val = 400; }
                sliderSigX.value = val;
                state.sigX = val;
                updateCalculations();
            }
            function syncSigYFromSlider() {
                const val = parseFloat(sliderSigY.value);
                inputSigY.value = val.toFixed(0);
                state.sigY = val;
                updateCalculations();
            }
            function syncSigYFromInput() {
                let val = parseFloat(inputSigY.value);
                if (isNaN(val)) { val = -400; }
                if (val < -400) { val = -400; }
                if (val > 400) { val = 400; }
                sliderSigY.value = val;
                state.sigY = val;
                updateCalculations();
            }
            function syncTauFromSlider() {
                const val = parseFloat(sliderTau.value);
                inputTau.value = val.toFixed(0);
                state.tauXY = val;
                updateCalculations();
            }
            function syncTauFromInput() {
                let val = parseFloat(inputTau.value);
                if (isNaN(val)) { val = 0; }
                if (val < 0) { val = 0; }
                if (val > 250) { val = 250; }
                sliderTau.value = val;
                state.tauXY = val;
                updateCalculations();
            }
            function syncSyFromSlider() {
                const val = parseInt(sliderSy.value);
                inputSy.value = val;
                state.Sy = val;
                updateCalculations();
            }
            function syncSyFromInput() {
                let val = parseInt(inputSy.value);
                if (isNaN(val)) { val = 100; }
                if (val < 100) { val = 100; }
                if (val > 600) { val = 600; }
                sliderSy.value = val;
                state.Sy = val;
                updateCalculations();
            }
            function syncSutFromSlider() {
                const val = parseInt(sliderSut.value);
                inputSut.value = val;
                state.Sut = val;
                updateCalculations();
            }
            function syncSutFromInput() {
                let val = parseInt(inputSut.value);
                if (isNaN(val)) { val = 100; }
                if (val < 100) { val = 100; }
                if (val > 600) { val = 600; }
                sliderSut.value = val;
                state.Sut = val;
                updateCalculations();
            }
            // Compressive strength
            function syncSucFromSlider() {
                const val = parseInt(sliderSuc.value);
                inputSuc.value = val;
                state.Suc = val;
                updateCalculations();
            }
            function syncSucFromInput() {
                let val = parseInt(inputSuc.value);
                if (isNaN(val)) { val = 100; }
                if (val < 100) { val = 100; }
                if (val > 1200) { val = 1200; }
                sliderSuc.value = val;
                state.Suc = val;
                updateCalculations();
            }
            // Calculations
            function updateCalculations() {
                const sX = state.sigX;
                const sY = state.sigY;
                const tXY = state.tauXY;
                // 1. Mohr Circle: Center & Radius
                const center = (sX + sY) / 2;
                const R = Math.sqrt(Math.pow((sX - sY) / 2, 2) + Math.pow(tXY, 2));
                // Principal stresses
                const sig1 = center + R;
                const sig2 = center - R;
                const tauMax = R;
                txtSig1.innerText = sig1.toFixed(1) + ' MPa';
                txtSig2.innerText = sig2.toFixed(1) + ' MPa';
                txtAvgStress.innerText = center.toFixed(1) + ' MPa';
                txtMohrR.innerText = R.toFixed(1) + ' MPa';
                txtTauMax.innerText = tauMax.toFixed(1) + ' MPa';
                let fs = 1.0;
                let eqStress = 0.0;
                if (state.type === 'ductile') {
                    // von Mises Stress
                    eqStress = Math.sqrt(Math.pow(sX, 2) - sX * sY + Math.pow(sY, 2) + 3 * Math.pow(tXY, 2));
                    fs = state.Sy / eqStress;
                    txtEqTitle.innerText = '본 미세스 등가응력 (σv)';
                    txtEqStress.innerText = eqStress.toFixed(1) + ' MPa';
                    txtEqStressOverlay.innerText = eqStress.toFixed(1) + ' MPa';
                    formulaTxt1.innerText = 'σv = √(σx²-σxσy+σy²+3τxy²)';
                    formulaTxt2.innerText = 'F.S. = Sy / σv';
                } else {
                    // Coulomb-Mohr Theory for Brittle
                    // 1/FS = sig1/Sut - sig2/Suc (where sig1 >= 0 and sig2 < 0)
                    // If both >= 0: FS = Sut / sig1
                    // If both < 0: FS = Suc / |sig2|
                    if (sig1 >= 0) {
                        if (sig2 < 0) {
                            const term1 = sig1 / state.Sut;
                            const term2 = sig2 / state.Suc;
                            fs = 1.0 / (term1 - term2);
                        }
                    }
                    if (sig1 >= 0) {
                        if (sig2 >= 0) {
                            if (sig1 > 0) {
                                fs = state.Sut / sig1;
                            } else {
                                fs = 99.0;
                            }
                        }
                    }
                    if (sig1 < 0) {
                        if (sig2 < 0) {
                            if (sig2 !== 0) {
                                fs = state.Suc / Math.abs(sig2);
                            } else {
                                fs = 99.0;
                            }
                        }
                    }
                    eqStress = Math.max(Math.abs(sig1), Math.abs(sig2)); // simplified display
                    txtEqTitle.innerText = '최대 주응력 절댓값';
                    txtEqStress.innerText = eqStress.toFixed(1) + ' MPa';
                    txtEqStressOverlay.innerText = eqStress.toFixed(1) + ' MPa';
                    formulaTxt1.innerText = '1/FS = σ₁/Sut - σ₂/Suc (인장/압축 혼합)';
                    formulaTxt2.innerText = 'FS = Sut/σ₁ or Suc/|σ₂| (동부호)';
                }
                if (isNaN(fs)) { fs = 0.0; }
                if (fs < 0) { fs = 0.0; }
                state.fs = fs;
                if (fs > 99) {
                    txtSafetyFactor.innerText = '무한대';
                } else {
                    txtSafetyFactor.innerText = fs.toFixed(2);
                }
                if (fs >= 1.0) {
                    txtSafetyDesc.innerText = '재료 파괴 한계 내 (안전 상태)';
                    txtSafetyDesc.style.color = '#10b981';
                } else {
                    txtSafetyDesc.innerText = '허용 파괴 한계 초과 (재설계 요망)';
                    txtSafetyDesc.style.color = '#db2777';
                }
            }
            // Tab selection
            tabDuctile.addEventListener('click', function() {
                tabDuctile.classList.add('active');
                tabBrittle.classList.remove('active');
                state.type = 'ductile';
                strength1.style.display = 'flex';
                strength2.style.display = 'none';
                strength3.style.display = 'none';
                txtEnvelopeScale.innerText = '연성: von Mises 휨 타원';
                updateCalculations();
            });
            tabBrittle.addEventListener('click', function() {
                tabBrittle.classList.add('active');
                tabDuctile.classList.remove('active');
                state.type = 'brittle';
                strength1.style.display = 'none';
                strength2.style.display = 'flex';
                strength3.style.display = 'flex';
                txtEnvelopeScale.innerText = '취성: Mohr-Coulomb 비대칭';
                updateCalculations();
            });
            // Event bindings
            sliderSigX.addEventListener('input', syncSigXFromSlider);
            inputSigX.addEventListener('change', syncSigXFromInput);
            sliderSigY.addEventListener('input', syncSigYFromSlider);
            inputSigY.addEventListener('change', syncSigYFromInput);
            sliderTau.addEventListener('input', syncTauFromSlider);
            inputTau.addEventListener('change', syncTauFromInput);
            sliderSy.addEventListener('input', syncSyFromSlider);
            inputSy.addEventListener('change', syncSyFromInput);
            sliderSut.addEventListener('input', syncSutFromSlider);
            inputSut.addEventListener('change', syncSutFromInput);
            sliderSuc.addEventListener('input', syncSucFromSlider);
            inputSuc.addEventListener('change', syncSucFromInput);
            // Animation Loop
            function animate() {
                requestAnimationFrame(animate);
                state.time += 0.05;
                const width = canvas.width / (window.devicePixelRatio || 1);
                const height = canvas.height / (window.devicePixelRatio || 1);
                ctx.clearRect(0, 0, width, height);
                // Draw snow white blueprint grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Split layout: Left = 2D Stress Element (width*0.42), Right = Stress Envelope Plot (width*0.58)
                const splitX = width * 0.42;
                // --- 2D STRESS ELEMENT DRAWING ---
                ctx.save();
                ctx.font = '700 11px Outfit, sans-serif';
                ctx.fillStyle = '#475569';
                ctx.fillText('평면 응력 요소 상태 (σx, σy, τxy)', 20, 25);
                const elCX = splitX / 2;
                const elCY = height / 2;
                const elSize = 65; // size of the square
                // Draw the square element
                ctx.fillStyle = 'rgba(241, 245, 249, 0.85)';
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 2.5;
                ctx.beginPath();
                ctx.rect(elCX - elSize/2, elCY - elSize/2, elSize, elSize);
                ctx.fill();
                ctx.stroke();
                // Draw Stress Arrows (σx, σy, τxy)
                ctx.lineWidth = 1.8;
                // 1. sigX arrows (Horizontal)
                if (state.sigX !== 0) {
                    const absVal = Math.abs(state.sigX);
                    const isTensile = state.sigX > 0;
                    ctx.strokeStyle = '#0284c7';
                    ctx.fillStyle = '#0284c7';
                    // Left arrow
                    ctx.beginPath();
                    if (isTensile) {
                        ctx.moveTo(elCX - elSize/2, elCY);
                        ctx.lineTo(elCX - elSize/2 - 25, elCY);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX - elSize/2 - 25, elCY);
                        ctx.lineTo(elCX - elSize/2 - 18, elCY - 4);
                        ctx.lineTo(elCX - elSize/2 - 18, elCY + 4);
                        ctx.fill();
                    } else {
                        ctx.moveTo(elCX - elSize/2 - 25, elCY);
                        ctx.lineTo(elCX - elSize/2, elCY);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX - elSize/2, elCY);
                        ctx.lineTo(elCX - elSize/2 - 7, elCY - 4);
                        ctx.lineTo(elCX - elSize/2 - 7, elCY + 4);
                        ctx.fill();
                    }
                    // Right arrow
                    ctx.beginPath();
                    if (isTensile) {
                        ctx.moveTo(elCX + elSize/2, elCY);
                        ctx.lineTo(elCX + elSize/2 + 25, elCY);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX + elSize/2 + 25, elCY);
                        ctx.lineTo(elCX + elSize/2 + 18, elCY - 4);
                        ctx.lineTo(elCX + elSize/2 + 18, elCY + 4);
                        ctx.fill();
                    } else {
                        ctx.moveTo(elCX + elSize/2 + 25, elCY);
                        ctx.lineTo(elCX + elSize/2, elCY);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX + elSize/2, elCY);
                        ctx.lineTo(elCX + elSize/2 + 7, elCY - 4);
                        ctx.lineTo(elCX + elSize/2 + 7, elCY + 4);
                        ctx.fill();
                    }
                }
                // 2. sigY arrows (Vertical)
                if (state.sigY !== 0) {
                    const absVal = Math.abs(state.sigY);
                    const isTensile = state.sigY > 0;
                    ctx.strokeStyle = '#db2777';
                    ctx.fillStyle = '#db2777';
                    // Top arrow
                    ctx.beginPath();
                    if (isTensile) {
                        ctx.moveTo(elCX, elCY - elSize/2);
                        ctx.lineTo(elCX, elCY - elSize/2 - 25);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX, elCY - elSize/2 - 25);
                        ctx.lineTo(elCX - 4, elCY - elSize/2 - 18);
                        ctx.lineTo(elCX + 4, elCY - elSize/2 - 18);
                        ctx.fill();
                    } else {
                        ctx.moveTo(elCX, elCY - elSize/2 - 25);
                        ctx.lineTo(elCX, elCY - elSize/2);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX, elCY - elSize/2);
                        ctx.lineTo(elCX - 4, elCY - elSize/2 - 7);
                        ctx.lineTo(elCX + 4, elCY - elSize/2 - 7);
                        ctx.fill();
                    }
                    // Bottom arrow
                    ctx.beginPath();
                    if (isTensile) {
                        ctx.moveTo(elCX, elCY + elSize/2);
                        ctx.lineTo(elCX, elCY + elSize/2 + 25);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX, elCY + elSize/2 + 25);
                        ctx.lineTo(elCX - 4, elCY + elSize/2 + 18);
                        ctx.lineTo(elCX + 4, elCY + elSize/2 + 18);
                        ctx.fill();
                    } else {
                        ctx.moveTo(elCX, elCY + elSize/2 + 25);
                        ctx.lineTo(elCX, elCY + elSize/2);
                        ctx.stroke();
                        // arrowhead
                        ctx.beginPath();
                        ctx.moveTo(elCX, elCY + elSize/2);
                        ctx.lineTo(elCX - 4, elCY + elSize/2 + 7);
                        ctx.lineTo(elCX + 4, elCY + elSize/2 + 7);
                        ctx.fill();
                    }
                }
                // 3. Shear Stress arrows (τxy)
                if (state.tauXY > 0) {
                    ctx.strokeStyle = '#7c3aed';
                    ctx.fillStyle = '#7c3aed';
                    const arrowShift = 10;
                    // Right edge (pointing up)
                    ctx.beginPath();
                    ctx.moveTo(elCX + elSize/2 + arrowShift, elCY + elSize/2 - 10);
                    ctx.lineTo(elCX + elSize/2 + arrowShift, elCY - elSize/2 + 10);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.moveTo(elCX + elSize/2 + arrowShift, elCY - elSize/2 + 10);
                    ctx.lineTo(elCX + elSize/2 + arrowShift - 4, elCY - elSize/2 + 16);
                    ctx.lineTo(elCX + elSize/2 + arrowShift + 4, elCY - elSize/2 + 16);
                    ctx.fill();
                    // Left edge (pointing down)
                    ctx.beginPath();
                    ctx.moveTo(elCX - elSize/2 - arrowShift, elCY - elSize/2 + 10);
                    ctx.lineTo(elCX - elSize/2 - arrowShift, elCY + elSize/2 - 10);
                    ctx.stroke();
                    ctx.beginPath();
                    ctx.moveTo(elCX - elSize/2 - arrowShift, elCY + elSize/2 - 10);
                    ctx.lineTo(elCX - elSize/2 - arrowShift - 4, elCY + elSize/2 - 16);
                    ctx.lineTo(elCX - elSize/2 - arrowShift + 4, elCY + elSize/2 - 16);
                    ctx.fill();
                }
                ctx.restore();
                // --- FAILURE ENVELOPE PLOT ---
                ctx.save();
                ctx.translate(splitX + 40, 40);
                const pW = (width - splitX) - 80;
                const pH = height - 100;
                // Center coordinates of plot
                const pCX = pW / 2;
                const pCY = pH / 2;
                // Axes
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 1.2;
                ctx.beginPath();
                ctx.moveTo(0, pCY);
                ctx.lineTo(pW, pCY);
                ctx.moveTo(pCX, 0);
                ctx.lineTo(pCX, pH);
                ctx.stroke();
                // Axis labels
                ctx.font = '500 10px Inter, sans-serif';
                ctx.fillStyle = '#475569';
                ctx.fillText('주응력 σ₁ (MPa)', pW - 35, pCY + 18);
                ctx.save();
                ctx.translate(pCX - 22, pCY);
                ctx.rotate(-Math.PI / 2);
                ctx.textAlign = 'center';
                ctx.fillText('주응력 σ₂ (MPa)', 0, 0);
                ctx.restore();
                // Geometry scaling
                // Let's set graph range: -600 to 600 MPa
                const maxVal = 600.0;
                function plotCoords(s1, s2) {
                    const sx = pCX + (s1 / maxVal) * pCX;
                    const sy = pCY - (s2 / maxVal) * pCY;
                    return { x: sx, y: sy };
                }
                // Draw background region guides (Safe vs Failure)
                ctx.save();
                ctx.font = '800 11px Inter, sans-serif';
                ctx.textAlign = 'center';
                const baseStrength = state.type === 'ductile' ? state.Sy : Math.min(state.Sut, state.Suc);
                // 1. Draw Safe Zone inside the envelope dynamically
                if (baseStrength >= 150) {
                    ctx.fillStyle = 'rgba(71, 85, 105, 0.12)';
                    const ptSafeText = plotCoords(-baseStrength * 0.45, -baseStrength * 0.45);
                    ctx.fillText('안전 영역 (Safe Zone)', ptSafeText.x, ptSafeText.y);
                }
                // 2. Draw Failure Zone outside the envelope dynamically
                ctx.fillStyle = 'rgba(219, 39, 119, 0.08)';
                const failVal = Math.min(540, Math.max(350, baseStrength * 1.35));
                const ptFailTR = plotCoords(failVal, failVal);
                const ptFailBL = plotCoords(-failVal, -failVal);
                ctx.fillText('파괴 영역 (Failure Zone)', ptFailTR.x, ptFailTR.y);
                ctx.fillText('파괴 영역 (Failure Zone)', ptFailBL.x, ptFailBL.y);
                ctx.restore();
                // Trace Failure Envelope
                ctx.save();
                ctx.lineWidth = 2.5;
                if (state.type === 'ductile') {
                    // Von Mises Ellipse: s1^2 - s1*s2 + s2^2 = Sy^2
                    // Traced in parametric angle theta
                    ctx.strokeStyle = '#0284c7';
                    ctx.fillStyle = 'rgba(2, 132, 199, 0.05)';
                    ctx.beginPath();
                    for (let th = 0; th <= 360; th += 5) {
                        const thRad = (th * Math.PI) / 180;
                        // Transform to principal coordinates of the tilted ellipse
                        const r = state.Sy / Math.sqrt(1 - Math.sin(thRad)*Math.cos(thRad));
                        const s1 = r * Math.cos(thRad);
                        const s2 = r * Math.sin(thRad);
                        const pt = plotCoords(s1, s2);
                        if (th === 0) {
                            ctx.moveTo(pt.x, pt.y);
                        } else {
                            ctx.lineTo(pt.x, pt.y);
                        }
                    }
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // Boundary label
                    ctx.save();
                    ctx.font = '600 9px Inter, sans-serif';
                    ctx.fillStyle = '#0284c7';
                    const ptLabel = plotCoords(state.Sy, state.Sy);
                    ctx.textAlign = 'left';
                    ctx.fillText('항복 임계 한계 (von Mises 타원)', ptLabel.x + 8, ptLabel.y - 4);
                    ctx.restore();
                } else {
                    // Mohr-Coulomb asymmetric envelope (polygon in sig1-sig2 space)
                    // The boundaries are defined by:
                    // Quadrant 1 (both tensile): sig1 <= Sut, sig2 <= Sut
                    // Quadrant 3 (both compressive): sig1 >= -Suc, sig2 >= -Suc
                    // Quadrant 2 & 4: sig1/Sut - sig2/Suc = 1 (and symmetric)
                    ctx.strokeStyle = '#db2777';
                    ctx.fillStyle = 'rgba(219, 39, 119, 0.05)';
                    const ut = state.Sut;
                    const uc = state.Suc;
                    const p1 = plotCoords(ut, 0);
                    const p2 = plotCoords(ut, ut);
                    const p3 = plotCoords(0, ut);
                    const p4 = plotCoords(-uc, 0);
                    const p5 = plotCoords(-uc, -uc);
                    const p6 = plotCoords(0, -uc);
                    // Reconstruct hexagon vertices
                    ctx.beginPath();
                    const v1 = plotCoords(ut, 0);
                    const v2 = plotCoords(ut, ut);
                    const v3 = plotCoords(0, ut);
                    const v4 = plotCoords(-uc + ut, -uc); // intersection approximation
                    const v5 = plotCoords(-uc, -uc);
                    const v6 = plotCoords(-uc, -uc + ut);
                    ctx.moveTo(v1.x, v1.y);
                    ctx.lineTo(v2.x, v2.y);
                    ctx.lineTo(v3.x, v3.y);
                    ctx.lineTo(plotCoords(0, -uc * (1 - 0/ut)).x, plotCoords(0, -uc).y); // corrected Mohr CM intersections
                    // CM standard points:
                    const ptA = plotCoords(ut, 0);
                    const ptB = plotCoords(ut, ut);
                    const ptC = plotCoords(0, ut);
                    const ptD = plotCoords(-uc, 0);
                    const ptE = plotCoords(-uc, -uc);
                    const ptF = plotCoords(0, -uc);
                    // Coulomb-Mohr hexagon is:
                    // 1. sig1 = Sut (sig2 < sig1)
                    // 2. sig1/Sut - sig2/Suc = 1
                    // 3. sig2 = -Suc
                    // etc.
                    ctx.beginPath();
                    ctx.lineTo(ptA.x, ptA.y);
                    ctx.lineTo(ptB.x, ptB.y);
                    ctx.lineTo(ptC.x, ptC.y);
                    ctx.lineTo(ptD.x, ptD.y);
                    ctx.lineTo(ptE.x, ptE.y);
                    ctx.lineTo(ptF.x, ptF.y);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // Boundary label
                    ctx.save();
                    ctx.font = '600 9px Inter, sans-serif';
                    ctx.fillStyle = '#db2777';
                    const ptLabel = plotCoords(state.Sut, state.Sut);
                    ctx.textAlign = 'left';
                    ctx.fillText('파괴 임계 한계 (Coulomb-Mohr)', ptLabel.x + 8, ptLabel.y - 4);
                    ctx.restore();
                }
                ctx.restore();
                // Plot actual current stress point
                const sX = state.sigX;
                const sY = state.sigY;
                const tXY = state.tauXY;
                const center = (sX + sY) / 2;
                const R = Math.sqrt(Math.pow((sX - sY) / 2, 2) + Math.pow(tXY, 2));
                const sig1 = center + R;
                const sig2 = center - R;
                const ptCurrent = plotCoords(sig1, sig2);
                ctx.beginPath();
                ctx.arc(ptCurrent.x, ptCurrent.y, 6 + Math.sin(state.time)*2, 0, Math.PI*2);
                // Color depending on safety factor
                let isInside = true;
                if (state.type === 'ductile') {
                    const vm = Math.sqrt(Math.pow(sX, 2) - sX * sY + Math.pow(sY, 2) + 3 * Math.pow(tXY, 2));
                    if (vm > state.Sy) { isInside = false; }
                } else {
                    let fs = 1.0;
                    if (sig1 >= 0) {
                        if (sig2 < 0) {
                            fs = 1.0 / (sig1/state.Sut - sig2/state.Suc);
                        }
                    }
                    if (sig1 >= 0) {
                        if (sig2 >= 0) {
                            if (sig1 > 0) {
                                fs = state.Sut / sig1;
                            }
                        }
                    }
                    if (sig1 < 0) {
                        if (sig2 < 0) {
                            if (sig2 !== 0) {
                                fs = state.Suc / Math.abs(sig2);
                            }
                        }
                    }
                    if (fs < 1.0) { isInside = false; }
                }
                if (isInside) {
                    ctx.fillStyle = '#10b981';
                } else {
                    ctx.fillStyle = '#db2777';
                }
                ctx.shadowBlur = 10;
                ctx.shadowColor = ctx.fillStyle;
                ctx.fill();
                ctx.shadowBlur = 0;
                // Stress point label
                ctx.save();
                ctx.font = '700 10px Inter, sans-serif';
                ctx.fillStyle = isInside ? '#10b981' : '#db2777';
                const curFs = state.fs || 1.0;
                const labelText = `현재 응력 상태 (F.S. = ${curFs > 99 ? '∞' : curFs.toFixed(2)})`;
                if (ptCurrent.x > pCX) {
                    ctx.textAlign = 'right';
                    ctx.fillText(labelText, ptCurrent.x - 12, ptCurrent.y + 3);
                } else {
                    ctx.textAlign = 'left';
                    ctx.fillText(labelText, ptCurrent.x + 12, ptCurrent.y + 3);
                }
                ctx.restore();
                // Draw dashed lines mapping current coordinate to axis
                ctx.save();
                ctx.strokeStyle = 'rgba(71, 85, 105, 0.3)';
                ctx.setLineDash([3, 3]);
                ctx.beginPath();
                ctx.moveTo(ptCurrent.x, ptCurrent.y);
                ctx.lineTo(ptCurrent.x, pCY);
                ctx.moveTo(ptCurrent.x, ptCurrent.y);
                ctx.lineTo(pCX, ptCurrent.y);
                ctx.stroke();
                ctx.restore();
                ctx.restore();
            }
            // Document security
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            updateCalculations();
            animate();
        }
        let initAttempts = 0;
        function tryInit() {
            initAttempts++;
            initSimulator();
            if (!window.__safety_initialized) {
                if (initAttempts < 50) {
                    setTimeout(tryInit, 100);
                }
            }
        }
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            tryInit();
        } else {
            document.addEventListener('DOMContentLoaded', tryInit);
            window.addEventListener('load', tryInit);
        }
        })();
</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>연성 및 취성 거동 선택: 분석하려는 재료에 맞춰 상단 탭에서 연성 재료(Ductile - Von Mises 이론) 또는 취성 재료(Brittle - Mohr-Coulomb 이론)를 지정합니다.</strong></li>
<li style="margin-bottom: 6px;">재료 설계 항복강도 설정: 슬라이더를 이용해 재료 고유의 강성 변수인 항복 강도(Sy), 인장 극한 강도(Sut) 및 압축 극한 강도(Suc)를 설정합니다.</li>
<li style="margin-bottom: 6px;">2차원 평면 응력값 조절: 슬라이더로 정상 응력 수치(σx, σy)와 전단 응력 수치(τxy)를 가변하여 응력 요소 상태를 변경합니다.</li>
<li style="margin-bottom: 6px;">파괴 포락선 분포 및 안전율 진단: 우측 그래프 상에 2차원 주응력(σ₁, σ₂)의 위치가 파괴 타원/외형 포락선 내부에 상주하는지 실시간 확인하고 계측된 안전율(F.S.) 결과를 통해 단면 두께 보정 여부를 판단합니다.</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;" /> 상세 재료 파괴 이론 및 연성/취성 재료 안전율 산정법 확인하기</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. 재료 파괴 이론(Failure Theories)의 기본 개념 및 필요성</h3>
<p>실제 기계 부품이나 구조물은 단순 단축 인장뿐만 아니라 다양한 방향으로 동시에 힘이 작용하는 <strong>다축 응력 상태(Multiaxial Stress State)</strong>에 놓이게 됩니다. 복합 응력 하에서 재료가 파괴되거나 영구 항복을 일으키는 시점을 단일 인장시험 데이터와 연계해 예측하기 위해 고안된 것이 바로 <strong>파괴 이론(Failure Theories)</strong>입니다.</p><p>재료는 거동 특성에 따라 크게 두 가지로 분류되며 적용하는 역학 이론도 상이합니다:</p><ul><li><strong>연성 재료 (Ductile Materials):</strong> 알루미늄, 연강처럼 파단 전에 큰 소성 변형을 겪는 재료로, 주로 전단 응력에 의해 슬립이 발생하여 항복합니다.</li><li><strong>취성 재료 (Brittle Materials):</strong> 주철, 유리처럼 항복 현상 없이 급격히 깨지는 재료로, 압축 강도에 비해 인장 강도가 현격히 떨어지며 주로 최대 인장 응력에 의해 파단됩니다.</li></ul>
<h3>2. 본 미세스(von Mises) 전단변형 에너지 이론 (연성 재료용)</h3>
<p>연성 금속의 파괴(항복) 예측에 가장 신뢰도가 높아 널리 활용되는 <strong>본 미세스 전단변형 에너지 이론(Maximum Distortion Energy Theory)</strong>은 단위 부피당 변형 에너지가 단순 인장 조건에서의 한계 에너지를 도달할 때 파괴가 일어난다는 학설입니다. 2차원 평면 응력 하에서의 본 미세스 등가 응력(&sigma;_v) 식은 다음과 같습니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">&sigma;_v = &radic;(&sigma;_x&sup2; - &sigma;_x&sigma;_y + &sigma;_y&sup2; + 3&tau;_xy&sup2;) &nbsp;[MPa]</p><p>이를 두 주응력 &sigma;₁, &sigma;₂ 공간으로 사영하면 장축이 45도 기울어진 <strong>타원(von Mises Ellipse)</strong>이 형성됩니다. 작용 주응력의 위치 좌표가 이 타원 내부에 존재할 경우 구조적으로 소성 항복에 대해 안전함을 의미합니다.</p>
<h3>3. 쿨롱-모르(Coulomb-Mohr) 이론 및 안전율(F.S.)의 기준</h3>
<p>취성 재료는 인장 강도(Sut)보다 압축 강도(Suc)가 훨씬 커서 타원형 대칭 포락선을 사용할 수 없습니다. 따라서 인장과 압축 응력 차이를 기하학적으로 반영한 <strong>모르-쿨롱 파괴 이론 (Coulomb-Mohr Theory)</strong>을 적용합니다.</p><p>두 주응력이 각각 인장과 압축 영역에 교차할 때(&sigma;₁ &ge; 0, &sigma;₂ &lt; 0), 파괴 임계 조건은 다음과 같이 계산됩니다.</p><p style="text-align: center; font-weight: bold; background: #f3f4f6; padding: 12px; border-radius: 8px;">1 / FS = &sigma;₁ / Sut - &sigma;₂ / Suc</p><p><strong>안전율(Factor of Safety, FS) 평가 기준:</strong></p><ul><li><strong>FS &gt; 1.0:</strong> 안전 영역 (Safe Region - 탄성 거동 보장)</li><li><strong>FS = 1.0:</strong> 임계 경계 (Critical Boundary - 파괴/항복 개시)</li><li><strong>FS &lt; 1.0:</strong> 위험 영구 파괴 (Failed Region - 단면 재설계 요구)</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/safety-factor-failure-theories-calculator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>응력-변형률 계산기 &#038; 시뮬레이터</title>
		<link>https://myengnote.com/stress-strain-relation-calculator-simulator/</link>
					<comments>https://myengnote.com/stress-strain-relation-calculator-simulator/#respond</comments>
		
		<dc:creator><![CDATA[동동]]></dc:creator>
		<pubDate>Wed, 03 Jun 2026 23:15:08 +0000</pubDate>
				<category><![CDATA[공학계산기]]></category>
		<category><![CDATA[안전율]]></category>
		<category><![CDATA[응력변형률]]></category>
		<category><![CDATA[인장시험]]></category>
		<category><![CDATA[재료역학]]></category>
		<guid isPermaLink="false">https://myengnote.com/stress-strain-relation-calculator-simulator/</guid>

					<description><![CDATA[재료의 단면적, 게이지 길이, 인장 하중과 재료 특성(탄성계수, 항복/인장강도)을 실시간 변동하여 응력-변형률 곡선 상의 거동 영역(탄성, 항복, 소성 변형, 네킹) 및 파단 시점과 안전율을 분석하는 시뮬레이터입니다.]]></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; 시뮬레이터</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 stress-strain-calculator-wrapper */
        /* Modern Reset and Design Tokens */
        .stress-strain-calculator-wrapper *, .stress-strain-calculator-wrapper *::before, .stress-strain-calculator-wrapper *::after {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        .stress-strain-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; /* Cobalt blue */
            --color-cyan-glow: rgba(2, 132, 199, 0.15);
            --color-magenta: #db2777; /* Pink/magenta */
            --color-magenta-glow: rgba(219, 39, 119, 0.15);
            --color-purple: #7c3aed;
            --color-purple-glow: rgba(124, 58, 237, 0.12);
            --color-success: #10b981;
            --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);
        }
        .stress-strain-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%;
        }
        .app-background-glow::after {
            background: var(--color-purple);
            bottom: -10%;
            left: -5%;
        }
        .app-container {
            width: 100%;
            max-width: 1440px;
            padding: 24px;
            display: flex;
            flex-direction: column;
            gap: 24px;
        }
        .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-muted));
            -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;
            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;
        }
        .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: var(--color-purple); }
        .control-panel {
            grid-column: 1;
        }
        .input-group {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }
        .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 {
            flex: 1;
            width: 0;
            min-width: 0;
            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;
            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 {
            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;
            flex-shrink: 0;
        }
        .custom-slider {
            -webkit-appearance: none;
            width: 100%;
            height: 6px;
            border-radius: 3px;
            background: rgba(0, 0, 0, 0.05);
            outline: none;
            margin: 8px 0;
        }
        .custom-slider::-webkit-slider-thumb {
            -webkit-appearance: none;
            appearance: none;
            width: 18px;
            height: 18px;
            border-radius: 50%;
            cursor: pointer;
            transition: transform 0.1s ease;
            background: var(--color-cyan);
            box-shadow: 0 0 10px var(--color-cyan-glow);
        }
        .custom-slider::-webkit-slider-thumb:hover {
            transform: scale(1.2);
        }
        .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: flex;
            flex-direction: column;
            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;
            transition: all 0.2s ease;
        }
        .preset-btn:hover {
            background: rgba(0, 0, 0, 0.04);
            transform: translateX(4px);
        }
        .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);
        }
        .preset-name {
            font-size: 12px;
            font-weight: 600;
        }
        .simulation-panel {
            grid-column: 2;
            align-self: start;
        }
        .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;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        #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: rgba(255, 255, 255, 0.9);
            border: 1px solid var(--color-border);
            padding: 6px 12px;
            border-radius: 8px;
            display: flex;
            align-items: center;
            gap: 8px;
            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;
        }
        .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: rgba(0, 0, 0, 0.03);
        }
        .results-panel {
            grid-column: 3;
        }
        .ratio-readout-box {
            background: linear-gradient(135deg, rgba(2, 132, 199, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%);
            border: 1px solid rgba(2, 132, 199, 0.25);
            border-radius: 16px;
            padding: 20px;
            text-align: center;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 6px;
        }
        .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);
            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);
            background: rgba(2, 132, 199, 0.02);
            border-color: rgba(2, 132, 199, 0.15);
        }
        .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);
        }
        .result-card:hover .card-icon {
            color: var(--color-cyan);
            border-color: rgba(2, 132, 199, 0.2);
            background: rgba(2, 132, 199, 0.05);
        }
        .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: rgba(0, 0, 0, 0.01);
            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: monospace;
            color: var(--color-cyan);
            font-size: 13px;
            background: #f8fafc;
            padding: 6px 10px;
            border-radius: 6px;
            border: 1px solid var(--color-border);
            text-align: center;
        }
        .preset-details {
            display: flex;
            flex-direction: column;
            gap: 2px;
            align-items: flex-start;
            text-align: left;
        }
        .preset-spec {
            font-size: 11px;
            color: var(--color-text-muted);
            font-weight: 500;
        }
        .preset-name {
            font-size: 13px !important;
        }
/* ━━ WordPress 레이아웃 Override: 디자인 1 (2열 컴팩트 대칭 레이아웃) ━━ */
.stress-strain-calculator-wrapper .app-main-grid,
.stress-strain-calculator-wrapper .main-grid,
.stress-strain-calculator-wrapper .sim-grid {
    display: grid !important;
    grid-template-columns: 340px 1fr !important;
    gap: 24px !important;
    align-items: start !important;
}
/* 3열 레이아웃을 2열 레이아웃으로 변경하는 특화 스타일 */
.stress-strain-calculator-wrapper .right-column {
    display: flex !important;
    flex-direction: column !important;
    gap: 24px !important;
    min-width: 0 !important;
    grid-column: 2 !important;
}
.stress-strain-calculator-wrapper .simulation-panel,
.stress-strain-calculator-wrapper .canvas-panel,
.stress-strain-calculator-wrapper .sim-panel {
    grid-column: auto !important;
    order: 1 !important;
}
.stress-strain-calculator-wrapper .control-panel {
    grid-column: 1 !important;
}
/* 2열 통합 결과 분석 영역 레이아웃 */
.stress-strain-calculator-wrapper .simulation-results-section {
    display: grid !important;
    grid-template-columns: 1.1fr 1.3fr !important;
    gap: 20px !important;
    border-top: 1px solid var(--color-border) !important;
    padding-top: 20px !important;
    margin-top: 10px !important;
    align-items: stretch !important;
}
/* Readout Box 스타일 */
.stress-strain-calculator-wrapper .simulation-results-section .ratio-readout-box,
.stress-strain-calculator-wrapper .simulation-results-section .re-readout-box,
.stress-strain-calculator-wrapper .simulation-results-section .status-readout-box,
.stress-strain-calculator-wrapper .simulation-results-section .gauge-container {
    grid-column: 1 !important;
    grid-row: 1 !important;
    margin: 0 !important;
    height: 100% !important;
    display: flex !important;
    flex-direction: column !important;
    justify-content: center !important;
}
.stress-strain-calculator-wrapper .simulation-results-section .results-grid {
    grid-column: 2 !important;
    grid-row: auto !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 12px !important;
    justify-content: flex-start !important;
}
.stress-strain-calculator-wrapper .simulation-results-section .results-grid .result-card {
    flex: none !important;
    display: flex !important;
    align-items: center !important;
    height: 72px !important;
}
.stress-strain-calculator-wrapper .simulation-results-section .formula-card {
    grid-column: 1 / span 2 !important;
    grid-row: auto !important;
}
/* 모바일/반응형 (800px 이하) ── 항상 세로형(1열) 정렬 및 시뮬레이터 캔버스 최상단 배치 */
@media (max-width: 800px) {
    .stress-strain-calculator-wrapper .app-main-grid,
    .stress-strain-calculator-wrapper .main-grid,
    .stress-strain-calculator-wrapper .sim-grid {
        grid-template-columns: 1fr !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .stress-strain-calculator-wrapper .control-panel {
        order: 2 !important;
        width: 100% !important;
    }
    .stress-strain-calculator-wrapper .right-column {
        order: 1 !important;
        width: 100% !important;
        display: flex !important;
        flex-direction: column !important;
    }
    .stress-strain-calculator-wrapper .simulation-panel,
    .stress-strain-calculator-wrapper .canvas-panel,
    .stress-strain-calculator-wrapper .sim-panel,
    .stress-strain-calculator-wrapper .canvas-section {
        order: -1 !important;
    }
    .stress-strain-calculator-wrapper .simulation-results-section {
        grid-template-columns: 1fr !important;
    }
    .stress-strain-calculator-wrapper .simulation-results-section .ratio-readout-box,
    .stress-strain-calculator-wrapper .simulation-results-section .re-readout-box,
    .stress-strain-calculator-wrapper .simulation-results-section .status-readout-box,
    .stress-strain-calculator-wrapper .simulation-results-section .gauge-container {
        grid-column: 1 !important;
        grid-row: auto !important;
        height: auto !important;
    }
    .stress-strain-calculator-wrapper .simulation-results-section .results-grid {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
    .stress-strain-calculator-wrapper .simulation-results-section .formula-card {
        grid-column: 1 !important;
        grid-row: auto !important;
    }
}
.stress-strain-calculator-wrapper .app-container,
.stress-strain-calculator-wrapper .main-container {
    max-width: 100% !important;
    padding: 12px !important;
}
</style>
<div class="stress-strain-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-chart-line"></i></div>
                <div>
                    <h1>STRESS &#038; STRAIN</h1>
                    <div class="subtitle">인장 시험기 &#038; 실시간 응력-변형률 시뮬레이터</div>
                </div>
            </div>
            <div class="header-badge">
                <div class="pulse-dot"></div>
                <div class="badge-text">TENSILE ENGINE 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>
                <!-- Load P -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-weight-hanging text-magenta"></i> 인장 하중 (P)</label>
                        <span class="helper-text">인장력 (0.0 ~ 500.0 kN)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-p" class="custom-number-input" min="0" max="500" step="1" value="80">
                        <div class="unit-badge">kN</div>
                    </div>
                    <input type="range" id="slider-p" class="custom-slider" min="0" max="500" step="1" value="80">
                </div>
                <!-- Diameter d0 -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle-dot text-cyan"></i> 시편 초기 지름 (d₀)</label>
                        <span class="helper-text">단면 지름 (5.0 ~ 30.0 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-d" class="custom-number-input" min="5" max="30" step="0.5" value="20.0">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-d" class="custom-slider" min="5" max="30" step="0.5" value="20.0">
                </div>
                <!-- Gauge Length L0 -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-ruler-vertical text-purple"></i> 초기 표점거리 (L₀)</label>
                        <span class="helper-text">게이지 길이 (50 ~ 300 mm)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-l" class="custom-number-input" min="50" max="300" step="5" value="150">
                        <div class="unit-badge">mm</div>
                    </div>
                    <input type="range" id="slider-l" class="custom-slider" min="50" max="300" step="5" value="150">
                </div>
                <!-- Yield Strength Sy -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-shield-halved"></i> 항복 강도 (Sy)</label>
                        <span class="helper-text">소성 한계 (100 ~ 800 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-sy" class="custom-number-input" min="100" max="800" value="250">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-sy" class="custom-slider" min="100" max="800" value="250">
                </div>
                <!-- Tensile Strength Su -->
                <div class="input-group">
                    <div class="input-label-row">
                        <label><i class="fa-solid fa-circle-exclamation"></i> 인장 강도 (Su)</label>
                        <span class="helper-text">최대 강도 (200 ~ 1200 MPa)</span>
                    </div>
                    <div class="number-input-wrapper">
                        <input type="number" id="input-su" class="custom-number-input" min="200" max="1200" value="400">
                        <div class="unit-badge">MPa</div>
                    </div>
                    <input type="range" id="slider-su" class="custom-slider" min="200" max="1200" value="400">
                </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" id="preset-steel">
                            <div class="preset-icon"><i class="fa-solid fa-cubes"></i></div>
                            <div class="preset-details"><span class="preset-name">일반 구조용 강재</span><span class="preset-spec">SS400</span></div>
                        </button>
                        <button class="preset-btn" id="preset-aluminum">
                            <div class="preset-icon"><i class="fa-solid fa-cube"></i></div>
                            <div class="preset-details"><span class="preset-name">알루미늄 합금</span><span class="preset-spec">Al 6061-T6</span></div>
                        </button>
                        <button class="preset-btn" id="preset-copper">
                            <div class="preset-icon"><i class="fa-solid fa-circle"></i></div>
                            <div class="preset-details"><span class="preset-name">연질 동재</span><span class="preset-spec">Copper</span></div>
                        </button>
                    </div>
                </div>
            </section>
            <!-- Center Panel: Physics Simulator -->
            <section class="panel simulation-panel">
                <div class="panel-header">
                    <div style="display: flex; align-items: center; gap: 12px;">
                        <i class="fa-solid fa-chart-line text-cyan"></i>
                        <h2>실시간 인장 변형 및 응력-변형률 선도</h2>
                    </div>
                    <div id="txt-state" class="canvas-scale-indicator">거동 상태: 탄성 대기</div>
                </div>
                <div class="canvas-wrapper">
                    <canvas id="physics-canvas"></canvas>
                    <!-- Overlay Floating Data -->
                    <div class="canvas-overlay-data">
                        <div class="overlay-item">
                            <span class="label">유도 응력 (σ):</span>
                            <span class="value text-cyan" id="txt-stress-overlay">0.0 MPa</span>
                        </div>
                    </div>
                </div>
                <!-- Mini Metrics -->
                <div class="simulation-metrics-strip">
                    <div class="mini-metric">
                        <div class="label">초기 면적 (A₀)</div>
                        <div id="txt-area" class="value">0.0 mm²</div>
                    </div>
                    <div class="mini-divider"></div>
                    <div class="mini-metric">
                        <div class="label">안전율 (F.S.)</div>
                        <div id="txt-safety-factor" class="value">0.00</div>
                    </div>
                </div>
                <!-- Integrated Results Analysis Section -->
                <div class="simulation-results-section" style="display: grid; grid-template-columns: 1.1fr 1.3fr; 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; background: linear-gradient(135deg, rgba(219, 39, 119, 0.08) 0%, rgba(124, 58, 237, 0.03) 100%); border: 1px solid rgba(219, 39, 119, 0.25); border-radius: 16px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 6px; box-shadow: 0 0 15px rgba(219, 39, 119, 0.05);">
                    <div class="ratio-title" style="font-size: 11px; font-weight: 700; color: #db2777; letter-spacing: 1px; text-transform: uppercase;">인장 상태 요약</div>
                    <div id="txt-status-text" class="ratio-value" style="font-family: var(--font-heading); font-weight: 800; font-size: 26px; background: linear-gradient(90deg, var(--color-text-main), var(--color-magenta)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;" style="font-size: 20px; color: var(--color-success);">탄성 거동 상태</div>
                    <div id="txt-status-desc" class="ratio-type">가해진 응력이 항복 이하입니다.</div>
                </div>
                        <div class="formula-card" style="padding: 12px; font-size: 11px; background: rgba(0, 0, 0, 0.01); border: 1px solid var(--color-border); border-radius: 12px; display: flex; flex-direction: column; gap: 8px;"
                    <h4 style="font-size: 12px; margin-bottom: 6px; font-weight: 700; color: var(--color-text-main);">대표 응력 변형 공식</h4>
                    <div class="formula-equation" style="font-family: monospace; color: #0284c7; font-size: 11px; background: #f8fafc; padding: 6px 10px; border-radius: 6px; border: 1px solid var(--color-border); text-align: center;">σ = P / A₀</div>
                    <div class="formula-equation" style="font-family: monospace; color: #0284c7; font-size: 11px; background: #f8fafc; padding: 6px 10px; border-radius: 6px; border: 1px solid var(--color-border); text-align: center;">ε = ΔL / L₀ = σ / E (탄성)</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">
                        <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 10px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease;"><i class="fa-solid fa-maximize text-cyan"></i></div>
                        <div class="card-content">
                            <span class="card-unit" style="font-size: 10px; font-weight: 600; color: var(--color-text-muted); text-transform: uppercase;">계산된 공칭 응력 (σ)</span>
                            <span id="txt-stress" class="card-value" style="font-family: var(--font-heading); font-weight: 700; font-size: 15px; color: var(--color-text-main);">0.0 MPa</span>
                        </div>
                    </div>
                        <div class="result-card">
                        <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 10px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease;"><i class="fa-solid fa-right-left text-magenta"></i></div>
                        <div class="card-content">
                            <span class="card-unit" style="font-size: 10px; font-weight: 600; color: var(--color-text-muted); text-transform: uppercase;">공칭 변형률 (ε)</span>
                            <span id="txt-strain" class="card-value" style="font-family: var(--font-heading); font-weight: 700; font-size: 15px; color: var(--color-text-main);">0.0000 %</span>
                        </div>
                    </div>
                        <div class="result-card">
                        <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 10px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease;"><i class="fa-solid fa-arrows-up-down text-purple"></i></div>
                        <div class="card-content">
                            <span class="card-unit" style="font-size: 10px; font-weight: 600; color: var(--color-text-muted); text-transform: uppercase;">현재 게이지 변형 길이 (L)</span>
                            <span id="txt-deformed-length" class="card-value" style="font-family: var(--font-heading); font-weight: 700; font-size: 15px; color: var(--color-text-main);">0.0 mm</span>
                        </div>
                    </div>
                        <div class="result-card">
                        <div class="card-icon" style="width: 32px; height: 32px; font-size: 12px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 10px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease;"><i class="fa-solid fa-shield text-success"></i></div>
                        <div class="card-content">
                            <span class="card-unit" style="font-size: 10px; font-weight: 600; color: var(--color-text-muted); text-transform: uppercase;">지정 탄성 계수 (E)</span>
                            <span id="txt-elastic-mod" class="card-value" style="font-family: var(--font-heading); font-weight: 700; font-size: 15px; color: var(--color-text-main);">200 GPa</span>
                        </div>
                    </div>
                    </div>
                </div>
            </section>
            <!-- Right Panel: Quantitative Results -->
        </main>
        <!-- Disclaimer Footer -->
        <footer class="app-disclaimer" style="margin-top: 20px; padding: 12px 16px; background: rgba(0, 0, 0, 0.015); border: 1px solid var(--color-border); border-radius: 12px; font-size: 11px; color: var(--color-text-muted); line-height: 1.5; text-align: left; display: flex; gap: 10px; align-items: flex-start; clear: both;">
            <i class="fa-solid fa-circle-info" style="color: var(--color-cyan); font-size: 14px; margin-top: 2px; flex-shrink: 0;"></i>
            <span><strong>면책 조항 (Disclaimer):</strong> 본 시뮬레이터의 계산 결과는 교육 및 참고용으로만 제공되며, 실제 제품 설계나 제작 시에는 반드시 최신 공학 규격 및 공식 표준 설계 기준을 재확인하시기 바랍니다. 계산 값의 무결성을 보장하지 않으며, 이로 인해 발생하는 직접적/간접적 손해에 대해 제작자 및 본 블로그는 어떠한 책임을 지지 않습니다.</span>
        </footer>
</div>
    <!-- Physics & Animation Engine -->
    </div>
</div>
<script>
        (function() {
        let isInit = false;
        function initSimulator() {
            if (isInit) return;
            const canvas = document.getElementById('physics-canvas');
            if (!canvas) return;
            const ctx = canvas.getContext('2d');
            if (!ctx) return;
            isInit = true;
            window.__stress_initialized = true;
            // DOM Elements
            const sliderP = document.getElementById('slider-p');
            const inputP = document.getElementById('input-p');
            const sliderD = document.getElementById('slider-d');
            const inputD = document.getElementById('input-d');
            const sliderL = document.getElementById('slider-l');
            const inputL = document.getElementById('input-l');
            const sliderSy = document.getElementById('slider-sy');
            const inputSy = document.getElementById('input-sy');
            const sliderSu = document.getElementById('slider-su');
            const inputSu = document.getElementById('input-su');
            const btnSteel = document.getElementById('preset-steel');
            const btnAluminum = document.getElementById('preset-aluminum');
            const btnCopper = document.getElementById('preset-copper');
            const txtStressOverlay = document.getElementById('txt-stress-overlay');
            const txtArea = document.getElementById('txt-area');
            const txtSafetyFactor = document.getElementById('txt-safety-factor');
            const txtStatusText = document.getElementById('txt-status-text');
            const txtStatusDesc = document.getElementById('txt-status-desc');
            const txtStress = document.getElementById('txt-stress');
            const txtStrain = document.getElementById('txt-strain');
            const txtDeformedLength = document.getElementById('txt-deformed-length');
            const txtElasticMod = document.getElementById('txt-elastic-mod');
            const txtState = document.getElementById('txt-state');
            // State variables
            const state = {
                P: 80.0, // kN
                d: 20.0, // mm
                L0: 150.0, // mm
                Sy: 250.0, // MPa
                Su: 400.0, // MPa
                E: 200, // GPa
                time: 0,
                // Curve parameter T (0=start, 100=fracture end) — always on curve path
                curveT: 0
            };
            // Canvas Resizing
            function resizeCanvas() {
                const rect = canvas.getBoundingClientRect();
                const dpr = window.devicePixelRatio || 1;
                canvas.width = rect.width * dpr;
                canvas.height = rect.height * dpr;
                ctx.scale(dpr, dpr);
            }
            resizeCanvas();
            window.addEventListener('resize', resizeCanvas);
            setTimeout(resizeCanvas, 300);
            // Synchronize control inputs
            function syncPFromSlider() {
                const val = parseFloat(sliderP.value);
                inputP.value = val.toFixed(0);
                state.P = val;
                updateCalculations();
            }
            function syncPFromInput() {
                let val = parseFloat(inputP.value);
                if (isNaN(val)) { val = 0; }
                if (val < 0) { val = 0; }
                if (val > 500) { val = 500; }
                sliderP.value = val;
                state.P = val;
                updateCalculations();
            }
            function syncDFromSlider() {
                const val = parseFloat(sliderD.value);
                inputD.value = val.toFixed(1);
                state.d = val;
                updateCalculations();
            }
            function syncDFromInput() {
                let val = parseFloat(inputD.value);
                if (isNaN(val)) { val = 5.0; }
                if (val < 5.0) { val = 5.0; }
                if (val > 30.0) { val = 30.0; }
                sliderD.value = val;
                state.d = val;
                updateCalculations();
            }
            function syncLFromSlider() {
                const val = parseFloat(sliderL.value);
                inputL.value = val.toFixed(0);
                state.L0 = val;
                updateCalculations();
            }
            function syncLFromInput() {
                let val = parseFloat(inputL.value);
                if (isNaN(val)) { val = 50; }
                if (val < 50) { val = 50; }
                if (val > 300) { val = 300; }
                sliderL.value = val;
                state.L0 = val;
                updateCalculations();
            }
            function syncSyFromSlider() {
                const val = parseInt(sliderSy.value);
                inputSy.value = val;
                state.Sy = val;
                validateSu();
                updateCalculations();
            }
            function syncSyFromInput() {
                let val = parseInt(inputSy.value);
                if (isNaN(val)) { val = 100; }
                if (val < 100) { val = 100; }
                if (val > 800) { val = 800; }
                sliderSy.value = val;
                state.Sy = val;
                validateSu();
                updateCalculations();
            }
            function syncSuFromSlider() {
                const val = parseInt(sliderSu.value);
                inputSu.value = val;
                state.Su = val;
                validateSu();
                updateCalculations();
            }
            function syncSuFromInput() {
                let val = parseInt(inputSu.value);
                if (isNaN(val)) { val = 200; }
                if (val < 200) { val = 200; }
                if (val > 1200) { val = 1200; }
                sliderSu.value = val;
                state.Su = val;
                validateSu();
                updateCalculations();
            }
            function validateSu() {
                if (state.Su < state.Sy + 50) {
                    state.Su = state.Sy + 50;
                    sliderSu.value = state.Su;
                    inputSu.value = state.Su;
                }
            }
            // Calculations
            function updateCalculations() {
                // Cross section area
                const A0 = (Math.PI * Math.pow(state.d, 2)) / 4; // mm^2
                txtArea.innerText = A0.toFixed(1) + ' mm²';
                // Applied stress = P / A0 (kN / mm^2 * 1000 = MPa)
                const stress = (state.P * 1000) / A0; // MPa
                txtStress.innerText = stress.toFixed(1) + ' MPa';
                txtStressOverlay.innerText = stress.toFixed(1) + ' MPa';
                // Strain mapping
                let strain = 0.0;
                let status = "elastic";
                // Safety factor
                let fs = state.Sy / stress;
                const E_MPa = state.E * 1000;
                if (stress <= state.Sy) {
                    // Elastic region
                    strain = stress / E_MPa;
                    status = "elastic";
                } else {
                    if (stress <= state.Su) {
                        // Plastic region
                        const excess = (stress - state.Sy) / (state.Su - state.Sy);
                        strain = (state.Sy / E_MPa) + 0.15 * Math.pow(excess, 1.5);
                        status = "plastic";
                    } else {
                        // Fractured!
                        strain = 0.25;
                        status = "fractured";
                        fs = 0.0;
                    }
                }
                txtStrain.innerText = (strain * 100).toFixed(4) + ' %';
                const deformedL = state.L0 * (1 + strain);
                txtDeformedLength.innerText = deformedL.toFixed(2) + ' mm';
                txtElasticMod.innerText = state.E + ' GPa';
                if (status === 'elastic') {
                    txtStatusText.innerText = "탄성 거동 상태";
                    txtStatusText.style.color = "var(--color-success)";
                    txtStatusDesc.innerText = "가해진 하중이 재료의 탄성 한계 이내에 들어와 있습니다.";
                    txtState.innerText = "거동 상태: 선형 탄성";
                    txtSafetyFactor.innerText = fs.toFixed(2);
                } else {
                    if (status === 'plastic') {
                        txtStatusText.innerText = "영구 소성 변형";
                        txtStatusText.style.color = "var(--color-purple)";
                        txtStatusDesc.innerText = "항복 강도를 초과하여 영구적인 형상 변화가 유도됩니다.";
                        txtState.innerText = "거동 상태: 소성 유동 및 네킹";
                        txtSafetyFactor.innerText = fs.toFixed(2);
                    } else {
                        txtStatusText.innerText = "시편 강제 파단";
                        txtStatusText.style.color = "var(--color-magenta)";
                        txtStatusDesc.innerText = "재료의 인장 강도를 초과하여 시편이 두 조각으로 끊어졌습니다.";
                        txtState.innerText = "거동 상태: 시편 파단 (Fracture)";
                        txtSafetyFactor.innerText = "0.00 (파괴)";
                    }
                }
            }
            // Presets
            btnSteel.addEventListener('click', function() {
                state.E = 200;
                state.Sy = 250;
                state.Su = 400;
                sliderSy.value = 250;
                inputSy.value = 250;
                sliderSu.value = 400;
                inputSu.value = 400;
                updateCalculations();
            });
            btnAluminum.addEventListener('click', function() {
                state.E = 70;
                state.Sy = 150;
                state.Su = 240;
                sliderSy.value = 150;
                inputSy.value = 150;
                sliderSu.value = 240;
                inputSu.value = 240;
                updateCalculations();
            });
            btnCopper.addEventListener('click', function() {
                state.E = 110;
                state.Sy = 70;
                state.Su = 200;
                sliderSy.value = 70;
                inputSy.value = 70;
                sliderSu.value = 200;
                inputSu.value = 200;
                updateCalculations();
            });
            // Event bindings
            sliderP.addEventListener('input', syncPFromSlider);
            inputP.addEventListener('change', syncPFromInput);
            sliderD.addEventListener('input', syncDFromSlider);
            inputD.addEventListener('change', syncDFromInput);
            sliderL.addEventListener('input', syncLFromSlider);
            inputL.addEventListener('change', syncLFromInput);
            sliderSy.addEventListener('input', syncSyFromSlider);
            inputSy.addEventListener('change', syncSyFromInput);
            sliderSu.addEventListener('input', syncSuFromSlider);
            inputSu.addEventListener('change', syncSuFromInput);
            // Animation Loop
            function animate() {
                requestAnimationFrame(animate);
                state.time += 0.05;
                const width = canvas.width / (window.devicePixelRatio || 1);
                const height = canvas.height / (window.devicePixelRatio || 1);
                ctx.clearRect(0, 0, width, height);
                // 1. Draw snow white blueprint grid
                ctx.save();
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.05)';
                ctx.lineWidth = 1;
                for (let x = 0; x < width; x += 30) {
                    ctx.beginPath();
                    ctx.moveTo(x, 0);
                    ctx.lineTo(x, height);
                    ctx.stroke();
                }
                for (let y = 0; y < height; y += 30) {
                    ctx.beginPath();
                    ctx.moveTo(0, y);
                    ctx.lineTo(width, y);
                    ctx.stroke();
                }
                ctx.restore();
                // Split Canvas Layout:
                // Left half (width * 0.45): 2D Specimen tensile tester
                // Right half (width * 0.55): Stress-Strain plot
                const testerWidth = width * 0.40;
                const plotWidth = width * 0.55;
                const splitX = testerWidth;
                // --- 2D SPECIMEN TENSILE TESTER RENDERING ---
                ctx.save();
                // Draw title
                ctx.font = '700 11px Outfit, sans-serif';
                ctx.fillStyle = '#475569';
                ctx.fillText('인장 시험편 실시간 2D 모사', 20, 25);
                const cX = testerWidth / 2;
                const gripWidth = 50;
                const gripHeight = 24;
                const A0 = (Math.PI * Math.pow(state.d, 2)) / 4;
                const stress = (state.P * 1000) / A0;
                let strain = 0.0;
                let isFractured = false;
                const E_MPa = state.E * 1000;
                if (stress <= state.Sy) {
                    strain = stress / E_MPa;
                } else {
                    if (stress <= state.Su) {
                        const excess = (stress - state.Sy) / (state.Su - state.Sy);
                        strain = (state.Sy / E_MPa) + 0.15 * Math.pow(excess, 1.5);
                    } else {
                        strain = 0.25;
                        isFractured = true;
                    }
                }
                // Gauge length screen scale
                const L_base = 120;
                const screenL = L_base * (1 + strain);
                const barTopY = height / 2 - screenL / 2;
                const barBottomY = height / 2 + screenL / 2;
                // Specimen necking factor
                let neckFactor = 1.0;
                if (stress > state.Sy) {
                    // Start thinning
                    const pFrac = Math.min((stress - state.Sy) / (state.Su - state.Sy), 1.0);
                    if (!isNaN(pFrac)) {
                        neckFactor = 1.0 - 0.4 * pFrac;
                    }
                }
                if (isFractured) {
                    neckFactor = 0.2;
                }
                // Draw Grips
                ctx.fillStyle = '#64748b';
                ctx.strokeStyle = '#475569';
                ctx.lineWidth = 2;
                // Top Grip
                ctx.beginPath();
                ctx.rect(cX - gripWidth/2, barTopY - gripHeight, gripWidth, gripHeight);
                ctx.fill();
                ctx.stroke();
                // Bottom Grip
                ctx.beginPath();
                ctx.rect(cX - gripWidth/2, barBottomY, gripWidth, gripHeight);
                ctx.fill();
                ctx.stroke();
                // Draw Specimen bar with possible necking
                ctx.fillStyle = '#cbd5e1';
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 1.5;
                const baseR = state.d * 0.7; // screen width scaled
                if (isFractured) {
                    // Fractured specimen - split into two pieces
                    const fractureGap = 15;
                    const midY = height / 2;
                    // Top piece
                    ctx.beginPath();
                    ctx.moveTo(cX - baseR, barTopY);
                    ctx.lineTo(cX + baseR, barTopY);
                    ctx.lineTo(cX + baseR, midY - fractureGap/2 - 10);
                    ctx.quadraticCurveTo(cX + baseR*0.5, midY - fractureGap/2 - 6, cX + baseR*0.2, midY - fractureGap/2);
                    ctx.lineTo(cX - baseR*0.2, midY - fractureGap/2);
                    ctx.quadraticCurveTo(cX - baseR*0.5, midY - fractureGap/2 - 6, cX - baseR, midY - fractureGap/2 - 10);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // Bottom piece
                    ctx.beginPath();
                    ctx.moveTo(cX - baseR, barBottomY);
                    ctx.lineTo(cX + baseR, barBottomY);
                    ctx.lineTo(cX + baseR, midY + fractureGap/2 + 10);
                    ctx.quadraticCurveTo(cX + baseR*0.5, midY + fractureGap/2 + 6, cX + baseR*0.2, midY + fractureGap/2);
                    ctx.lineTo(cX - baseR*0.2, midY + fractureGap/2);
                    ctx.quadraticCurveTo(cX - baseR*0.5, midY + fractureGap/2 + 6, cX - baseR, midY + fractureGap/2 + 10);
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // FRACTURED Tag
                    ctx.fillStyle = '#db2777';
                    ctx.font = '800 13px Outfit, sans-serif';
                    ctx.textAlign = 'center';
                    ctx.fillText('파단됨 (FRACTURED)', cX, midY + 4);
                } else {
                    // Continuous bar
                    ctx.beginPath();
                    ctx.moveTo(cX - baseR, barTopY);
                    ctx.lineTo(cX + baseR, barTopY);
                    // Bezier points to model necking in the middle
                    const midY = height / 2;
                    ctx.bezierCurveTo(
                        cX + baseR, midY - screenL*0.2, 
                        cX + baseR * neckFactor, midY - screenL*0.1, 
                        cX + baseR * neckFactor, midY
                    );
                    ctx.bezierCurveTo(
                        cX + baseR * neckFactor, midY + screenL*0.1, 
                        cX + baseR, midY + screenL*0.2, 
                        cX + baseR, barBottomY
                    );
                    ctx.lineTo(cX - baseR, barBottomY);
                    ctx.bezierCurveTo(
                        cX - baseR, midY + screenL*0.2, 
                        cX - baseR * neckFactor, midY + screenL*0.1, 
                        cX - baseR * neckFactor, midY
                    );
                    ctx.bezierCurveTo(
                        cX - baseR * neckFactor, midY - screenL*0.1, 
                        cX - baseR, midY - screenL*0.2, 
                        cX - baseR, barTopY
                    );
                    ctx.closePath();
                    ctx.fill();
                    ctx.stroke();
                    // Draw tension arrow on bottom grip
                    ctx.strokeStyle = '#db2777';
                    ctx.lineWidth = 3;
                    ctx.beginPath();
                    ctx.moveTo(cX, barBottomY + gripHeight);
                    ctx.lineTo(cX, barBottomY + gripHeight + 25);
                    ctx.stroke();
                    ctx.fillStyle = '#db2777';
                    ctx.beginPath();
                    ctx.moveTo(cX, barBottomY + gripHeight + 30);
                    ctx.lineTo(cX - 6, barBottomY + gripHeight + 22);
                    ctx.lineTo(cX + 6, barBottomY + gripHeight + 22);
                    ctx.closePath();
                    ctx.fill();
                    // Tension tag
                    ctx.font = '700 11px Outfit, sans-serif';
                    ctx.fillText('P = ' + state.P.toFixed(0) + ' kN', cX, barBottomY + gripHeight + 42);
                }
                ctx.restore();
                // --- STRESS-STRAIN PLOT RENDERING ---
                ctx.save();
                ctx.translate(splitX + 40, 40);
                const pW = plotWidth - 80;
                const pH = height - 100;
                // Draw Axes
                ctx.strokeStyle = '#1e293b';
                ctx.lineWidth = 1.5;
                ctx.beginPath();
                ctx.moveTo(0, 0);
                ctx.lineTo(0, pH);
                ctx.lineTo(pW, pH);
                ctx.stroke();
                // Axis labels
                ctx.font = '500 10px Inter, sans-serif';
                ctx.fillStyle = '#475569';
                ctx.textAlign = 'right';
                ctx.fillText('변형률 ε (%)', pW, pH + 20);
                ctx.save();
                ctx.translate(-25, 0);
                ctx.rotate(-Math.PI / 2);
                ctx.textAlign = 'center';
                ctx.fillText('응력 σ (MPa)', -pH/2, 0);
                ctx.restore();
                // Stress-Strain Curve geometry mapping
                // E in GPa -> E_MPa = E * 1000
                // Max strain on graph: 25% (0.25)
                // Max stress on graph: Su * 1.3
                const maxGraphStrain = 0.25;
                const maxGraphStress = state.Su * 1.25;
                function getScreenCoords(eps, str) {
                    const sx = (eps / maxGraphStrain) * pW;
                    const sy = pH - (str / maxGraphStress) * pH;
                    return { x: sx, y: sy };
                }
                // Trace the stress strain curve
                ctx.beginPath();
                let isFirst = true;
                // We plot 100 points
                for (let eStep = 0; eStep <= 100; eStep++) {
                    const currEps = (eStep / 100) * 0.25;
                    let currStress = 0.0;
                    const epsY = state.Sy / E_MPa;
                    const epsU = 0.12;
                    const epsF = 0.22;
                    if (currEps <= epsY) {
                        currStress = E_MPa * currEps;
                    } else {
                        if (currEps <= epsF) {
                            const pFrac = (currEps - epsY) / (epsF - epsY);
                            currStress = state.Sy + (state.Su - state.Sy) * Math.sin(pFrac * Math.PI * 0.7);
                        } else {
                            // Necking/Fractured downward path
                            const fFrac = (currEps - epsF) / (0.25 - epsF);
                            currStress = (state.Sy + (state.Su - state.Sy) * Math.sin(Math.PI * 0.7)) * (1.0 - fFrac);
                        }
                    }
                    const pt = getScreenCoords(currEps, currStress);
                    if (isFirst) {
                        ctx.moveTo(pt.x, pt.y);
                        isFirst = false;
                    } else {
                        ctx.lineTo(pt.x, pt.y);
                    }
                }
                ctx.strokeStyle = 'rgba(71, 85, 105, 0.2)';
                ctx.lineWidth = 3;
                ctx.stroke();
                // Plot the Yield Strength and Ultimate Tensile Strength marker lines
                const ptSy = getScreenCoords(state.Sy / E_MPa, state.Sy);
                const ptSu = getScreenCoords(0.11, state.Su); // approximate peak
                ctx.strokeStyle = 'rgba(2, 132, 199, 0.15)';
                ctx.setLineDash([3, 3]);
                ctx.beginPath();
                ctx.moveTo(0, ptSy.y);
                ctx.lineTo(pW, ptSy.y);
                ctx.stroke();
                ctx.strokeStyle = 'rgba(219, 39, 119, 0.15)';
                ctx.beginPath();
                ctx.moveTo(0, ptSu.y);
                ctx.lineTo(pW, ptSu.y);
                ctx.stroke();
                ctx.setLineDash([]);
                // Add text markers
                ctx.textAlign = 'left';
                ctx.fillStyle = '#0284c7';
                ctx.font = '500 9px Outfit, sans-serif';
                ctx.fillText('Sy = ' + state.Sy.toFixed(0) + ' MPa', 5, ptSy.y - 4);
                ctx.fillStyle = '#db2777';
                ctx.fillText('Su = ' + state.Su.toFixed(0) + ' MPa', 5, ptSu.y - 4);
                // T-parameter dot: curveT (0–100) = position along the drawn curve
                // Dot always lies on the curve by evaluating same formula as curve drawing
                const epsY_c = state.Sy / E_MPa;
                const epsF_c = 0.22;
                // Compute target T from current stress region
                let targetT;
                if (isFractured) {
                    // Fractured → slide to the very end of the curve
                    targetT = 100;
                } else if (stress <= state.Sy) {
                    // Elastic: ε = σ/E → T = (ε/0.25)*100
                    targetT = (stress / E_MPa / 0.25) * 100;
                } else {
                    // Plastic ascending: invert sin formula to get ε, then T
                    const ratio = Math.min((stress - state.Sy) / (state.Su - state.Sy), 1.0);
                    const pFrac = Math.asin(ratio) / (Math.PI * 0.7);
                    const epsTarget = epsY_c + pFrac * (epsF_c - epsY_c);
                    targetT = (epsTarget / 0.25) * 100;
                }
                // Lerp curveT toward targetT (smooth glide)
                state.curveT += (targetT - state.curveT) * 0.16;
                // Evaluate curve formula at current curveT to get exact on-curve position
                const dotEps = (state.curveT / 100) * 0.25;
                let dotSigma = 0;
                if (dotEps <= epsY_c) {
                    dotSigma = E_MPa * dotEps;
                } else if (dotEps <= epsF_c) {
                    const pf = (dotEps - epsY_c) / (epsF_c - epsY_c);
                    dotSigma = state.Sy + (state.Su - state.Sy) * Math.sin(pf * Math.PI * 0.7);
                } else {
                    const ff = (dotEps - epsF_c) / (0.25 - epsF_c);
                    dotSigma = (state.Sy + (state.Su - state.Sy) * Math.sin(Math.PI * 0.7)) * (1 - ff);
                }
                const currentPt = getScreenCoords(dotEps, dotSigma);
                ctx.beginPath();
                ctx.arc(currentPt.x, currentPt.y, 6 + Math.sin(state.time) * 2, 0, Math.PI * 2);
                if (isFractured) {
                    ctx.fillStyle = '#db2777';   // hot-pink = fracture
                } else if (stress > state.Sy) {
                    ctx.fillStyle = '#7c3aed';   // purple = plastic
                } else {
                    ctx.fillStyle = '#0284c7';   // cyan = elastic
                }
                ctx.shadowBlur = 10;
                ctx.shadowColor = ctx.fillStyle;
                ctx.fill();
                ctx.shadowBlur = 0;
                ctx.restore();
            }
            // Document security
            document.addEventListener('contextmenu', function(e) {
                e.preventDefault();
                alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                return false;
            }, { capture: true });
            document.addEventListener('selectstart', function(e) {
                e.preventDefault();
                return false;
            }, { capture: true });
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F12') {
                    e.preventDefault();
                    alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                    return false;
                }
                if (e.ctrlKey) {
                    if (e.key === 'u' || e.key === 'c' || e.key === 's' || e.key === 'U' || e.key === 'C' || e.key === 'S') {
                        e.preventDefault();
                        alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                        return false;
                    }
                    if (e.shiftKey) {
                        if (['i','I','j','J','c','C'].includes(e.key)) {
                            e.preventDefault();
                            alert("이 콘텐츠는 저작권법의 보호를 받습니다. 무단 복제 및 우클릭을 금지합니다.");
                            return false;
                        }
                    }
                }
            }, { capture: true });
            // Initialize
            updateCalculations();
            animate();
        }
        let initAttempts = 0;
        function tryInit() {
            initAttempts++;
            initSimulator();
            if (!window.__stress_initialized) {
                if (initAttempts < 50) {
                    setTimeout(tryInit, 100);
                }
            }
        }
        if (document.readyState === 'complete' || document.readyState === 'interactive') {
            tryInit();
        } else {
            document.addEventListener('DOMContentLoaded', tryInit);
            window.addEventListener('load', tryInit);
        }
        })();
</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>재료 물성 설정: 프리셋 버튼(구조용 강재, 알루미늄 합금, 황동)을 누르거나 직접 탄성계수(E), 항복강도(Sy), 인장강도(Su)를 세밀하게 입력합니다.</strong></li>
<li style="margin-bottom: 6px;">시편 규격 및 하중 조절: 시편의 초기 지름(d₀)과 게이지 길이(L₀)를 설정하고 가해지는 인장 하중(P)을 조절합니다.</li>
<li style="margin-bottom: 6px;">실시간 2D 인장 거동 관찰: 하중 증가에 따라 원통형 인장 시편이 늘어나는 2D 애니메이션과 임계 하중 초과 시 단면이 얇아지는 네킹(Necking) 및 파단(Fracture) 현상을 직접 관찰합니다.</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;" /> 상세 재료역학 이론 및 응력-변형률 선도 (Stress-Strain Diagram) 해설 확인하기</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. 응력(Stress)과 변형률(Strain)의 정의 및 공학적 성질</h3>
<p>재료역학에서 외력이 가해졌을 때 재료 내부의 저항력을 정의하는 <strong>응력(Stress, &sigma;)</strong>과 외력에 따른 변형 비율을 의미하는 <strong>변형률(Strain, &epsilon;)</strong>은 구조 설계의 가장 기본적인 척도입니다.</p><ul><li><strong>공칭 응력(Engineering Stress, &sigma;):</strong> 가해진 인장 하중 <code>P</code>를 재료의 변형 전 초기 단면적 <code>A_0</code>로 나눈 값입니다. (&sigma; = P / A_0)</li><li><strong>공칭 변형률(Engineering Strain, &epsilon;):</strong> 늘어난 길이 <code>&Delta;L</code>을 초기 게이지 길이 <code>L_0</code>로 나눈 무차원 비율입니다. (&epsilon; = &Delta;L / L_0)</li></ul><p>외력 초기에는 응력과 변형률이 선형적으로 비례하며, 이 비례 관계를 결정하는 상수를 <strong>탄성계수(Young's Modulus, E)</strong> 혹은 영률이라고 부르며, 훅의 법칙(Hooke's Law: &sigma; = E&epsilon;)으로 잘 알려져 있습니다.</p>
<h3>2. 응력-변형률 선도(Stress-Strain Diagram)의 주요 영역 분할</h3>
<p>인장 시험(Tensile Test)을 진행하며 하중을 서서히 높일 때, 금속 재료는 다음과 같은 네 가지 거동 영역을 거쳐 변형됩니다.</p><ol><li><strong>탄성 영역 (Elastic Region):</strong> 하중을 제거하면 원상태로 돌아가는 영역으로, 극한 훅의 법칙이 성립하는 비례한도와 탄성한도 이내입니다.</li><li><strong>항복 영역 (Yield Point / Plastic Flow):</strong> 미세한 응력 증가에도 변형이 급격히 증가하는 소성 변형(Plastic Deformation)의 시작 지점입니다. 0.2% 오프셋 항복강도(<code>Sy</code>)가 대표적인 안전 기준이 됩니다.</li><li><strong>소성/인장 영역 (Ultimate Tensile Strength / UTS):</strong> 재료가 견딜 수 있는 최대 응력 지점(<code>Su</code>)까지 영구 변형이 확산되는 구간입니다.</li><li><strong>네킹 및 파단 (Necking & Fracture):</strong> 최대 강도 도달 이후 시편 중앙부의 국부적 단면 수축(Necking)이 극대화되며 응력이 급감하고 최종적으로 두 조각으로 파단(Fracture)되는 현상입니다.</li></ol>
<h3>3. 구조재료 설계 규격 및 허용 응력에 기초한 허용 하중</h3>
<p>엔지니어링 구조 설계를 수행할 때는 기계적 파단을 방지하기 위해 사용 재료의 항복강도(<code>Sy</code>)를 설계 기준 강도로 취하고, 여기에 공학적 여유 배율인 <strong>안전율(Safety Factor, FS)</strong>을 적용하여 <strong>허용 응력(&sigma;_allow)</strong>을 산정합니다.</p><p style="text-align: center; font-weight: bold; background: #e0f2fe; padding: 16px; border-radius: 8px; font-size: 1.05em; color: #0369a1;">&sigma;_allow = Sy / FS &nbsp;&rarr;&nbsp; P_allow = &sigma;_allow &times; A₀ &nbsp;[N]</p><p>강재의 경우 통상적으로 정하중 하에서 1.5 ~ 2.0 수준의 안전율을 설계에 채택하며, 진동이나 반복 하중이 작용하는 동적 환경하에서는 3.0 ~ 5.0 이상의 보수적인 가이드라인을 부여하여 취성 파괴를 미연에 방지합니다.</p>
        </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/stress-strain-relation-calculator-simulator/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
