{"id":4493,"date":"2026-09-10T17:44:48","date_gmt":"2026-09-10T09:44:48","guid":{"rendered":"https:\/\/www.kcvents.com\/?page_id=4493"},"modified":"2026-09-10T18:04:35","modified_gmt":"2026-09-10T10:04:35","slug":"calculadora-de-cfm-de-ventilacao","status":"publish","type":"page","link":"https:\/\/www.kcvents.com\/pt\/calculadora-de-cfm-de-ventilacao\/","title":{"rendered":""},"content":{"rendered":"\n<!-- ==========================================================================\n     KCVENTS Ventilation Calculator - WordPress Custom HTML Embed Snippet\n     Instructions:\n     1. In WordPress page editor (Gutenberg), add a \"Custom HTML\" block.\n        (Or in Elementor, drag and drop an \"HTML\" widget).\n     2. Paste this entire file content into the block and click \"Publish\/Update\".\n     ========================================================================== -->\n\n<style>\n  \/* 0. WordPress Blocksy \/ Theme Container Overrides *\/\n  .ct-container-full {\n    padding-top: 0px !important;\n    padding-left: 8vw !important;\n    padding-right: 8vw !important;\n    box-sizing: border-box !important;\n  }\n\n  \/* Scoped Styles for KCVENTS Calculator (No interference with WordPress Theme) *\/\n  .kcvents-calc-app {\n    --bg-canvas: #F8F9FA;\n    --bg-card: #FFFFFF;\n    --bg-card-subtle: #F4F5F4;\n    --bg-accent-soft: #EDF3F0;\n    \n    --text-main: #192024;\n    --text-secondary: #5A656B;\n    --text-muted: #8E999F;\n    \n    --nordic-pine: #1E3A2F;\n    --nordic-pine-dark: #12241D;\n    --nordic-gold: #C09553;\n    \n    --border-subtle: rgba(25, 32, 36, 0.08);\n    \n    --radius-sm: 8px;\n    --radius-md: 14px;\n    --radius-lg: 20px;\n    --radius-full: 9999px;\n    \n    --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.03);\n    --shadow-card: 0 10px 30px -5px rgba(25, 32, 36, 0.05), 0 2px 6px -1px rgba(0, 0, 0, 0.02);\n    \n    --font-stack: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n    --font-num: \"SF Pro Display\", -apple-system, \"Segoe UI\", sans-serif;\n    \n    font-family: var(--font-stack);\n    color: var(--text-main);\n    line-height: 1.6;\n    background-color: var(--bg-canvas);\n    background-image: radial-gradient(at 10% 10%, rgba(30, 58, 47, 0.03) 0px, transparent 60%),\n                      radial-gradient(at 90% 90%, rgba(192, 149, 83, 0.03) 0px, transparent 60%);\n    border-radius: var(--radius-lg);\n    padding: 0;\n    margin: 0 auto;\n    max-width: 100%;\n    width: 100%;\n    box-sizing: border-box;\n    -webkit-font-smoothing: antialiased;\n    border: none !important;\n  }\n\n  .kcvents-calc-app * {\n    box-sizing: border-box;\n  }\n\n  \/* 4. Page Content Snippet (Rank Math H1 & Intro) *\/\n  .kcvents-page-intro {\n    padding: 36px 0 24px;\n    border-bottom: 1px solid var(--border-subtle);\n    background: #FFFFFF;\n    border-top-left-radius: var(--radius-lg);\n    border-top-right-radius: var(--radius-lg);\n  }\n\n  .kcvents-intro-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    gap: 20px;\n    margin-bottom: 12px;\n    flex-wrap: wrap;\n  }\n\n  .kcvents-page-title {\n    font-size: 28px !important;\n    font-weight: 700 !important;\n    color: var(--text-main) !important;\n    line-height: 1.25 !important;\n    letter-spacing: -0.5px;\n    margin: 0 !important;\n    flex: 1;\n    min-width: 280px;\n  }\n\n  .kcvents-page-desc {\n    font-size: 14px !important;\n    line-height: 1.65 !important;\n    color: var(--text-secondary) !important;\n    margin: 0 !important;\n    max-width: 960px;\n  }\n\n  .kcvents-page-desc a {\n    color: var(--nordic-pine);\n    text-decoration: underline;\n    text-underline-offset: 3px;\n    font-weight: 500;\n    transition: color 0.15s ease;\n  }\n\n  .kcvents-page-desc a:hover {\n    color: var(--nordic-pine-dark);\n  }\n\n  .kcvents-unit-switch {\n    background: var(--bg-card-subtle);\n    padding: 3px;\n    border-radius: var(--radius-full);\n    display: flex;\n    border: 1px solid var(--border-subtle);\n    flex-shrink: 0;\n  }\n\n  .kcvents-unit-btn {\n    padding: 6px 14px;\n    border-radius: var(--radius-full);\n    border: none;\n    background: transparent;\n    font-size: 13px;\n    font-weight: 500;\n    color: var(--text-secondary);\n    cursor: pointer;\n    transition: all 0.2s ease;\n    white-space: nowrap;\n  }\n\n  .kcvents-unit-btn.active {\n    background: var(--bg-card);\n    color: var(--nordic-pine);\n    box-shadow: var(--shadow-sm);\n    font-weight: 600;\n  }\n\n  .kcvents-main {\n    padding: 0px 0px;\n    display: grid;\n    grid-template-columns: 1.15fr 0.85fr;\n    gap: 32px;\n  }\n\n  @media (max-width: 1024px) {\n    .kcvents-page-intro {\n      padding: 24px 0 18px;\n    }\n    .kcvents-page-title {\n      font-size: 22px !important;\n    }\n    .kcvents-main {\n      grid-template-columns: 1fr;\n      padding: 20px 0;\n      gap: 24px;\n    }\n    .kcvents-intro-header {\n      flex-direction: column;\n      align-items: flex-start;\n      gap: 14px;\n    }\n    .kcvents-faq-section {\n      padding: 24px 0 32px !important;\n    }\n  }\n\n  .kcvents-card {\n    background: var(--bg-card);\n    border-radius: var(--radius-lg);\n    border: 1px solid var(--border-subtle);\n    box-shadow: var(--shadow-card);\n    padding: 28px;\n    position: relative;\n  }\n\n  .kcvents-card-title-bar {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    margin-bottom: 20px;\n    flex-wrap: wrap;\n    gap: 10px;\n  }\n\n  .kcvents-card-title {\n    font-size: 16px !important;\n    font-weight: 600 !important;\n    color: var(--text-main) !important;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    margin: 0 !important;\n  }\n\n  .kcvents-card-title::before {\n    content: \"\";\n    display: inline-block;\n    width: 4px;\n    height: 16px;\n    background: var(--nordic-pine);\n    border-radius: 2px;\n  }\n\n  .kcvents-airflow-card .kcvents-card-title::before,\n  .kcvents-spec-card .kcvents-card-title::before {\n    display: none;\n  }\n\n  .kcvents-card-badge {\n    font-size: 12px;\n    padding: 3px 10px;\n    background: var(--bg-accent-soft);\n    color: var(--nordic-pine);\n    border-radius: var(--radius-full);\n    font-weight: 500;\n  }\n\n  .kcvents-scenario-select-wrap {\n    position: relative;\n    margin-bottom: 10px;\n  }\n\n  .kcvents-scenario-select {\n    width: 100%;\n    appearance: none;\n    -webkit-appearance: none;\n    -moz-appearance: none;\n    font-family: var(--font-stack) !important;\n    font-size: 14px !important;\n    font-weight: 500;\n    line-height: 1.4 !important;\n    height: auto !important;\n    color: var(--text-main) !important;\n    background: var(--bg-card) !important;\n    border: 1px solid var(--border-subtle) !important;\n    border-radius: var(--radius-md) !important;\n    padding: 12px 44px 12px 16px !important;\n    box-shadow: none;\n    cursor: pointer;\n    outline: none;\n    transition: border-color 0.2s ease, box-shadow 0.2s ease;\n  }\n\n  .kcvents-scenario-select:hover {\n    border-color: rgba(30, 58, 47, 0.28) !important;\n  }\n\n  .kcvents-scenario-select:focus {\n    border-color: var(--nordic-pine) !important;\n    box-shadow: 0 0 0 3px rgba(30, 58, 47, 0.08) !important;\n  }\n\n  .kcvents-scenario-select-wrap::after {\n    content: \"\";\n    position: absolute;\n    top: 50%;\n    right: 18px;\n    width: 8px;\n    height: 8px;\n    border-right: 2px solid var(--text-secondary);\n    border-bottom: 2px solid var(--text-secondary);\n    transform: translateY(-50%) rotate(45deg);\n    pointer-events: none;\n  }\n\n  .kcvents-scenario-meta {\n    font-size: 12px;\n    color: var(--text-muted);\n    margin: 0 0 24px 0;\n    min-height: 18px;\n  }\n\n  .kcvents-section-divider {\n    height: 1px;\n    background: var(--border-subtle);\n    margin: 26px 0;\n  }\n\n  .kcvents-control-row {\n    margin-bottom: 20px;\n  }\n\n  .kcvents-control-header {\n    display: flex;\n    justify-content: space-between;\n    align-items: baseline;\n    margin-bottom: 8px;\n  }\n\n  .kcvents-control-label {\n    font-size: 13px;\n    font-weight: 500;\n    color: var(--text-secondary);\n  }\n\n  .kcvents-control-val-box {\n    display: flex;\n    align-items: baseline;\n    gap: 4px;\n  }\n\n  .kcvents-control-val-input {\n    font-family: var(--font-num);\n    font-size: 15px;\n    font-weight: 600;\n    color: var(--text-main);\n    border: 1px solid var(--border-subtle);\n    border-radius: var(--radius-sm);\n    padding: 2px 8px;\n    width: 74px;\n    text-align: right;\n    outline: none;\n    transition: border-color 0.2s ease;\n    background: var(--bg-card);\n  }\n\n  .kcvents-control-val-input:focus {\n    border-color: var(--nordic-pine);\n  }\n\n  .kcvents-control-unit {\n    font-size: 12px;\n    color: var(--text-muted);\n  }\n\n  .kcvents-slider-range {\n    -webkit-appearance: none;\n    width: 100%;\n    height: 6px;\n    border-radius: 3px;\n    background: #E5E7EB;\n    outline: none;\n    cursor: pointer;\n    position: relative;\n  }\n\n  .kcvents-slider-range::-webkit-slider-thumb {\n    -webkit-appearance: none;\n    appearance: none;\n    width: 18px;\n    height: 18px;\n    border-radius: 50%;\n    background: var(--nordic-pine);\n    border: 2px solid #FFFFFF;\n    box-shadow: 0 2px 6px rgba(0,0,0,0.18);\n    cursor: pointer;\n    transition: transform 0.15s ease;\n  }\n\n  .kcvents-slider-range::-webkit-slider-thumb:hover {\n    transform: scale(1.15);\n  }\n\n  .kcvents-slider-range::-moz-range-thumb {\n    width: 18px;\n    height: 18px;\n    border-radius: 50%;\n    background: var(--nordic-pine);\n    border: 2px solid #FFFFFF;\n    box-shadow: 0 2px 6px rgba(0,0,0,0.18);\n    cursor: pointer;\n  }\n\n  .kcvents-dimension-grid {\n    display: grid;\n    grid-template-columns: 1fr;\n    gap: 18px;\n    margin-bottom: 12px;\n  }\n\n  .kcvents-volume-summary-bar {\n    display: flex;\n    justify-content: space-between;\n    background: var(--bg-card-subtle);\n    padding: 12px 18px;\n    border-radius: var(--radius-md);\n    margin-top: 14px;\n    font-size: 13px;\n  }\n\n  .kcvents-volume-summary-item {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n  }\n\n  .kcvents-volume-summary-item .label {\n    color: var(--text-muted);\n  }\n\n  .kcvents-volume-summary-item .value {\n    font-weight: 600;\n    color: var(--text-main);\n    font-family: var(--font-num);\n  }\n\n  .kcvents-rate-pill {\n    display: inline-flex;\n    align-items: center;\n    gap: 6px;\n    font-size: 12px;\n    color: var(--nordic-pine);\n    background: var(--bg-accent-soft);\n    padding: 5px 12px;\n    border-radius: var(--radius-full);\n    font-weight: 500;\n  }\n\n  details.kcvents-advanced-box {\n    border: 1px solid var(--border-subtle);\n    border-radius: var(--radius-md);\n    padding: 14px 18px;\n    margin-top: 22px;\n    background: #FAFAF9;\n    transition: all 0.2s ease;\n  }\n\n  details.kcvents-advanced-box[open] {\n    background: #FFFFFF;\n    box-shadow: var(--shadow-sm);\n  }\n\n  details.kcvents-advanced-box summary {\n    cursor: pointer;\n    font-size: 13px;\n    font-weight: 600;\n    color: var(--text-secondary);\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    user-select: none;\n  }\n\n  details.kcvents-advanced-box summary::marker,\n  details.kcvents-advanced-box summary::-webkit-details-marker {\n    display: none;\n  }\n\n  details.kcvents-advanced-box summary::after {\n    content: \"\u2193\";\n    font-size: 12px;\n    transition: transform 0.2s ease;\n  }\n\n  details.kcvents-advanced-box[open] summary::after {\n    transform: rotate(180deg);\n  }\n\n  .kcvents-advanced-content {\n    padding-top: 18px;\n  }\n\n  .kcvents-result-column {\n    display: flex;\n    flex-direction: column;\n    gap: 24px;\n  }\n\n  .kcvents-hero-card {\n    background: linear-gradient(145deg, #12241D 0%, #1E3A2F 100%);\n    color: #FFFFFF;\n    border-radius: var(--radius-lg);\n    padding: 32px;\n    box-shadow: 0 16px 36px -6px rgba(18, 36, 29, 0.35);\n    position: relative;\n    overflow: hidden;\n  }\n\n  .kcvents-hero-card::before {\n    content: \"\";\n    position: absolute;\n    top: -40px;\n    right: -40px;\n    width: 180px;\n    height: 180px;\n    border-radius: 50%;\n    background: radial-gradient(circle, rgba(192, 149, 83, 0.25) 0%, transparent 70%);\n    pointer-events: none;\n  }\n\n  .kcvents-hero-eyebrow {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    margin-bottom: 10px;\n  }\n\n  .kcvents-hero-eyebrow span {\n    font-size: 12px;\n    text-transform: uppercase;\n    letter-spacing: 1.2px;\n    color: rgba(255, 255, 255, 0.7);\n  }\n\n  .kcvents-hero-scene-tag {\n    font-size: 12px;\n    background: rgba(255, 255, 255, 0.15);\n    padding: 3px 10px;\n    border-radius: var(--radius-full);\n    backdrop-filter: blur(8px);\n    border: 1px solid rgba(255, 255, 255, 0.18);\n  }\n\n  .kcvents-hero-value-wrap {\n    display: flex;\n    align-items: baseline;\n    gap: 12px;\n    margin: 6px 0 16px;\n  }\n\n  .kcvents-hero-value {\n    font-family: var(--font-num);\n    font-size: 64px;\n    font-weight: 700;\n    line-height: 1;\n    letter-spacing: -1.5px;\n  }\n\n  .kcvents-hero-unit {\n    font-size: 24px;\n    font-weight: 400;\n    color: rgba(255, 255, 255, 0.85);\n  }\n\n  .kcvents-hero-sub-metrics {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 14px;\n    padding-top: 18px;\n    border-top: 1px solid rgba(255, 255, 255, 0.15);\n  }\n\n  .kcvents-sub-metric-item {\n    display: flex;\n    flex-direction: column;\n  }\n\n  .kcvents-sub-metric-label {\n    font-size: 12px;\n    color: rgba(255, 255, 255, 0.65);\n    margin-bottom: 2px;\n  }\n\n  .kcvents-sub-metric-val {\n    font-family: var(--font-num);\n    font-size: 20px;\n    font-weight: 600;\n    color: #FFFFFF;\n  }\n\n  .kcvents-airflow-card {\n    background: var(--bg-card);\n    border-radius: var(--radius-lg);\n    border: 1px solid var(--border-subtle);\n    box-shadow: var(--shadow-card);\n    padding: 24px;\n  }\n\n  .kcvents-airflow-container {\n    position: relative;\n    height: 136px;\n    border-radius: var(--radius-md);\n    background: #F4F6F5;\n    overflow: hidden;\n    margin-top: 14px;\n    border: 1px solid var(--border-subtle);\n  }\n\n  canvas#kcventsFlowCanvas {\n    width: 100%;\n    height: 100%;\n    display: block;\n  }\n\n  .kcvents-flow-badge {\n    position: absolute;\n    top: 12px;\n    right: 12px;\n    background: rgba(255, 255, 255, 0.85);\n    backdrop-filter: blur(6px);\n    padding: 4px 10px;\n    border-radius: var(--radius-full);\n    font-size: 11px;\n    font-weight: 600;\n    color: var(--nordic-pine);\n    box-shadow: var(--shadow-sm);\n  }\n\n  .kcvents-spec-card {\n    background: var(--bg-card);\n    border-radius: var(--radius-lg);\n    border: 1px solid var(--border-subtle);\n    box-shadow: var(--shadow-card);\n    padding: 24px;\n  }\n\n  .kcvents-spec-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 12px;\n    margin-top: 14px;\n  }\n\n  .kcvents-spec-item {\n    background: var(--bg-card-subtle);\n    border-radius: var(--radius-md);\n    padding: 14px 16px;\n    border: 1px solid var(--border-subtle);\n  }\n\n  .kcvents-spec-item-label {\n    font-size: 11px;\n    color: var(--text-muted);\n    text-transform: uppercase;\n    letter-spacing: 0.5px;\n    margin-bottom: 4px;\n  }\n\n  .kcvents-spec-item-value {\n    font-family: var(--font-num);\n    font-size: 17px;\n    font-weight: 600;\n    color: var(--nordic-pine);\n  }\n\n  .kcvents-spec-item-hint {\n    font-size: 11px;\n    color: var(--text-secondary);\n    margin-top: 2px;\n  }\n\n  .kcvents-math-card {\n    background: var(--bg-card-subtle);\n    border-radius: var(--radius-md);\n    padding: 16px 18px;\n    font-size: 12px;\n    color: var(--text-secondary);\n    line-height: 1.7;\n    border: 1px solid var(--border-subtle);\n  }\n\n  .kcvents-math-step {\n    display: flex;\n    justify-content: space-between;\n    padding: 3px 0;\n    border-bottom: 1px dashed rgba(0, 0, 0, 0.05);\n  }\n\n  .kcvents-math-step:last-child {\n    border-bottom: none;\n    font-weight: 600;\n    color: var(--text-main);\n    padding-top: 6px;\n  }\n\n  .kcvents-action-bar {\n    display: flex;\n    gap: 10px;\n    margin-top: 14px;\n  }\n\n  .kcvents-btn-secondary {\n    flex: 1;\n    padding: 12px;\n    border-radius: var(--radius-md);\n    border: 1px solid var(--border-subtle);\n    background: var(--bg-card);\n    color: var(--text-main);\n    font-size: 13px;\n    font-weight: 500;\n    cursor: pointer;\n    transition: all 0.2s ease;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 6px;\n  }\n\n  .kcvents-btn-secondary:hover {\n    background: var(--bg-card-subtle);\n    border-color: var(--nordic-pine);\n    color: var(--nordic-pine);\n  }\n\n  .kcvents-btn-secondary:active {\n    transform: scale(0.98);\n  }\n\n  \/* 5. Rank Math FAQ Section Styles *\/\n  .kcvents-faq-section {\n    padding: 36px 0 44px;\n    border-top: 1px solid var(--border-subtle);\n    background: #FFFFFF;\n    border-bottom-left-radius: var(--radius-lg);\n    border-bottom-right-radius: var(--radius-lg);\n  }\n\n  .kcvents-faq-header {\n    margin-bottom: 24px;\n  }\n\n  .kcvents-faq-eyebrow {\n    font-size: 11px;\n    text-transform: uppercase;\n    letter-spacing: 1px;\n    font-weight: 600;\n    color: var(--nordic-pine);\n    display: block;\n    margin-bottom: 6px;\n  }\n\n  .kcvents-faq-title {\n    font-size: 20px !important;\n    font-weight: 700 !important;\n    color: var(--text-main) !important;\n    margin: 0 0 6px 0 !important;\n    line-height: 1.3 !important;\n  }\n\n  .kcvents-faq-subtitle {\n    font-size: 13px !important;\n    color: var(--text-secondary) !important;\n    margin: 0 !important;\n    line-height: 1.5 !important;\n  }\n\n  .kcvents-faq-list {\n    display: flex;\n    flex-direction: column;\n    gap: 12px;\n  }\n\n  details.kcvents-faq-item {\n    border: 1px solid var(--border-subtle);\n    border-radius: var(--radius-md);\n    background: #FAFAF9;\n    transition: all 0.2s ease;\n    overflow: hidden;\n  }\n\n  details.kcvents-faq-item[open] {\n    background: #FFFFFF;\n    border-color: rgba(30, 58, 47, 0.25);\n    box-shadow: var(--shadow-sm);\n  }\n\n  details.kcvents-faq-item summary {\n    padding: 16px 20px;\n    cursor: pointer;\n    font-size: 14px;\n    font-weight: 600;\n    color: var(--text-main);\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    user-select: none;\n    list-style: none;\n  }\n\n  details.kcvents-faq-item summary::-webkit-details-marker,\n  details.kcvents-faq-item summary::marker {\n    display: none;\n  }\n\n  details.kcvents-faq-item summary::after {\n    content: \"+\";\n    font-size: 20px;\n    font-weight: 400;\n    color: var(--nordic-pine);\n    transition: transform 0.2s ease;\n    line-height: 1;\n    margin-left: 12px;\n  }\n\n  details.kcvents-faq-item[open] summary::after {\n    content: \"\\2212\"; \/* Minus symbol *\/\n  }\n\n  .kcvents-faq-answer {\n    padding: 0 20px 18px 20px;\n    font-size: 13px;\n    line-height: 1.65;\n    color: var(--text-secondary);\n  }\n\n  .kcvents-faq-answer p {\n    margin: 0 !important;\n  }\n\n  .kcvents-faq-answer code {\n    background: var(--bg-accent-soft);\n    color: var(--nordic-pine);\n    padding: 2px 6px;\n    border-radius: 4px;\n    font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n    font-size: 12px;\n  }\n\n  #kcventsToast {\n    position: fixed;\n    bottom: 28px;\n    left: 50%;\n    transform: translateX(-50%) translateY(100px);\n    background: var(--nordic-pine-dark);\n    color: #FFFFFF;\n    padding: 12px 24px;\n    border-radius: var(--radius-full);\n    font-size: 13px;\n    font-weight: 500;\n    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.18);\n    transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n    z-index: 99999;\n    pointer-events: none;\n    display: flex;\n    align-items: center;\n    gap: 8px;\n  }\n\n  #kcventsToast.show {\n    transform: translateX(-50%) translateY(0);\n  }\n<\/style>\n\n<!-- KCVENTS Custom HTML App Root Element -->\n<div class=\"kcvents-calc-app\" id=\"kcventsCalculator\">\n  \n  <!-- 4. Page Content Snippet (Rank Math SEO Optimized) -->\n  <header class=\"kcvents-page-intro\">\n    <div class=\"kcvents-intro-header\">\n      <h1 class=\"kcvents-page-title\">HVAC Ventilation CFM &amp; Air Change Rate Calculator<\/h1>\n      <div class=\"kcvents-unit-switch\">\n        <button type=\"button\" class=\"kcvents-unit-btn active\" id=\"unitMetricBtn\" onclick=\"kcventsSetUnit('metric')\">Metric (m \/ m\u00b3)<\/button>\n        <button type=\"button\" class=\"kcvents-unit-btn\" id=\"unitImperialBtn\" onclick=\"kcventsSetUnit('imperial')\">Imperial (ft \/ cu ft)<\/button>\n      <\/div>\n    <\/div>\n    <p class=\"kcvents-page-desc\">Use our free ventilation CFM calculator. Designed based on data from <a href=\"https:\/\/www.ashrae.org\/technical-resources\/standards-and-guidelines\" target=\"_blank\" rel=\"noopener noreferrer\">ASHRAE 62.1<\/a> and <a href=\"https:\/\/www.ashrae.org\/technical-resources\/standards-and-guidelines\" target=\"_blank\" rel=\"noopener noreferrer\">ASHRAE 62.2<\/a> standards, this tool helps you estimate required airflow based on your space dimensions and ventilation needs. It can calculate required airflow (CFM &amp; CMH) for residential, commercial, medical, and industrial spaces.<\/p>\n  <\/header>\n\n  <!-- Main Grid -->\n  <div class=\"kcvents-main\">\n    <!-- Left Column: Inputs -->\n    <div class=\"kcvents-input-column\">\n      <div class=\"kcvents-card\">\n        \n        <div class=\"kcvents-card-title-bar\">\n          <div style=\"display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;\">\n            <div class=\"kcvents-card-title\">Application Scenario<\/div>\n            <span style=\"font-size: 12px; color: var(--text-muted);\">\n              Based on <a href=\"https:\/\/www.ashrae.org\/technical-resources\/standards-and-guidelines\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color: var(--nordic-pine); text-decoration: underline; text-underline-offset: 3px; font-weight: 500;\">ASHRAE 62.1<\/a> (Commercial) &amp; <a href=\"https:\/\/www.ashrae.org\/technical-resources\/standards-and-guidelines\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"color: var(--nordic-pine); text-decoration: underline; text-underline-offset: 3px; font-weight: 500;\">ASHRAE 62.2<\/a> (Residential)\n            <\/span>\n          <\/div>\n        <\/div>\n\n        <!-- Scenario Preset Dropdown -->\n        <div class=\"kcvents-scenario-select-wrap\">\n          <select id=\"scenarioSelect\" class=\"kcvents-scenario-select\" aria-label=\"Space type and application scenario preset\" onchange=\"kcventsOnScenarioChange(this.value)\"><\/select>\n        <\/div>\n        <div class=\"kcvents-scenario-meta\" id=\"scenarioMeta\">Moisture &amp; odor exhaust (9 ACH)<\/div>\n\n        <!-- Dimensions -->\n        <div class=\"kcvents-section-divider\"><\/div>\n        <div class=\"kcvents-card-title-bar\">\n          <div class=\"kcvents-card-title\">Room Dimensions &amp; Volume<\/div>\n          <span class=\"kcvents-card-badge\" id=\"unitIndicatorBadge\">Current Unit: Meters (m)<\/span>\n        <\/div>\n\n        <div class=\"kcvents-dimension-grid\">\n          <div class=\"kcvents-control-row\">\n            <div class=\"kcvents-control-header\">\n              <span class=\"kcvents-control-label\">Length (L)<\/span>\n              <div class=\"kcvents-control-val-box\">\n                <input type=\"number\" id=\"inputLength\" class=\"kcvents-control-val-input\" step=\"0.1\" value=\"6.0\" oninput=\"kcventsOnDimensionChange()\">\n                <span class=\"kcvents-control-unit\" id=\"unitLenLabel\">m<\/span>\n              <\/div>\n            <\/div>\n            <input type=\"range\" id=\"sliderLength\" class=\"kcvents-slider-range\" min=\"1\" max=\"30\" step=\"0.2\" value=\"6.0\" oninput=\"kcventsSyncSlider('Length')\">\n          <\/div>\n\n          <div class=\"kcvents-control-row\">\n            <div class=\"kcvents-control-header\">\n              <span class=\"kcvents-control-label\">Width (W)<\/span>\n              <div class=\"kcvents-control-val-box\">\n                <input type=\"number\" id=\"inputWidth\" class=\"kcvents-control-val-input\" step=\"0.1\" value=\"4.5\" oninput=\"kcventsOnDimensionChange()\">\n                <span class=\"kcvents-control-unit\" id=\"unitWidLabel\">m<\/span>\n              <\/div>\n            <\/div>\n            <input type=\"range\" id=\"sliderWidth\" class=\"kcvents-slider-range\" min=\"1\" max=\"30\" step=\"0.2\" value=\"4.5\" oninput=\"kcventsSyncSlider('Width')\">\n          <\/div>\n\n          <div class=\"kcvents-control-row\">\n            <div class=\"kcvents-control-header\">\n              <span class=\"kcvents-control-label\">Ceiling Height (H)<\/span>\n              <div class=\"kcvents-control-val-box\">\n                <input type=\"number\" id=\"inputHeight\" class=\"kcvents-control-val-input\" step=\"0.1\" value=\"2.8\" oninput=\"kcventsOnDimensionChange()\">\n                <span class=\"kcvents-control-unit\" id=\"unitHgtLabel\">m<\/span>\n              <\/div>\n            <\/div>\n            <input type=\"range\" id=\"sliderHeight\" class=\"kcvents-slider-range\" min=\"1.8\" max=\"10\" step=\"0.1\" value=\"2.8\" oninput=\"kcventsSyncSlider('Height')\">\n          <\/div>\n        <\/div>\n\n        <div class=\"kcvents-volume-summary-bar\">\n          <div class=\"kcvents-volume-summary-item\">\n            <span class=\"label\">Floor Area:<\/span>\n            <span class=\"value\" id=\"summaryArea\">27.0 m\u00b2<\/span>\n          <\/div>\n          <div class=\"kcvents-volume-summary-item\">\n            <span class=\"label\">Room Volume:<\/span>\n            <span class=\"value\" id=\"summaryVolume\">75.6 m\u00b3 (2,670 cu ft)<\/span>\n          <\/div>\n        <\/div>\n\n        <!-- Air Change Cycle Control -->\n        <div class=\"kcvents-section-divider\"><\/div>\n        <div class=\"kcvents-card-title-bar\">\n          <div class=\"kcvents-card-title\">Air Change Cycle (Ventilation Interval)<\/div>\n          <span class=\"kcvents-card-badge\" id=\"cycleBadge\">Cycle Driven<\/span>\n        <\/div>\n\n        <div id=\"panelCycle\">\n          <div class=\"kcvents-control-header\">\n            <span class=\"kcvents-control-label\">One Full Air Change Every:<\/span>\n            <div class=\"kcvents-control-val-box\">\n              <input type=\"number\" id=\"inputIntervalMinutes\" class=\"kcvents-control-val-input\" step=\"0.5\" value=\"6.7\" oninput=\"kcventsOnIntervalMinutesChange()\">\n              <span class=\"kcvents-control-unit\">Minutes \/ Change<\/span>\n            <\/div>\n          <\/div>\n          <input type=\"range\" id=\"sliderIntervalMinutes\" class=\"kcvents-slider-range\" min=\"1\" max=\"180\" step=\"0.5\" value=\"6.7\" oninput=\"kcventsOnIntervalMinutesSlider()\">\n          \n          <div style=\"margin-top: 12px; display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px;\">\n            <div class=\"kcvents-rate-pill\" id=\"cycleEquivalentPill\">\n              <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><circle cx=\"12\" cy=\"12\" r=\"10\"><\/circle><polyline points=\"12 6 12 12 16 14\"><\/polyline><\/svg>\n              Every 6.7 minutes (0.11 hrs) \u00b7 Equivalent to 9.0 ACH\n            <\/div>\n            <div style=\"font-size: 11px; color: var(--text-muted); display: flex; align-items: center; gap: 4px;\">\n              <span>Quick:<\/span>\n              <button type=\"button\" class=\"kcvents-unit-btn\" style=\"padding: 2px 8px; font-size: 11px;\" onclick=\"kcventsSetQuickCycle(30)\">30m<\/button>\n              <button type=\"button\" class=\"kcvents-unit-btn\" style=\"padding: 2px 8px; font-size: 11px;\" onclick=\"kcventsSetQuickCycle(60)\">1 hr<\/button>\n              <button type=\"button\" class=\"kcvents-unit-btn\" style=\"padding: 2px 8px; font-size: 11px;\" onclick=\"kcventsSetQuickCycle(120)\">2 hrs<\/button>\n            <\/div>\n          <\/div>\n        <\/div>\n\n        <!-- Advanced Options -->\n        <details class=\"kcvents-advanced-box\">\n          <summary>\n            <span>Engineering Factors &amp; System Loss Margin (Duct Loss \/ Occupancy)<\/span>\n          <\/summary>\n          <div class=\"kcvents-advanced-content\">\n            <div class=\"kcvents-control-row\">\n              <div class=\"kcvents-control-header\">\n                <span class=\"kcvents-control-label\">Duct Resistance &amp; Filter Loss Safety Margin<\/span>\n                <div class=\"kcvents-control-val-box\">\n                  <input type=\"number\" id=\"inputMargin\" class=\"kcvents-control-val-input\" step=\"1\" value=\"15\" oninput=\"kcventsOnMarginChange()\">\n                  <span class=\"kcvents-control-unit\">%<\/span>\n                <\/div>\n              <\/div>\n              <input type=\"range\" id=\"sliderMargin\" class=\"kcvents-slider-range\" min=\"0\" max=\"40\" step=\"1\" value=\"15\" oninput=\"kcventsSyncMarginSlider()\">\n              <p style=\"font-size: 11px; color: var(--text-muted); margin: 4px 0 0 0;\">Industry standard recommends 10% \u2013 20% allowance to offset bends, dampers, silencers, and filter loading.<\/p>\n            <\/div>\n\n            <div class=\"kcvents-control-row\" style=\"margin-top: 16px;\">\n              <div style=\"display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;\">\n                <span class=\"kcvents-control-label\">People Fresh Air Allowance (ASHRAE 62.1)<\/span>\n                <label style=\"display: flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer;\">\n                  <input type=\"checkbox\" id=\"checkOccupancy\" onchange=\"kcventsToggleOccupancy()\"> Enable People Check\n                <\/label>\n              <\/div>\n              <div id=\"occupancyBox\" style=\"display: none; background: #F3F4F6; padding: 12px; border-radius: var(--radius-sm);\">\n                <div style=\"display: flex; justify-content: space-between; align-items: center;\">\n                  <span style=\"font-size: 12px; color: var(--text-secondary);\">Estimated Occupants:<\/span>\n                  <input type=\"number\" id=\"inputPeopleCount\" class=\"kcvents-control-val-input\" value=\"4\" min=\"1\" max=\"200\" oninput=\"kcventsCalculate()\">\n                <\/div>\n                <div style=\"display: flex; justify-content: space-between; align-items: center; margin-top: 8px;\">\n                  <span style=\"font-size: 12px; color: var(--text-secondary);\">Rate per Person (CFM\/person):<\/span>\n                  <input type=\"number\" id=\"inputCfmPerPerson\" class=\"kcvents-control-val-input\" value=\"20\" min=\"5\" max=\"50\" oninput=\"kcventsCalculate()\">\n                <\/div>\n              <\/div>\n            <\/div>\n\n            <div class=\"kcvents-control-row\" style=\"margin-top: 14px;\">\n              <div class=\"kcvents-control-header\">\n                <span class=\"kcvents-control-label\">Target In-Duct Airflow Velocity<\/span>\n                <div class=\"kcvents-control-val-box\">\n                  <select id=\"selectVelocity\" onchange=\"kcventsCalculate()\" style=\"font-size: 12px; padding: 4px 8px; border-radius: var(--radius-sm); border: 1px solid var(--border-subtle); outline: none;\">\n                    <option value=\"600\">Quiet Residential (600 FPM \/ 3.0 m\/s)<\/option>\n                    <option value=\"800\" selected=\"\">Standard Commercial (800 FPM \/ 4.1 m\/s)<\/option>\n                    <option value=\"1100\">High-Velocity Industrial (1100 FPM \/ 5.6 m\/s)<\/option>\n                  <\/select>\n                <\/div>\n              <\/div>\n            <\/div>\n          <\/div>\n        <\/details>\n\n      <\/div>\n    <\/div>\n\n    <!-- Right Column: Results Dashboard -->\n    <div class=\"kcvents-result-column\">\n      \n      <!-- Hero Primary Result Card -->\n      <div class=\"kcvents-hero-card\">\n        <div class=\"kcvents-hero-eyebrow\">\n          <span>Recommended Design Airflow<\/span>\n          <span class=\"kcvents-hero-scene-tag\" id=\"displaySceneName\">Bathrooms &amp; Restrooms<\/span>\n        <\/div>\n        <div class=\"kcvents-hero-value-wrap\">\n          <div class=\"kcvents-hero-value\" id=\"displayCFM\">461<\/div>\n          <div class=\"kcvents-hero-unit\">CFM<\/div>\n        <\/div>\n        <div class=\"kcvents-hero-sub-metrics\">\n          <div class=\"kcvents-sub-metric-item\">\n            <span class=\"kcvents-sub-metric-label\">Metric Flow Rate (CMH)<\/span>\n            <span class=\"kcvents-sub-metric-val\" id=\"displayCMH\">783 m\u00b3\/h<\/span>\n          <\/div>\n          <div class=\"kcvents-sub-metric-item\">\n            <span class=\"kcvents-sub-metric-label\">Flow Rate per Second (L\/s)<\/span>\n            <span class=\"kcvents-sub-metric-val\" id=\"displayLS\">218 L\/s<\/span>\n          <\/div>\n        <\/div>\n      <\/div>\n\n      <!-- Airflow Dynamics Simulation -->\n      <div class=\"kcvents-airflow-card\">\n        <div class=\"kcvents-card-title-bar\" style=\"margin-bottom: 6px;\">\n          <div class=\"kcvents-card-title\" style=\"font-size: 14px;\">Airflow Convection Simulation<\/div>\n          <span style=\"font-size: 11px; color: var(--text-muted);\" id=\"flowStatusLabel\">Normal Convection<\/span>\n        <\/div>\n        <div class=\"kcvents-airflow-container\">\n          <canvas id=\"kcventsFlowCanvas\"><\/canvas>\n          <div class=\"kcvents-flow-badge\" id=\"flowSpeedBadge\">Rate: 9.0 ACH<\/div>\n        <\/div>\n      <\/div>\n\n      <!-- Duct Sizing & Equipment Specs -->\n      <div class=\"kcvents-spec-card\">\n        <div class=\"kcvents-card-title-bar\" style=\"margin-bottom: 4px;\">\n          <div class=\"kcvents-card-title\" style=\"font-size: 14px;\">Duct &amp; Equipment Sizing Recommendations<\/div>\n        <\/div>\n        <div class=\"kcvents-spec-grid\">\n          <div class=\"kcvents-spec-item\">\n            <div class=\"kcvents-spec-item-label\">Recommended Round Duct Size<\/div>\n            <div class=\"kcvents-spec-item-value\" id=\"displayDuctSize\">\u03a6 250 mm (10&#8243;)<\/div>\n            <div class=\"kcvents-spec-item-hint\" id=\"displayDuctHint\">Economic velocity ~ 4.1 m\/s<\/div>\n          <\/div>\n          <div class=\"kcvents-spec-item\">\n            <div class=\"kcvents-spec-item-label\">Full Air Change Interval<\/div>\n            <div class=\"kcvents-spec-item-value\" id=\"displayCycleTime\">6.7 Minutes<\/div>\n            <div class=\"kcvents-spec-item-hint\" id=\"displayCycleHint\">Equivalent to 9.0 ACH<\/div>\n          <\/div>\n        <\/div>\n\n        <div class=\"kcvents-math-card\" style=\"margin-top: 14px;\">\n          <div style=\"font-weight: 600; color: var(--text-main); margin-bottom: 6px;\">? Engineering Calculation Derivation:<\/div>\n          <div class=\"kcvents-math-step\">\n            <span>Room Net Volume (V)<\/span>\n            <span id=\"mathVolume\">2,670 cu ft (75.6 m\u00b3)<\/span>\n          <\/div>\n          <div class=\"kcvents-math-step\">\n            <span>Cycle Interval \/ ACH<\/span>\n            <span id=\"mathACH\">Cycle: 6.7 min (9.0 ACH)<\/span>\n          <\/div>\n          <div class=\"kcvents-math-step\">\n            <span>Base Airflow (V \u00d7 ACH \/ 60)<\/span>\n            <span id=\"mathBaseCFM\">401 CFM<\/span>\n          <\/div>\n          <div class=\"kcvents-math-step\">\n            <span>Duct &amp; Filter Loss Allowance (+15%)<\/span>\n            <span id=\"mathMargin\">+60 CFM<\/span>\n          <\/div>\n          <div class=\"kcvents-math-step\">\n            <span>Final Recommended Design Airflow<\/span>\n            <span id=\"mathFinalCFM\">461 CFM (783 m\u00b3\/h)<\/span>\n          <\/div>\n        <\/div>\n\n        <div class=\"kcvents-action-bar\">\n          <button type=\"button\" class=\"kcvents-btn-secondary\" onclick=\"kcventsCopyReport()\">\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"9\" y=\"9\" width=\"13\" height=\"13\" rx=\"2\" ry=\"2\"><\/rect><path d=\"M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1\"><\/path><\/svg>\n            Copy Calculation Report\n          <\/button>\n          <button type=\"button\" class=\"kcvents-btn-secondary\" onclick=\"window.print()\">\n            <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><polyline points=\"6 9 6 2 18 2 18 9\"><\/polyline><path d=\"M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2\"><\/path><rect x=\"6\" y=\"14\" width=\"12\" height=\"8\"><\/rect><\/svg>\n            Print \/ Save PDF\n          <\/button>\n        <\/div>\n      <\/div>\n\n    <\/div>\n  <\/div>\n\n  <!-- 5. Rank Math FAQ Block (Rich Snippet Schema + Accordion) -->\n  <section class=\"kcvents-faq-section\" id=\"kcventsFAQ\">\n    <div class=\"kcvents-faq-header\">\n      <span class=\"kcvents-faq-eyebrow\">Frequently Asked Questions<\/span>\n      <h2 class=\"kcvents-faq-title\">HVAC Ventilation &amp; Airflow Sizing Guide<\/h2>\n      <p class=\"kcvents-faq-subtitle\">Answers to common technical questions regarding CFM calculation formulas, metric unit conversions, and ASHRAE ventilation rates.<\/p>\n    <\/div>\n    <div class=\"kcvents-faq-list\">\n      <details class=\"kcvents-faq-item\" open=\"\">\n        <summary>How do I calculate CFM for room ventilation?<\/summary>\n        <div class=\"kcvents-faq-answer\">\n          <p>Room CFM is calculated using the formula: <code>CFM = (Room Volume in cubic feet \u00d7 Air Changes per Hour) \u00f7 60<\/code>. Alternatively, using air change cycle time: <code>CFM = Room Volume in cubic feet \u00f7 Cycle Duration in minutes<\/code>.<\/p>\n        <\/div>\n      <\/details>\n      <details class=\"kcvents-faq-item\">\n        <summary>What is the difference between CFM and CMH?<\/summary>\n        <div class=\"kcvents-faq-answer\">\n          <p>CFM stands for Cubic Feet per Minute (Imperial), while CMH represents Cubic Meters per Hour (Metric, m\u00b3\/h). To convert CFM to CMH, multiply CFM by 1.69901 (<code>1 CFM \u2248 1.7 CMH<\/code>).<\/p>\n        <\/div>\n      <\/details>\n      <details class=\"kcvents-faq-item\">\n        <summary>How many air changes per hour (ACH) are recommended by ASHRAE?<\/summary>\n        <div class=\"kcvents-faq-answer\">\n          <p>Recommended ventilation rates vary by occupancy and room function under ASHRAE 62.1 and 62.2 standards. Typically, residential bedrooms and living rooms require 0.5 to 1.0 ACH, offices require 6 to 8 ACH, classrooms require 8 ACH, and commercial kitchens or labs require 15 to 30 ACH.<\/p>\n        <\/div>\n      <\/details>\n    <\/div>\n  <\/section>\n\n  <div id=\"kcventsToast\">\n    <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#68D391\" stroke-width=\"3\"><polyline points=\"20 6 9 17 4 12\"><\/polyline><\/svg>\n    <span id=\"kcventsToastMsg\">Report copied to clipboard!<\/span>\n  <\/div>\n\n<\/div>\n\n<!-- Rank Math \/ Google FAQPage Schema Markup -->\n<script type=\"application\/ld+json\">\n{\n  \"@context\": \"https:\/\/schema.org\",\n  \"@type\": \"FAQPage\",\n  \"mainEntity\": [\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How do I calculate CFM for room ventilation?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Room CFM is calculated using the formula: CFM = (Room Volume in cubic feet \u00d7 Air Changes per Hour) \u00f7 60. Alternatively, using air change cycle time: CFM = Room Volume in cubic feet \u00f7 Cycle Duration in minutes.\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"What is the difference between CFM and CMH?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"CFM stands for Cubic Feet per Minute (Imperial), while CMH represents Cubic Meters per Hour (Metric, m\u00b3\/h). To convert CFM to CMH, multiply CFM by 1.69901 (1 CFM \u2248 1.7 CMH).\"\n      }\n    },\n    {\n      \"@type\": \"Question\",\n      \"name\": \"How many air changes per hour (ACH) are recommended by ASHRAE?\",\n      \"acceptedAnswer\": {\n        \"@type\": \"Answer\",\n        \"text\": \"Recommended ventilation rates vary by occupancy and room function under ASHRAE 62.1 and 62.2 standards. Typically, residential bedrooms and living rooms require 0.5 to 1.0 ACH, offices require 6 to 8 ACH, classrooms require 8 ACH, and commercial kitchens or labs require 15 to 30 ACH.\"\n      }\n    }\n  ]\n}\n<\/script>\n\n<script>\n  (function() {\n    const SCENARIOS = {\n      residential: [\n        { id: 'res_bath', name: 'Bathrooms & Restrooms', mins: 6.67, ach: 9.0, note: 'Moisture & odor exhaust (9 ACH)' },\n        { id: 'res_kitchen', name: 'Residential Kitchen', mins: 4.0, ach: 15.0, note: 'Cooking exhaust (15 ACH)' },\n        { id: 'res_bed', name: 'Master & Bedrooms', mins: 75.0, ach: 0.8, note: 'Quiet continuous comfort (0.8 ACH)' },\n        { id: 'res_living_comfort', name: 'Living Room (Comfort)', mins: 60.0, ach: 1.0, note: 'ASHRAE 62.2 baseline (1.0 ACH)' },\n        { id: 'res_living_strong', name: 'Living Room (High-Purge)', mins: 10.0, ach: 6.0, note: 'Rapid air exchange (6.0 ACH)' },\n        { id: 'res_basement', name: 'Basement & Media Room', mins: 10.0, ach: 6.0, note: 'Anti-mold & radon (6.0 ACH)' },\n        { id: 'res_laundry', name: 'Laundry & Utility Room', mins: 4.62, ach: 13.0, note: 'Heat & humidity relief (13 ACH)' },\n        { id: 'res_storage', name: 'Closet & Walk-in Wardrobe', mins: 30.0, ach: 2.0, note: 'Low-speed circulation (2.0 ACH)' },\n        { id: 'res_grow', name: 'Indoor Grow Room \/ Sunroom', mins: 2.5, ach: 24.0, note: 'CO2 & humidity exchange (24 ACH)' }\n      ],\n      public_edu: [\n        { id: 'edu_class', name: 'School Classroom', mins: 7.5, ach: 8.0, note: 'Low CO2 anti-drowsiness (8.0 ACH)' },\n        { id: 'edu_gym', name: 'Gym & Fitness Center', mins: 10.0, ach: 6.0, note: 'High metabolic exchange (6.0 ACH)' },\n        { id: 'edu_auditorium', name: 'Auditorium & Multi-hall', mins: 7.5, ach: 8.0, note: 'Large assembly airflow (8.0 ACH)' },\n        { id: 'edu_cinema', name: 'Movie Theater & Cinema', mins: 7.5, ach: 8.0, note: 'Enclosed tiered seating (8.0 ACH)' },\n        { id: 'edu_lab', name: 'Chemistry & Biology Lab', mins: 4.0, ach: 15.0, note: 'Acid & fume exhaust (15 ACH)' },\n        { id: 'edu_studio', name: 'Art Studio & Workshop', mins: 3.0, ach: 20.0, note: 'Solvents & VOC purge (20 ACH)' },\n        { id: 'edu_pool', name: 'Indoor Swimming Pool', mins: 8.6, ach: 7.0, note: 'Dehumidification & chlorine (7.0 ACH)' },\n        { id: 'edu_museum', name: 'Museum & Art Gallery', mins: 12.0, ach: 5.0, note: 'Preservation airflow (5.0 ACH)' },\n        { id: 'edu_library_std', name: 'Library (ASHRAE Rec.)', mins: 12.0, ach: 5.0, note: 'Quiet reading environment (5.0 ACH)' },\n        { id: 'edu_library_vendor', name: 'Library (Vendor Upper)', mins: 3.0, ach: 20.0, note: 'Vendor rule-of-thumb max (20 ACH)' }\n      ],\n      commercial: [\n        { id: 'com_office', name: 'Open-Plan Office', mins: 8.57, ach: 7.0, note: 'Standard commercial space (7.0 ACH)' },\n        { id: 'com_meeting', name: 'Conference & Meeting Room', mins: 5.5, ach: 11.0, note: 'High density CO2 control (11 ACH)' },\n        { id: 'com_ckitchen', name: 'Commercial Kitchen', mins: 2.0, ach: 30.0, note: 'Heavy grease & heat exhaust (30 ACH)' },\n        { id: 'com_restaurant', name: 'Restaurant Dining Hall', mins: 6.0, ach: 10.0, note: 'Customer turnover exchange (10 ACH)' },\n        { id: 'com_bar', name: 'Bar & Nightclub', mins: 5.0, ach: 12.0, note: 'Crowded social turnover (12 ACH)' },\n        { id: 'com_smoking', name: 'Smoking Lounge', mins: 3.5, ach: 17.0, note: 'Negative pressure containment (17 ACH)' },\n        { id: 'com_salon', name: 'Beauty & Nail Salon', mins: 5.0, ach: 12.0, note: 'Chemical & solvent fumes (12 ACH)' },\n        { id: 'com_hotel', name: 'Hotel Guest Room', mins: 35.0, ach: 1.7, note: 'Quiet in-room ventilation (1.7 ACH)' },\n        { id: 'com_bank', name: 'Bank Lobby', mins: 15.0, ach: 4.0, note: 'Steady customer flow (4.0 ACH)' },\n        { id: 'com_retail', name: 'Retail Store & Boutique', mins: 7.5, ach: 8.0, note: 'Shopping air turnover (8.0 ACH)' },\n        { id: 'com_supermarket', name: 'Supermarket & Hypermarket', mins: 20.0, ach: 3.0, note: 'Large volume dilution (3.0 ACH)' }\n      ],\n      healthcare: [\n        { id: 'med_ward', name: 'Hospital General Ward', mins: 12.0, ach: 5.0, note: 'Infection control baseline (5.0 ACH)' },\n        { id: 'med_iso', name: 'Negative Pressure Isolation', mins: 4.3, ach: 14.0, note: 'CDC\/ASHRAE 170 compliance (14 ACH)' },\n        { id: 'med_dental', name: 'Dental Clinic Operatory', mins: 7.5, ach: 8.0, note: 'Aerosol extraction (8.0 ACH)' },\n        { id: 'med_clinic', name: 'Medical Exam & Clinic', mins: 10.0, ach: 6.0, note: 'Sterile airflow baseline (6.0 ACH)' }\n      ],\n      industrial: [\n        { id: 'ind_parking', name: 'Parking Garage', mins: 7.5, ach: 8.0, note: 'Carbon monoxide purging (8.0 ACH)' },\n        { id: 'ind_repair', name: 'Auto Repair & Spray Booth', mins: 3.8, ach: 16.0, note: 'Paint mist & fumes exhaust (16 ACH)' },\n        { id: 'ind_warehouse', name: 'Logistics Warehouse', mins: 30.0, ach: 2.0, note: 'Bulk storage protection (2.0 ACH)' },\n        { id: 'ind_server', name: 'Server Room & Data Center', mins: 6.0, ach: 10.0, note: 'Equipment cooling support (10 ACH)' },\n        { id: 'ind_trash', name: 'Refuse & Trash Room', mins: 4.6, ach: 13.0, note: 'Odor & decay containment (13 ACH)' }\n      ],\n      custom: [\n        { id: 'cust_free', name: 'Custom Interval', mins: 10.0, ach: 6.0, note: 'User-specified parameters' }\n      ]\n    };\n\n    const CATEGORY_LABELS = {\n      residential: 'Residential',\n      public_edu: 'Public & Education',\n      commercial: 'Commercial',\n      healthcare: 'Healthcare',\n      industrial: 'Industrial',\n      custom: 'Custom'\n    };\n\n    \/\/ Pre-flatten scenarios into a fast ID lookup map\n    const SCENARIO_MAP = {};\n    Object.values(SCENARIOS).forEach(arr => {\n      arr.forEach(s => { SCENARIO_MAP[s.id] = s; });\n    });\n\n    let currentUnitSystem = 'metric';\n    let currentScenario = SCENARIOS.residential[0];\n\n    let state = {\n      length_m: 6.0,\n      width_m: 4.5,\n      height_m: 2.8,\n      intervalMinutes: 6.67,\n      ach: 9.0,\n      marginPct: 15,\n      occupancyEnabled: false,\n      peopleCount: 4,\n      cfmPerPerson: 20,\n      targetVelocityFpm: 800\n    };\n\n    let canvas, ctx, particles = [];\n    let flowSpeedMultiplier = 1;\n\n    function init() {\n      renderScenarioOptions();\n      updateDimensionInputs();\n      calculate();\n      initCanvas();\n    }\n\n    if (document.readyState === 'loading') {\n      document.addEventListener('DOMContentLoaded', init);\n    } else {\n      init();\n    }\n\n    function formatCycleLabel(mins) {\n      return mins >= 60 ? `every ${(mins \/ 60).toFixed(1)} hr` : `every ${mins.toFixed(1)} min`;\n    }\n\n    function renderScenarioOptions() {\n      const select = document.getElementById('scenarioSelect');\n      if (!select) return;\n      select.innerHTML = '';\n      Object.keys(SCENARIOS).forEach(catKey => {\n        const group = document.createElement('optgroup');\n        group.label = CATEGORY_LABELS[catKey] || catKey;\n        (SCENARIOS[catKey] || []).forEach(item => {\n          const option = document.createElement('option');\n          option.value = item.id;\n          option.textContent = `${item.name} \u2014 ${formatCycleLabel(item.mins)} (${item.ach.toFixed(1)} ACH)`;\n          group.appendChild(option);\n        });\n        select.appendChild(group);\n      });\n      syncScenarioUI();\n    }\n\n    function syncScenarioUI() {\n      const select = document.getElementById('scenarioSelect');\n      if (select && select.value !== currentScenario.id) select.value = currentScenario.id;\n\n      const meta = document.getElementById('scenarioMeta');\n      if (meta) meta.innerText = currentScenario.note || '';\n    }\n\n    window.kcventsOnScenarioChange = function(scenarioId) {\n      selectScenario(scenarioId);\n    };\n\n    function selectScenario(scenarioId) {\n      const found = SCENARIO_MAP[scenarioId];\n      if (!found) return;\n      currentScenario = found;\n      syncScenarioUI();\n      setCycleMinutes(found.mins, true);\n    }\n\n    function setCycleMinutes(mins, updateControls = true) {\n      state.intervalMinutes = Math.max(0.2, Number(mins));\n      state.ach = 60 \/ state.intervalMinutes;\n\n      if (updateControls) {\n        const inp = document.getElementById('inputIntervalMinutes');\n        const sld = document.getElementById('sliderIntervalMinutes');\n        if (inp) inp.value = state.intervalMinutes < 10 ? state.intervalMinutes.toFixed(1) : Math.round(state.intervalMinutes);\n        if (sld) sld.value = Math.min(180, state.intervalMinutes);\n      }\n\n      updateCyclePill();\n      calculate();\n    }\n\n    window.kcventsOnIntervalMinutesChange = function() {\n      const val = parseFloat(document.getElementById('inputIntervalMinutes').value) || 1;\n      setCycleMinutes(val, false);\n      const sld = document.getElementById('sliderIntervalMinutes');\n      if (sld) sld.value = Math.min(180, val);\n    };\n\n    window.kcventsOnIntervalMinutesSlider = function() {\n      const val = parseFloat(document.getElementById('sliderIntervalMinutes').value) || 1;\n      const inp = document.getElementById('inputIntervalMinutes');\n      if (inp) inp.value = val < 10 ? val.toFixed(1) : Math.round(val);\n      setCycleMinutes(val, false);\n    };\n\n    window.kcventsSetQuickCycle = function(mins) {\n      setCycleMinutes(mins, true);\n    };\n\n    function updateCyclePill() {\n      const mins = state.intervalMinutes;\n      const hours = mins \/ 60;\n      const ach = state.ach;\n\n      let timeText = '';\n      if (hours >= 1) {\n        timeText = `Every ${hours.toFixed(1)} hrs (${mins.toFixed(0)} mins)`;\n      } else {\n        timeText = `Every ${mins.toFixed(1)} mins (${hours.toFixed(2)} hrs)`;\n      }\n\n      const pill = document.getElementById('cycleEquivalentPill');\n      if (pill) {\n        pill.innerHTML = `\n          <svg width=\"12\" height=\"12\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.5\"><circle cx=\"12\" cy=\"12\" r=\"10\"><\/circle><polyline points=\"12 6 12 12 16 14\"><\/polyline><\/svg>\n          ${timeText} \u00b7 Equivalent to ${ach.toFixed(1)} ACH\n        `;\n      }\n    }\n\n    window.kcventsSetUnit = function(sys) {\n      if (currentUnitSystem === sys) return;\n      currentUnitSystem = sys;\n\n      const metricBtn = document.getElementById('unitMetricBtn');\n      const imperialBtn = document.getElementById('unitImperialBtn');\n      const badge = document.getElementById('unitIndicatorBadge');\n\n      if (sys === 'metric') {\n        metricBtn.classList.add('active');\n        imperialBtn.classList.remove('active');\n        if (badge) badge.innerText = 'Current Unit: Meters (m)';\n        document.getElementById('unitLenLabel').innerText = 'm';\n        document.getElementById('unitWidLabel').innerText = 'm';\n        document.getElementById('unitHgtLabel').innerText = 'm';\n      } else {\n        imperialBtn.classList.add('active');\n        metricBtn.classList.remove('active');\n        if (badge) badge.innerText = 'Current Unit: Feet (ft)';\n        document.getElementById('unitLenLabel').innerText = 'ft';\n        document.getElementById('unitWidLabel').innerText = 'ft';\n        document.getElementById('unitHgtLabel').innerText = 'ft';\n      }\n\n      updateDimensionInputs();\n      calculate();\n    };\n\n    const SLIDER_RANGES = {\n      metric: {\n        Length: { min: 1, max: 30, step: 0.2 },\n        Width: { min: 1, max: 30, step: 0.2 },\n        Height: { min: 1.8, max: 10, step: 0.1 }\n      },\n      imperial: {\n        Length: { min: 3, max: 100, step: 0.5 },\n        Width: { min: 3, max: 100, step: 0.5 },\n        Height: { min: 6, max: 35, step: 0.5 }\n      }\n    };\n\n    function syncSliderBounds() {\n      const ranges = SLIDER_RANGES[currentUnitSystem] || SLIDER_RANGES.metric;\n      const isMetric = (currentUnitSystem === 'metric');\n      const toDisplay = (valMeters) => isMetric ? valMeters : valMeters * 3.28084;\n      [['Length', state.length_m], ['Width', state.width_m], ['Height', state.height_m]].forEach(([dim, meters]) => {\n        const slider = document.getElementById('slider' + dim);\n        if (!slider) return;\n        const base = ranges[dim];\n        slider.min = base.min;\n        slider.step = base.step;\n        slider.max = Math.max(base.max, Math.ceil(toDisplay(meters)));\n      });\n    }\n\n    function updateDimensionInputs() {\n      const isMetric = (currentUnitSystem === 'metric');\n      const toDisplay = (valMeters) => isMetric ? valMeters : valMeters * 3.28084;\n      \n      const len = toDisplay(state.length_m);\n      const wid = toDisplay(state.width_m);\n      const hgt = toDisplay(state.height_m);\n\n      document.getElementById('inputLength').value = len.toFixed(1);\n      document.getElementById('inputWidth').value = wid.toFixed(1);\n      document.getElementById('inputHeight').value = hgt.toFixed(1);\n\n      const sL = document.getElementById('sliderLength');\n      const sW = document.getElementById('sliderWidth');\n      const sH = document.getElementById('sliderHeight');\n\n      syncSliderBounds();\n\n      sL.value = len;\n      sW.value = wid;\n      sH.value = hgt;\n    }\n\n    window.kcventsOnDimensionChange = function() {\n      const isMetric = (currentUnitSystem === 'metric');\n      const fromDisplay = (displayVal) => isMetric ? displayVal : displayVal \/ 3.28084;\n\n      const rawL = parseFloat(document.getElementById('inputLength').value) || 1;\n      const rawW = parseFloat(document.getElementById('inputWidth').value) || 1;\n      const rawH = parseFloat(document.getElementById('inputHeight').value) || 1;\n\n      state.length_m = Math.max(0.5, fromDisplay(rawL));\n      state.width_m = Math.max(0.5, fromDisplay(rawW));\n      state.height_m = Math.max(0.5, fromDisplay(rawH));\n\n      syncSliderBounds();\n\n      document.getElementById('sliderLength').value = rawL;\n      document.getElementById('sliderWidth').value = rawW;\n      document.getElementById('sliderHeight').value = rawH;\n\n      calculate();\n    };\n\n    window.kcventsSyncSlider = function(dim) {\n      const sliderVal = parseFloat(document.getElementById(`slider${dim}`).value);\n      document.getElementById(`input${dim}`).value = sliderVal.toFixed(1);\n      kcventsOnDimensionChange();\n    };\n\n    window.kcventsOnMarginChange = function() {\n      state.marginPct = parseFloat(document.getElementById('inputMargin').value) || 0;\n      document.getElementById('sliderMargin').value = state.marginPct;\n      calculate();\n    };\n\n    window.kcventsSyncMarginSlider = function() {\n      state.marginPct = parseFloat(document.getElementById('sliderMargin').value) || 0;\n      document.getElementById('inputMargin').value = state.marginPct;\n      calculate();\n    };\n\n    window.kcventsToggleOccupancy = function() {\n      state.occupancyEnabled = document.getElementById('checkOccupancy').checked;\n      document.getElementById('occupancyBox').style.display = state.occupancyEnabled ? 'block' : 'none';\n      calculate();\n    };\n\n    function calculate() {\n      const area_m2 = state.length_m * state.width_m;\n      const volume_m3 = area_m2 * state.height_m;\n      \n      const length_ft = state.length_m * 3.28084;\n      const width_ft = state.width_m * 3.28084;\n      const height_ft = state.height_m * 3.28084;\n      \n      const area_sqft = length_ft * width_ft;\n      const volume_cuft = volume_m3 * 35.3147;\n\n      if (currentUnitSystem === 'metric') {\n        document.getElementById('summaryArea').innerText = `${area_m2.toFixed(1)} m\u00b2 (${area_sqft.toFixed(0)} sq ft)`;\n        document.getElementById('summaryVolume').innerText = `${volume_m3.toFixed(1)} m\u00b3 (${volume_cuft.toFixed(0).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \",\")} cu ft)`;\n      } else {\n        document.getElementById('summaryArea').innerText = `${area_sqft.toFixed(1)} sq ft (${area_m2.toFixed(1)} m\u00b2)`;\n        document.getElementById('summaryVolume').innerText = `${volume_cuft.toFixed(0).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \",\")} cu ft (${volume_m3.toFixed(1)} m\u00b3)`;\n      }\n\n      const baseCFM = (volume_cuft * state.ach) \/ 60;\n      const marginFactor = 1 + (state.marginPct \/ 100);\n      let calculatedCFM = baseCFM * marginFactor;\n\n      if (state.occupancyEnabled) {\n        const count = parseFloat(document.getElementById('inputPeopleCount').value) || 1;\n        const rate = parseFloat(document.getElementById('inputCfmPerPerson').value) || 20;\n        const peopleCFM = count * rate;\n        if (peopleCFM > calculatedCFM) {\n          calculatedCFM = peopleCFM * marginFactor;\n        }\n      }\n\n      const finalCFM = Math.round(calculatedCFM);\n      const finalCMH = Math.round(finalCFM * 1.69901);\n      const finalLS = Math.round(finalCFM * 0.471947);\n\n      document.getElementById('displayCFM').innerText = finalCFM.toLocaleString();\n      document.getElementById('displayCMH').innerText = `${finalCMH.toLocaleString()} m\u00b3\/h`;\n      document.getElementById('displayLS').innerText = `${finalLS.toLocaleString()} L\/s`;\n      document.getElementById('displaySceneName').innerText = currentScenario.name;\n\n      const minsPerChange = state.intervalMinutes;\n      const hoursPerChange = minsPerChange \/ 60;\n      if (hoursPerChange >= 1) {\n        document.getElementById('displayCycleTime').innerText = `${hoursPerChange.toFixed(1)} Hours`;\n      } else {\n        document.getElementById('displayCycleTime').innerText = `${minsPerChange.toFixed(1)} Minutes`;\n      }\n\n      const hint = document.getElementById('displayCycleHint');\n      if (hint) {\n        hint.innerText = `Equivalent to ${state.ach.toFixed(1)} ACH`;\n      }\n\n      const selectVel = parseFloat(document.getElementById('selectVelocity').value) || 800;\n      const ductAreaSqFt = finalCFM \/ selectVel;\n      const ductDiamInches = Math.sqrt((4 * ductAreaSqFt) \/ Math.PI) * 12;\n      const ductDiamMm = ductDiamInches * 25.4;\n\n      const standardMm = [100, 125, 150, 160, 200, 250, 315, 355, 400, 450, 500, 630];\n      const recMm = standardMm.find(d => d >= ductDiamMm) || Math.round(ductDiamMm \/ 10) * 10;\n      const recInches = (recMm \/ 25.4).toFixed(0);\n\n      document.getElementById('displayDuctSize').innerText = `\u03a6 ${recMm} mm (${recInches}\")`;\n      document.getElementById('displayDuctHint').innerText = `Target velocity ~ ${(selectVel * 0.00508).toFixed(1)} m\/s (${selectVel} FPM)`;\n\n      const cycleStr = hoursPerChange >= 1 ? `${hoursPerChange.toFixed(1)} hrs` : `${minsPerChange.toFixed(1)} min`;\n      document.getElementById('mathVolume').innerText = `${volume_cuft.toFixed(0).replace(\/\\B(?=(\\d{3})+(?!\\d))\/g, \",\")} cu ft (${volume_m3.toFixed(1)} m\u00b3)`;\n      document.getElementById('mathACH').innerText = `Cycle: ${cycleStr} (${state.ach.toFixed(1)} ACH)`;\n      document.getElementById('mathBaseCFM').innerText = `${Math.round(baseCFM)} CFM`;\n      document.getElementById('mathMargin').innerText = `+${Math.round(baseCFM * (state.marginPct \/ 100))} CFM (+${state.marginPct}%)`;\n      document.getElementById('mathFinalCFM').innerText = `${finalCFM} CFM (${finalCMH} m\u00b3\/h)`;\n\n      updateFlowSpeed(state.ach);\n    }\n    window.kcventsCalculate = calculate;\n\n    function initCanvas() {\n      canvas = document.getElementById('kcventsFlowCanvas');\n      if (!canvas) return;\n      ctx = canvas.getContext('2d');\n      resizeCanvas();\n      window.addEventListener('resize', resizeCanvas);\n\n      const particleCount = 45;\n      particles = [];\n      for (let i = 0; i < particleCount; i++) {\n        particles.push({\n          x: Math.random() * (canvas.width || 300),\n          y: Math.random() * (canvas.height || 140),\n          len: 12 + Math.random() * 20,\n          speed: 0.8 + Math.random() * 1.5,\n          opacity: 0.2 + Math.random() * 0.5,\n          waveOffset: Math.random() * Math.PI * 2\n        });\n      }\n\n      requestAnimationFrame(renderFlow);\n    }\n\n    function resizeCanvas() {\n      if (!canvas) return;\n      const parent = canvas.parentElement;\n      canvas.width = parent ? (parent.clientWidth || 300) : 300;\n      canvas.height = parent ? (parent.clientHeight || 136) : 136;\n    }\n\n    function updateFlowSpeed(ach) {\n      flowSpeedMultiplier = Math.min(4.0, Math.max(0.3, ach \/ 4));\n      const badge = document.getElementById('flowSpeedBadge');\n      if (badge) badge.innerText = `Rate: ${ach.toFixed(1)} ACH`;\n      \n      const label = document.getElementById('flowStatusLabel');\n      if (label) {\n        if (ach < 2) label.innerText = 'Gentle Eco Cruise';\n        else if (ach <= 8) label.innerText = 'Comfort Fresh Circulation';\n        else if (ach <= 18) label.innerText = 'High Turnover Exchange';\n        else label.innerText = 'Heavy Duty Convection';\n      }\n    }\n\n    let tick = 0;\n    function renderFlow() {\n      tick += 0.02;\n      if (!ctx || !canvas) return;\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n\n      ctx.strokeStyle = 'rgba(30, 58, 47, 0.05)';\n      ctx.lineWidth = 1;\n      ctx.beginPath();\n      ctx.moveTo(30, 20); ctx.lineTo(canvas.width - 30, 20);\n      ctx.moveTo(30, canvas.height - 20); ctx.lineTo(canvas.width - 30, canvas.height - 20);\n      ctx.stroke();\n\n      particles.forEach(p => {\n        p.x += p.speed * flowSpeedMultiplier * 1.6;\n        if (p.x - p.len > canvas.width) {\n          p.x = -p.len;\n          p.y = 15 + Math.random() * (canvas.height - 30);\n        }\n\n        const yOscillation = Math.sin(tick + p.waveOffset) * 4;\n        const currentY = p.y + yOscillation;\n\n        const grad = ctx.createLinearGradient(p.x - p.len, currentY, p.x, currentY);\n        grad.addColorStop(0, 'rgba(30, 58, 47, 0)');\n        grad.addColorStop(1, `rgba(30, 58, 47, ${p.opacity})`);\n\n        ctx.strokeStyle = grad;\n        ctx.lineWidth = 1.8;\n        ctx.lineCap = 'round';\n\n        ctx.beginPath();\n        ctx.moveTo(p.x - p.len, currentY);\n        ctx.lineTo(p.x, currentY);\n        ctx.stroke();\n      });\n\n      requestAnimationFrame(renderFlow);\n    }\n\n    window.kcventsCopyReport = function() {\n      const cfm = document.getElementById('displayCFM').innerText;\n      const cmh = document.getElementById('displayCMH').innerText;\n      const ls = document.getElementById('displayLS').innerText;\n      const duct = document.getElementById('displayDuctSize').innerText;\n      const scene = currentScenario.name;\n      const volume = document.getElementById('summaryVolume').innerText;\n      const cycle = document.getElementById('displayCycleTime').innerText;\n\n      const reportText = \n`=======================================\n\u3010KCVENTS Ventilation Sizing Report\u3011\n=======================================\n\u00b7 Application Scenario : ${scene}\n\u00b7 Room Volume          : ${volume}\n\u00b7 Air Change Cycle     : Full change every ${cycle}\n\u00b7 Air Changes\/Hour     : ${state.ach.toFixed(1)} ACH\n\u00b7 Static Loss Margin   : +${state.marginPct}%\n---------------------------------------\n\u3010Recommended Airflow & Duct Sizing\u3011\n\u00b7 Design Airflow CFM   : ${cfm} CFM\n\u00b7 Metric Airflow CMH   : ${cmh}\n\u00b7 Flow Rate L\/s        : ${ls}\n\u00b7 Recommended Duct Dia : ${duct} (at economic speed)\n=======================================\nEngineered in compliance with ASHRAE 62.1 & 62.2 standards.\nManufacturer: KCVENTS (https:\/\/www.kcvents.com)`;\n\n      navigator.clipboard.writeText(reportText).then(() => {\n        showToast('Calculation report copied to clipboard!');\n      }).catch(() => {\n        showToast('Copy failed, please select manually.');\n      });\n    };\n\n    function showToast(msg) {\n      const toast = document.getElementById('kcventsToast');\n      const toastMsg = document.getElementById('kcventsToastMsg');\n      if (!toast || !toastMsg) return;\n      toastMsg.innerText = msg;\n      toast.classList.add('show');\n      setTimeout(() => {\n        toast.classList.remove('show');\n      }, 2600);\n    }\n\n  })();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>HVAC Ventilation CFM &amp; Air Change Rate Calculator Metric (m \/ m\u00b3) Imperial (ft \/ cu ft) Use our free ventilation CFM calculator. Designed based on data from ASHRAE 62.1 and ASHRAE 62.2 standards, this tool helps you estimate required airflow based on your space dimensions and ventilation needs. It can calculate required airflow (CFM [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_gspb_post_css":"","iawp_total_views":38,"footnotes":""},"class_list":["post-4493","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/pages\/4493","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/comments?post=4493"}],"version-history":[{"count":6,"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/pages\/4493\/revisions"}],"predecessor-version":[{"id":4500,"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/pages\/4493\/revisions\/4500"}],"wp:attachment":[{"href":"https:\/\/www.kcvents.com\/pt\/wp-json\/wp\/v2\/media?parent=4493"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}