{"id":2162,"date":"2026-06-01T03:07:32","date_gmt":"2026-06-01T03:07:32","guid":{"rendered":"https:\/\/aiper.com\/blogs\/?page_id=2162"},"modified":"2026-11-04T09:47:44","modified_gmt":"2026-11-04T09:47:44","slug":"calculateur-taux-renouvellement-piscine","status":"publish","type":"page","link":"https:\/\/aiper.com\/blogs\/fr\/calculateur-taux-renouvellement-piscine\/","title":{"rendered":"Calculateur de taux de renouvellement de piscine"},"content":{"rendered":"<p><a href=\"#aiper-upgrade-runtime\">Dur\u00e9e quotidienne et co\u00fbt \u00e9lectrique<\/a><\/p>\n<!-- Pool Turnover Rate Calculator - WordPress HTML Block -->\n<style>\n    :root {\n        --pool-primary: #1ecad3;\n        --pool-primary-dark: #17a8b0;\n        --pool-primary-light: #e6f9fa;\n        --pool-bg: #ffffff;\n        --pool-text: #2c3e50;\n        --pool-text-light: #5a6c7d;\n        --pool-border: #e0e8ef;\n        --pool-shadow: 0 4px 24px rgba(0, 0, 0, 0.07);\n        --pool-radius: 14px;\n        --pool-radius-sm: 8px;\n        --pool-transition: 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n        --pool-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n    }\n\n    .pool-calc-wrapper {\n        font-family: var(--pool-font);\n        max-width: 580px;\n        margin: 0 auto;\n        background: var(--pool-bg);\n        border-radius: var(--pool-radius);\n        box-shadow: var(--pool-shadow);\n        padding: 32px 28px 28px;\n        box-sizing: border-box;\n        border: 1px solid var(--pool-border);\n        position: relative;\n        overflow: hidden;\n    }\n\n    \/* Decorative top accent bar *\/\n    .pool-calc-wrapper::before {\n        content: '';\n        position: absolute;\n        top: 0;\n        left: 0;\n        right: 0;\n        height: 4px;\n        background: linear-gradient(90deg, #1ecad3 0%, #5de0e8 50%, #1ecad3 100%);\n        border-radius: var(--pool-radius) var(--pool-radius) 0 0;\n    }\n\n    .pool-calc-header {\n        text-align: center;\n        margin-bottom: 26px;\n        padding-top: 6px;\n    }\n\n    .pool-calc-header .pool-icon {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        width: 50px;\n        height: 50px;\n        border-radius: 50%;\n        background: var(--pool-primary-light);\n        margin-bottom: 12px;\n    }\n\n    .pool-calc-header .pool-icon svg {\n        width: 26px;\n        height: 26px;\n        fill: var(--pool-primary);\n    }\n\n    .pool-calc-header h3 {\n        margin: 0 0 4px;\n        font-size: 1.35rem;\n        font-weight: 700;\n        color: var(--pool-text);\n        letter-spacing: -0.3px;\n    }\n\n    .pool-calc-header p {\n        margin: 0;\n        font-size: 0.9rem;\n        color: var(--pool-text-light);\n        line-height: 1.4;\n    }\n\n    \/* Step indicator *\/\n    .pool-steps-indicator {\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        gap: 6px;\n        margin-bottom: 22px;\n        flex-wrap: wrap;\n    }\n\n    .pool-step-dot {\n        width: 30px;\n        height: 30px;\n        border-radius: 50%;\n        background: #eef2f7;\n        color: #8b9aab;\n        font-weight: 700;\n        font-size: 0.8rem;\n        display: flex;\n        align-items: center;\n        justify-content: center;\n        transition: var(--pool-transition);\n        flex-shrink: 0;\n    }\n\n    .pool-step-dot.active {\n        background: var(--pool-primary);\n        color: #fff;\n        box-shadow: 0 3px 10px rgba(30, 202, 211, 0.35);\n    }\n\n    .pool-step-dot.done {\n        background: #b8f0f4;\n        color: #0d8a90;\n    }\n\n    .pool-step-line {\n        width: 28px;\n        height: 2px;\n        background: #dce3eb;\n        border-radius: 1px;\n        flex-shrink: 0;\n    }\n\n    .pool-step-line.done {\n        background: var(--pool-primary);\n    }\n\n    \/* Form groups *\/\n    .pool-form-group {\n        margin-bottom: 20px;\n        position: relative;\n    }\n\n    .pool-form-group label {\n        display: block;\n        font-weight: 600;\n        font-size: 0.9rem;\n        color: var(--pool-text);\n        margin-bottom: 6px;\n        letter-spacing: -0.2px;\n    }\n\n    .pool-form-group label .label-number {\n        display: inline-flex;\n        align-items: center;\n        justify-content: center;\n        width: 22px;\n        height: 22px;\n        border-radius: 50%;\n        background: var(--pool-primary);\n        color: #fff;\n        font-size: 0.7rem;\n        font-weight: 700;\n        margin-right: 6px;\n        vertical-align: middle;\n    }\n\n    .pool-input-row {\n        display: flex;\n        gap: 10px;\n        align-items: stretch;\n    }\n\n    .pool-input-field {\n        flex: 1;\n        padding: 12px 14px;\n        border: 2px solid var(--pool-border);\n        border-radius: var(--pool-radius-sm);\n        font-size: 1rem;\n        font-family: var(--pool-font);\n        color: var(--pool-text);\n        background: #fafbfc;\n        transition: var(--pool-transition);\n        box-sizing: border-box;\n        min-width: 0;\n        outline: none;\n        -webkit-appearance: none;\n        -moz-appearance: textfield;\n    }\n\n    .pool-input-field:focus {\n        border-color: var(--pool-primary);\n        background: #fff;\n        box-shadow: 0 0 0 4px rgba(30, 202, 211, 0.08);\n    }\n\n    .pool-input-field::placeholder {\n        color: #b0bec5;\n    }\n\n    \/* Unit toggle buttons *\/\n    .pool-unit-toggle {\n        display: flex;\n        border-radius: var(--pool-radius-sm);\n        overflow: hidden;\n        border: 2px solid var(--pool-border);\n        flex-shrink: 0;\n        background: #fafbfc;\n    }\n\n    .pool-unit-toggle button {\n        padding: 10px 14px;\n        border: none;\n        background: transparent;\n        cursor: pointer;\n        font-family: var(--pool-font);\n        font-size: 0.82rem;\n        font-weight: 600;\n        color: #6b7d8e;\n        transition: var(--pool-transition);\n        white-space: nowrap;\n        letter-spacing: -0.2px;\n        position: relative;\n    }\n\n    .pool-unit-toggle button:not(:last-child) {\n        border-right: 1px solid var(--pool-border);\n    }\n\n    .pool-unit-toggle button:hover {\n        background: #eef7f8;\n        color: #3a5a6a;\n    }\n\n    .pool-unit-toggle button.active {\n        background: var(--pool-primary);\n        color: #ffffff;\n        box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.08);\n    }\n\n    \/* Helper text \/ links *\/\n    .pool-helper-text {\n        margin-top: 6px;\n        font-size: 0.82rem;\n        color: #7d8fa0;\n        line-height: 1.4;\n    }\n\n    .pool-helper-text a {\n        color: var(--pool-primary-dark);\n        text-decoration: underline;\n        text-underline-offset: 2px;\n        font-weight: 600;\n        transition: color 0.2s;\n    }\n\n    .pool-helper-text a:hover {\n        color: #0f7a80;\n        text-decoration: none;\n    }\n\n    .pool-helper-text .helper-icon {\n        display: inline-block;\n        margin-right: 3px;\n        font-size: 0.75rem;\n    }\n\n    \/* Buttons row *\/\n    .pool-buttons-row {\n        display: flex;\n        gap: 12px;\n        margin-top: 26px;\n        flex-wrap: wrap;\n    }\n\n    .pool-btn-calculate {\n        flex: 1;\n        min-width: 140px;\n        padding: 14px 24px;\n        background: var(--pool-primary);\n        color: #ffffff;\n        border: none;\n        border-radius: var(--pool-radius-sm);\n        font-size: 1rem;\n        font-weight: 700;\n        font-family: var(--pool-font);\n        cursor: pointer;\n        transition: var(--pool-transition);\n        letter-spacing: -0.2px;\n        box-shadow: 0 4px 14px rgba(30, 202, 211, 0.3);\n        position: relative;\n        overflow: hidden;\n    }\n\n    .pool-btn-calculate:hover {\n        background: var(--pool-primary-dark);\n        box-shadow: 0 6px 20px rgba(30, 202, 211, 0.4);\n        transform: translateY(-1px);\n    }\n\n    .pool-btn-calculate:active {\n        transform: translateY(0);\n        box-shadow: 0 2px 8px rgba(30, 202, 211, 0.25);\n        background: #149ca5;\n    }\n\n    .pool-btn-reset {\n        padding: 14px 20px;\n        background: #ffffff;\n        color: #5a6c7d;\n        border: 2px solid #dce3eb;\n        border-radius: var(--pool-radius-sm);\n        font-size: 0.9rem;\n        font-weight: 600;\n        font-family: var(--pool-font);\n        cursor: pointer;\n        transition: var(--pool-transition);\n        letter-spacing: -0.2px;\n        white-space: nowrap;\n    }\n\n    .pool-btn-reset:hover {\n        background: #f5f7fa;\n        border-color: #c0ccd8;\n        color: #3a4a5a;\n    }\n\n    .pool-btn-reset:active {\n        background: #eef1f5;\n    }\n\n    \/* Result display *\/\n    .pool-result {\n        margin-top: 22px;\n        padding: 18px 20px;\n        background: linear-gradient(135deg, #f0fdfd 0%, #e6f9fa 100%);\n        border-radius: var(--pool-radius-sm);\n        border: 2px solid #c5f0f3;\n        display: none;\n        align-items: center;\n        gap: 14px;\n        animation: poolFadeIn 0.4s ease-out;\n    }\n\n    .pool-result.show {\n        display: flex;\n    }\n\n    @keyframes poolFadeIn {\n        from {\n            opacity: 0;\n            transform: translateY(8px);\n        }\n        to {\n            opacity: 1;\n            transform: translateY(0);\n        }\n    }\n\n    .pool-result-icon {\n        flex-shrink: 0;\n        width: 44px;\n        height: 44px;\n        border-radius: 50%;\n        background: var(--pool-primary);\n        display: flex;\n        align-items: center;\n        justify-content: center;\n    }\n\n    .pool-result-icon svg {\n        width: 20px;\n        height: 20px;\n        fill: #fff;\n    }\n\n    .pool-result-text {\n        flex: 1;\n        min-width: 0;\n    }\n\n    .pool-result-label {\n        font-size: 0.78rem;\n        font-weight: 600;\n        color: #5a7d82;\n        text-transform: uppercase;\n        letter-spacing: 0.5px;\n        margin-bottom: 2px;\n    }\n\n    .pool-result-value {\n        font-size: 1.6rem;\n        font-weight: 800;\n        color: #0d5c62;\n        letter-spacing: -0.5px;\n        line-height: 1.2;\n        word-break: break-all;\n    }\n\n    .pool-result-sub {\n        font-size: 0.8rem;\n        color: #5a8a8f;\n        margin-top: 2px;\n    }\n\n    \/* Error state *\/\n    .pool-input-field.error {\n        border-color: #e74c3c;\n        background: #fefafa;\n        box-shadow: 0 0 0 4px rgba(231, 76, 60, 0.06);\n    }\n\n    .pool-error-msg {\n        color: #c0392b;\n        font-size: 0.78rem;\n        margin-top: 4px;\n        display: none;\n        font-weight: 500;\n    }\n\n    .pool-error-msg.show {\n        display: block;\n    }\n\n    \/* Responsive *\/\n    @media (max-width: 500px) {\n        .pool-calc-wrapper {\n            padding: 22px 16px 20px;\n            border-radius: 10px;\n        }\n        .pool-calc-wrapper::before {\n            border-radius: 10px 10px 0 0;\n        }\n        .pool-input-row {\n            flex-direction: column;\n            gap: 8px;\n        }\n        .pool-unit-toggle {\n            align-self: flex-start;\n            border-radius: var(--pool-radius-sm);\n        }\n        .pool-unit-toggle button {\n            padding: 9px 12px;\n            font-size: 0.76rem;\n        }\n        .pool-btn-calculate {\n            font-size: 0.95rem;\n            padding: 12px 18px;\n        }\n        .pool-btn-reset {\n            padding: 12px 16px;\n            font-size: 0.85rem;\n        }\n        .pool-result-value {\n            font-size: 1.3rem;\n        }\n        .pool-calc-header h3 {\n            font-size: 1.15rem;\n        }\n        .pool-step-dot {\n            width: 26px;\n            height: 26px;\n            font-size: 0.7rem;\n        }\n        .pool-step-line {\n            width: 18px;\n        }\n    }\n<\/style>\n\n<div class=\"pool-calc-wrapper\" id=\"poolTurnoverCalculator\" role=\"form\" aria-label=\"Calculateur de taux de renouvellement de piscine\">\n    <!-- Header -->\n    <div class=\"pool-calc-header\">\n\n\n        <p>D\u00e9couvrez combien de temps il faut pour faire circuler tout le volume de votre piscine.<\/p>\n    <\/div>\n\n    <!-- Step indicators -->\n    <div class=\"pool-steps-indicator\" aria-hidden=\"true\">\n        <span class=\"pool-step-dot active\" id=\"stepDot1\">1<\/span>\n        <span class=\"pool-step-line\" id=\"stepLine1\"><\/span>\n        <span class=\"pool-step-dot\" id=\"stepDot2\">2<\/span>\n        <span class=\"pool-step-line\" id=\"stepLine2\"><\/span>\n        <span class=\"pool-step-dot\" id=\"stepDot3\">3<\/span>\n    <\/div>\n\n    <!-- Step 1: Pool Volume -->\n    <div class=\"pool-form-group\" id=\"formGroupVolume\">\n        <label for=\"poolVolumeInput\">\n            <span class=\"label-number\">1<\/span> Volume de la piscine\n        <\/label>\n        <div class=\"pool-input-row\">\n            <input\n            type=\"number\"\n            id=\"poolVolumeInput\"\n            class=\"pool-input-field\"\n            placeholder=\"Saisissez le volume de la piscine\"\n            min=\"1\"\n            step=\"any\"\n            aria-label=\"Volume de la piscine\"\n            autocomplete=\"off\"\n            >\n            <div class=\"pool-unit-toggle\" id=\"unitToggle\" role=\"radiogroup\" aria-label=\"Unit\u00e9 de volume\">\n                <button type=\"button\" data-unit=\"gallons\" class=\"active\" aria-checked=\"true\" role=\"radio\">Gallons<\/button>\n                <button type=\"button\" data-unit=\"liters\" aria-checked=\"false\" role=\"radio\">Litres<\/button>\n                <button type=\"button\" data-unit=\"cubic-meters\" aria-checked=\"false\" role=\"radio\">m\u00b3<\/button>\n            <\/div>\n        <\/div>\n        <p class=\"pool-helper-text\">\n            <span class=\"helper-icon\">\ud83d\udca1<\/span> Vous ne savez pas&nbsp;? Utilisez notre\n            <a href=\"https:\/\/aiper.com\/blogs\/pool-volume-calculator\/\" target=\"_blank\" rel=\"noopener noreferrer\">calculateur de volume de piscine<\/a>\n        <\/p>\n        <p class=\"pool-error-msg\" id=\"volumeError\">Veuillez saisir un volume de piscine valide.<\/p>\n    <\/div>\n\n    <!-- Step 2: Pump Flow Rate -->\n    <div class=\"pool-form-group\" id=\"formGroupFlow\">\n        <label for=\"pumpFlowInput\">\n            <span class=\"label-number\">2<\/span> D\u00e9bit de la pompe\n        <\/label>\n        <div class=\"pool-input-row\">\n            <input\n            type=\"number\"\n            id=\"pumpFlowInput\"\n            class=\"pool-input-field\"\n            placeholder=\"Saisissez le d\u00e9bit de la pompe\"\n            min=\"1\"\n            step=\"any\"\n            aria-label=\"D\u00e9bit de la pompe en GPM\"\n            autocomplete=\"off\"\n            >\n            <span style=\"display:inline-flex;align-items:center;padding:12px 14px;background:#f0f4f7;border-radius:8px;font-weight:700;font-size:0.9rem;color:#3a5a6a;white-space:nowrap;flex-shrink:0;border:2px solid #e0e8ef;\">\n                GPM\n            <\/span>\n        <\/div>\n        <p class=\"pool-helper-text\">\n            <span class=\"helper-icon\">\ud83d\udd27<\/span> Besoin d&#8217;aide pour trouver votre d\u00e9bit&nbsp;?\n            <a href=\"https:\/\/aiper.com\/blogs\/pool-flow-rate-calculator\/\" target=\"_blank\" rel=\"noopener noreferrer\">calculateur de d\u00e9bit de piscine<\/a>\n        <\/p>\n        <p class=\"pool-error-msg\" id=\"flowError\">Veuillez saisir un d\u00e9bit de pompe valide en GPM.<\/p>\n    <\/div>\n\n    <!-- Step 3: Action Buttons -->\n    <div class=\"pool-buttons-row\">\n        <button type=\"button\" class=\"pool-btn-calculate\" id=\"btnCalculate\" aria-label=\"Calculer le taux de renouvellement\">\n            \u23f1\ufe0f Calculer\n        <\/button>\n        <button type=\"button\" class=\"pool-btn-reset\" id=\"btnReset\" aria-label=\"R\u00e9initialiser tous les champs\">\n            \u21bb R\u00e9initialiser\n        <\/button>\n    <\/div>\n\n    <!-- Result Display -->\n    <div class=\"pool-result\" id=\"poolResult\" role=\"status\" aria-live=\"polite\">\n        <div class=\"pool-result-icon\">\n            <svg viewBox=\"0 0 24 24\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                <path d=\"M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8zm.5-13H11v6l5.25 3.15.75-1.23-4.5-2.67z\"\/>\n            <\/svg>\n        <\/div>\n        <div class=\"pool-result-text\">\n            <div class=\"pool-result-label\">Temps de renouvellement estim\u00e9<\/div>\n            <div class=\"pool-result-value\" id=\"resultValue\">&#8212; h &#8212; m<\/div>\n            <div class=\"pool-result-sub\" id=\"resultSub\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n\n<script>\n    (function() {\n        \/\/ DOM references\n        const volumeInput = document.getElementById('poolVolumeInput');\n        const flowInput = document.getElementById('pumpFlowInput');\n        const unitToggle = document.getElementById('unitToggle');\n        const btnCalculate = document.getElementById('btnCalculate');\n        const btnReset = document.getElementById('btnReset');\n        const poolResult = document.getElementById('poolResult');\n        const resultValue = document.getElementById('resultValue');\n        const resultSub = document.getElementById('resultSub');\n        const volumeError = document.getElementById('volumeError');\n        const flowError = document.getElementById('flowError');\n        const stepDot1 = document.getElementById('stepDot1');\n        const stepDot2 = document.getElementById('stepDot2');\n        const stepDot3 = document.getElementById('stepDot3');\n        const stepLine1 = document.getElementById('stepLine1');\n        const stepLine2 = document.getElementById('stepLine2');\n\n        \/\/ State\n        let selectedUnit = 'gallons';\n\n        \/\/ --- Unit toggle logic ---\n        const unitButtons = unitToggle.querySelectorAll('button');\n\n        unitButtons.forEach(btn => {\n            btn.addEventListener('click', function() {\n                unitButtons.forEach(b => {\n                    b.classList.remove('active');\n                    b.setAttribute('aria-checked', 'false');\n                });\n                this.classList.add('active');\n                this.setAttribute('aria-checked', 'true');\n                selectedUnit = this.getAttribute('data-unit');\n                \/\/ Clear result on unit change\n                hideResult();\n                \/\/ Clear errors\n                clearErrors();\n                \/\/ Update placeholder based on unit\n                updatePlaceholder();\n            });\n        });\n\n        function updatePlaceholder() {\n            const placeholders = {\n                'gallons': 'p. ex. 15 000',\n                'liters': 'p. ex. 56 000',\n                'cubic-meters': 'p. ex. 56'\n            };\n            volumeInput.placeholder = placeholders[selectedUnit] || 'Saisissez le volume de la piscine';\n        }\n\n        \/\/ --- Focus event: update step indicators ---\n        volumeInput.addEventListener('focus', function() {\n            setActiveStep(1);\n        });\n        flowInput.addEventListener('focus', function() {\n            setActiveStep(2);\n        });\n\n        function setActiveStep(step) {\n            \/\/ Reset all\n            [stepDot1, stepDot2, stepDot3].forEach(d => d.classList.remove('active', 'done'));\n            [stepLine1, stepLine2].forEach(l => l.classList.remove('done'));\n\n            if (step === 1) {\n                stepDot1.classList.add('active');\n            } else if (step === 2) {\n                stepDot1.classList.add('done');\n                stepLine1.classList.add('done');\n                stepDot2.classList.add('active');\n            } else if (step === 3) {\n                stepDot1.classList.add('done');\n                stepLine1.classList.add('done');\n                stepDot2.classList.add('done');\n                stepLine2.classList.add('done');\n                stepDot3.classList.add('active');\n            }\n        }\n\n        \/\/ --- Clear errors ---\n        function clearErrors() {\n            volumeInput.classList.remove('error');\n            flowInput.classList.remove('error');\n            volumeError.classList.remove('show');\n            flowError.classList.remove('show');\n        }\n\n        \/\/ --- Hide result ---\n        function hideResult() {\n            poolResult.classList.remove('show');\n        }\n\n        \/\/ --- Show result ---\n        function showResult(hours, minutes, totalMinutes) {\n            resultValue.textContent = hours + ' h ' + minutes + ' m';\n            const totalMinRounded = Math.round(totalMinutes);\n            resultSub.textContent = 'Total\u00a0: ~' + totalMinRounded + ' minutes pour effectuer un renouvellement complet';\n            poolResult.classList.add('show');\n            setActiveStep(3);\n            \/\/ Scroll result into view smoothly\n            poolResult.scrollIntoView({ behavior: 'smooth', block: 'nearest' });\n        }\n\n        \/\/ --- Conversion helpers ---\n        function convertToGallons(value, unit) {\n            switch (unit) {\n                case 'gallons':\n                    return value;\n                case 'liters':\n                    \/\/ 1 gallon = 3.78541 liters\n                    return value \/ 3.78541;\n                case 'cubic-meters':\n                    \/\/ 1 cubic meter = 264.172 gallons\n                    return value * 264.172;\n                default:\n                    return value;\n            }\n        }\n\n        \/\/ --- Main calculation ---\n        function calculateTurnover() {\n            clearErrors();\n            hideResult();\n\n            const volumeRaw = volumeInput.value.trim();\n            const flowRaw = flowInput.value.trim();\n\n            let hasError = false;\n\n            \/\/ Validate volume\n            const volumeNum = parseFloat(volumeRaw);\n            if (!volumeRaw || isNaN(volumeNum) || volumeNum <= 0) {\n                volumeInput.classList.add('error');\n                volumeError.classList.add('show');\n                hasError = true;\n            }\n\n            \/\/ Validate flow rate\n            const flowNum = parseFloat(flowRaw);\n            if (!flowRaw || isNaN(flowNum) || flowNum <= 0) {\n                flowInput.classList.add('error');\n                flowError.classList.add('show');\n                hasError = true;\n            }\n\n            if (hasError) {\n                \/\/ Shake or focus first error\n                if (volumeInput.classList.contains('error')) {\n                    volumeInput.focus();\n                } else if (flowInput.classList.contains('error')) {\n                    flowInput.focus();\n                }\n                return;\n            }\n\n            \/\/ Convert pool volume to gallons\n            const volumeGallons = convertToGallons(volumeNum, selectedUnit);\n\n            \/\/ Turnover time in hours = Volume (gal) \/ (Flow Rate GPM \u00d7 60)\n            const turnoverHours = volumeGallons \/ (flowNum * 60);\n\n            \/\/ Convert to hours and minutes\n            const wholeHours = Math.floor(turnoverHours);\n            const remainingMinutes = Math.round((turnoverHours - wholeHours) * 60);\n\n            \/\/ Handle edge case where minutes round to 60\n            let displayHours = wholeHours;\n            let displayMinutes = remainingMinutes;\n            if (remainingMinutes >= 60) {\n                displayHours += 1;\n                displayMinutes = remainingMinutes - 60;\n            }\n            \/\/ If minutes round to 60 again (unlikely but safe)\n            if (displayMinutes === 60) {\n                displayHours += 1;\n                displayMinutes = 0;\n            }\n\n            const totalMinutes = turnoverHours * 60;\n\n            showResult(displayHours, displayMinutes, totalMinutes);\n        }\n\n        \/\/ --- Reset ---\n        function resetAll() {\n            volumeInput.value = '';\n            flowInput.value = '';\n            clearErrors();\n            hideResult();\n            \/\/ Reset unit to gallons\n            selectedUnit = 'gallons';\n            unitButtons.forEach(b => {\n                b.classList.remove('active');\n                b.setAttribute('aria-checked', 'false');\n            });\n            const gallonsBtn = unitToggle.querySelector('[data-unit=\"gallons\"]');\n            if (gallonsBtn) {\n                gallonsBtn.classList.add('active');\n                gallonsBtn.setAttribute('aria-checked', 'true');\n            }\n            updatePlaceholder();\n            setActiveStep(1);\n            volumeInput.focus({ preventScroll: true });\n            \/\/ Reset step dots\n            [stepDot1, stepDot2, stepDot3].forEach(d => d.classList.remove('active', 'done'));\n            [stepLine1, stepLine2].forEach(l => l.classList.remove('done'));\n            stepDot1.classList.add('active');\n        }\n\n        \/\/ --- Event listeners ---\n        btnCalculate.addEventListener('click', calculateTurnover);\n        btnReset.addEventListener('click', resetAll);\n\n        \/\/ Allow Enter key to trigger calculation\n        volumeInput.addEventListener('keydown', function(e) {\n            if (e.key === 'Enter') {\n                e.preventDefault();\n                flowInput.focus();\n            }\n        });\n        flowInput.addEventListener('keydown', function(e) {\n            if (e.key === 'Enter') {\n                e.preventDefault();\n                calculateTurnover();\n            }\n        });\n\n        \/\/ Clear errors on input\n        volumeInput.addEventListener('input', function() {\n            if (volumeInput.classList.contains('error')) {\n                volumeInput.classList.remove('error');\n                volumeError.classList.remove('show');\n            }\n            hideResult();\n        });\n        flowInput.addEventListener('input', function() {\n            if (flowInput.classList.contains('error')) {\n                flowInput.classList.remove('error');\n                flowError.classList.remove('show');\n            }\n            hideResult();\n        });\n\n        \/\/ Initialize\n        updatePlaceholder();\n        setActiveStep(1);\n\n        \/\/ Expose for potential external use\n        window.poolTurnoverCalc = {\n            reset: resetAll,\n            calculate: calculateTurnover,\n            getResult: function() {\n                return {\n                    hours: resultValue.textContent,\n                    visible: poolResult.classList.contains('show')\n                };\n            }\n        };\n    })();\n<\/script>\n\n\n\n<div class=\"aiper-chart-container\">\n    <h2 class=\"aiper-chart-title\">Taux de renouvellement de piscine \u2013 aide-m\u00e9moire<\/h2>\n    <p class=\"aiper-chart-desc\">\n        La plupart des piscines r\u00e9sidentielles n\u00e9cessitent un renouvellement complet toutes les <b>8 heures<\/b>. D\u00e9placez le curseur ci-dessous pour voir comment le volume de votre piscine correspond aux d\u00e9bits de pompe standard.\n    <\/p>\n\n    <!-- Interactive slider with live highlight -->\n    <div class=\"aiper-interactive-slider-box\">\n        <label for=\"chartVolumeSlider\" class=\"aiper-slider-label\">\n            Volume de votre piscine&nbsp;: <span id=\"sliderVolumeVal\">15,000<\/span> gallons\n        <\/label>\n        <input type=\"range\" id=\"chartVolumeSlider\" min=\"5000\" max=\"40000\" step=\"2500\" value=\"15000\" class=\"aiper-range-slider\">\n\n        <div class=\"aiper-quick-stat-grid\">\n            <div class=\"aiper-stat-card\">\n                <span class=\"aiper-stat-meta\">D\u00e9bit recommand\u00e9<\/span>\n                <span id=\"statFlow\" class=\"aiper-stat-num\">31 GPM<\/span>\n            <\/div>\n            <div class=\"aiper-stat-card\">\n                <span class=\"aiper-stat-meta\">Temps de renouv. est.<\/span>\n                <span id=\"statTime\" class=\"aiper-stat-num\">8 h 0 m<\/span>\n            <\/div>\n        <\/div>\n    <\/div>\n\n    <!-- Core comparison data table -->\n    <div class=\"aiper-table-responsive\">\n        <table class=\"aiper-data-table\" id=\"turnoverDataTable\">\n            <thead>\n                <tr>\n                    <th>Taille de la piscine (gal)<\/th>\n                    <th>D\u00e9bit de la pompe (GPM)<\/th>\n                    <th>Temps de renouvellement cible<\/th>\n                <\/tr>\n            <\/thead>\n            <tbody>\n                <tr data-vol=\"5000\">\n                    <td>5,000 gallons<\/td>\n                    <td>15 GPM<\/td>\n                    <td>5 h 33 m<\/td>\n                <\/tr>\n                <tr data-vol=\"10000\">\n                    <td>10,000 gallons<\/td>\n                    <td>25 GPM<\/td>\n                    <td>6 h 40 m<\/td>\n                <\/tr>\n                <tr data-vol=\"15000\" class=\"aiper-row-highlight\">\n                    <td>15,000 gallons<\/td>\n                    <td>35 GPM<\/td>\n                    <td>7 h 9 m<\/td>\n                <\/tr>\n                <tr data-vol=\"20000\">\n                    <td>20,000 gallons<\/td>\n                    <td>45 GPM<\/td>\n                    <td>7 h 24 m<\/td>\n                <\/tr>\n                <tr data-vol=\"25000\">\n                    <td>25,000 gallons<\/td>\n                    <td>55 GPM<\/td>\n                    <td>7 h 35 m<\/td>\n                <\/tr>\n                <tr data-vol=\"30000\">\n                    <td>30,000 gallons<\/td>\n                    <td>65 GPM<\/td>\n                    <td>7 h 42 m<\/td>\n                <\/tr>\n                <tr data-vol=\"40000\">\n                    <td>40,000 gallons<\/td>\n                    <td>85 GPM<\/td>\n                    <td>7 h 51 m<\/td>\n                <\/tr>\n            <\/tbody>\n        <\/table>\n    <\/div>\n<\/div>\n\n<style>\n    .aiper-chart-container {\n        font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif;\n        max-width: 600px;\n        margin: 30px auto;\n        padding: 25px;\n        border: 1px solid #e1e1e1;\n        border-radius: 8px;\n        background-color: #ffffff;\n        box-shadow: 0 4px 12px rgba(0,0,0,0.05);\n    }\n    .aiper-chart-title {\n        color: #333333;\n        margin-top: 0;\n        margin-bottom: 10px;\n        font-size: 20px;\n        font-weight: 600;\n        text-align: center;\n    }\n    .aiper-chart-desc {\n        font-size: 14px;\n        color: #666666;\n        text-align: center;\n        line-height: 1.5;\n        margin-bottom: 25px;\n    }\n    .aiper-interactive-slider-box {\n        background-color: #fafafa;\n        padding: 20px;\n        border-radius: 6px;\n        margin-bottom: 25px;\n        border: 1px dashed #dedede;\n    }\n    .aiper-slider-label {\n        display: block;\n        font-size: 15px;\n        font-weight: 600;\n        color: #444444;\n        margin-bottom: 12px;\n    }\n    .aiper-slider-label span {\n        color: #1ecad3;\n        font-size: 18px;\n    }\n    .aiper-range-slider {\n        -webkit-appearance: none;\n        width: 100%;\n        height: 6px;\n        border-radius: 3px;\n        background: #e1e1e1;\n        outline: none;\n        margin-bottom: 20px;\n    }\n    .aiper-range-slider::-webkit-slider-thumb {\n        -webkit-appearance: none;\n        appearance: none;\n        width: 18px;\n        height: 18px;\n        border-radius: 50%;\n        background: #1ecad3;\n        cursor: pointer;\n        transition: transform 0.1s ease;\n    }\n    .aiper-range-slider::-webkit-slider-thumb:hover {\n        transform: scale(1.2);\n    }\n    .aiper-quick-stat-grid {\n        display: flex;\n        gap: 15px;\n    }\n    .aiper-stat-card {\n        flex: 1;\n        background: #ffffff;\n        padding: 10px;\n        border-radius: 4px;\n        border: 1px solid #eaeaea;\n        text-align: center;\n    }\n    .aiper-stat-meta {\n        display: block;\n        font-size: 11px;\n        text-transform: uppercase;\n        color: #888888;\n        margin-bottom: 4px;\n        letter-spacing: 0.5px;\n    }\n    .aiper-stat-num {\n        font-size: 16px;\n        font-weight: 700;\n        color: #333333;\n    }\n    .aiper-table-responsive {\n        width: 100%;\n        overflow-x: auto;\n    }\n    .aiper-data-table {\n        width: 100%;\n        border-collapse: collapse;\n        font-size: 14px;\n        text-align: left;\n    }\n    .aiper-data-table th {\n        background-color: #f5f5f5;\n        color: #555555;\n        font-weight: 600;\n        padding: 12px;\n        border-bottom: 2px solid #e5e5e5;\n    }\n    .aiper-data-table td {\n        padding: 12px;\n        border-bottom: 1px solid #eeeeee;\n        color: #444444;\n        transition: background-color 0.2s ease;\n    }\n    \/* Highlight row *\/\n    .aiper-data-table tr.aiper-row-highlight td {\n        background-color: #f4fdfd !important;\n        color: #1ecad3;\n        font-weight: 600;\n    }\n    .aiper-data-table tr:hover td {\n        background-color: #fcfcfc;\n    }\n<\/style>\n\n<script>\ndocument.addEventListener(\"DOMContentLoaded\", function() {\n    const slider = document.getElementById(\"chartVolumeSlider\");\n    const volumeLabel = document.getElementById(\"sliderVolumeVal\");\n    const statFlow = document.getElementById(\"statFlow\");\n    const statTime = document.getElementById(\"statTime\");\n    const tableRows = document.querySelectorAll(\"#turnoverDataTable tbody tr\");\n\n    \/\/ Estimate standard parameters from the slider (recommended flow for ~8h turnover)\n    function updateChartMetrics(volume) {\n        volumeLabel.textContent = Number(volume).toLocaleString();\n\n        \/\/ Derive recommended flow from an 8-hour (480-minute) turnover baseline (GPM = Volume \/ 480)\n        \/\/ Round up so the flow rate stays a whole number\n        const recommendedGPM = Math.max(15, Math.ceil(volume \/ 480));\n        statFlow.textContent = recommendedGPM + \" GPM\";\n\n        \/\/ Compute the real turnover time and format it\n        const totalMinutes = volume \/ recommendedGPM;\n        const h = Math.floor(totalMinutes \/ 60);\n        const m = Math.round(totalMinutes % 60);\n        statTime.textContent = h + \" h \" + m + \" m\";\n\n        \/\/ Highlight the closest matching table row\n        let closestRow = null;\n        let minDiff = Infinity;\n\n        tableRows.forEach(row => {\n            row.classList.remove(\"aiper-row-highlight\");\n            const rowVol = parseInt(row.getAttribute(\"data-vol\"));\n            const diff = Math.abs(volume - rowVol);\n            if (diff < minDiff) {\n                minDiff = diff;\n                closestRow = row;\n            }\n        });\n\n        if (closestRow) {\n            closestRow.classList.add(\"aiper-row-highlight\");\n        }\n    }\n\n    slider.addEventListener(\"input\", function(e) {\n        updateChartMetrics(e.target.value);\n    });\n\n    \/\/ Run once on init\n    updateChartMetrics(slider.value);\n});\n<\/script>\n\n\n\n<!-- Pool Flow Rate Chart - Independent Module -->\n<style>\n    :root {\n        --chart-primary: #1ecad3;\n        --chart-primary-dark: #17a8b0;\n        --chart-primary-light: #e6f9fa;\n        --chart-bg: #ffffff;\n        --chart-text: #2c3e50;\n        --chart-text-light: #5a6c7d;\n        --chart-border: #e0e8ef;\n        --chart-shadow: 0 4px 24px rgba(0, 0, 0, 0.07);\n        --chart-radius: 14px;\n        --chart-radius-sm: 8px;\n        --chart-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;\n    }\n\n    .flow-chart-wrapper {\n        font-family: var(--chart-font);\n        max-width: 680px;\n        margin: 0 auto 24px;\n        background: var(--chart-bg);\n        border-radius: var(--chart-radius);\n        box-shadow: var(--chart-shadow);\n        padding: 28px 24px 22px;\n        box-sizing: border-box;\n        border: 1px solid var(--chart-border);\n        position: relative;\n        overflow: hidden;\n    }\n\n    \/* Top accent *\/\n    .flow-chart-wrapper::before {\n        content: '';\n        position: absolute;\n        top: 0;\n        left: 0;\n        right: 0;\n        height: 4px;\n        background: linear-gradient(90deg, #1ecad3 0%, #5de0e8 50%, #1ecad3 100%);\n        border-radius: var(--chart-radius) var(--chart-radius) 0 0;\n    }\n\n    .flow-chart-header {\n        display: flex;\n        align-items: center;\n        gap: 12px;\n        margin-bottom: 6px;\n        padding-top: 4px;\n    }\n\n    .flow-chart-header .chart-icon {\n        flex-shrink: 0;\n        width: 44px;\n        height: 44px;\n        border-radius: 50%;\n        background: var(--chart-primary-light);\n        display: flex;\n        align-items: center;\n        justify-content: center;\n    }\n\n    .flow-chart-header .chart-icon svg {\n        width: 22px;\n        height: 22px;\n        fill: var(--chart-primary);\n    }\n\n    .flow-chart-header .chart-title-group {\n        flex: 1;\n        min-width: 0;\n    }\n\n    .flow-chart-header h4 {\n        margin: 0 0 2px;\n        font-size: 1.15rem;\n        font-weight: 700;\n        color: var(--chart-text);\n        letter-spacing: -0.3px;\n    }\n\n    .flow-chart-header .chart-subtitle {\n        font-size: 0.82rem;\n        color: var(--chart-text-light);\n        margin: 0;\n    }\n\n    .chart-container {\n        position: relative;\n        width: 100%;\n        height: 380px;\n        margin: 10px 0 6px;\n    }\n\n    .chart-container canvas {\n        width: 100% !important;\n        height: 100% !important;\n    }\n\n    \/* Legend custom row *\/\n    .chart-legend-custom {\n        display: flex;\n        flex-wrap: wrap;\n        gap: 18px;\n        justify-content: center;\n        margin-top: 10px;\n        font-size: 0.82rem;\n        color: var(--chart-text-light);\n    }\n\n    .chart-legend-custom .legend-item {\n        display: flex;\n        align-items: center;\n        gap: 6px;\n        font-weight: 500;\n    }\n\n    .legend-dot {\n        width: 12px;\n        height: 12px;\n        border-radius: 3px;\n        flex-shrink: 0;\n    }\n\n    .legend-dot.min {\n        background: #f9c74f;\n        border: 1px solid #e0a800;\n    }\n    .legend-dot.max {\n        background: #f9844a;\n        border: 1px solid #d9662c;\n    }\n    .legend-dot.optimal {\n        background: #1ecad3;\n        border: 1px solid #149ca5;\n    }\n\n    .chart-note {\n        text-align: center;\n        margin-top: 8px;\n        font-size: 0.75rem;\n        color: #8b9aab;\n        line-height: 1.4;\n    }\n\n    .chart-note a {\n        color: var(--chart-primary-dark);\n        text-decoration: underline;\n        text-underline-offset: 2px;\n        font-weight: 600;\n    }\n\n    .chart-note a:hover {\n        color: #0f7a80;\n        text-decoration: none;\n    }\n\n    \/* Responsive *\/\n    @media (max-width: 500px) {\n        .flow-chart-wrapper {\n            padding: 20px 12px 16px;\n            border-radius: 10px;\n        }\n        .flow-chart-wrapper::before {\n            border-radius: 10px 10px 0 0;\n        }\n        .chart-container {\n            height: 290px;\n        }\n        .flow-chart-header h4 {\n            font-size: 1rem;\n        }\n        .chart-legend-custom {\n            gap: 10px;\n            font-size: 0.75rem;\n        }\n    }\n<\/style>\n\n<div class=\"flow-chart-wrapper\" id=\"poolFlowChartModule\" role=\"region\" aria-label=\"Tableau de recommandation des d\u00e9bits de tuyauterie de piscine\">\n    <!-- Header -->\n    <div class=\"flow-chart-header\">\n        <div class=\"chart-icon\">\n            <!-- Flow \/ pipe icon -->\n            <svg viewBox=\"0 0 24 24\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                <path d=\"M22 14h-2v-2h-2v2h-2v2h2v2h2v-2h2v-2zM4 8h2v2h2V8h2V6H8V4H6v2H4v2zm12-4V2H6v2H4v2H2v2h2v2h2v2h2v2h2v-2H8v-2h2v-2H8V8h2V6h-2V4h6zm-2 12v-2h-2v2h-2v2h2v2h2v-2h2v-2h-2z\" fill-rule=\"evenodd\"\/>\n            <\/svg>\n        <\/div>\n        <div class=\"chart-title-group\">\n            <h2>D\u00e9bits recommand\u00e9s selon la taille du tuyau<\/h2>\n            <p class=\"chart-subtitle\">Tuyauterie de piscine PVC standard (schedule 40)<\/p>\n        <\/div>\n    <\/div>\n\n    <!-- Chart canvas -->\n    <div class=\"chart-container\">\n        <canvas id=\"flowRateChartCanvas\" aria-label=\"Diagramme \u00e0 barres montrant les d\u00e9bits minimum, maximum et optimal en GPM pour des tuyaux de 1,5 \u00e0 3 pouces\"><\/canvas>\n    <\/div>\n\n    <!-- Custom legend -->\n    <div class=\"chart-legend-custom\">\n        <span class=\"legend-item\"><span class=\"legend-dot min\"><\/span> D\u00e9bit min (GPM)<\/span>\n        <span class=\"legend-item\"><span class=\"legend-dot max\"><\/span> D\u00e9bit max (GPM)<\/span>\n        <span class=\"legend-item\"><span class=\"legend-dot optimal\"><\/span> Optimal (6\u20138 ft\/s)<\/span>\n    <\/div>\n\n    <p class=\"chart-note\">\n        \ud83d\udca1 D'apr\u00e8s les recommandations courantes du secteur pour la tuyauterie de piscine en PVC.\n\n    <\/p>\n<\/div>\n\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.8\/dist\/chart.umd.min.js\">\n<\/script>\n\n<script>\n    (function() {\n        \/\/ Wait for Chart.js to load\n        function initChart() {\n            const canvas = document.getElementById('flowRateChartCanvas');\n            if (!canvas) return;\n\n            const ctx = canvas.getContext('2d');\n\n            \/\/ Destroy previous chart instance if any (for re-initialization safety)\n            if (window.flowRateChartInstance) {\n                window.flowRateChartInstance.destroy();\n            }\n\n            \/\/ Data: Pipe sizes (inches) vs Flow rates (GPM)\n            const pipeSizes = ['1.5\"', '2\"', '2.5\"', '3\"'];\n\n            \/\/ Typical min\/max\/optimal flow rates in GPM for each pipe size\n            const minFlow = [28, 45, 70, 100];\n            const maxFlow = [50, 80, 120, 170];\n            const optimalFlow = [38, 62, 95, 135];\n\n            \/\/ Create gradient fill for optimal bars\n            const gradientOptimal = ctx.createLinearGradient(0, 0, 0, 400);\n            gradientOptimal.addColorStop(0, 'rgba(30, 202, 211, 0.9)');\n            gradientOptimal.addColorStop(1, 'rgba(30, 202, 211, 0.45)');\n\n            const gradientMin = ctx.createLinearGradient(0, 0, 0, 400);\n            gradientMin.addColorStop(0, 'rgba(249, 199, 79, 0.85)');\n            gradientMin.addColorStop(1, 'rgba(249, 199, 79, 0.35)');\n\n            const gradientMax = ctx.createLinearGradient(0, 0, 0, 400);\n            gradientMax.addColorStop(0, 'rgba(249, 132, 74, 0.85)');\n            gradientMax.addColorStop(1, 'rgba(249, 132, 74, 0.35)');\n\n            window.flowRateChartInstance = new Chart(ctx, {\n                type: 'bar',\n                data: {\n                    labels: pipeSizes,\n                    datasets: [{\n                        label: 'D\u00e9bit min (GPM)',\n                        data: minFlow,\n                        backgroundColor: gradientMin,\n                        borderColor: '#f9c74f',\n                        borderWidth: 1.5,\n                        borderRadius: 6,\n                        borderSkipped: false,\n                        order: 3,\n                    }, {\n                        label: 'D\u00e9bit max (GPM)',\n                        data: maxFlow,\n                        backgroundColor: gradientMax,\n                        borderColor: '#f9844a',\n                        borderWidth: 1.5,\n                        borderRadius: 6,\n                        borderSkipped: false,\n                        order: 2,\n                    }, {\n                        label: 'Optimal (6\u20138 ft\/s)',\n                        data: optimalFlow,\n                        backgroundColor: gradientOptimal,\n                        borderColor: '#1ecad3',\n                        borderWidth: 2,\n                        borderRadius: 6,\n                        borderSkipped: false,\n                        order: 1,\n                    }]\n                },\n                options: {\n                    responsive: true,\n                    maintainAspectRatio: false,\n                    interaction: {\n                        mode: 'index',\n                        intersect: false,\n                    },\n                    plugins: {\n                        legend: {\n                            display: false, \/\/ We use custom legend\n                        },\n                        tooltip: {\n                            backgroundColor: '#1e293b',\n                            titleFont: { weight: 'bold', size: 13, family: \"'Segoe UI', Roboto, sans-serif\" },\n                            bodyFont: { size: 12, family: \"'Segoe UI', Roboto, sans-serif\" },\n                            padding: 12,\n                            cornerRadius: 8,\n                            displayColors: true,\n                            callbacks: {\n                                label: function(context) {\n                                    return context.dataset.label + ': ' + context.raw + ' GPM';\n                                }\n                            }\n                        },\n                    },\n                    scales: {\n                        x: {\n                            title: {\n                                display: true,\n                                text: 'Diam\u00e8tre du tuyau (pouces)',\n                                color: '#5a6c7d',\n                                font: {\n                                    weight: '600',\n                                    size: 12,\n                                    family: \"'Segoe UI', Roboto, sans-serif\"\n                                },\n                            },\n                            grid: {\n                                display: false,\n                            },\n                            ticks: {\n                                color: '#3a4a5a',\n                                font: {\n                                    weight: '500',\n                                    size: 11,\n                                },\n                            },\n                        },\n                        y: {\n                            title: {\n                                display: true,\n                                text: 'D\u00e9bit (GPM)',\n                                color: '#5a6c7d',\n                                font: {\n                                    weight: '600',\n                                    size: 12,\n                                    family: \"'Segoe UI', Roboto, sans-serif\"\n                                },\n                            },\n                            beginAtZero: true,\n                            max: 190,\n                            ticks: {\n                                stepSize: 20,\n                                callback: function(value) {\n                                    return value + ' GPM';\n                                },\n                                color: '#3a4a5a',\n                                font: {\n                                    size: 10.5,\n                                },\n                            },\n                            grid: {\n                                color: '#eef2f7',\n                                drawBorder: false,\n                            },\n                        },\n                    },\n                    animation: {\n                        duration: 1000,\n                        easing: 'easeOutQuart',\n                    },\n                },\n            });\n        }\n\n        \/\/ Initialize on load\n        if (typeof Chart !== 'undefined') {\n            initChart();\n        } else {\n            \/\/ If Chart.js loads asynchronously, wait for it\n            window.addEventListener('load', function() {\n                if (typeof Chart !== 'undefined') {\n                    initChart();\n                } else {\n                    \/\/ Fallback: retry after a short delay\n                    setTimeout(function() {\n                        if (typeof Chart !== 'undefined') {\n                            initChart();\n                        }\n                    }, 600);\n                }\n            });\n        }\n\n        \/\/ Handle dynamic re-insertion (e.g., in some page builders)\n        document.addEventListener('DOMContentLoaded', function() {\n            if (typeof Chart !== 'undefined' && !window.flowRateChartInstance) {\n                initChart();\n            }\n        });\n    })();\n<\/script>\n\n\n<!-- aiper-tool-links -->\n<style>.aiper-i18n-upgrade{box-sizing:border-box;width:100%;max-width:100%;overflow-wrap:anywhere;padding:24px;margin:24px 0;border:1px solid #1ecad3;border-radius:12px}.aiper-i18n-upgrade form{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:16px}.aiper-i18n-upgrade label{display:flex;flex-direction:column;gap:8px}.aiper-i18n-upgrade input{min-width:0;padding:12px;border:1px solid #a0b8c4;border-radius:8px}.aiper-i18n-upgrade button{padding:12px;background:#087e8b;color:white;border:0;border-radius:8px}.aiper-i18n-upgrade p{line-height:1.7}@media(max-width:600px){.aiper-i18n-upgrade form{grid-template-columns:minmax(0,1fr)}}<\/style><section class=\"aiper-i18n-upgrade\" id=\"aiper-upgrade-runtime\"><h2>Dur\u00e9e quotidienne et co\u00fbt \u00e9lectrique<\/h2><p>Utilisez des mesures r\u00e9elles. Un temps de renouvellement ne garantit pas la qualit\u00e9 de l\u2019eau ; respectez les d\u00e9bits et dur\u00e9es minimaux des \u00e9quipements.<\/p><p>Saisir les d\u00e9cimales avec un point (ex. 1.5). Gallons am\u00e9ricains sauf indication contraire. Montants en dollars am\u00e9ricains (USD).<\/p><form><label>Volume du bassin (US gal)<input type=\"number\" name=\"v\" value=\"20000\" min=\"0\" step=\"any\" required><\/label><label>D\u00e9bit mesur\u00e9 (GPM)<input type=\"number\" name=\"q\" value=\"50\" min=\"0\" step=\"any\" required><\/label><label>\u00c9quivalents de volume par jour<input type=\"number\" name=\"n\" value=\"1\" min=\"0\" step=\"any\" required><\/label><label>Dur\u00e9e minimale des \u00e9quipements (h)<input type=\"number\" name=\"min\" value=\"0\" min=\"0\" step=\"any\" required><\/label><label>Puissance mesur\u00e9e (W)<input type=\"number\" name=\"w\" value=\"700\" min=\"0\" step=\"any\" required><\/label><label>Tarif \u00e9lectrique ($\/kWh)<input type=\"number\" name=\"r\" value=\"0.2\" min=\"0\" step=\"any\" required><\/label><button type=\"submit\">Calculer<\/button><button type=\"reset\">R\u00e9initialiser<\/button><\/form><p role=\"status\" aria-live=\"polite\"><\/p><\/section><script>(()=>{const root=document.currentScript.previousElementSibling,form=root.querySelector('form'),out=root.querySelector('[role=status]'),D={\"calc\": \"Calculer\", \"reset\": \"R\u00e9initialiser\", \"format\": \"Saisir les d\u00e9cimales avec un point (ex. 1.5). Gallons am\u00e9ricains sauf indication contraire.\", \"error\": \"Renseignez tous les champs avec des valeurs non n\u00e9gatives. Volume, d\u00e9bit et dimensions doivent \u00eatre positifs.\", \"unreachable\": \"Cette eau de remplissage ne permet pas d\u2019atteindre la cible.\", \"day\": \"Par jour\", \"month\": \"Pour 30 jours\", \"hours\": \"heures\", \"water\": \"Surface du plan d\u2019eau\", \"interior\": \"Surface int\u00e9rieure, fond et parois\", \"replace\": \"Remplacement en une fois\", \"stages\": \"\u00c9tapes identiques de remplacement\", \"total\": \"Eau totale utilis\u00e9e\", \"flow\": \"D\u00e9bit mesur\u00e9 du tuyau\", \"time\": \"Dur\u00e9e de remplissage estim\u00e9e\", \"target\": \"Cible temporaire de chlore libre\", \"dose\": \"Quantit\u00e9 de chlore liquide\", \"budget\": \"Budget du projet avec r\u00e9serve\", \"invalid\": \"Cet objectif ne tient pas dans une journ\u00e9e de 24 heures.\", \"review\": \"V\u00e9rifiez les mesures, corrigez un seul param\u00e8tre \u00e0 la fois, faites circuler puis contr\u00f4lez de nouveau. Le rev\u00eatement, le syst\u00e8me de d\u00e9sinfection et l\u2019\u00e9tiquette d\u00e9terminent les cibles.\", \"schedule\": \"Programme quotidien \u00e0 plusieurs vitesses\", \"off\": \"Arr\u00eat\", \"equal\": \"Comparaison \u00e0 volume d\u2019eau \u00e9gal\", \"units\": \"US gal ; ft ; GPM ; W ; $\/kWh\", \"title\": [\"Dur\u00e9e quotidienne et co\u00fbt \u00e9lectrique\", \"V\u00e9rifier la surface int\u00e9rieure et le plan d\u2019eau\", \"Remplacer l\u2019eau pour r\u00e9duire une concentration\", \"Mesurer le d\u00e9bit du tuyau\", \"Compl\u00e9ment SLAM selon le CYA\", \"Ordre des corrections chimiques\", \"Budget d\u00e9taill\u00e9 du projet\"], \"labels\": [\"Volume du bassin (US gal)\", \"D\u00e9bit mesur\u00e9 (GPM)\", \"\u00c9quivalents de volume par jour\", \"Dur\u00e9e minimale des \u00e9quipements (h)\", \"Puissance mesur\u00e9e (W)\", \"Tarif \u00e9lectrique ($\/kWh)\", \"Longueur (ft)\", \"Largeur (ft)\", \"Profondeur faible (ft)\", \"Profondeur maximale (ft)\", \"Concentration actuelle (ppm)\", \"Concentration cible (ppm)\", \"Concentration de l\u2019eau neuve (ppm)\", \"Nombre d\u2019\u00e9tapes identiques (1\u201310)\", \"Volume du seau (US gal)\", \"Temps pour remplir le seau (s)\", \"CYA (ppm)\", \"Chlore libre actuel (ppm)\", \"Concentration du chlore liquide (%)\", \"Alcalinit\u00e9 totale (ppm)\", \"pH\", \"Duret\u00e9 calcique (ppm)\", \"Bassin et \u00e9quipements standard ($)\", \"Terrassement ($)\", \"Plage de piscine ($)\", \"Permis et \u00e9tudes ($)\", \"\u00c9quipements optionnels ($)\", \"R\u00e9serve (%)\", \"D\u00e9bit \u00e0 petite vitesse (GPM)\", \"Puissance \u00e0 petite vitesse (W)\", \"D\u00e9bit \u00e0 vitesse moyenne (GPM)\", \"Puissance \u00e0 vitesse moyenne (W)\", \"Dur\u00e9e \u00e0 vitesse moyenne (h)\", \"D\u00e9bit \u00e0 grande vitesse (GPM)\", \"Puissance \u00e0 grande vitesse (W)\", \"Dur\u00e9e \u00e0 grande vitesse (h)\"], \"help\": [\"Utilisez des mesures r\u00e9elles. Un temps de renouvellement ne garantit pas la qualit\u00e9 de l\u2019eau ; respectez les d\u00e9bits et dur\u00e9es minimaux des \u00e9quipements.\", \"Le rectangle inclut le fond inclin\u00e9. Les autres formes sont des approximations sans marches, banquettes ni arrondis. Faites mesurer avant toute commande.\", \"Calculez une seule substance \u00e0 la fois. Avant une vidange importante, v\u00e9rifiez nappe phr\u00e9atique, structure et r\u00e8gles de rejet ; m\u00e9langez et contr\u00f4lez entre les \u00e9tapes.\", \"Cette mesure concerne le tuyau de remplissage, pas le d\u00e9bit de circulation requis. R\u00e9p\u00e9tez avec la configuration r\u00e9elle.\", \"Pour un CYA mesur\u00e9 entre 20 et 90 ppm. La cible et la dose ne remplacent pas la proc\u00e9dure SLAM compl\u00e8te. Mesurez le chlore libre avant chaque compl\u00e9ment, suivez l\u2019\u00e9tiquette et ne vous baignez pas pendant le traitement.\", \"Cette liste ne remplace pas une analyse compl\u00e8te et ne cr\u00e9e pas de recette automatique de produits.\", \"Saisissez des devis locaux. Le co\u00fbt du projet est distinct des mensualit\u00e9s de cr\u00e9dit et de l\u2019entretien.\"], \"pure\": \"Quantit\u00e9s th\u00e9oriques de CaCl\u2082 anhydre pur \u00e0 100 % ou de chlorure de calcium dihydrat\u00e9 pur ; duret\u00e9 exprim\u00e9e en ppm CaCO\u2083. Si le produit indique une fraction de CaCl\u2082 anhydre, divisez la quantit\u00e9 anhydre par cette fraction (77 % = 0.77). Ne r\u00e9appliquez pas ce facteur \u00e0 la quantit\u00e9 dihydrat\u00e9e. L\u2019hypochlorite de calcium pour traitement choc est un autre produit.\"},type=\"runtime\";form.addEventListener('submit',e=>{e.preventDefault();const inputs=[...form.querySelectorAll('input')];if(inputs.some(x=>x.value.trim()===''||!Number.isFinite(Number(x.value))||Number(x.value)<0)){out.textContent=D.error;return;}const x=Object.fromEntries(inputs.map(x=>[x.name,Number(x.value)])),fmt=n=>new Intl.NumberFormat(\"fr-FR\",{maximumFractionDigits:2}).format(n),line=(k,v,u='')=>D[k]+': '+fmt(v)+' '+u;let result='';if(type==='dilution'){if(x.t>=x.c){result=line('replace',0,'US gal');}else if(x.f>=x.t){result=D.unreachable;}else if(x.v<=0||x.n<1||x.n>10||!Number.isInteger(x.n)){result=D.error;}else{let f=(x.c-x.t)\/(x.c-x.f),st=1-Math.pow((x.t-x.f)\/(x.c-x.f),1\/x.n);result=line('replace',x.v*f,'US gal')+' ('+fmt(f*100)+'%); '+line('stages',x.n)+': '+fmt(st*100)+'%; '+line('total',st*x.v*x.n,'US gal');}}else if(type==='runtime'){if(x.v<=0||x.q<=0||x.n<=0){result=D.error;}else{let h=Math.max(x.v*x.n\/x.q\/60,x.min);result=h>24?D.invalid:\"Dur\u00e9e quotidienne de fonctionnement\"+': '+fmt(h)+' '+D.hours+'; '+line('day',h*x.w\/1000,'kWh')+'; '+line('month',h*x.w\/1000*x.r*30,'$');}}else if(type==='surface'){if(Math.min(x.l,x.b,x.s,x.d)<=0||x.d<x.s){result=D.error;}else{let a=x.b*Math.hypot(x.l,x.d-x.s)+x.l*(x.s+x.d)+x.b*(x.s+x.d);result=line('water',x.l*x.b,'ft\u00b2')+'; '+line('interior',a,'ft\u00b2');}}else if(type==='fill'){if(Math.min(x.v,x.b,x.s)<=0){result=D.error;}else{let q=x.b*60\/x.s;result=line('flow',q,'GPM')+'; '+line('time',x.v\/q\/60,D.hours);const f=document.getElementById('flowRate');if(f){f.value=q.toFixed(3);f.dispatchEvent(new Event('input',{bubbles:true}));}}}else if(type==='slam'){if(x.v<=0||x.s<=0||x.s>15||x.c<20||x.c>90){result=D.error;}else{let target=.4*x.c,litres=Math.max(0,target-x.f)*x.v*3.785411784\/(x.s*10000);result=line('target',target,'ppm')+'; '+line('dose',litres,'L');}}else if(type==='chemical'){result=D.review;}else if(type==='budget'){result=line('budget',(x.shell+x.site+x.deck+x.permit+x.extras)*(1+x.reserve\/100),'$');}else if(type==='schedule'){if(Math.min(x.v,x.n,x.q1,x.q2,x.q3)<=0){result=D.error;}else{let target=x.v*x.n,h1=Math.max(0,(target-60*(x.q2*x.h2+x.q3*x.h3))\/(60*x.q1)),h=h1+x.h2+x.h3;let energy=(h1*x.w1+x.h2*x.w2+x.h3*x.w3)\/1000;result=h>24?D.invalid:D.schedule+': '+fmt(h1)+' \/ '+fmt(x.h2)+' \/ '+fmt(x.h3)+' '+D.hours+'; '+line('off',24-h,D.hours)+'; '+line('day',energy*x.r,'$')+'; '+D.equal+': '+[1,2,3].map(i=>fmt(target\/x['q'+i]\/60*x['w'+i]\/1000*x.r)+' $').join(' \/ ');}}out.textContent=result;});form.addEventListener('reset',()=>out.textContent='');})();<\/script><section class=\"aiper-i18n-upgrade\" id=\"aiper-upgrade-schedule\"><h2>Programme quotidien \u00e0 plusieurs vitesses<\/h2><p>Utilisez des mesures r\u00e9elles. Un temps de renouvellement ne garantit pas la qualit\u00e9 de l\u2019eau ; respectez les d\u00e9bits et dur\u00e9es minimaux des \u00e9quipements.<\/p><p>Saisir les d\u00e9cimales avec un point (ex. 1.5). Gallons am\u00e9ricains sauf indication contraire. Montants en dollars am\u00e9ricains (USD).<\/p><form><label>Volume du bassin (US gal)<input type=\"number\" name=\"v\" value=\"20000\" min=\"0\" step=\"any\" required><\/label><label>\u00c9quivalents de volume par jour<input type=\"number\" name=\"n\" value=\"1\" min=\"0\" step=\"any\" required><\/label><label>Tarif \u00e9lectrique ($\/kWh)<input type=\"number\" name=\"r\" value=\"0.2\" min=\"0\" step=\"any\" required><\/label><label>D\u00e9bit \u00e0 petite vitesse (GPM)<input type=\"number\" name=\"q1\" value=\"25\" min=\"0\" step=\"any\" required><\/label><label>Puissance \u00e0 petite vitesse (W)<input type=\"number\" name=\"w1\" value=\"180\" min=\"0\" step=\"any\" required><\/label><label>D\u00e9bit \u00e0 vitesse moyenne (GPM)<input type=\"number\" name=\"q2\" value=\"45\" min=\"0\" step=\"any\" required><\/label><label>Puissance \u00e0 vitesse moyenne (W)<input type=\"number\" name=\"w2\" value=\"600\" min=\"0\" step=\"any\" required><\/label><label>Dur\u00e9e \u00e0 vitesse moyenne (h)<input type=\"number\" name=\"h2\" value=\"1\" min=\"0\" step=\"any\" required><\/label><label>D\u00e9bit \u00e0 grande vitesse (GPM)<input type=\"number\" name=\"q3\" value=\"65\" min=\"0\" step=\"any\" required><\/label><label>Puissance \u00e0 grande vitesse (W)<input type=\"number\" name=\"w3\" value=\"1400\" min=\"0\" step=\"any\" required><\/label><label>Dur\u00e9e \u00e0 grande vitesse (h)<input type=\"number\" name=\"h3\" value=\"1\" min=\"0\" step=\"any\" required><\/label><button type=\"submit\">Calculer<\/button><button type=\"reset\">R\u00e9initialiser<\/button><\/form><p role=\"status\" aria-live=\"polite\"><\/p><\/section><script>(()=>{const root=document.currentScript.previousElementSibling,form=root.querySelector('form'),out=root.querySelector('[role=status]'),D={\"calc\": \"Calculer\", \"reset\": \"R\u00e9initialiser\", \"format\": \"Saisir les d\u00e9cimales avec un point (ex. 1.5). Gallons am\u00e9ricains sauf indication contraire.\", \"error\": \"Renseignez tous les champs avec des valeurs non n\u00e9gatives. Volume, d\u00e9bit et dimensions doivent \u00eatre positifs.\", \"unreachable\": \"Cette eau de remplissage ne permet pas d\u2019atteindre la cible.\", \"day\": \"Par jour\", \"month\": \"Pour 30 jours\", \"hours\": \"heures\", \"water\": \"Surface du plan d\u2019eau\", \"interior\": \"Surface int\u00e9rieure, fond et parois\", \"replace\": \"Remplacement en une fois\", \"stages\": \"\u00c9tapes identiques de remplacement\", \"total\": \"Eau totale utilis\u00e9e\", \"flow\": \"D\u00e9bit mesur\u00e9 du tuyau\", \"time\": \"Dur\u00e9e de remplissage estim\u00e9e\", \"target\": \"Cible temporaire de chlore libre\", \"dose\": \"Quantit\u00e9 de chlore liquide\", \"budget\": \"Budget du projet avec r\u00e9serve\", \"invalid\": \"Cet objectif ne tient pas dans une journ\u00e9e de 24 heures.\", \"review\": \"V\u00e9rifiez les mesures, corrigez un seul param\u00e8tre \u00e0 la fois, faites circuler puis contr\u00f4lez de nouveau. Le rev\u00eatement, le syst\u00e8me de d\u00e9sinfection et l\u2019\u00e9tiquette d\u00e9terminent les cibles.\", \"schedule\": \"Programme quotidien \u00e0 plusieurs vitesses\", \"off\": \"Arr\u00eat\", \"equal\": \"Comparaison \u00e0 volume d\u2019eau \u00e9gal\", \"units\": \"US gal ; ft ; GPM ; W ; $\/kWh\", \"title\": [\"Dur\u00e9e quotidienne et co\u00fbt \u00e9lectrique\", \"V\u00e9rifier la surface int\u00e9rieure et le plan d\u2019eau\", \"Remplacer l\u2019eau pour r\u00e9duire une concentration\", \"Mesurer le d\u00e9bit du tuyau\", \"Compl\u00e9ment SLAM selon le CYA\", \"Ordre des corrections chimiques\", \"Budget d\u00e9taill\u00e9 du projet\"], \"labels\": [\"Volume du bassin (US gal)\", \"D\u00e9bit mesur\u00e9 (GPM)\", \"\u00c9quivalents de volume par jour\", \"Dur\u00e9e minimale des \u00e9quipements (h)\", \"Puissance mesur\u00e9e (W)\", \"Tarif \u00e9lectrique ($\/kWh)\", \"Longueur (ft)\", \"Largeur (ft)\", \"Profondeur faible (ft)\", \"Profondeur maximale (ft)\", \"Concentration actuelle (ppm)\", \"Concentration cible (ppm)\", \"Concentration de l\u2019eau neuve (ppm)\", \"Nombre d\u2019\u00e9tapes identiques (1\u201310)\", \"Volume du seau (US gal)\", \"Temps pour remplir le seau (s)\", \"CYA (ppm)\", \"Chlore libre actuel (ppm)\", \"Concentration du chlore liquide (%)\", \"Alcalinit\u00e9 totale (ppm)\", \"pH\", \"Duret\u00e9 calcique (ppm)\", \"Bassin et \u00e9quipements standard ($)\", \"Terrassement ($)\", \"Plage de piscine ($)\", \"Permis et \u00e9tudes ($)\", \"\u00c9quipements optionnels ($)\", \"R\u00e9serve (%)\", \"D\u00e9bit \u00e0 petite vitesse (GPM)\", \"Puissance \u00e0 petite vitesse (W)\", \"D\u00e9bit \u00e0 vitesse moyenne (GPM)\", \"Puissance \u00e0 vitesse moyenne (W)\", \"Dur\u00e9e \u00e0 vitesse moyenne (h)\", \"D\u00e9bit \u00e0 grande vitesse (GPM)\", \"Puissance \u00e0 grande vitesse (W)\", \"Dur\u00e9e \u00e0 grande vitesse (h)\"], \"help\": [\"Utilisez des mesures r\u00e9elles. Un temps de renouvellement ne garantit pas la qualit\u00e9 de l\u2019eau ; respectez les d\u00e9bits et dur\u00e9es minimaux des \u00e9quipements.\", \"Le rectangle inclut le fond inclin\u00e9. Les autres formes sont des approximations sans marches, banquettes ni arrondis. Faites mesurer avant toute commande.\", \"Calculez une seule substance \u00e0 la fois. Avant une vidange importante, v\u00e9rifiez nappe phr\u00e9atique, structure et r\u00e8gles de rejet ; m\u00e9langez et contr\u00f4lez entre les \u00e9tapes.\", \"Cette mesure concerne le tuyau de remplissage, pas le d\u00e9bit de circulation requis. R\u00e9p\u00e9tez avec la configuration r\u00e9elle.\", \"Pour un CYA mesur\u00e9 entre 20 et 90 ppm. La cible et la dose ne remplacent pas la proc\u00e9dure SLAM compl\u00e8te. Mesurez le chlore libre avant chaque compl\u00e9ment, suivez l\u2019\u00e9tiquette et ne vous baignez pas pendant le traitement.\", \"Cette liste ne remplace pas une analyse compl\u00e8te et ne cr\u00e9e pas de recette automatique de produits.\", \"Saisissez des devis locaux. Le co\u00fbt du projet est distinct des mensualit\u00e9s de cr\u00e9dit et de l\u2019entretien.\"], \"pure\": \"Quantit\u00e9s th\u00e9oriques de CaCl\u2082 anhydre pur \u00e0 100 % ou de chlorure de calcium dihydrat\u00e9 pur ; duret\u00e9 exprim\u00e9e en ppm CaCO\u2083. Si le produit indique une fraction de CaCl\u2082 anhydre, divisez la quantit\u00e9 anhydre par cette fraction (77 % = 0.77). Ne r\u00e9appliquez pas ce facteur \u00e0 la quantit\u00e9 dihydrat\u00e9e. L\u2019hypochlorite de calcium pour traitement choc est un autre produit.\"},type=\"schedule\";form.addEventListener('submit',e=>{e.preventDefault();const inputs=[...form.querySelectorAll('input')];if(inputs.some(x=>x.value.trim()===''||!Number.isFinite(Number(x.value))||Number(x.value)<0)){out.textContent=D.error;return;}const x=Object.fromEntries(inputs.map(x=>[x.name,Number(x.value)])),fmt=n=>new Intl.NumberFormat(\"fr-FR\",{maximumFractionDigits:2}).format(n),line=(k,v,u='')=>D[k]+': '+fmt(v)+' '+u;let result='';if(type==='dilution'){if(x.t>=x.c){result=line('replace',0,'US gal');}else if(x.f>=x.t){result=D.unreachable;}else if(x.v<=0||x.n<1||x.n>10||!Number.isInteger(x.n)){result=D.error;}else{let f=(x.c-x.t)\/(x.c-x.f),st=1-Math.pow((x.t-x.f)\/(x.c-x.f),1\/x.n);result=line('replace',x.v*f,'US gal')+' ('+fmt(f*100)+'%); '+line('stages',x.n)+': '+fmt(st*100)+'%; '+line('total',st*x.v*x.n,'US gal');}}else if(type==='runtime'){if(x.v<=0||x.q<=0||x.n<=0){result=D.error;}else{let h=Math.max(x.v*x.n\/x.q\/60,x.min);result=h>24?D.invalid:\"Dur\u00e9e quotidienne de fonctionnement\"+': '+fmt(h)+' '+D.hours+'; '+line('day',h*x.w\/1000,'kWh')+'; '+line('month',h*x.w\/1000*x.r*30,'$');}}else if(type==='surface'){if(Math.min(x.l,x.b,x.s,x.d)<=0||x.d<x.s){result=D.error;}else{let a=x.b*Math.hypot(x.l,x.d-x.s)+x.l*(x.s+x.d)+x.b*(x.s+x.d);result=line('water',x.l*x.b,'ft\u00b2')+'; '+line('interior',a,'ft\u00b2');}}else if(type==='fill'){if(Math.min(x.v,x.b,x.s)<=0){result=D.error;}else{let q=x.b*60\/x.s;result=line('flow',q,'GPM')+'; '+line('time',x.v\/q\/60,D.hours);const f=document.getElementById('flowRate');if(f){f.value=q.toFixed(3);f.dispatchEvent(new Event('input',{bubbles:true}));}}}else if(type==='slam'){if(x.v<=0||x.s<=0||x.s>15||x.c<20||x.c>90){result=D.error;}else{let target=.4*x.c,litres=Math.max(0,target-x.f)*x.v*3.785411784\/(x.s*10000);result=line('target',target,'ppm')+'; '+line('dose',litres,'L');}}else if(type==='chemical'){result=D.review;}else if(type==='budget'){result=line('budget',(x.shell+x.site+x.deck+x.permit+x.extras)*(1+x.reserve\/100),'$');}else if(type==='schedule'){if(Math.min(x.v,x.n,x.q1,x.q2,x.q3)<=0){result=D.error;}else{let target=x.v*x.n,h1=Math.max(0,(target-60*(x.q2*x.h2+x.q3*x.h3))\/(60*x.q1)),h=h1+x.h2+x.h3;let energy=(h1*x.w1+x.h2*x.w2+x.h3*x.w3)\/1000;result=h>24?D.invalid:D.schedule+': '+fmt(h1)+' \/ '+fmt(x.h2)+' \/ '+fmt(x.h3)+' '+D.hours+'; '+line('off',24-h,D.hours)+'; '+line('day',energy*x.r,'$')+'; '+D.equal+': '+[1,2,3].map(i=>fmt(target\/x['q'+i]\/60*x['w'+i]\/1000*x.r)+' $').join(' \/ ');}}out.textContent=result;});form.addEventListener('reset',()=>out.textContent='');})();<\/script><div class=\"aiper-tool-links\">\n  <div class=\"aiper-tl-section\">\n    <div class=\"aiper-tl-heading\">Plus d'outils<\/div>\n    <div class=\"aiper-tl-tags\">\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/calculateur-des-couts-d-entretien-de-la-piscine\/\" class=\"aiper-tl-tag\">Calculateur des co\u00fbts d\u2019entretien de la piscine<\/a>\n      <a href=\"https:\/\/aiper.com\/blogs\/fr\/calculateur-debit-piscine\/\" class=\"aiper-tl-tag\">Calculateur de d\u00e9bit de piscine<\/a>\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/calculateur-temps-remplissage-piscine\/\" class=\"aiper-tl-tag\">Calculateur de temps de remplissage de piscine<\/a>\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/calculateur-indice-saturation-piscine\/\" class=\"aiper-tl-tag\">Calculateur d'indice de saturation pour piscine (LSI)<\/a>\n    <\/div>\n  <\/div>\n  <div class=\"aiper-tl-section\">\n    <div class=\"aiper-tl-heading\">Articles connexes<\/div>\n    <div class=\"aiper-tl-tags\">\n      <a href=\"https:\/\/aiper.com\/blogs\/fr\/reduire-alcalinite-piscine\/\" class=\"aiper-tl-tag\">Comment r\u00e9duire l'alcalinit\u00e9 d'une piscine<\/a>\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/robot-piscine-bloque-drain-principal\/\" class=\"aiper-tl-tag\">Pourquoi le robot de piscine reste bloqu\u00e9 sur le drain principal ?<\/a>\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/hivernage-piscine-enterree\/\" class=\"aiper-tl-tag\">Hivernage piscine enterr\u00e9e : guide \u00e9tape par \u00e9tape<\/a>\n<a href=\"https:\/\/aiper.com\/blogs\/fr\/laisser-robot-nettoyeur-piscine-dans-eau\/\" class=\"aiper-tl-tag\">Peut-on laisser un robot nettoyeur de piscine dans l'eau ?<\/a>\n    <\/div>\n  <\/div>\n<\/div>\n\n<style>\n.aiper-tool-links{margin:2.5rem 0 1.5rem;padding:1.5rem 1.75rem;background:linear-gradient(135deg,rgba(148,134,247,.04),rgba(30,202,211,.04));border-radius:12px;border:1px solid rgba(148,134,247,.10)}\n.aiper-tl-section{margin-bottom:1.25rem}\n.aiper-tl-section:last-child{margin-bottom:0}\n.aiper-tl-heading{font-size:.75rem;font-weight:600;letter-spacing:.09em;text-transform:uppercase;color:#8899aa;margin:0 0 .65rem}\n.aiper-tl-tags{display:flex;flex-wrap:wrap;gap:.5rem}\n.aiper-tl-tag{display:inline-block;padding:.4rem .95rem;font-size:.82rem;font-weight:500;line-height:1.35;color:#9486f7;background:rgba(148,134,247,.055);border:1px solid rgba(30,202,211,.25);border-radius:22px;text-decoration:none;transition:all .18s}\n.aiper-tl-tag:hover{background:#1ecad3;color:#fff;border-color:#1ecad3;box-shadow:0 2px 10px rgba(30,202,211,.22)}\n<\/style>","protected":false},"excerpt":{"rendered":"<p>Dur\u00e9e quotidienne et co\u00fbt \u00e9lectrique D\u00e9couvrez combien de temps il faut pour faire circuler tout le volume de votre piscine. 1 2 3 1 Volume de la piscine Gallons Litres m\u00b3 \ud83d\udca1 Vous ne savez pas&nbsp;? Utilisez notre calculateur de volume de piscine Veuillez saisir un volume de piscine valide. 2 D\u00e9bit de la pompe [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"aiper_reading_time":3,"aiper_last_reviewed":"","aiper_reviewer":0,"aiper_related_ids":[],"aiper_layout":"","footnotes":""},"class_list":["post-2162","page","type-page","status-publish","hentry"],"lang":"fr","translations":{"fr":2162,"de":2161,"en":1844,"es":2163},"_links":{"self":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/2162","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/comments?post=2162"}],"version-history":[{"count":6,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/2162\/revisions"}],"predecessor-version":[{"id":13279,"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/pages\/2162\/revisions\/13279"}],"wp:attachment":[{"href":"https:\/\/aiper.com\/blogs\/wp-json\/wp\/v2\/media?parent=2162"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}