{"id":2654,"date":"2026-06-22T18:24:46","date_gmt":"2026-06-22T18:24:46","guid":{"rendered":"https:\/\/app.digitalfreelancer.org\/?page_id=2654"},"modified":"2026-07-15T19:04:06","modified_gmt":"2026-07-15T19:04:06","slug":"dashboard-celestial-pendulum","status":"publish","type":"page","link":"https:\/\/app.digitalfreelancer.org\/index.php\/dashboard-celestial-pendulum\/","title":{"rendered":"Dashboard celestial-pendulum"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"2654\" class=\"elementor elementor-2654\">\n\t\t\t\t<div class=\"elementor-element elementor-element-3c81a5b elementor-hidden-mobile e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"3c81a5b\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-b4fe4d1 elementor-widget elementor-widget-spacer\" data-id=\"b4fe4d1\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-53668a6 e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"53668a6\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t<div class=\"elementor-element elementor-element-e6e2f09 e-con-full e-flex wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-child\" data-id=\"e6e2f09\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t<div class=\"elementor-element elementor-element-76c9d27 elementor-widget elementor-widget-shortcode\" data-id=\"76c9d27\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"shortcode.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-shortcode\">\n        <style>\n        #pdp-1-88b6b1 {\n            --pdp-bg: #0b0a14;\n            --pdp-bg-panel: #14121f;\n            --pdp-bg-panel-2: #1a1729;\n            --pdp-bg-elevated: #201c33;\n            --pdp-accent: #7c5cff;\n            --pdp-accent-2: #a78bfa;\n            --pdp-accent-glow: rgba(124, 92, 255, 0.35);\n            --pdp-gold: #e8b969;\n            --pdp-gold-light: #f6d69a;\n            --pdp-teal: #4fd1c5;\n            --pdp-danger: #ff6b6b;\n            --pdp-text: #f4f2fb;\n            --pdp-text-muted: #a9a3c2;\n            --pdp-text-faint: #746e94;\n            --pdp-border: rgba(255, 255, 255, 0.08);\n            --pdp-border-strong: rgba(124, 92, 255, 0.35);\n            --pdp-radius-lg: 20px;\n            --pdp-radius-md: 14px;\n            --pdp-radius-sm: 10px;\n\n            all: initial;\n            *, *::before, *::after { box-sizing: border-box; }\n            font-family: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Inter, Roboto, sans-serif;\n            color: var(--pdp-text);\n            display: block;\n            width: 100%;\n        }\n\n        #pdp-1-88b6b1 .pdp-app {\n            background:\n                radial-gradient(circle at 15% 0%, rgba(124, 92, 255, 0.16) 0%, transparent 45%),\n                radial-gradient(circle at 100% 100%, rgba(79, 209, 197, 0.10) 0%, transparent 50%),\n                var(--pdp-bg);\n            border: 1px solid var(--pdp-border);\n            border-radius: var(--pdp-radius-lg);\n            padding: 28px;\n            display: grid;\n            gap: 22px;\n        }\n\n        \/* ===== HEADER ===== *\/\n        #pdp-1-88b6b1 .pdp-header {\n            display: flex;\n            justify-content: space-between;\n            align-items: flex-start;\n            flex-wrap: wrap;\n            gap: 14px;\n        }\n\n        #pdp-1-88b6b1 .pdp-title-group {\n            display: flex;\n            align-items: center;\n            gap: 14px;\n        }\n\n        #pdp-1-88b6b1 .pdp-icon {\n            width: 46px;\n            height: 46px;\n            border-radius: 12px;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            font-size: 22px;\n            background: linear-gradient(135deg, var(--pdp-accent), #4c37b3);\n            box-shadow: 0 6px 20px var(--pdp-accent-glow);\n            flex-shrink: 0;\n        }\n\n        #pdp-1-88b6b1 .pdp-header h1 {\n            margin: 0;\n            font-size: 22px;\n            font-weight: 700;\n            letter-spacing: -0.3px;\n        }\n\n        #pdp-1-88b6b1 .pdp-header .pdp-subtitle {\n            margin: 3px 0 0;\n            color: var(--pdp-text-muted);\n            font-size: 13.5px;\n        }\n\n        #pdp-1-88b6b1 .pdp-date-pill {\n            background: var(--pdp-bg-panel);\n            border: 1px solid var(--pdp-border);\n            padding: 8px 14px;\n            border-radius: 999px;\n            font-size: 12.5px;\n            color: var(--pdp-text-muted);\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        #pdp-1-88b6b1 .pdp-dot {\n            width: 7px;\n            height: 7px;\n            border-radius: 50%;\n            background: var(--pdp-teal);\n            box-shadow: 0 0 8px var(--pdp-teal);\n        }\n\n        \/* ===== LAYOUT ===== *\/\n        #pdp-1-88b6b1 .pdp-grid {\n            display: grid;\n            grid-template-columns: 1.15fr 0.85fr;\n            gap: 20px;\n            align-items: start;\n        }\n\n        @media (max-width: 860px) {\n            #pdp-1-88b6b1 .pdp-grid { grid-template-columns: 1fr; }\n        }\n\n        \/* ===== STAGE (canvas panel) ===== *\/\n        #pdp-1-88b6b1 .pdp-stage {\n            background: var(--pdp-bg-panel);\n            border: 1px solid var(--pdp-border);\n            border-radius: var(--pdp-radius-lg);\n            padding: 22px;\n            display: grid;\n            gap: 16px;\n        }\n\n        #pdp-1-88b6b1 .pdp-canvas-wrap {\n            position: relative;\n            border-radius: var(--pdp-radius-md);\n            overflow: hidden;\n            background: radial-gradient(circle at 50% 0%, #191533 0%, #0a0812 75%);\n            border: 1px solid var(--pdp-border);\n        }\n\n        #pdp-1-88b6b1 canvas {\n            display: block;\n            width: 100%;\n            height: auto;\n        }\n\n        #pdp-1-88b6b1 .pdp-stat-strip {\n            position: absolute;\n            top: 14px;\n            left: 14px;\n            right: 14px;\n            display: flex;\n            gap: 10px;\n            flex-wrap: wrap;\n            pointer-events: none;\n        }\n\n        #pdp-1-88b6b1 .pdp-stat-chip {\n            background: rgba(10, 8, 18, 0.65);\n            backdrop-filter: blur(6px);\n            border: 1px solid var(--pdp-border);\n            border-radius: 999px;\n            padding: 6px 12px;\n            font-size: 11.5px;\n            color: var(--pdp-text-muted);\n            display: flex;\n            gap: 6px;\n            align-items: baseline;\n        }\n\n        #pdp-1-88b6b1 .pdp-stat-chip strong {\n            color: var(--pdp-gold-light);\n            font-size: 13px;\n            font-variant-numeric: tabular-nums;\n        }\n\n        \/* ===== CONTROLS ===== *\/\n        #pdp-1-88b6b1 .pdp-controls-row {\n            display: flex;\n            gap: 10px;\n            flex-wrap: wrap;\n        }\n\n        #pdp-1-88b6b1 .pdp-btn {\n            padding: 11px 20px;\n            border-radius: var(--pdp-radius-sm);\n            border: none;\n            cursor: pointer;\n            font-weight: 600;\n            font-size: 13.5px;\n            display: inline-flex;\n            align-items: center;\n            gap: 8px;\n            transition: transform .15s ease, box-shadow .15s ease, opacity .15s ease;\n            font-family: inherit;\n        }\n\n        #pdp-1-88b6b1 .pdp-btn:hover { transform: translateY(-1px); }\n        #pdp-1-88b6b1 .pdp-btn:active { transform: translateY(0); }\n\n        #pdp-1-88b6b1 .pdp-btn-primary {\n            background: linear-gradient(135deg, var(--pdp-accent), #5a3fd6);\n            color: #fff;\n            box-shadow: 0 6px 18px var(--pdp-accent-glow);\n        }\n\n        #pdp-1-88b6b1 .pdp-btn-ghost {\n            background: var(--pdp-bg-elevated);\n            color: var(--pdp-text);\n            border: 1px solid var(--pdp-border);\n        }\n\n        #pdp-1-88b6b1 .pdp-btn-ghost:hover {\n            border-color: var(--pdp-border-strong);\n        }\n\n        #pdp-1-88b6b1 .pdp-btn:disabled {\n            opacity: 0.45;\n            cursor: not-allowed;\n            transform: none;\n        }\n\n        \/* ===== SLIDERS PANEL ===== *\/\n        #pdp-1-88b6b1 .pdp-panel {\n            background: var(--pdp-bg-panel);\n            border: 1px solid var(--pdp-border);\n            border-radius: var(--pdp-radius-lg);\n            padding: 22px;\n            display: grid;\n            gap: 18px;\n            align-content: start;\n        }\n\n        #pdp-1-88b6b1 .pdp-panel-title {\n            font-size: 13px;\n            font-weight: 700;\n            text-transform: uppercase;\n            letter-spacing: 1px;\n            color: var(--pdp-text-faint);\n            display: flex;\n            align-items: center;\n            gap: 8px;\n        }\n\n        #pdp-1-88b6b1 .pdp-field {\n            display: grid;\n            gap: 8px;\n        }\n\n        #pdp-1-88b6b1 .pdp-field-label {\n            display: flex;\n            justify-content: space-between;\n            font-size: 13px;\n            color: var(--pdp-text-muted);\n        }\n\n        #pdp-1-88b6b1 .pdp-field-label span:last-child {\n            color: var(--pdp-gold-light);\n            font-weight: 600;\n            font-variant-numeric: tabular-nums;\n        }\n\n        #pdp-1-88b6b1 input[type=\"range\"] {\n            -webkit-appearance: none;\n            appearance: none;\n            width: 100%;\n            height: 6px;\n            border-radius: 999px;\n            background: linear-gradient(90deg, var(--pdp-accent) 0%, var(--pdp-accent) var(--val, 50%), var(--pdp-bg-elevated) var(--val, 50%), var(--pdp-bg-elevated) 100%);\n            outline: none;\n            cursor: pointer;\n        }\n\n        #pdp-1-88b6b1 input[type=\"range\"]::-webkit-slider-thumb {\n            -webkit-appearance: none;\n            appearance: none;\n            width: 18px;\n            height: 18px;\n            border-radius: 50%;\n            background: var(--pdp-gold-light);\n            border: 3px solid var(--pdp-bg-panel);\n            box-shadow: 0 0 0 1px var(--pdp-gold);\n            cursor: pointer;\n        }\n\n        #pdp-1-88b6b1 input[type=\"range\"]::-moz-range-thumb {\n            width: 18px;\n            height: 18px;\n            border-radius: 50%;\n            background: var(--pdp-gold-light);\n            border: 3px solid var(--pdp-bg-panel);\n            box-shadow: 0 0 0 1px var(--pdp-gold);\n            cursor: pointer;\n        }\n\n        #pdp-1-88b6b1 .pdp-toggle-row {\n            display: flex;\n            align-items: center;\n            justify-content: space-between;\n            padding: 10px 14px;\n            background: var(--pdp-bg-elevated);\n            border-radius: var(--pdp-radius-sm);\n            border: 1px solid var(--pdp-border);\n        }\n\n        #pdp-1-88b6b1 .pdp-toggle-row span { font-size: 13.5px; color: var(--pdp-text-muted); }\n\n        #pdp-1-88b6b1 .pdp-switch {\n            position: relative;\n            width: 42px;\n            height: 24px;\n            flex-shrink: 0;\n        }\n\n        #pdp-1-88b6b1 .pdp-switch input { opacity: 0; width: 0; height: 0; }\n\n        #pdp-1-88b6b1 .pdp-switch-slider {\n            position: absolute;\n            inset: 0;\n            background: #35304f;\n            border-radius: 999px;\n            transition: .2s;\n            cursor: pointer;\n        }\n\n        #pdp-1-88b6b1 .pdp-switch-slider::before {\n            content: \"\";\n            position: absolute;\n            width: 18px;\n            height: 18px;\n            border-radius: 50%;\n            left: 3px;\n            top: 3px;\n            background: #fff;\n            transition: .2s;\n        }\n\n        #pdp-1-88b6b1 .pdp-switch input:checked + .pdp-switch-slider {\n            background: var(--pdp-accent);\n        }\n\n        #pdp-1-88b6b1 .pdp-switch input:checked + .pdp-switch-slider::before {\n            transform: translateX(18px);\n        }\n\n        \/* ===== METRICS ===== *\/\n        #pdp-1-88b6b1 .pdp-metrics {\n            display: grid;\n            grid-template-columns: repeat(3, 1fr);\n            gap: 10px;\n        }\n\n        #pdp-1-88b6b1 .pdp-metric {\n            background: var(--pdp-bg-elevated);\n            border: 1px solid var(--pdp-border);\n            border-radius: var(--pdp-radius-sm);\n            padding: 12px;\n            text-align: center;\n        }\n\n        #pdp-1-88b6b1 .pdp-metric-label {\n            font-size: 10.5px;\n            text-transform: uppercase;\n            letter-spacing: .6px;\n            color: var(--pdp-text-faint);\n        }\n\n        #pdp-1-88b6b1 .pdp-metric-value {\n            font-size: 17px;\n            font-weight: 700;\n            margin-top: 4px;\n            color: var(--pdp-text);\n            font-variant-numeric: tabular-nums;\n        }\n\n        \/* ===== ENERGY BAR ===== *\/\n        #pdp-1-88b6b1 .pdp-energy-bar {\n            height: 10px;\n            border-radius: 999px;\n            background: var(--pdp-bg-elevated);\n            overflow: hidden;\n            display: flex;\n            border: 1px solid var(--pdp-border);\n        }\n\n        #pdp-1-88b6b1 .pdp-energy-fill-ke {\n            background: linear-gradient(90deg, var(--pdp-teal), #2fb8ab);\n            height: 100%;\n            transition: width .1s linear;\n        }\n\n        #pdp-1-88b6b1 .pdp-energy-fill-pe {\n            background: linear-gradient(90deg, var(--pdp-gold), var(--pdp-gold-light));\n            height: 100%;\n            transition: width .1s linear;\n        }\n\n        #pdp-1-88b6b1 .pdp-energy-legend {\n            display: flex;\n            justify-content: space-between;\n            font-size: 11.5px;\n            color: var(--pdp-text-faint);\n            margin-top: 6px;\n        }\n\n        #pdp-1-88b6b1 .pdp-legend-item { display: flex; align-items: center; gap: 6px; }\n        #pdp-1-88b6b1 .pdp-legend-dot { width: 8px; height: 8px; border-radius: 50%; }\n\n        \/* ===== INSIGHT CARD ===== *\/\n        #pdp-1-88b6b1 .pdp-insight {\n            background: linear-gradient(135deg, var(--pdp-bg-panel-2), var(--pdp-bg-panel));\n            border: 1px solid var(--pdp-border);\n            border-radius: var(--pdp-radius-lg);\n            padding: 20px 22px;\n            display: flex;\n            gap: 14px;\n            align-items: flex-start;\n        }\n\n        #pdp-1-88b6b1 .pdp-insight-icon {\n            font-size: 20px;\n            flex-shrink: 0;\n            margin-top: 2px;\n        }\n\n        #pdp-1-88b6b1 .pdp-insight h3 {\n            margin: 0 0 4px;\n            font-size: 14.5px;\n            color: var(--pdp-gold-light);\n            font-weight: 700;\n        }\n\n        #pdp-1-88b6b1 .pdp-insight p {\n            margin: 0;\n            color: var(--pdp-text-muted);\n            font-size: 13.5px;\n            line-height: 1.55;\n        }\n\n        #pdp-1-88b6b1 .pdp-footer-note {\n            text-align: center;\n            font-size: 11.5px;\n            color: var(--pdp-text-faint);\n        }\n        <\/style>\n\n        <div id=\"pdp-1-88b6b1\">\n            <div class=\"pdp-app\">\n\n                <!-- HEADER -->\n                <header class=\"pdp-header\">\n                    <div class=\"pdp-title-group\">\n                        <div class=\"pdp-icon\">\ud83d\udd70\ufe0f<\/div>\n                        <div>\n                            <h1>Bem-vindo(a), Explorador<\/h1>\n                            <p class=\"pdp-subtitle\">Simulador de p\u00eandulo f\u00edsico em tempo real<\/p>\n                        <\/div>\n                    <\/div>\n                    <div class=\"pdp-date-pill\">\n                        <span class=\"pdp-dot\"><\/span>\n                        18 de July de 2026                    <\/div>\n                <\/header>\n\n                <!-- GRID -->\n                <div class=\"pdp-grid\">\n\n                    <!-- STAGE -->\n                    <section class=\"pdp-stage\">\n                        <div class=\"pdp-canvas-wrap\">\n                            <div class=\"pdp-stat-strip\">\n                                <div class=\"pdp-stat-chip\">\u00c2ngulo <strong data-out=\"angle\">0\u00b0<\/strong><\/div>\n                                <div class=\"pdp-stat-chip\">Per\u00edodo <strong data-out=\"period\">0.0s<\/strong><\/div>\n                                <div class=\"pdp-stat-chip\">Oscila\u00e7\u00f5es <strong data-out=\"cycles\">0<\/strong><\/div>\n                            <\/div>\n                            <canvas data-canvas width=\"640\" height=\"420\"><\/canvas>\n                        <\/div>\n\n                        <div class=\"pdp-controls-row\">\n                            <button class=\"pdp-btn pdp-btn-primary\" data-action=\"toggle\">\u25b6 Iniciar<\/button>\n                            <button class=\"pdp-btn pdp-btn-ghost\" data-action=\"reset\">\u27f2 Reiniciar<\/button>\n                            <button class=\"pdp-btn pdp-btn-ghost\" data-action=\"trail\">\u2728 Rasto: Ligado<\/button>\n                        <\/div>\n                    <\/section>\n\n                    <!-- CONTROL PANEL -->\n                    <section class=\"pdp-panel\">\n                        <div class=\"pdp-panel-title\">\u2699 Par\u00e2metros do sistema<\/div>\n\n                        <div class=\"pdp-field\">\n                            <div class=\"pdp-field-label\"><span>\u00c2ngulo inicial<\/span><span data-val=\"angle\">45\u00b0<\/span><\/div>\n                            <input type=\"range\" data-param=\"angle\" min=\"5\" max=\"85\" value=\"45\" step=\"1\">\n                        <\/div>\n\n                        <div class=\"pdp-field\">\n                            <div class=\"pdp-field-label\"><span>Comprimento do fio<\/span><span data-val=\"length\">1.2 m<\/span><\/div>\n                            <input type=\"range\" data-param=\"length\" min=\"0.4\" max=\"2.5\" value=\"1.2\" step=\"0.05\">\n                        <\/div>\n\n                        <div class=\"pdp-field\">\n                            <div class=\"pdp-field-label\"><span>Gravidade<\/span><span data-val=\"gravity\">9.8 m\/s\u00b2<\/span><\/div>\n                            <input type=\"range\" data-param=\"gravity\" min=\"1.6\" max=\"24.8\" value=\"9.8\" step=\"0.1\">\n                        <\/div>\n\n                        <div class=\"pdp-field\">\n                            <div class=\"pdp-field-label\"><span>Amortecimento<\/span><span data-val=\"damping\">0.2%<\/span><\/div>\n                            <input type=\"range\" data-param=\"damping\" min=\"0\" max=\"2\" value=\"0.2\" step=\"0.05\">\n                        <\/div>\n\n                        <div class=\"pdp-toggle-row\">\n                            <span>Gravidade personalizada<\/span>\n                            <label class=\"pdp-switch\">\n                                <input type=\"checkbox\" data-toggle=\"gravity\">\n                                <span class=\"pdp-switch-slider\"><\/span>\n                            <\/label>\n                        <\/div>\n\n                        <div class=\"pdp-panel-title\" style=\"margin-top:4px;\">\ud83d\udcca Energia do sistema<\/div>\n                        <div>\n                            <div class=\"pdp-energy-bar\">\n                                <div class=\"pdp-energy-fill-ke\" data-bar=\"ke\" style=\"width:50%\"><\/div>\n                                <div class=\"pdp-energy-fill-pe\" data-bar=\"pe\" style=\"width:50%\"><\/div>\n                            <\/div>\n                            <div class=\"pdp-energy-legend\">\n                                <div class=\"pdp-legend-item\"><span class=\"pdp-legend-dot\" style=\"background:var(--pdp-teal)\"><\/span>Cin\u00e9tica<\/div>\n                                <div class=\"pdp-legend-item\"><span class=\"pdp-legend-dot\" style=\"background:var(--pdp-gold)\"><\/span>Potencial<\/div>\n                            <\/div>\n                        <\/div>\n\n                        <div class=\"pdp-metrics\">\n                            <div class=\"pdp-metric\">\n                                <div class=\"pdp-metric-label\">Vel. M\u00e1x<\/div>\n                                <div class=\"pdp-metric-value\" data-out=\"velocity\">0.00<\/div>\n                            <\/div>\n                            <div class=\"pdp-metric\">\n                                <div class=\"pdp-metric-label\">Freq.<\/div>\n                                <div class=\"pdp-metric-value\" data-out=\"freq\">0.00<\/div>\n                            <\/div>\n                            <div class=\"pdp-metric\">\n                                <div class=\"pdp-metric-label\">Tempo<\/div>\n                                <div class=\"pdp-metric-value\" data-out=\"time\">0.0s<\/div>\n                            <\/div>\n                        <\/div>\n                    <\/section>\n\n                <\/div>\n\n                <!-- INSIGHT -->\n                <section class=\"pdp-insight\">\n                    <div class=\"pdp-insight-icon\">\ud83d\udcab<\/div>\n                    <div>\n                        <h3>Energia do Sistema<\/h3>\n                        <p>O sistema est\u00e1 em equil\u00edbrio din\u00e2mico. Ajusta os par\u00e2metros e observa a f\u00edsica em a\u00e7\u00e3o.<\/p>\n                    <\/div>\n                <\/section>\n\n                <div class=\"pdp-footer-note\">Simula\u00e7\u00e3o f\u00edsica baseada na equa\u00e7\u00e3o exata do p\u00eandulo \u00b7 Pendulum Dashboard Pro<\/div>\n            <\/div>\n        <\/div>\n\n        <script>\n        (function () {\n            const root = document.getElementById(\"pdp-1-88b6b1\");\n            if (!root) return;\n\n            const canvas = root.querySelector('[data-canvas]');\n            const ctx = canvas.getContext('2d');\n\n            const W = canvas.width, H = canvas.height;\n            const originX = W \/ 2, originY = 60;\n\n            \/\/ ---- State ----\n            let state = {\n                angle: (45 * Math.PI) \/ 180,\n                velocity: 0,\n                lengthM: 1.2,      \/\/ metres\n                gravity: 9.8,      \/\/ m\/s^2\n                damping: 0.2,      \/\/ %\n                running: false,\n                trailOn: true,\n                elapsed: 0,\n                cycles: 0,\n                lastSign: 1,\n                maxVelocity: 0\n            };\n\n            const PX_PER_M = 130; \/\/ pixels per metre for the rod\n\n            \/\/ ---- Trail ----\n            let trail = [];\n            const MAX_TRAIL = 70;\n\n            \/\/ ---- DOM refs ----\n            const out = {\n                angle: root.querySelector('[data-out=\"angle\"]'),\n                period: root.querySelector('[data-out=\"period\"]'),\n                cycles: root.querySelector('[data-out=\"cycles\"]'),\n                velocity: root.querySelector('[data-out=\"velocity\"]'),\n                freq: root.querySelector('[data-out=\"freq\"]'),\n                time: root.querySelector('[data-out=\"time\"]')\n            };\n            const bars = {\n                ke: root.querySelector('[data-bar=\"ke\"]'),\n                pe: root.querySelector('[data-bar=\"pe\"]')\n            };\n            const btnToggle = root.querySelector('[data-action=\"toggle\"]');\n            const btnReset = root.querySelector('[data-action=\"reset\"]');\n            const btnTrail = root.querySelector('[data-action=\"trail\"]');\n            const gravityToggle = root.querySelector('[data-toggle=\"gravity\"]');\n\n            const sliders = {\n                angle: root.querySelector('[data-param=\"angle\"]'),\n                length: root.querySelector('[data-param=\"length\"]'),\n                gravity: root.querySelector('[data-param=\"gravity\"]'),\n                damping: root.querySelector('[data-param=\"damping\"]')\n            };\n            const sliderLabels = {\n                angle: root.querySelector('[data-val=\"angle\"]'),\n                length: root.querySelector('[data-val=\"length\"]'),\n                gravity: root.querySelector('[data-val=\"gravity\"]'),\n                damping: root.querySelector('[data-val=\"damping\"]')\n            };\n\n            function updateSliderFill(el) {\n                const min = parseFloat(el.min), max = parseFloat(el.max), val = parseFloat(el.value);\n                const pct = ((val - min) \/ (max - min)) * 100;\n                el.style.setProperty('--val', pct + '%');\n            }\n\n            function syncLabels() {\n                sliderLabels.angle.textContent = sliders.angle.value + '\u00b0';\n                sliderLabels.length.textContent = parseFloat(sliders.length.value).toFixed(2) + ' m';\n                sliderLabels.gravity.textContent = parseFloat(sliders.gravity.value).toFixed(1) + ' m\/s\u00b2';\n                sliderLabels.damping.textContent = parseFloat(sliders.damping.value).toFixed(2) + '%';\n            }\n\n            function applySlidersToState() {\n                state.lengthM = parseFloat(sliders.length.value);\n                state.gravity = parseFloat(sliders.gravity.value);\n                state.damping = parseFloat(sliders.damping.value);\n            }\n\n            Object.values(sliders).forEach(el => {\n                updateSliderFill(el);\n                el.addEventListener('input', () => {\n                    updateSliderFill(el);\n                    syncLabels();\n                    if (el === sliders.angle && !state.running) {\n                        state.angle = (parseFloat(sliders.angle.value) * Math.PI) \/ 180;\n                    }\n                    applySlidersToState();\n                });\n            });\n\n            gravityToggle.addEventListener('change', () => {\n                const presets = {\n                    off: 9.8\n                };\n                if (!gravityToggle.checked) {\n                    sliders.gravity.value = 9.8;\n                    updateSliderFill(sliders.gravity);\n                    syncLabels();\n                    applySlidersToState();\n                }\n                sliders.gravity.disabled = !gravityToggle.checked;\n            });\n\n            btnToggle.addEventListener('click', () => {\n                state.running = !state.running;\n                btnToggle.textContent = state.running ? '\u23f8 Pausar' : '\u25b6 Iniciar';\n                btnToggle.classList.toggle('pdp-btn-primary', true);\n            });\n\n            btnReset.addEventListener('click', () => {\n                state.running = false;\n                state.angle = (parseFloat(sliders.angle.value) * Math.PI) \/ 180;\n                state.velocity = 0;\n                state.elapsed = 0;\n                state.cycles = 0;\n                state.maxVelocity = 0;\n                trail = [];\n                btnToggle.textContent = '\u25b6 Iniciar';\n            });\n\n            btnTrail.addEventListener('click', () => {\n                state.trailOn = !state.trailOn;\n                btnTrail.textContent = state.trailOn ? '\u2728 Rasto: Ligado' : '\u2728 Rasto: Desligado';\n                if (!state.trailOn) trail = [];\n            });\n\n            syncLabels();\n            applySlidersToState();\n\n            function estimatedPeriod() {\n                \/\/ Approximate period for larger amplitudes (better than small-angle formula)\n                const L = state.lengthM, g = state.gravity;\n                const theta0 = Math.abs(state.angle);\n                const T0 = 2 * Math.PI * Math.sqrt(L \/ g);\n                const correction = 1 + (theta0 * theta0) \/ 16;\n                return T0 * correction;\n            }\n\n            let lastTime = null;\n\n            function step(ts) {\n                if (lastTime === null) lastTime = ts;\n                let dt = (ts - lastTime) \/ 1000;\n                lastTime = ts;\n                dt = Math.min(dt, 0.033);\n\n                if (state.running) {\n                    \/\/ Real pendulum equation: theta'' = -(g\/L) * sin(theta)\n                    const subSteps = 4;\n                    const sdt = dt \/ subSteps;\n                    for (let i = 0; i < subSteps; i++) {\n                        const accel = -(state.gravity \/ state.lengthM) * Math.sin(state.angle);\n                        state.velocity += accel * sdt;\n                        state.velocity *= (1 - state.damping \/ 100);\n                        state.angle += state.velocity * sdt;\n                    }\n\n                    state.elapsed += dt;\n\n                    const sign = Math.sign(state.velocity) || state.lastSign;\n                    if (sign !== state.lastSign && Math.abs(state.angle) < 0.05) {\n                        \/\/ crossing near bottom counts progress; simpler: count sign changes of angle\n                    }\n                    \/\/ Count full cycles via angle sign changes at zero crossing going same direction\n                    if (state.lastAngleSign === undefined) state.lastAngleSign = Math.sign(state.angle) || 1;\n                    const curSign = Math.sign(state.angle);\n                    if (curSign !== 0 && curSign !== state.lastAngleSign) {\n                        state.halfCrossings = (state.halfCrossings || 0) + 1;\n                        state.cycles = Math.floor(state.halfCrossings \/ 2);\n                        state.lastAngleSign = curSign;\n                    }\n\n                    state.maxVelocity = Math.max(state.maxVelocity, Math.abs(state.velocity));\n\n                    if (state.trailOn) {\n                        const L_px = state.lengthM * PX_PER_M;\n                        const bx = originX + L_px * Math.sin(state.angle);\n                        const by = originY + L_px * Math.cos(state.angle);\n                        trail.push({ x: bx, y: by });\n                        if (trail.length > MAX_TRAIL) trail.shift();\n                    }\n                }\n\n                draw();\n                requestAnimationFrame(step);\n            }\n\n            function draw() {\n                ctx.clearRect(0, 0, W, H);\n\n                \/\/ Ceiling mount\n                ctx.fillStyle = '#2a2444';\n                ctx.fillRect(originX - 50, originY - 14, 100, 14);\n                for (let i = -40; i <= 40; i += 10) {\n                    ctx.beginPath();\n                    ctx.moveTo(originX + i, originY - 14);\n                    ctx.lineTo(originX + i - 6, originY);\n                    ctx.strokeStyle = 'rgba(255,255,255,0.12)';\n                    ctx.lineWidth = 1.5;\n                    ctx.stroke();\n                }\n                ctx.beginPath();\n                ctx.arc(originX, originY, 5, 0, Math.PI * 2);\n                ctx.fillStyle = '#e8b969';\n                ctx.fill();\n\n                const L_px = state.lengthM * PX_PER_M;\n                const bx = originX + L_px * Math.sin(state.angle);\n                const by = originY + L_px * Math.cos(state.angle);\n\n                \/\/ Trail\n                if (state.trailOn && trail.length > 1) {\n                    for (let i = 1; i < trail.length; i++) {\n                        const p0 = trail[i - 1], p1 = trail[i];\n                        const alpha = i \/ trail.length;\n                        ctx.beginPath();\n                        ctx.moveTo(p0.x, p0.y);\n                        ctx.lineTo(p1.x, p1.y);\n                        ctx.strokeStyle = `rgba(124, 92, 255, ${alpha * 0.5})`;\n                        ctx.lineWidth = 2;\n                        ctx.stroke();\n                    }\n                }\n\n                \/\/ Arc guide (range of motion)\n                const theta0 = Math.abs(parseFloat(sliders.angle.value) * Math.PI \/ 180);\n                ctx.beginPath();\n                ctx.arc(originX, originY, L_px, Math.PI \/ 2 - theta0, Math.PI \/ 2 + theta0);\n                ctx.strokeStyle = 'rgba(255,255,255,0.06)';\n                ctx.lineWidth = 1;\n                ctx.stroke();\n\n                \/\/ Rod\n                ctx.beginPath();\n                ctx.moveTo(originX, originY);\n                ctx.lineTo(bx, by);\n                ctx.strokeStyle = '#8b7bb8';\n                ctx.lineWidth = 2.5;\n                ctx.stroke();\n\n                \/\/ Bob glow\n                const grad = ctx.createRadialGradient(bx, by, 4, bx, by, 26);\n                grad.addColorStop(0, 'rgba(124, 92, 255, 0.55)');\n                grad.addColorStop(1, 'rgba(124, 92, 255, 0)');\n                ctx.beginPath();\n                ctx.arc(bx, by, 26, 0, Math.PI * 2);\n                ctx.fillStyle = grad;\n                ctx.fill();\n\n                \/\/ Bob\n                const bobGrad = ctx.createRadialGradient(bx - 5, by - 5, 2, bx, by, 16);\n                bobGrad.addColorStop(0, '#a78bfa');\n                bobGrad.addColorStop(1, '#5a3fd6');\n                ctx.beginPath();\n                ctx.arc(bx, by, 15, 0, Math.PI * 2);\n                ctx.fillStyle = bobGrad;\n                ctx.fill();\n                ctx.strokeStyle = 'rgba(255,255,255,0.25)';\n                ctx.lineWidth = 1;\n                ctx.stroke();\n\n                updateReadouts();\n            }\n\n            function updateReadouts() {\n                const degrees = (state.angle * 180) \/ Math.PI;\n                out.angle.textContent = degrees.toFixed(1) + '\u00b0';\n                out.period.textContent = estimatedPeriod().toFixed(2) + 's';\n                out.cycles.textContent = state.cycles || 0;\n                out.velocity.textContent = state.maxVelocity.toFixed(2);\n                out.freq.textContent = (1 \/ estimatedPeriod()).toFixed(2) + ' Hz';\n                out.time.textContent = state.elapsed.toFixed(1) + 's';\n\n                \/\/ Energy split (relative to max height at initial amplitude)\n                const theta0 = Math.abs(parseFloat(sliders.angle.value) * Math.PI \/ 180) || 0.001;\n                const h = state.lengthM * (1 - Math.cos(Math.abs(state.angle)));\n                const hMax = state.lengthM * (1 - Math.cos(theta0));\n                const peRatio = hMax > 0 ? Math.min(1, h \/ hMax) : 0.5;\n                const keRatio = 1 - peRatio;\n                bars.ke.style.width = (keRatio * 100).toFixed(1) + '%';\n                bars.pe.style.width = (peRatio * 100).toFixed(1) + '%';\n            }\n\n            draw();\n            requestAnimationFrame(step);\n        })();\n        <\/script>\n\n        \n[abertura_pendulo]<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-eeafcb6 elementor-hidden-mobile e-flex e-con-boxed wpr-particle-no wpr-jarallax-no wpr-parallax-no wpr-sticky-section-no e-con e-parent\" data-id=\"eeafcb6\" data-element_type=\"container\" data-e-type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-0e629fa elementor-widget elementor-widget-spacer\" data-id=\"0e629fa\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"spacer.default\">\n\t\t\t\t\t\t\t<div class=\"elementor-spacer\">\n\t\t\t<div class=\"elementor-spacer-inner\"><\/div>\n\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>[abertura_pendulo]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-2654","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/pages\/2654","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/comments?post=2654"}],"version-history":[{"count":19,"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/pages\/2654\/revisions"}],"predecessor-version":[{"id":2887,"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/pages\/2654\/revisions\/2887"}],"wp:attachment":[{"href":"https:\/\/app.digitalfreelancer.org\/index.php\/wp-json\/wp\/v2\/media?parent=2654"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}