{"id":8579,"date":"2026-08-12T11:34:41","date_gmt":"2026-08-12T14:34:41","guid":{"rendered":"https:\/\/fisica2.fica.unsl.edu.ar\/?page_id=8579"},"modified":"2026-08-21T10:08:35","modified_gmt":"2026-08-21T13:08:35","slug":"caida_libre","status":"publish","type":"page","link":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/","title":{"rendered":"caida_libre"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"8579\" class=\"elementor elementor-8579\">\n\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-0e73543 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"0e73543\" data-element_type=\"section\" data-e-type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-0c286f0\" data-id=\"0c286f0\" data-element_type=\"column\" data-e-type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-a8c0d06 elementor-widget elementor-widget-html\" data-id=\"a8c0d06\" data-element_type=\"widget\" data-e-type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\r\n<html lang=\"es\">\r\n<head>\r\n<meta charset=\"UTF-8\">\r\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n<title>FISAR_SIM \u2013 Ca\u00edda Libre | F\u00edsica 2 \u2013 UNSL<\/title>\r\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js@4.4.7\/dist\/chart.umd.min.js\"><\/script>\r\n<style>\r\n\/* ============================================\r\n   FISAR_SIM \u2013 Ca\u00edda Libre \u2013 Universidad Nacional de San Luis\r\n   Layout: CSS Grid con scroll vertical\r\n   Scoped under .fisar-cl for WordPress\/Elementor compatibility\r\n   ============================================ *\/\r\n.fisar-cl {\r\n  --bg: #f8f9fa; --surface: #ffffff; --border: #e0e4e8;\r\n  --text: #1a1a2e; --text2: #555770; --text3: #8b8da3;\r\n  --primary: #0d7377; --primary-light: #14a3a8; --primary-dark: #095456;\r\n  --accent: #e8871e;\r\n  --vel-color: #2e7d32; --acc-color: #c62828; --pos-color: #1565c0;\r\n  --cmp-color: #7b1fa2;\r\n  --radius: 10px; --radius-sm: 6px;\r\n  --shadow-sm: 0 1px 3px rgba(0,0,0,0.08);\r\n  --font: 'Segoe UI', system-ui, -apple-system, sans-serif;\r\n  --mono: 'SF Mono','Fira Code','Consolas', monospace;\r\n  min-height: 100%;\r\n  overflow-y: auto;\r\n  font-family: var(--font);\r\n  background: var(--bg);\r\n  color: var(--text);\r\n  font-size: 14px;\r\n  line-height: 1.5;\r\n}\r\n.fisar-cl, .fisar-cl *, .fisar-cl *::before, .fisar-cl *::after { margin: 0; padding: 0; box-sizing: border-box; }\r\n\r\n\/* === HEADER === *\/\r\n.fisar-cl .fl-header {\r\n  background: var(--surface);\r\n  border-bottom: 1px solid var(--border);\r\n  padding: 10px 24px;\r\n  display: flex;\r\n  align-items: center;\r\n  gap: 16px;\r\n  height: 54px;\r\n  flex-shrink: 0;\r\n}\r\n.fisar-cl .brand-top { font-size: 0.75rem; font-weight: 700; color: var(--primary); letter-spacing: 2px; text-transform: uppercase; }\r\n.fisar-cl .brand-sub { font-size: 0.7rem; color: var(--text3); }\r\n.fisar-cl .fl-header h1 { font-size: 1.1rem; font-weight: 600; }\r\n.fisar-cl .fl-header .subtitle { font-size: 0.78rem; color: var(--text2); font-weight: 400; }\r\n\r\n\/* === MAIN === *\/\r\n.fisar-cl .fl-main {\r\n  display: grid;\r\n  grid-template-rows: auto auto auto auto auto;\r\n  gap: 8px;\r\n  padding: 8px 10px;\r\n}\r\n\r\n\/* === TOP ROW === *\/\r\n.fisar-cl .top-row {\r\n  display: grid;\r\n  grid-template-columns: 220px 1fr 220px;\r\n  gap: 8px;\r\n  min-height: 0;\r\n}\r\n\r\n\/* === CARDS === *\/\r\n.fisar-cl .card {\r\n  background: var(--surface);\r\n  border: 1px solid var(--border);\r\n  border-radius: var(--radius);\r\n  box-shadow: var(--shadow-sm);\r\n  overflow: hidden;\r\n  display: flex;\r\n  flex-direction: column;\r\n}\r\n.fisar-cl .card-header {\r\n  padding: 6px 12px;\r\n  font-size: 0.75rem;\r\n  font-weight: 600;\r\n  color: var(--text2);\r\n  text-transform: uppercase;\r\n  letter-spacing: 0.5px;\r\n  border-bottom: 1px solid var(--border);\r\n  background: #fafbfc;\r\n  flex-shrink: 0;\r\n}\r\n.fisar-cl .card-body { padding: 10px 12px; flex: 1; min-height: 0; overflow-y: auto; }\r\n.fisar-cl .card-body::-webkit-scrollbar { width: 3px; }\r\n.fisar-cl .card-body::-webkit-scrollbar-thumb { background: #c8cbd0; border-radius: 2px; }\r\n\r\n\/* === SIDEBARS === *\/\r\n.fisar-cl .sidebar-left, .fisar-cl .sidebar-right {\r\n  display: flex;\r\n  flex-direction: column;\r\n  gap: 6px;\r\n}\r\n.fisar-cl .sidebar-left .card, .fisar-cl .sidebar-right .card {\r\n  flex: 1 1 auto;\r\n  min-height: 0;\r\n}\r\n\r\n\/* === PAR\u00c1METROS === *\/\r\n.fisar-cl .param-group { margin-bottom: 6px; }\r\n.fisar-cl .param-group:last-child { margin-bottom: 0; }\r\n.fisar-cl .param-row {\r\n  display: flex; align-items: center; justify-content: space-between; gap: 6px;\r\n}\r\n.fisar-cl .param-row .param-name {\r\n  font-size: 0.82rem; font-weight: 500; white-space: nowrap;\r\n}\r\n.fisar-cl .param-row .unit { font-size: 0.72rem; color: var(--text3); margin-left: 2px; }\r\n.fisar-cl .param-row input[type=\"number\"] {\r\n  width: 72px; padding: 3px 5px; border: 1px solid var(--border);\r\n  border-radius: var(--radius-sm); font-family: var(--mono); font-size: 0.82rem;\r\n  text-align: center; outline: none;\r\n}\r\n.fisar-cl .param-row input[type=\"number\"]:focus { border-color: var(--primary); }\r\n.fisar-cl .param-row input[readonly] {\r\n  background: #f0f1f3; color: var(--text2); cursor: default;\r\n}\r\n\r\n\/* === PRESETS === *\/\r\n.fisar-cl .preset-grid { display: flex; flex-wrap: wrap; gap: 4px; }\r\n.fisar-cl .preset-btn {\r\n  padding: 4px 10px; font-size: 0.72rem; border: 1px solid var(--border);\r\n  border-radius: var(--radius-sm); background: #fafbfc; cursor: pointer;\r\n  text-align: center; color: var(--text2); transition: all 0.15s; font-family: var(--font);\r\n}\r\n.fisar-cl .preset-btn:hover { background: var(--primary); color: #fff; border-color: var(--primary); }\r\n\r\n\/* === OPCIONES === *\/\r\n.fisar-cl .option-row { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; font-size: 0.78rem; }\r\n.fisar-cl .option-row input[type=\"checkbox\"] { accent-color: var(--primary); width: 14px; height: 14px; cursor: pointer; }\r\n.fisar-cl .opt-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; }\r\n\r\n\/* === CONTROLES === *\/\r\n.fisar-cl .controls-row { display: flex; gap: 6px; flex-wrap: wrap; }\r\n.fisar-cl .btn {\r\n  display: inline-flex; align-items: center; justify-content: center; gap: 5px;\r\n  padding: 6px 12px; border: 1px solid var(--border); border-radius: var(--radius-sm);\r\n  background: var(--surface); font-family: var(--font); font-size: 0.78rem;\r\n  font-weight: 500; cursor: pointer; color: var(--text); transition: all 0.15s;\r\n  white-space: nowrap; min-height: 32px;\r\n}\r\n.fisar-cl .btn:hover { background: #f0f1f3; border-color: #c8cbd0; }\r\n.fisar-cl .btn:active { transform: scale(0.97); }\r\n.fisar-cl .btn-primary { background: var(--primary); color: #fff; border-color: var(--primary); }\r\n.fisar-cl .btn-primary:hover { background: var(--primary-dark); }\r\n.fisar-cl .btn-sm { padding: 4px 9px; font-size: 0.72rem; min-height: 26px; }\r\n.fisar-cl .btn-icon { padding: 6px 10px; font-size: 1rem; }\r\n.fisar-cl .speed-selector {\r\n  display: flex; margin-left: auto; border: 1px solid var(--border);\r\n  border-radius: var(--radius-sm); overflow: hidden;\r\n}\r\n.fisar-cl .speed-btn {\r\n  padding: 5px 9px; border: none; background: var(--surface); font-family: var(--mono);\r\n  font-size: 0.7rem; cursor: pointer; color: var(--text2);\r\n  border-right: 1px solid var(--border); transition: all 0.15s;\r\n}\r\n.fisar-cl .speed-btn:last-child { border-right: none; }\r\n.fisar-cl .speed-btn:hover { background: #f0f1f3; }\r\n.fisar-cl .speed-btn.active { background: var(--primary); color: #fff; }\r\n\r\n\/* === CANVAS === *\/\r\n.fisar-cl .canvas-col {\r\n  display: flex;\r\n  flex-direction: column;\r\n  min-height: 0;\r\n  height: 100%;\r\n}\r\n.fisar-cl .time-display {\r\n  text-align: center; padding: 4px 0; font-family: var(--mono);\r\n  font-size: 1rem; font-weight: 600; color: var(--primary-dark);\r\n  background: var(--surface); border: 1px solid var(--border);\r\n  border-bottom: none; border-radius: var(--radius) var(--radius) 0 0;\r\n  flex-shrink: 0;\r\n}\r\n.fisar-cl .canvas-container {\r\n  flex: 1; min-height: 120px; position: relative;\r\n  background: var(--surface); border-left: 1px solid var(--border);\r\n  border-right: 1px solid var(--border);\r\n}\r\n.fisar-cl .canvas-container canvas { width: 100%; height: 100%; display: block; }\r\n.fisar-cl .controls-bar {\r\n  display: flex; align-items: center; gap: 5px; padding: 6px 10px;\r\n  background: var(--surface); border: 1px solid var(--border);\r\n  border-top: none; border-radius: 0 0 var(--radius) var(--radius);\r\n  flex-shrink: 0; flex-wrap: wrap;\r\n}\r\n\r\n\/* === VARIABLES EN TIEMPO REAL === *\/\r\n.fisar-cl .info-row {\r\n  display: flex; justify-content: space-between; align-items: center;\r\n  padding: 3px 0; border-bottom: 1px solid #f0f1f3;\r\n}\r\n.fisar-cl .info-row:last-child { border-bottom: none; }\r\n.fisar-cl .info-label { font-size: 0.78rem; font-weight: 500; color: var(--text2); }\r\n.fisar-cl .info-value { font-family: var(--mono); font-size: 0.8rem; font-weight: 600; }\r\n.fisar-cl .iv-t { color: var(--primary); }\r\n.fisar-cl .iv-y { color: var(--pos-color); }\r\n.fisar-cl .iv-v { color: var(--vel-color); }\r\n.fisar-cl .iv-g { color: var(--acc-color); }\r\n.fisar-cl .iv-dy { color: var(--accent); }\r\n\r\n\/* === EVENT BOX === *\/\r\n.fisar-cl .event-box {\r\n  margin-top: 8px; padding: 6px 8px; background: #fff8e1;\r\n  border-left: 3px solid var(--accent); border-radius: 0 var(--radius-sm) var(--radius-sm) 0;\r\n  font-size: 0.74rem; display: none;\r\n}\r\n.fisar-cl .event-box.visible { display: block; }\r\n.fisar-cl .event-title { font-weight: 600; color: #e65100; margin-bottom: 2px; }\r\n.fisar-cl .event-detail { color: var(--text2); font-family: var(--mono); font-size: 0.7rem; }\r\n\r\n\/* === MODELO MATEM\u00c1TICO === *\/\r\n.fisar-cl .math-panel {\r\n  padding: 8px 6px; background: #f7f8fa; border-radius: var(--radius-sm);\r\n  text-align: center; line-height: 1.8;\r\n}\r\n.fisar-cl .math-panel div { margin: 1px 0; }\r\n.fisar-cl .eq { font-family: 'Times New Roman', 'Georgia', serif; font-size: 1.05rem; }\r\n.fisar-cl .eq-var { font-style: italic; }\r\n.fisar-cl .eq-unit { font-size: 0.72rem; color: var(--text3); font-family: var(--font); font-style: normal; margin-left: 4px; }\r\n.fisar-cl .eq-sup { font-size: 0.72em; vertical-align: super; }\r\n.fisar-cl .eq-sub { font-size: 0.72em; vertical-align: sub; }\r\n.fisar-cl .eq-half { font-size: 0.82em; }\r\n\r\n\/* === OVERLAY MODAL === *\/\r\n.fisar-cl .overlay {\r\n  display: none; position: fixed; inset: 0; z-index: 900;\r\n  background: rgba(0,0,0,0.4); align-items: center; justify-content: center;\r\n}\r\n.fisar-cl .overlay.open { display: flex; }\r\n.fisar-cl .overlay-box {\r\n  background: var(--surface); border-radius: var(--radius); box-shadow: 0 8px 30px rgba(0,0,0,0.2);\r\n  max-width: 480px; width: 90%; max-height: 80vh; overflow-y: auto;\r\n}\r\n.fisar-cl .overlay-header {\r\n  padding: 10px 16px; border-bottom: 1px solid var(--border);\r\n  display: flex; justify-content: space-between; align-items: center;\r\n  font-size: 0.85rem; font-weight: 600; color: var(--text2); text-transform: uppercase; letter-spacing: 0.5px;\r\n}\r\n.fisar-cl .overlay-close {\r\n  background: none; border: none; font-size: 1.2rem; cursor: pointer;\r\n  color: var(--text3); padding: 2px 6px; border-radius: 4px;\r\n}\r\n.fisar-cl .overlay-close:hover { background: #f0f1f3; color: var(--text); }\r\n.fisar-cl .overlay-body { padding: 14px 16px; }\r\n\r\n\/* === PEDAG\u00d3GICO === *\/\r\n.fisar-cl .pedagogical-list { list-style: none; padding: 0; }\r\n.fisar-cl .pedagogical-list li {\r\n  padding: 4px 0 4px 14px; font-size: 0.75rem; color: var(--text2);\r\n  border-bottom: 1px solid #f0f1f3; position:relative; word-break:break-word; line-height:1.35;\r\n}\r\n.fisar-cl .pedagogical-list li:last-child { border-bottom: none; }\r\n.fisar-cl .pedagogical-list li::before {\r\n  content: '\u25cf'; color: var(--primary); font-size: 0.45rem;\r\n  position: absolute; left: 2px; top: 8px;\r\n}\r\n\r\n\/* === GRAPHS ROW === *\/\r\n.fisar-cl .graphs-row {\r\n  display: grid;\r\n  grid-template-columns: repeat(3, 1fr);\r\n  gap: 8px;\r\n}\r\n.fisar-cl .graph-card { min-height: 200px; overflow: hidden; }\r\n.fisar-cl .graph-card .card-body { position: relative; padding: 4px 6px 2px; }\r\n.fisar-cl .graph-card canvas { width: 100% !important; height: 100% !important; }\r\n\r\n\/* === EXTRAS ROW === *\/\r\n.fisar-cl .extras-row {\r\n  display: grid;\r\n  grid-template-columns: 1fr 1.2fr 0.8fr;\r\n  gap: 8px;\r\n}\r\n.fisar-cl .extras-row .card-body { overflow: visible; }\r\n\r\n\/* === BOTTOM ROW === *\/\r\n.fisar-cl .bottom-row {\r\n  display: grid;\r\n  grid-template-columns: 1fr 1fr;\r\n  gap: 8px;\r\n  flex-shrink: 0;\r\n}\r\n.fisar-cl .exp-controls { display: flex; gap: 5px; flex-wrap: wrap; margin-bottom: 6px; }\r\n.fisar-cl .data-table-wrap {\r\n  max-height: 80px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius-sm);\r\n}\r\n.fisar-cl .data-table-wrap table { width: 100%; border-collapse: collapse; font-size: 0.72rem; }\r\n.fisar-cl .data-table-wrap th, .fisar-cl .data-table-wrap td { padding: 3px 8px; text-align: center; border-bottom: 1px solid #f0f1f3; }\r\n.fisar-cl .data-table-wrap th { background: #fafbfc; font-weight: 600; color: var(--text2); position: sticky; top: 0; }\r\n.fisar-cl .empty-msg { padding: 10px; text-align: center; color: var(--text3); font-size: 0.75rem; }\r\n.fisar-cl .data-table-wrap::-webkit-scrollbar { width: 4px; }\r\n.fisar-cl .data-table-wrap::-webkit-scrollbar-thumb { background: #c8cbd0; border-radius: 2px; }\r\n\r\n\/* === FOOTER === *\/\r\n.fisar-cl .fl-footer {\r\n  text-align: center; padding: 8px 24px; font-size: 0.65rem; color: var(--text3);\r\n  border-top: 1px solid var(--border); background: var(--surface);\r\n}\r\n\r\n\/* === TOAST === *\/\r\n.fisar-cl .toast {\r\n  position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%) translateY(10px);\r\n  padding: 8px 16px; background: var(--text); color: #fff; border-radius: var(--radius-sm);\r\n  font-size: 0.8rem; opacity: 0; transition: all 0.3s; z-index: 1000; pointer-events: none;\r\n}\r\n.fisar-cl .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }\r\n\r\n\/* === RESPONSIVE === *\/\r\n@media (max-width: 1100px) {\r\n  .fisar-cl .top-row { grid-template-columns: 200px 1fr 200px; }\r\n}\r\n@media (max-width: 900px) {\r\n  .fisar-cl .fl-main { grid-template-rows: auto auto auto auto auto; }\r\n  .fisar-cl .top-row { grid-template-columns: 1fr; }\r\n  .fisar-cl .graphs-row { grid-template-columns: 1fr; }\r\n  .fisar-cl .extras-row { grid-template-columns: 1fr; }\r\n  .fisar-cl .bottom-row { grid-template-columns: 1fr; }\r\n}\r\n<\/style>\r\n<\/head>\r\n<body>\r\n<div class=\"fisar-cl\">\r\n\r\n<!-- =============== HEADER =============== -->\r\n<div class=\"fl-header\">\r\n  <div>\r\n    <span class=\"brand-top\"><a href=\"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/FISAR_SIM\/\"><img decoding=\"async\" src=\"https:\/\/fisica2.fica.unsl.edu.ar\/wp-content\/uploads\/2026\/08\/FISAR_SIM_2.png\r\n\" alt=\"FISAR_SIM\"  width=\"80\" o height=\"37\"><\/a><\/span><br> <span class=\"brand-sub\">FICA \u2013 Universidad Nacional de San Luis<\/span>\r\n\r\n  <\/div>\r\n  <div>\r\n    <h1>Ca\u00edda libre de los cuerpos<\/h1>\r\n    <span class=\"subtitle\">Simulaci\u00f3n interactiva sin rozamiento<\/span>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- =============== MAIN CONTENT =============== -->\r\n<div class=\"fl-main\">\r\n\r\n  <!-- ====== FILA SUPERIOR: Par\u00e1metros | Canvas | Variables ====== -->\r\n  <div class=\"top-row\">\r\n\r\n    <!-- COLUMNA IZQUIERDA -->\r\n    <div class=\"sidebar-left\">\r\n      <div class=\"card\">\r\n        <div class=\"card-header\">Par\u00e1metros<\/div>\r\n        <div class=\"card-body\">\r\n          <div class=\"param-group\">\r\n            <div class=\"param-row\">\r\n              <span><span class=\"param-name\">y\u2080<\/span><span class=\"unit\">m<\/span><\/span>\r\n              <input type=\"number\" id=\"input-y0\" min=\"0\" max=\"200\" step=\"1\" value=\"45\">\r\n            <\/div>\r\n          <\/div>\r\n          <div class=\"param-group\">\r\n            <div class=\"param-row\">\r\n              <span><span class=\"param-name\">v\u2080<\/span><span class=\"unit\">m\/s<\/span><\/span>\r\n              <input type=\"number\" id=\"input-v0\" min=\"-50\" max=\"50\" step=\"0.5\" value=\"0\">\r\n            <\/div>\r\n          <\/div>\r\n          <div class=\"param-group\">\r\n            <div class=\"param-row\">\r\n              <span><span class=\"param-name\">g<\/span><span class=\"unit\">m\/s\u00b2<\/span><\/span>\r\n              <input type=\"number\" id=\"input-g\" min=\"-20\" max=\"-0.1\" step=\"0.1\" value=\"-9.8\">\r\n            <\/div>\r\n          <\/div>\r\n          <div class=\"param-group\">\r\n            <div class=\"param-row\">\r\n              <span><span class=\"param-name\">t m\u00e1ximo<\/span><span class=\"unit\">s<\/span><\/span>\r\n              <input type=\"number\" id=\"input-tmax\" min=\"0.5\" max=\"20\" step=\"0.1\" value=\"3.03\">\r\n            <\/div>\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <!-- COLUMNA CENTRAL: Canvas + Controles -->\r\n    <div class=\"canvas-col\">\r\n      <div class=\"time-display\" id=\"time-display\">t = 0.00 s<\/div>\r\n      <div class=\"canvas-container\">\r\n        <canvas id=\"simCanvas\"><\/canvas>\r\n      <\/div>\r\n      <div class=\"controls-bar\">\r\n        <div class=\"controls-row\">\r\n          <button class=\"btn btn-primary btn-icon\" id=\"btn-play\" title=\"Iniciar (Espacio)\">\u25b6<\/button>\r\n          <button class=\"btn btn-icon\" id=\"btn-pause\" title=\"Pausar\">\u23f8<\/button>\r\n          <button class=\"btn btn-icon\" id=\"btn-reset\" title=\"Reiniciar (R)\">\u21bb<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-step\" title=\"Paso a paso (\u2192)\">Paso \u2192<\/button>\r\n        <\/div>\r\n        <span style=\"font-size:0.75rem;color:var(--text3);margin-left:6px;\">Velocidad:<\/span>\r\n        <div class=\"speed-selector\">\r\n          <button class=\"speed-btn\" data-speed=\"0.25\">0.25\u00d7<\/button>\r\n          <button class=\"speed-btn\" data-speed=\"0.5\">0.5\u00d7<\/button>\r\n          <button class=\"speed-btn active\" data-speed=\"1\">1\u00d7<\/button>\r\n          <button class=\"speed-btn\" data-speed=\"2\">2\u00d7<\/button>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n\r\n    <!-- COLUMNA DERECHA -->\r\n    <div class=\"sidebar-right\">\r\n      <div class=\"card\">\r\n        <div class=\"card-header\">Variables en tiempo real<\/div>\r\n        <div class=\"card-body\">\r\n          <div class=\"info-row\"><span class=\"info-label\">Tiempo (t)<\/span><span class=\"info-value iv-t\" id=\"info-t\">0.00 s<\/span><\/div>\r\n          <div class=\"info-row\"><span class=\"info-label\">Altura (y)<\/span><span class=\"info-value iv-y\" id=\"info-y\">45.00 m<\/span><\/div>\r\n          <div class=\"info-row\"><span class=\"info-label\">Velocidad (v)<\/span><span class=\"info-value iv-v\" id=\"info-v\">0.00 m\/s<\/span><\/div>\r\n          <div class=\"info-row\"><span class=\"info-label\">Gravedad (g)<\/span><span class=\"info-value iv-g\" id=\"info-g\">-9.80 m\/s\u00b2<\/span><\/div>\r\n          <div class=\"info-row\"><span class=\"info-label\">Desplazamiento (\u0394y)<\/span><span class=\"info-value iv-dy\" id=\"info-dy\">0.00 m<\/span><\/div>\r\n          <div class=\"event-box\" id=\"event-box\">\r\n            <div class=\"event-title\" id=\"event-title\"><\/div>\r\n            <div class=\"event-detail\" id=\"event-detail\"><\/div>\r\n          <\/div>\r\n        <\/div>\r\n      <\/div>\r\n      <div class=\"card\">\r\n        <div class=\"card-header\" style=\"cursor:pointer;\" id=\"ped-header\">\r\n          \u00bfQu\u00e9 podemos observar? <span style=\"float:right;font-size:0.7rem;\">\u2192<\/span>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ====== FILA GR\u00c1FICAS ====== -->\r\n  <div class=\"graphs-row\">\r\n    <div class=\"card graph-card\">\r\n      <div class=\"card-header\">Altura vs. Tiempo<\/div>\r\n      <div class=\"card-body\"><canvas id=\"chartY\"><\/canvas><\/div>\r\n    <\/div>\r\n    <div class=\"card graph-card\">\r\n      <div class=\"card-header\">Velocidad vs. Tiempo<\/div>\r\n      <div class=\"card-body\"><canvas id=\"chartV\"><\/canvas><\/div>\r\n    <\/div>\r\n    <div class=\"card graph-card\">\r\n      <div class=\"card-header\">Aceleraci\u00f3n vs. Tiempo<\/div>\r\n      <div class=\"card-body\"><canvas id=\"chartG\"><\/canvas><\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ====== FILA EXTRAS: Presets | Modelo | Opciones ====== -->\r\n  <div class=\"extras-row\">\r\n    <div class=\"card\">\r\n      <div class=\"card-header\">Experimentos predefinidos<\/div>\r\n      <div class=\"card-body\">\r\n        <div class=\"preset-grid\">\r\n          <button class=\"preset-btn\" data-preset=\"dejar-caer\">Dejar caer<\/button>\r\n          <button class=\"preset-btn\" data-preset=\"tirar-arriba\">Tirar hacia arriba<\/button>\r\n          <button class=\"preset-btn\" data-preset=\"tirar-vuelve\">Tirar arriba y vuelve<\/button>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"card\">\r\n      <div class=\"card-header\">Modelo matem\u00e1tico<\/div>\r\n      <div class=\"card-body\">\r\n        <div class=\"math-panel\" id=\"math-panel\">\r\n          <div id=\"eq-y\"><\/div>\r\n          <div id=\"eq-v\"><\/div>\r\n          <div id=\"eq-g\"><\/div>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"card\">\r\n      <div class=\"card-header\">Opciones de visualizaci\u00f3n<\/div>\r\n      <div class=\"card-body\">\r\n        <label class=\"option-row\"><input type=\"checkbox\" id=\"chk-vel\" checked><span class=\"opt-dot\" style=\"background:var(--vel-color)\"><\/span>Vector velocidad<\/label>\r\n        <label class=\"option-row\"><input type=\"checkbox\" id=\"chk-acc\" checked><span class=\"opt-dot\" style=\"background:var(--acc-color)\"><\/span>Vector gravedad<\/label>\r\n        <label class=\"option-row\"><input type=\"checkbox\" id=\"chk-trail\" checked><span>Estela del objeto<\/span><\/label>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n  <!-- ====== FILA INFERIOR: Experimento + Comparaci\u00f3n ====== -->\r\n  <div class=\"bottom-row\">\r\n    <div class=\"card\">\r\n      <div class=\"card-header\">Modo experimento<\/div>\r\n      <div class=\"card-body\">\r\n        <div class=\"exp-controls\">\r\n          <button class=\"btn btn-sm btn-primary\" id=\"btn-register\">Registrar dato<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-delete-last\">Eliminar \u00faltimo<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-clear-data\">Limpiar tabla<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-export-csv\">Exportar CSV<\/button>\r\n        <\/div>\r\n        <div class=\"data-table-wrap\" id=\"data-table-wrap\">\r\n          <div class=\"empty-msg\" id=\"empty-msg\">No hay datos registrados.<\/div>\r\n          <table id=\"data-table\" style=\"display:none;\">\r\n            <thead><tr><th>t (s)<\/th><th>y (m)<\/th><th>v (m\/s)<\/th><th>g (m\/s\u00b2)<\/th><\/tr><\/thead>\r\n            <tbody id=\"data-tbody\"><\/tbody>\r\n          <\/table>\r\n        <\/div>\r\n      <\/div>\r\n    <\/div>\r\n    <div class=\"card\">\r\n      <div class=\"card-header\">Comparaci\u00f3n de movimientos<\/div>\r\n      <div class=\"card-body\">\r\n        <p style=\"font-size:0.76rem;color:var(--text2);margin-bottom:8px;\">Guard\u00e1 la configuraci\u00f3n actual y luego modific\u00e1 los par\u00e1metros para comparar.<\/p>\r\n        <div style=\"display:flex;gap:5px;flex-wrap:wrap;\">\r\n          <button class=\"btn btn-sm\" id=\"btn-save-a\" style=\"border-color:var(--pos-color);color:var(--pos-color);\">Guardar como Exp. A<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-save-b\" style=\"border-color:var(--cmp-color);color:var(--cmp-color);\">Guardar como Exp. B<\/button>\r\n          <button class=\"btn btn-sm\" id=\"btn-clear-cmp\">Limpiar comparaci\u00f3n<\/button>\r\n        <\/div>\r\n        <div id=\"cmp-display\" style=\"margin-top:8px;\"><\/div>\r\n      <\/div>\r\n    <\/div>\r\n  <\/div>\r\n\r\n<\/div>\r\n\r\n<!-- =============== OVERLAY =============== -->\r\n<div class=\"overlay\" id=\"ped-overlay\">\r\n  <div class=\"overlay-box\">\r\n    <div class=\"overlay-header\">\r\n      \u00bfQu\u00e9 podemos observar?\r\n      <button class=\"overlay-close\" id=\"ped-close\">\u2715<\/button>\r\n    <\/div>\r\n    <div class=\"overlay-body\">\r\n      <ul class=\"pedagogical-list\">\r\n        <li>En la ca\u00edda libre <b>sin rozamiento<\/b>, la \u00fanica fuerza es el peso, y la aceleraci\u00f3n es constante e igual a <b>g \u2248 \u22129.8 m\/s\u00b2<\/b>.<\/li>\r\n        <li>La gr\u00e1fica <span class=\"eq-var\">y<\/span>(<span class=\"eq-var\">t<\/span>) es una <b>par\u00e1bola<\/b>, ya que el movimiento es uniformemente acelerado en direcci\u00f3n vertical.<\/li>\r\n        <li>La gr\u00e1fica <span class=\"eq-var\">v<\/span>(<span class=\"eq-var\">t<\/span>) es una <b>recta<\/b> cuya pendiente es <b>g<\/b>.<\/li>\r\n        <li>La gr\u00e1fica <span class=\"eq-var\">g<\/span>(<span class=\"eq-var\">t<\/span>) es una <b>recta horizontal<\/b> (aceleraci\u00f3n constante).<\/li>\r\n        <li>Cuando <span class=\"eq-var\">v<\/span> = 0, el cuerpo alcanza su <b>altura m\u00e1xima<\/b>.<\/li>\r\n        <li>El <b>tiempo de subida<\/b> es igual al <b>tiempo de bajada<\/b> hasta el mismo punto (si no hay rozamiento).<\/li>\r\n        <li>La <b>velocidad<\/b> al pasar por un punto es la misma en magnitud al subir que al bajar, pero con signo opuesto.<\/li>\r\n        <li>El <b>\u00e1rea bajo la curva<\/b> <span class=\"eq-var\">v<\/span>(<span class=\"eq-var\">t<\/span>) representa el <b>desplazamiento vertical<\/b>.<\/li>\r\n        <li>La ca\u00edda libre es un caso particular del MRUA con <span class=\"eq-var\">a<\/span> = <span class=\"eq-var\">g<\/span> constante.<\/li>\r\n      <\/ul>\r\n    <\/div>\r\n  <\/div>\r\n<\/div>\r\n\r\n<!-- =============== FOOTER =============== -->\r\n<div class=\"fl-footer\">\r\n  FISAR_SIM \u2013 F\u00edsica 2 \u2013 Universidad Nacional de San Luis<br>\r\n  Simulaci\u00f3n educativa de Ca\u00edda Libre sin rozamiento\r\n<\/div>\r\n\r\n<!-- Toast -->\r\n<div class=\"toast\" id=\"toast\"><\/div>\r\n<\/div>\r\n\r\n<!-- =============== JAVASCRIPT =============== -->\r\n<script>\r\n\/\/ =====================================================================\r\n\/\/ FISAR_SIM \u2013 Ca\u00edda Libre Simulator\r\n\/\/ Universidad Nacional de San Luis \u2013 F\u00edsica 2\r\n\/\/ =====================================================================\r\n\r\n\/\/ ===== CONFIGURACI\u00d3N GENERAL =====\r\nvar CFG = {\r\n  dt: 0.04,\r\n  stepSize: 0.05,\r\n  trailLen: 40,\r\n  vecScaleV: 6,\r\n  vecScaleG: 4,\r\n  ballR: 13,\r\n  margin: { l: 50, r: 30, t: 30, b: 35 },\r\n  col: {\r\n    grid: '#f0f1f3',\r\n    axis: '#9ca3af',\r\n    ground: '#8B6914',\r\n    groundFill: '#c8b88a',\r\n    skyTop: '#e8f0fe',\r\n    skyBot: '#fafbfc',\r\n    pos: '#1565c0',\r\n    vel: '#2e7d32',\r\n    acc: '#c62828',\r\n    ball: '#0d7377',\r\n    ballHi: '#14a3a8',\r\n    ballDk: '#095456',\r\n    y0Mark: '#e8871e',\r\n    maxMark: '#d32f2f',\r\n    cmp: '#7b1fa2',\r\n    trail: 'rgba(13,115,119,0.25)'\r\n  }\r\n};\r\n\r\n\/\/ ===== PRESETS =====\r\nvar PRESETS = {\r\n  'dejar-caer': function() {\r\n    \/\/ Ca\u00edda desde reposo: y\u2080=45m, v\u2080=0, cae hasta y=0\r\n    var y0 = 45, v0 = 0, g = -9.8;\r\n    var tMax = Math.sqrt(2 * y0 \/ Math.abs(g));\r\n    tMax = Math.ceil(tMax * 20) \/ 20; \/\/ redondear hacia arriba a 0.05\r\n    return { y0: y0, v0: v0, g: g, tMax: tMax };\r\n  },\r\n  'tirar-arriba': function() {\r\n    \/\/ Tirar hacia arriba, termina en altura m\u00e1xima (v=0)\r\n    var y0 = 0, v0 = 20, g = -9.8;\r\n    var tMax = v0 \/ Math.abs(g);\r\n    tMax = Math.ceil(tMax * 20) \/ 20;\r\n    return { y0: y0, v0: v0, g: g, tMax: tMax };\r\n  },\r\n  'tirar-vuelve': function() {\r\n    \/\/ Tirar hacia arriba y vuelve a la posici\u00f3n inicial\r\n    var y0 = 0, v0 = 20, g = -9.8;\r\n    var tMax = 2 * v0 \/ Math.abs(g);\r\n    tMax = Math.ceil(tMax * 20) \/ 20;\r\n    return { y0: y0, v0: v0, g: g, tMax: tMax };\r\n  }\r\n};\r\n\r\n\/\/ ===== ESTADO DE LA SIMULACI\u00d3N =====\r\nvar sim = {\r\n  y0: 45, v0: 0, g: -9.8, tMax: 3.05,\r\n  t: 0, playing: false, speed: 1,\r\n  showVel: true, showAcc: true, showTrail: true,\r\n  lastTs: null, finished: false\r\n};\r\n\r\n\/\/ Datos de gr\u00e1ficas\r\nvar gData = { t: [], y: [], v: [], g: [] };\r\nvar lastGT = -1;\r\n\r\n\/\/ Datos del modo experimento\r\nvar expData = [];\r\n\r\n\/\/ Estado de comparaci\u00f3n\r\nvar cmp = { a: null, b: null, showA: false, showB: false };\r\n\r\n\/\/ Referencias al canvas y charts\r\nvar canvas, ctx, cW, cH, dpr;\r\nvar chartY, chartV, chartG;\r\n\r\n\/\/ =====================================================================\r\n\/\/ F\u00cdSICA \u2013 Ecuaciones de Ca\u00edda Libre\r\n\/\/ =====================================================================\r\n\r\n\/** Posici\u00f3n: y(t) = y\u2080 + v\u2080\u00b7t + \u00bd\u00b7g\u00b7t\u00b2 *\/\r\nfunction calcY(t, y0, v0, g) {\r\n  return y0 + v0 * t + 0.5 * g * t * t;\r\n}\r\n\r\n\/** Velocidad: v(t) = v\u2080 + g\u00b7t *\/\r\nfunction calcV(t, v0, g) {\r\n  return v0 + g * t;\r\n}\r\n\r\n\/** Rango \u00f3ptimo de y para los ejes *\/\r\nfunction yRange(y0, v0, g, tM) {\r\n  var yS = y0, yE = calcY(tM, y0, v0, g);\r\n  \/\/ Clamp yE a 0 m\u00ednimo (el cuerpo no baja del suelo)\r\n  yE = Math.max(yE, 0);\r\n  var mn = Math.min(yS, yE), mx = Math.max(yS, yE);\r\n  \/\/ Punto donde v=0 (altura m\u00e1xima)\r\n  var tv = -v0 \/ g;\r\n  if (tv > 0 && tv < tM) {\r\n    var yv = calcY(tv, y0, v0, g);\r\n    mn = Math.min(mn, yv);\r\n    mx = Math.max(mx, yv);\r\n  }\r\n  \/\/ Solo incluir y=0 si el cuerpo llega al suelo\r\n  var tGH = findGroundHit(y0, v0, g, tM);\r\n  if (tGH !== null) {\r\n    mn = Math.min(mn, 0);\r\n  }\r\n  var rng = mx - mn;\r\n  var pad = Math.max(rng * 0.12, 3);\r\n  return { min: mn - pad, max: mx + pad };\r\n}\r\n\r\n\/** Rango \u00f3ptimo de v *\/\r\nfunction vRange(v0, g, tM) {\r\n  var mn = Math.min(v0, calcV(tM, v0, g));\r\n  var mx = Math.max(v0, calcV(tM, v0, g));\r\n  var pad = Math.max((mx - mn) * 0.15, 2);\r\n  return { min: mn - pad, max: mx + pad };\r\n}\r\n\r\n\/** Tiempo donde v = 0 (altura m\u00e1xima) *\/\r\nfunction findMaxHeight(v0, g, tM) {\r\n  if (g === 0) return null;\r\n  var ts = -v0 \/ g;\r\n  return (ts > 0.001 && ts < tM - 0.001) ? ts : null;\r\n}\r\n\r\n\/** Tiempo donde y = 0 (impacto con el suelo) *\/\r\nfunction findGroundHit(y0, v0, g, tM) {\r\n  if (g === 0) {\r\n    \/\/ Si no hay gravedad y v0 != 0, calcular t = -y0\/v0\r\n    if (v0 < 0 && y0 > 0) { var t = -y0 \/ v0; return (t > 0.001 && t < tM) ? t : null; }\r\n    return null;\r\n  }\r\n  \/\/ y\u2080 + v\u2080\u00b7t + \u00bd\u00b7g\u00b7t\u00b2 = 0  \u2192  \u00bd\u00b7g\u00b7t\u00b2 + v\u2080\u00b7t + y\u2080 = 0\r\n  var a = 0.5 * g;\r\n  var b = v0;\r\n  var c = y0;\r\n  var disc = b * b - 4 * a * c;\r\n  if (disc < 0) return null;\r\n  var sqrtDisc = Math.sqrt(disc);\r\n  var t1 = (-b + sqrtDisc) \/ (2 * a);\r\n  var t2 = (-b - sqrtDisc) \/ (2 * a);\r\n  \/\/ Queremos la ra\u00edz positiva m\u00e1s peque\u00f1a (despu\u00e9s de t=0)\r\n  var candidates = [];\r\n  if (t1 > 0.01) candidates.push(t1);\r\n  if (t2 > 0.01) candidates.push(t2);\r\n  if (candidates.length === 0) return null;\r\n  var tHit = Math.min.apply(null, candidates);\r\n  return (tHit < tM) ? tHit : null;\r\n}\r\n\r\n\/** Calcula tMax autom\u00e1tico basado en los par\u00e1metros de ca\u00edda libre *\/\r\nfunction autoTMax(y0, v0, g) {\r\n  \/\/ Si el cuerpo llega al suelo (y=0), usar ese tiempo + margen\r\n  var tGH = findGroundHit(y0, v0, g, 30);\r\n  if (tGH !== null) {\r\n    return Math.ceil((tGH + 0.15) * 20) \/ 20;\r\n  }\r\n  \/\/ Si v0 > 0 y g < 0 (tiro vertical hacia arriba, no llega al suelo en rango)\r\n  if (v0 > 0 && g < 0) {\r\n    var tUp = v0 \/ Math.abs(g); \/\/ tiempo hasta v=0\r\n    var yMax = calcY(tUp, y0, v0, g);\r\n    \/\/ Verificar si desde la altura m\u00e1xima vuelve al suelo\r\n    var tGH2 = findGroundHit(yMax, 0, g, 30);\r\n    if (tGH2 !== null) {\r\n      return Math.ceil((tUp + tGH2 + 0.15) * 20) \/ 20;\r\n    }\r\n    \/\/ Si no llega al suelo, mostrar hasta que vuelve a la altura inicial\r\n    return Math.ceil((2 * tUp + 0.3) * 20) \/ 20;\r\n  }\r\n  \/\/ v0 = 0, ca\u00edda desde reposo\r\n  if (y0 > 0 && Math.abs(g) > 0) {\r\n    var tFall = Math.sqrt(2 * y0 \/ Math.abs(g));\r\n    return Math.ceil((tFall + 0.15) * 20) \/ 20;\r\n  }\r\n  \/\/ Fallback\r\n  return 5;\r\n}\r\n\r\n\/** Tiempo de impacto con el suelo para los par\u00e1metros actuales (sin l\u00edmite tM) *\/\r\nfunction groundHitTime() {\r\n  return findGroundHit(sim.y0, sim.v0, sim.g, 30);\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ UTILIDADES\r\n\/\/ =====================================================================\r\n\r\nfunction fmt(n, d) { d = d || 2; return Number(n).toFixed(d); }\r\n\r\nvar toastTO;\r\nfunction showToast(msg) {\r\n  var el = document.getElementById('toast');\r\n  el.textContent = msg;\r\n  el.classList.add('show');\r\n  clearTimeout(toastTO);\r\n  toastTO = setTimeout(function() { el.classList.remove('show'); }, 2200);\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ CANVAS \u2013 Dibujo de la escena vertical\r\n\/\/ =====================================================================\r\n\r\nfunction initCanvas() {\r\n  canvas = document.getElementById('simCanvas');\r\n  ctx = canvas.getContext('2d');\r\n  resizeCanvas();\r\n}\r\n\r\nfunction resizeCanvas() {\r\n  var r = canvas.parentElement.getBoundingClientRect();\r\n  dpr = window.devicePixelRatio || 1;\r\n  canvas.width = r.width * dpr;\r\n  canvas.height = r.height * dpr;\r\n  canvas.style.width = r.width + 'px';\r\n  canvas.style.height = r.height + 'px';\r\n  cW = r.width;\r\n  cH = r.height;\r\n  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);\r\n}\r\n\r\n\/** Mapea altura f\u00edsica y a coordenada canvas (vertical invertida) *\/\r\nfunction getDraw() {\r\n  var m = CFG.margin;\r\n  var dW = cW - m.l - m.r;\r\n  var dH = cH - m.t - m.b;\r\n  var yR = yRange(sim.y0, sim.v0, sim.g, sim.tMax);\r\n  var ySp = yR.max - yR.min;\r\n  \/\/ Centro horizontal de la escena\r\n  var sceneX = m.l + dW * 0.45;\r\n  \/\/ Funci\u00f3n de mapeo: y f\u00edsico \u2192 canvas Y (invertido)\r\n  function yPx(v) { return m.t + (yR.max - v) \/ ySp * dH; }\r\n  return { m: m, dW: dW, dH: dH, yR: yR, ySp: ySp, sceneX: sceneX, yPx: yPx };\r\n}\r\n\r\n\/** Dibuja toda la escena *\/\r\nfunction drawScene() {\r\n  ctx.clearRect(0, 0, cW, cH);\r\n  var dp = getDraw();\r\n  var m = dp.m, yPx = dp.yPx, yR = dp.yR, sceneX = dp.sceneX;\r\n  var font = getComputedStyle(document.body).fontFamily;\r\n  var tL = m.l - 10, tR = cW - m.r + 10;\r\n  var tT = m.t - 5, tB = cH - m.b + 5;\r\n\r\n  \/\/ Fondo con gradiente de cielo\r\n  var skyGrad = ctx.createLinearGradient(0, m.t, 0, cH - m.b);\r\n  skyGrad.addColorStop(0, CFG.col.skyTop);\r\n  skyGrad.addColorStop(1, CFG.col.skyBot);\r\n  ctx.fillStyle = skyGrad;\r\n  ctx.fillRect(0, 0, cW, cH);\r\n\r\n  \/\/ Suelo (y=0)\r\n  var groundY = yPx(0);\r\n  if (groundY >= m.t && groundY <= cH - m.b) {\r\n    \/\/ \u00c1rea de suelo\r\n    ctx.fillStyle = CFG.col.groundFill;\r\n    ctx.fillRect(tL, groundY, tR - tL, tB - groundY);\r\n    \/\/ L\u00ednea de suelo\r\n    ctx.strokeStyle = CFG.col.ground;\r\n    ctx.lineWidth = 2.5;\r\n    ctx.beginPath(); ctx.moveTo(tL, groundY); ctx.lineTo(tR, groundY); ctx.stroke();\r\n    \/\/ Etiqueta\r\n    ctx.fillStyle = CFG.col.ground;\r\n    ctx.font = 'bold 10px ' + font;\r\n    ctx.textAlign = 'left';\r\n    ctx.fillText('y = 0 (suelo)', tR - 80, groundY + 14);\r\n    \/\/ Patr\u00f3n de suelo (rayas)\r\n    ctx.strokeStyle = 'rgba(139,105,20,0.2)';\r\n    ctx.lineWidth = 1;\r\n    for (var sx = tL + 8; sx < tR; sx += 12) {\r\n      ctx.beginPath();\r\n      ctx.moveTo(sx, groundY + 3);\r\n      ctx.lineTo(sx - 8, groundY + 14);\r\n      ctx.stroke();\r\n    }\r\n  }\r\n\r\n  \/\/ L\u00edneas de grilla horizontales (alturas)\r\n  var ySp = dp.ySp;\r\n  var step = 1;\r\n  if (ySp > 200) step = 50;\r\n  else if (ySp > 100) step = 20;\r\n  else if (ySp > 50) step = 10;\r\n  else if (ySp > 20) step = 5;\r\n  else if (ySp > 10) step = 2;\r\n\r\n  var st = Math.ceil(yR.min \/ step) * step;\r\n  ctx.font = '10px ' + font;\r\n  ctx.textAlign = 'right';\r\n  for (var val = st; val <= yR.max; val += step) {\r\n    var py = yPx(val);\r\n    if (py < m.t - 5 || py > cH - m.b + 5) continue;\r\n    \/\/ Grid line\r\n    ctx.strokeStyle = CFG.col.grid; ctx.lineWidth = 1;\r\n    ctx.beginPath(); ctx.moveTo(m.l, py); ctx.lineTo(tR, py); ctx.stroke();\r\n    \/\/ Tick en eje\r\n    ctx.strokeStyle = '#9ca3af';\r\n    ctx.beginPath(); ctx.moveTo(m.l - 4, py); ctx.lineTo(m.l + 4, py); ctx.stroke();\r\n    \/\/ Etiqueta\r\n    ctx.fillStyle = '#6b7280';\r\n    ctx.fillText(fmt(val, step < 1 ? 1 : 0), m.l - 8, py + 3);\r\n  }\r\n\r\n  \/\/ Eje vertical (y)\r\n  ctx.strokeStyle = CFG.col.axis; ctx.lineWidth = 1.5;\r\n  ctx.beginPath(); ctx.moveTo(sceneX, tT); ctx.lineTo(sceneX, tB); ctx.stroke();\r\n  \/\/ Flecha arriba\r\n  ctx.fillStyle = CFG.col.axis;\r\n  ctx.beginPath(); ctx.moveTo(sceneX, tT); ctx.lineTo(sceneX - 5, tT + 10); ctx.lineTo(sceneX + 5, tT + 10); ctx.closePath(); ctx.fill();\r\n  ctx.font = '11px ' + font; ctx.textAlign = 'center';\r\n  ctx.fillText('y', sceneX, tT - 2);\r\n  ctx.textAlign = 'start';\r\n\r\n  \/\/ Marca y\u2080\r\n  var y0Px = yPx(sim.y0);\r\n  if (y0Px >= m.t && y0Px <= cH - m.b) {\r\n    ctx.strokeStyle = CFG.col.y0Mark; ctx.lineWidth = 1.5; ctx.setLineDash([5, 4]);\r\n    ctx.beginPath(); ctx.moveTo(sceneX - 20, y0Px); ctx.lineTo(sceneX + 60, y0Px); ctx.stroke();\r\n    ctx.setLineDash([]);\r\n    ctx.fillStyle = CFG.col.y0Mark;\r\n    ctx.font = 'bold 10px ' + font;\r\n    ctx.textAlign = 'left';\r\n    ctx.fillText('y\\u2080=' + fmt(sim.y0, 1) + ' m', sceneX + 65, y0Px + 4);\r\n    ctx.textAlign = 'start';\r\n  }\r\n\r\n  \/\/ Marca altura m\u00e1xima (si se alcanz\u00f3)\r\n  var tMaxH = findMaxHeight(sim.v0, sim.g, sim.tMax);\r\n  if (tMaxH !== null && sim.t >= tMaxH) {\r\n    var yMaxH = calcY(tMaxH, sim.y0, sim.v0, sim.g);\r\n    var yMHPx = yPx(yMaxH);\r\n    if (yMHPx >= m.t && yMHPx <= cH - m.b) {\r\n      ctx.strokeStyle = CFG.col.maxMark; ctx.lineWidth = 1.5; ctx.setLineDash([4, 3]);\r\n      ctx.beginPath(); ctx.moveTo(sceneX - 15, yMHPx); ctx.lineTo(sceneX + 55, yMHPx); ctx.stroke();\r\n      ctx.setLineDash([]);\r\n      ctx.fillStyle = CFG.col.maxMark;\r\n      ctx.font = 'bold 10px ' + font;\r\n      ctx.textAlign = 'left';\r\n      ctx.fillText('y\\u2098\\u2090\\u2093=' + fmt(yMaxH, 1) + ' m', sceneX + 60, yMHPx + 4);\r\n      ctx.textAlign = 'start';\r\n    }\r\n  }\r\n\r\n  \/\/ Objetos de comparaci\u00f3n (Exp A y B)\r\n  if (cmp.showA && cmp.a) {\r\n    var yA = Math.max(0, calcY(sim.t, cmp.a.y0, cmp.a.v0, cmp.a.g));\r\n    drawBall(sceneX + 35, yPx(yA), true);\r\n  }\r\n  if (cmp.showB && cmp.b) {\r\n    var yB = Math.max(0, calcY(sim.t, cmp.b.y0, cmp.b.v0, cmp.b.g));\r\n    drawBall(sceneX + 50, yPx(yB), true);\r\n  }\r\n\r\n  \/\/ Estela del objeto\r\n  if (sim.showTrail && gData.t.length > 1) {\r\n    var tc = Math.min(gData.t.length, CFG.trailLen);\r\n    var si = gData.t.length - tc;\r\n    for (var i = si; i < gData.t.length; i++) {\r\n      var al = ((i - si) \/ tc) * 0.4;\r\n      var px = yPx(gData.y[i]);\r\n      if (px >= m.t && px <= cH - m.b) {\r\n        ctx.fillStyle = 'rgba(13,115,119,' + al + ')';\r\n        ctx.beginPath(); ctx.arc(sceneX, px, 2.5, 0, Math.PI * 2); ctx.fill();\r\n      }\r\n    }\r\n  }\r\n\r\n  \/\/ Objeto principal (bloque de masa)\r\n  var curYRaw = calcY(sim.t, sim.y0, sim.v0, sim.g);\r\n  var curY = Math.max(0, curYRaw);\r\n  var curV = calcV(sim.t, sim.v0, sim.g);\r\n  var ballPx = yPx(curY);\r\n  drawBall(sceneX, ballPx, false);\r\n\r\n  \/\/ Vectores de velocidad y gravedad (desplazados a la derecha)\r\n  var vecX = sceneX + 28;\r\n  if (sim.showVel) {\r\n    \/\/ En canvas, arriba es negativo. v>0 \u2192 arriba \u2192 vector hacia arriba (negativo)\r\n    var vLen = curV * CFG.vecScaleV;\r\n    drawVecV(ctx, vecX, ballPx, -vLen, CFG.col.vel, 'v');\r\n  }\r\n  if (sim.showAcc) {\r\n    \/\/ g es negativo \u2192 hacia abajo \u2192 vector positivo en canvas\r\n    var gLen = -sim.g * CFG.vecScaleG;\r\n    drawVecV(ctx, vecX + 22, ballPx, gLen, CFG.col.acc, 'g');\r\n  }\r\n\r\n  \/\/ Indicador de direcci\u00f3n de movimiento\r\n  if (Math.abs(curV) > 0.1) {\r\n    var dirText = curV > 0 ? '\\u2191 Subiendo' : '\\u2193 Bajando';\r\n    var dirCol = curV > 0 ? '#2e7d32' : '#c62828';\r\n    ctx.fillStyle = dirCol;\r\n    ctx.font = 'bold 10px ' + font;\r\n    ctx.textAlign = 'left';\r\n    ctx.fillText(dirText, sceneX - 45, ballPx + 4);\r\n    ctx.textAlign = 'start';\r\n  } else if (sim.v0 > 0) {\r\n    ctx.fillStyle = '#e8871e';\r\n    ctx.font = 'bold 10px ' + font;\r\n    ctx.textAlign = 'left';\r\n    ctx.fillText('\\u25cf v=0', sceneX - 30, ballPx + 4);\r\n    ctx.textAlign = 'start';\r\n  }\r\n\r\n  \/\/ Etiqueta de posici\u00f3n\r\n  ctx.fillStyle = CFG.col.pos;\r\n  ctx.font = 'bold 11px ' + font;\r\n  ctx.textAlign = 'left';\r\n  ctx.fillText('y = ' + fmt(curY, 1) + ' m', sceneX + 50, ballPx + 4);\r\n  ctx.textAlign = 'start';\r\n}\r\n\r\n\/** Dibuja el cuerpo como una esfera con gradiente 3D *\/\r\nfunction drawBall(cx, cy, isCmp) {\r\n  var r = isCmp ? 7 : 10; \/\/ smaller than before (was 13 for sphere, 14 for cube)\r\n  ctx.save();\r\n\r\n  \/\/ Sombra\r\n  ctx.fillStyle = 'rgba(0,0,0,0.12)';\r\n  ctx.beginPath(); ctx.ellipse(cx + 2, cy + r + 2, r * 0.6, 2, 0, 0, Math.PI * 2); ctx.fill();\r\n\r\n  \/\/ Cuerpo principal con gradiente 3D esf\u00e9rico\r\n  var grad = ctx.createRadialGradient(cx - r * 0.3, cy - r * 0.3, r * 0.05, cx + r * 0.1, cy + r * 0.1, r * 1.05);\r\n  if (isCmp) {\r\n    grad.addColorStop(0, '#e0b0ff');\r\n    grad.addColorStop(0.45, '#c070e0');\r\n    grad.addColorStop(1, '#7b1fa2');\r\n  } else {\r\n    grad.addColorStop(0, '#7de8ec');\r\n    grad.addColorStop(0.4, '#14a3a8');\r\n    grad.addColorStop(0.85, '#0d7377');\r\n    grad.addColorStop(1, '#095456');\r\n  }\r\n  ctx.fillStyle = grad;\r\n  ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fill();\r\n\r\n  \/\/ Borde\r\n  ctx.strokeStyle = isCmp ? '#5c1380' : '#074e50';\r\n  ctx.lineWidth = 1.2;\r\n  ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.stroke();\r\n\r\n  \/\/ Brillo principal\r\n  ctx.fillStyle = 'rgba(255,255,255,0.5)';\r\n  ctx.beginPath(); ctx.ellipse(cx - r * 0.28, cy - r * 0.28, r * 0.28, r * 0.18, -0.5, 0, Math.PI * 2); ctx.fill();\r\n\r\n  \/\/ Brillo peque\u00f1o\r\n  ctx.fillStyle = 'rgba(255,255,255,0.75)';\r\n  ctx.beginPath(); ctx.arc(cx - r * 0.32, cy - r * 0.32, r * 0.09, 0, Math.PI * 2); ctx.fill();\r\n\r\n  ctx.restore();\r\n}\r\n\r\n\/** Dibuja un vector flecha vertical *\/\r\nfunction drawVecV(c, cx, cy, len, col, lbl) {\r\n  if (Math.abs(len) < 2) return;\r\n  var cl = Math.sign(len) * Math.min(Math.abs(len), 100);\r\n  var ey = cy + cl; \/\/ en canvas, positivo = abajo\r\n  var d = Math.sign(cl);\r\n  var hl = 7;\r\n  var font = getComputedStyle(document.body).fontFamily;\r\n  ctx.save();\r\n  ctx.strokeStyle = col; ctx.fillStyle = col;\r\n  ctx.lineWidth = 2.5; ctx.lineCap = 'round';\r\n  \/\/ L\u00ednea\r\n  ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx, ey - d * hl); ctx.stroke();\r\n  \/\/ Punta\r\n  ctx.beginPath();\r\n  ctx.moveTo(cx, ey);\r\n  ctx.lineTo(cx - 5, ey - d * hl);\r\n  ctx.lineTo(cx + 5, ey - d * hl);\r\n  ctx.closePath(); ctx.fill();\r\n  \/\/ Etiqueta\r\n  ctx.font = 'bold 10px ' + font;\r\n  ctx.textAlign = 'left';\r\n  ctx.fillText(lbl, cx + 6, (cy + ey) \/ 2 + 3);\r\n  ctx.textAlign = 'start';\r\n  ctx.restore();\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ CHART.JS \u2013 Gr\u00e1ficas y(t), v(t), g(t)\r\n\/\/ =====================================================================\r\n\r\n\/** Plugin: l\u00ednea vertical indicadora del tiempo actual *\/\r\nvar vLinePlugin = {\r\n  id: 'vLine',\r\n  afterDraw: function(chart) {\r\n    var xv = chart.options.plugins.vLineVal;\r\n    if (xv === undefined || xv === null) return;\r\n    var xs = chart.scales.x, ys = chart.scales.y;\r\n    var cx = xs.getPixelForValue(xv);\r\n    var cc = chart.ctx;\r\n    cc.save();\r\n    cc.strokeStyle = 'rgba(0,0,0,0.25)'; cc.lineWidth = 1; cc.setLineDash([4, 4]);\r\n    cc.beginPath(); cc.moveTo(cx, ys.top); cc.lineTo(cx, ys.bottom); cc.stroke();\r\n    \/\/ Punto en la curva\r\n    if (chart.data.datasets[0] && chart.data.datasets[0].data.length > 0) {\r\n      var data = chart.data.datasets[0].data;\r\n      var closest = data[0], md = Math.abs(data[0].x - xv);\r\n      for (var i = 1; i < data.length; i++) {\r\n        var dd = Math.abs(data[i].x - xv);\r\n        if (dd < md) { md = dd; closest = data[i]; }\r\n      }\r\n      var yPx = ys.getPixelForValue(closest.y);\r\n      cc.setLineDash([]);\r\n      cc.fillStyle = chart.data.datasets[0].borderColor || '#333';\r\n      cc.beginPath(); cc.arc(cx, yPx, 5, 0, Math.PI * 2); cc.fill();\r\n      cc.strokeStyle = '#fff'; cc.lineWidth = 2; cc.stroke();\r\n    }\r\n    cc.restore();\r\n  }\r\n};\r\nChart.register(vLinePlugin);\r\n\r\n\/** Crea un gr\u00e1fico Chart.js *\/\r\nfunction makeChart(id, label, color, yLbl) {\r\n  return new Chart(document.getElementById(id).getContext('2d'), {\r\n    type: 'line',\r\n    data: {\r\n      datasets: [\r\n        {\r\n          label: label, data: [], borderColor: color,\r\n          backgroundColor: color + '18', fill: false,\r\n          pointRadius: 0, borderWidth: 2.5, tension: 0.1\r\n        },\r\n        {\r\n          label: 'Exp. A', data: [], borderColor: CFG.col.pos,\r\n          backgroundColor: 'transparent', borderDash: [6, 4],\r\n          pointRadius: 0, borderWidth: 1.8, tension: 0.1, hidden: true\r\n        },\r\n        {\r\n          label: 'Exp. B', data: [], borderColor: CFG.col.cmp,\r\n          backgroundColor: 'transparent', borderDash: [6, 4],\r\n          pointRadius: 0, borderWidth: 1.8, tension: 0.1, hidden: true\r\n        }\r\n      ]\r\n    },\r\n    options: {\r\n      responsive: true, maintainAspectRatio: false, animation: false,\r\n      interaction: { mode: 'nearest', axis: 'x', intersect: false },\r\n      onClick: function(evt, els, chart) {\r\n        var rect = chart.canvas.getBoundingClientRect();\r\n        var mx = evt.native.clientX - rect.left;\r\n        var tv = chart.scales.x.getValueForPixel(mx);\r\n        if (tv !== undefined && tv >= 0 && tv <= sim.tMax) {\r\n          setSimTime(tv); updateAll();\r\n        }\r\n      },\r\n      scales: {\r\n        x: {\r\n          type: 'linear',\r\n          title: { display: true, text: 't (s)', font: { size: 11 } },\r\n          min: 0, max: sim.tMax,\r\n          grid: { color: '#f0f1f3' },\r\n          ticks: { font: { size: 10 } }\r\n        },\r\n        y: {\r\n          title: { display: true, text: yLbl, font: { size: 11 } },\r\n          grid: { color: '#f0f1f3' },\r\n          ticks: { font: { size: 10 } }\r\n        }\r\n      },\r\n      plugins: {\r\n        legend: { display: false },\r\n        tooltip: {\r\n          backgroundColor: 'rgba(0,0,0,0.8)',\r\n          titleFont: { size: 11 },\r\n          bodyFont: { size: 11, family: 'monospace' },\r\n          callbacks: {\r\n            title: function(items) { return 't = ' + fmt(items[0].parsed.x) + ' s'; },\r\n            label: function(item) { return item.dataset.label + ' = ' + fmt(item.parsed.y); }\r\n          }\r\n        }\r\n      },\r\n      vLineVal: 0\r\n    }\r\n  });\r\n}\r\n\r\nfunction initCharts() {\r\n  chartY = makeChart('chartY', 'y(t)', CFG.col.pos, 'y (m)');\r\n  chartV = makeChart('chartV', 'v(t)', CFG.col.vel, 'v (m\/s)');\r\n  chartG = makeChart('chartG', 'g(t)', CFG.col.acc, 'g (m\/s\u00b2)');\r\n  updateChartAxes();\r\n}\r\n\r\n\/** Actualiza rangos de ejes *\/\r\nfunction updateChartAxes() {\r\n  var yR = yRange(sim.y0, sim.v0, sim.g, sim.tMax);\r\n  var vR = vRange(sim.v0, sim.g, sim.tMax);\r\n  chartY.options.scales.x.max = sim.tMax;\r\n  chartY.options.scales.y.min = yR.min;\r\n  chartY.options.scales.y.max = yR.max;\r\n  chartV.options.scales.x.max = sim.tMax;\r\n  chartV.options.scales.y.min = vR.min;\r\n  chartV.options.scales.y.max = vR.max;\r\n  chartG.options.scales.x.max = sim.tMax;\r\n  chartG.options.scales.y.min = sim.g < 0 ? sim.g - 1 : -1;\r\n  chartG.options.scales.y.max = sim.g < 0 ? 1 : sim.g + 1;\r\n  if (sim.g === 0) { chartG.options.scales.y.min = -1; chartG.options.scales.y.max = 1; }\r\n  \/\/ Ajustar por comparaciones\r\n  if (cmp.a) {\r\n    var yRA = yRange(cmp.a.y0, cmp.a.v0, cmp.a.g, sim.tMax);\r\n    var vRA = vRange(cmp.a.v0, cmp.a.g, sim.tMax);\r\n    chartY.options.scales.y.min = Math.min(chartY.options.scales.y.min, yRA.min);\r\n    chartY.options.scales.y.max = Math.max(chartY.options.scales.y.max, yRA.max);\r\n    chartV.options.scales.y.min = Math.min(chartV.options.scales.y.min, vRA.min);\r\n    chartV.options.scales.y.max = Math.max(chartV.options.scales.y.max, vRA.max);\r\n  }\r\n  if (cmp.b) {\r\n    var yRB = yRange(cmp.b.y0, cmp.b.v0, cmp.b.g, sim.tMax);\r\n    var vRB = vRange(cmp.b.v0, cmp.b.g, sim.tMax);\r\n    chartY.options.scales.y.min = Math.min(chartY.options.scales.y.min, yRB.min);\r\n    chartY.options.scales.y.max = Math.max(chartY.options.scales.y.max, yRB.max);\r\n    chartV.options.scales.y.min = Math.min(chartV.options.scales.y.min, vRB.min);\r\n    chartV.options.scales.y.max = Math.max(chartV.options.scales.y.max, vRB.max);\r\n  }\r\n}\r\n\r\n\/** Agrega punto de datos a las gr\u00e1ficas *\/\r\nfunction addGP(t) {\r\n  if (t <= lastGT) return;\r\n  var y = Math.max(0, calcY(t, sim.y0, sim.v0, sim.g));\r\n  var v = calcV(t, sim.v0, sim.g);\r\n  gData.t.push(t); gData.y.push(y); gData.v.push(v); gData.g.push(sim.g);\r\n  lastGT = t;\r\n  chartY.data.datasets[0].data.push({ x: t, y: y });\r\n  chartV.data.datasets[0].data.push({ x: t, y: v });\r\n  chartG.data.datasets[0].data.push({ x: t, y: sim.g });\r\n}\r\n\r\n\/** Reconstruye datos hasta el tiempo actual *\/\r\nfunction rebuildGD() {\r\n  gData = { t: [], y: [], v: [], g: [] };\r\n  lastGT = -1;\r\n  chartY.data.datasets[0].data = [];\r\n  chartV.data.datasets[0].data = [];\r\n  chartG.data.datasets[0].data = [];\r\n  var t = 0;\r\n  while (t <= sim.t + 0.001) {\r\n    addGP(Math.min(t, sim.t));\r\n    t += CFG.dt;\r\n  }\r\n  if (sim.t > 0) addGP(sim.t);\r\n}\r\n\r\n\/** Actualiza gr\u00e1ficas *\/\r\nfunction updateCharts() {\r\n  chartY.options.plugins.vLineVal = sim.t;\r\n  chartV.options.plugins.vLineVal = sim.t;\r\n  chartG.options.plugins.vLineVal = sim.t;\r\n  updateCmpGraphs();\r\n  chartY.update('none');\r\n  chartV.update('none');\r\n  chartG.update('none');\r\n}\r\n\r\n\/** Actualiza curvas de comparaci\u00f3n *\/\r\nfunction updateCmpGraphs() {\r\n  [['a', cmp.a, 1], ['b', cmp.b, 2]].forEach(function(pair) {\r\n    var key = pair[0], cfg = pair[1], di = pair[2];\r\n    var dsY = chartY.data.datasets[di];\r\n    var dsV = chartV.data.datasets[di];\r\n    var dsG = chartG.data.datasets[di];\r\n    if (cmp['show' + key.toUpperCase()] && cfg) {\r\n      dsY.data = []; dsV.data = []; dsG.data = [];\r\n      for (var t = 0; t <= sim.tMax; t += CFG.dt) {\r\n        dsY.data.push({ x: t, y: calcY(t, cfg.y0, cfg.v0, cfg.g) });\r\n        dsV.data.push({ x: t, y: calcV(t, cfg.v0, cfg.g) });\r\n        dsG.data.push({ x: t, y: cfg.g });\r\n      }\r\n      dsY.data.push({ x: sim.tMax, y: calcY(sim.tMax, cfg.y0, cfg.v0, cfg.g) });\r\n      dsV.data.push({ x: sim.tMax, y: calcV(sim.tMax, cfg.v0, cfg.g) });\r\n      dsG.data.push({ x: sim.tMax, y: cfg.g });\r\n      dsY.hidden = false; dsV.hidden = false; dsG.hidden = false;\r\n    } else {\r\n      dsY.data = []; dsV.data = []; dsG.data = [];\r\n      dsY.hidden = true; dsV.hidden = true; dsG.hidden = true;\r\n    }\r\n  });\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ INFORMACI\u00d3N EN PANTALLA\r\n\/\/ =====================================================================\r\n\r\nfunction updateInfo() {\r\n  var yRaw = calcY(sim.t, sim.y0, sim.v0, sim.g);\r\n  var y = Math.max(0, yRaw);\r\n  var v = calcV(sim.t, sim.v0, sim.g);\r\n  var dy = y - sim.y0;\r\n\r\n  document.getElementById('time-display').textContent = 't = ' + fmt(sim.t) + ' s';\r\n  document.getElementById('info-t').textContent = fmt(sim.t) + ' s';\r\n  document.getElementById('info-y').textContent = fmt(y) + ' m';\r\n  document.getElementById('info-v').textContent = fmt(v) + ' m\/s';\r\n  document.getElementById('info-g').textContent = fmt(sim.g) + ' m\/s\u00b2';\r\n  document.getElementById('info-dy').textContent = fmt(dy) + ' m';\r\n\r\n  \/\/ Eventos\r\n  var eb = document.getElementById('event-box');\r\n  var et = document.getElementById('event-title');\r\n  var ed = document.getElementById('event-detail');\r\n\r\n  \/\/ Altura m\u00e1xima\r\n  var tMH = findMaxHeight(sim.v0, sim.g, sim.tMax);\r\n  var tGH = findGroundHit(sim.y0, sim.v0, sim.g, sim.tMax);\r\n\r\n  var eventText = '';\r\n  var eventDetail = '';\r\n\r\n  if (tGH !== null && sim.t >= tGH - 0.05) {\r\n    var vImpact = calcV(tGH, sim.v0, sim.g);\r\n    if (sim.t >= tGH) {\r\n      eventText = '\\u25cf Impacto con el suelo';\r\n      eventDetail = 'y = 0 en t = ' + fmt(tGH) + ' s, v = ' + fmt(vImpact) + ' m\/s';\r\n    } else {\r\n      eventText = '\\u25cf Acerc\u00e1ndose al suelo';\r\n      eventDetail = 'Impacto estimado en t = ' + fmt(tGH) + ' s';\r\n    }\r\n  } else if (tMH !== null && sim.t >= tMH - 0.1 && sim.v0 > 0) {\r\n    var yMH = calcY(tMH, sim.y0, sim.v0, sim.g);\r\n    if (sim.t >= tMH) {\r\n      eventText = '\\u25cf Altura m\u00e1xima alcanzada';\r\n      eventDetail = 'v = 0 en t = ' + fmt(tMH) + ' s, y = ' + fmt(yMH) + ' m';\r\n    } else {\r\n      eventText = '\\u25cf Acerc\u00e1ndose a la altura m\u00e1xima';\r\n      eventDetail = 'v = 0 en t = ' + fmt(tMH) + ' s, y = ' + fmt(yMH) + ' m';\r\n    }\r\n  }\r\n\r\n  if (eventText) {\r\n    eb.classList.add('visible');\r\n    et.textContent = eventText;\r\n    ed.textContent = eventDetail;\r\n  } else {\r\n    eb.classList.remove('visible');\r\n  }\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ ECUACIONES \u2013 HTML puro (compatible WordPress\/Elementor)\r\n\/\/ =====================================================================\r\n\r\nfunction buildTerm(coeff, varHTML, isFirst) {\r\n  if (coeff === 0) return '';\r\n  var abs = Math.abs(coeff);\r\n  var s = isFirst ? (coeff < 0 ? '\u2212' : '') : (coeff > 0 ? ' + ' : ' \u2212 ');\r\n  return s + fmt(abs) + varHTML;\r\n}\r\n\r\nfunction updateEquations() {\r\n  var y0 = sim.y0, v0 = sim.v0, g = sim.g;\r\n  var elY = document.getElementById('eq-y');\r\n  var elV = document.getElementById('eq-v');\r\n  var elG = document.getElementById('eq-g');\r\n  var halfG = 0.5 * g;\r\n\r\n  \/\/ y(t) = y\u2080 + v\u2080\u00b7t + \u00bd\u00b7g\u00b7t\u00b2\r\n  var yHTML = '<span class=\"eq\">';\r\n  yHTML += '<span class=\"eq-var\">y<\/span>(<span class=\"eq-var\">t<\/span>) = ';\r\n  var first = true;\r\n  if (y0 !== 0) { yHTML += buildTerm(y0, '', true); first = false; }\r\n  if (v0 !== 0) { yHTML += buildTerm(v0, ' \u00b7 <span class=\"eq-var\">t<\/span>', first); first = false; }\r\n  if (g !== 0) { yHTML += buildTerm(halfG, ' \u00b7 <span class=\"eq-var\">t<\/span><span class=\"eq-sup\">2<\/span>', first); first = false; }\r\n  if (first) yHTML += '0';\r\n  yHTML += '<\/span> <span class=\"eq-unit\">[m]<\/span>';\r\n\r\n  \/\/ v(t) = v\u2080 + g\u00b7t\r\n  var vHTML = '<span class=\"eq\">';\r\n  vHTML += '<span class=\"eq-var\">v<\/span>(<span class=\"eq-var\">t<\/span>) = ';\r\n  first = true;\r\n  if (v0 !== 0 || g === 0) { vHTML += fmt(v0); first = false; }\r\n  if (g !== 0) { vHTML += buildTerm(g, ' \u00b7 <span class=\"eq-var\">t<\/span>', first); }\r\n  vHTML += '<\/span> <span class=\"eq-unit\">[m\/s]<\/span>';\r\n\r\n  \/\/ g(t) = g (constante)\r\n  var gHTML = '<span class=\"eq\">';\r\n  gHTML += '<span class=\"eq-var\">g<\/span>(<span class=\"eq-var\">t<\/span>) = ' + fmt(g);\r\n  gHTML += '<\/span> <span class=\"eq-unit\">[m\/s\u00b2]<\/span>';\r\n\r\n  elY.innerHTML = yHTML;\r\n  elV.innerHTML = vHTML;\r\n  elG.innerHTML = gHTML;\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ CONTROLES DE SIMULACI\u00d3N\r\n\/\/ =====================================================================\r\n\r\nfunction setSimTime(t) {\r\n  sim.t = Math.max(0, t);\r\n  \/\/ Limitar al tiempo de impacto con el suelo (y=0)\r\n  var tGH = groundHitTime();\r\n  if (tGH !== null && sim.t > tGH) {\r\n    sim.t = tGH;\r\n  }\r\n  \/\/ No exceder tMax\r\n  if (sim.t > sim.tMax) {\r\n    sim.t = sim.tMax;\r\n  }\r\n  if (sim.t >= sim.tMax || (tGH !== null && sim.t >= tGH)) {\r\n    sim.playing = false;\r\n    sim.finished = true;\r\n    updatePlayBtn();\r\n  }\r\n  rebuildGD();\r\n}\r\n\r\nfunction play() {\r\n  if (sim.finished) resetSim();\r\n  sim.playing = true;\r\n  sim.lastTs = null;\r\n  updatePlayBtn();\r\n}\r\n\r\nfunction pause() {\r\n  sim.playing = false;\r\n  updatePlayBtn();\r\n}\r\n\r\nfunction resetSim() {\r\n  sim.t = 0; sim.playing = false; sim.finished = false; sim.lastTs = null;\r\n  gData = { t: [], y: [], v: [], g: [] };\r\n  lastGT = -1;\r\n  chartY.data.datasets[0].data = [];\r\n  chartV.data.datasets[0].data = [];\r\n  chartG.data.datasets[0].data = [];\r\n  addGP(0);\r\n  updatePlayBtn();\r\n  updateAll();\r\n}\r\n\r\nfunction stepFwd() {\r\n  if (sim.finished) return;\r\n  pause();\r\n  setSimTime(sim.t + CFG.stepSize);\r\n  updateAll();\r\n}\r\n\r\nfunction updatePlayBtn() {}\r\n\r\n\/\/ =====================================================================\r\n\/\/ PAR\u00c1METROS\r\n\/\/ =====================================================================\r\n\r\nfunction syncP(iId, key, min, max) {\r\n  var inp = document.getElementById(iId);\r\n  var upd = function(v) {\r\n    v = parseFloat(v);\r\n    if (isNaN(v)) return;\r\n    v = Math.max(min, Math.min(max, v));\r\n    sim[key] = v;\r\n    inp.value = v;\r\n    onParamChange();\r\n  };\r\n  inp.addEventListener('change', function() { upd(inp.value); });\r\n  inp.addEventListener('keydown', function(e) { if (e.key === 'Enter') inp.blur(); });\r\n}\r\n\r\nfunction onParamChange() {\r\n  \/\/ Auto-calcular tMax basado en los par\u00e1metros actuales\r\n  var newTMax = autoTMax(sim.y0, sim.v0, sim.g);\r\n  newTMax = Math.max(0.5, Math.min(newTMax, 30));\r\n  sim.tMax = newTMax;\r\n  document.getElementById('input-tmax').value = newTMax;\r\n  updateChartAxes();\r\n  updateEquations();\r\n  resetSim();\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ PRESETS\r\n\/\/ =====================================================================\r\n\r\nfunction loadPreset(n) {\r\n  var fn = PRESETS[n];\r\n  if (!fn) return;\r\n  var p = fn();\r\n  sim.y0 = p.y0; sim.v0 = p.v0; sim.g = p.g; sim.tMax = p.tMax;\r\n  document.getElementById('input-y0').value = p.y0;\r\n  document.getElementById('input-v0').value = p.v0;\r\n  document.getElementById('input-g').value = p.g;\r\n  document.getElementById('input-tmax').value = p.tMax;\r\n  onParamChange();\r\n  showToast('Preset cargado');\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ MODO EXPERIMENTO\r\n\/\/ =====================================================================\r\n\r\nfunction regData() {\r\n  var y = calcY(sim.t, sim.y0, sim.v0, sim.g);\r\n  var v = calcV(sim.t, sim.v0, sim.g);\r\n  expData.push({ t: sim.t, y: y, v: v, g: sim.g });\r\n  renderTbl();\r\n  showToast('Dato registrado en t = ' + fmt(sim.t) + ' s');\r\n}\r\n\r\nfunction delLast() {\r\n  if (!expData.length) return;\r\n  expData.pop();\r\n  renderTbl();\r\n}\r\n\r\nfunction clearData() {\r\n  expData = [];\r\n  renderTbl();\r\n}\r\n\r\nfunction renderTbl() {\r\n  var tb = document.getElementById('data-tbody');\r\n  var tbl = document.getElementById('data-table');\r\n  var em = document.getElementById('empty-msg');\r\n  if (!expData.length) {\r\n    tbl.style.display = 'none'; em.style.display = 'block'; tb.innerHTML = '';\r\n    return;\r\n  }\r\n  tbl.style.display = 'table'; em.style.display = 'none';\r\n  tb.innerHTML = expData.map(function(d) {\r\n    return '<tr><td>' + fmt(d.t) + '<\/td><td>' + fmt(d.y) + '<\/td><td>' + fmt(d.v) + '<\/td><td>' + fmt(d.g) + '<\/td><\/tr>';\r\n  }).join('');\r\n}\r\n\r\nfunction exportCSV() {\r\n  if (!expData.length) { showToast('No hay datos'); return; }\r\n  var csv = 't (s);y (m);v (m\/s);g (m\/s\u00b2)\\n';\r\n  expData.forEach(function(d) {\r\n    csv += fmt(d.t) + ';' + fmt(d.y) + ';' + fmt(d.v) + ';' + fmt(d.g) + '\\n';\r\n  });\r\n  var b = new Blob([csv], { type: 'text\/csv;charset=utf-8;' });\r\n  var u = URL.createObjectURL(b);\r\n  var a = document.createElement('a');\r\n  a.href = u; a.download = 'caidalibre_datos.csv'; a.click();\r\n  URL.revokeObjectURL(u);\r\n  showToast('CSV exportado');\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ COMPARACI\u00d3N DE MOVIMIENTOS\r\n\/\/ =====================================================================\r\n\r\nfunction saveCmp(w) {\r\n  cmp[w] = { y0: sim.y0, v0: sim.v0, g: sim.g, tMax: sim.tMax };\r\n  cmp['show' + w.toUpperCase()] = true;\r\n  updateCmpDisplay();\r\n  updateChartAxes();\r\n  updateCharts();\r\n  showToast('Experimento ' + w.toUpperCase() + ' guardado');\r\n}\r\n\r\nfunction clearCmp() {\r\n  cmp = { a: null, b: null, showA: false, showB: false };\r\n  updateCmpDisplay();\r\n  updateChartAxes();\r\n  updateCharts();\r\n  showToast('Comparaci\u00f3n limpiada');\r\n}\r\n\r\nfunction updateCmpDisplay() {\r\n  var el = document.getElementById('cmp-display');\r\n  var h = '';\r\n  if (cmp.a) {\r\n    var c = cmp.a;\r\n    h += '<div style=\"font-size:0.76rem;margin-bottom:5px;padding:5px 8px;background:#e3f2fd;border-radius:4px;border-left:3px solid ' + CFG.col.pos + ';\"><b style=\"color:' + CFG.col.pos + ';\">Exp. A:<\/b> y\u2080=' + fmt(c.y0, 1) + ' m, v\u2080=' + fmt(c.v0, 1) + ' m\/s, g=' + fmt(c.g, 1) + ' m\/s\u00b2<\/div>';\r\n  }\r\n  if (cmp.b) {\r\n    var c = cmp.b;\r\n    h += '<div style=\"font-size:0.76rem;padding:5px 8px;background:#f3e5f5;border-radius:4px;border-left:3px solid ' + CFG.col.cmp + ';\"><b style=\"color:' + CFG.col.cmp + ';\">Exp. B:<\/b> y\u2080=' + fmt(c.y0, 1) + ' m, v\u2080=' + fmt(c.v0, 1) + ' m\/s, g=' + fmt(c.g, 1) + ' m\/s\u00b2<\/div>';\r\n  }\r\n  if (!cmp.a && !cmp.b) {\r\n    h = '<span style=\"font-size:0.76rem;color:var(--text3);\">Sin experimentos guardados.<\/span>';\r\n  }\r\n  el.innerHTML = h;\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ ACTUALIZACI\u00d3N GENERAL Y LOOP\r\n\/\/ =====================================================================\r\n\r\nfunction updateAll() {\r\n  drawScene();\r\n  updateCharts();\r\n  updateInfo();\r\n}\r\n\r\nfunction animLoop(ts) {\r\n  if (sim.playing) {\r\n    if (sim.lastTs === null) sim.lastTs = ts;\r\n    var dt = (ts - sim.lastTs) \/ 1000;\r\n    sim.lastTs = ts;\r\n    sim.t += dt * sim.speed;\r\n    \/\/ Verificar impacto con el suelo: detener en y=0\r\n    var tGH = groundHitTime();\r\n    if (tGH !== null && sim.t >= tGH) {\r\n      sim.t = tGH;\r\n      sim.playing = false;\r\n      sim.finished = true;\r\n      updatePlayBtn();\r\n    }\r\n    if (sim.t >= sim.tMax) {\r\n      sim.t = sim.tMax;\r\n      sim.playing = false;\r\n      sim.finished = true;\r\n      updatePlayBtn();\r\n    }\r\n    \/\/ No permitir y < 0\r\n    var yCheck = calcY(sim.t, sim.y0, sim.v0, sim.g);\r\n    if (yCheck < 0 && tGH !== null) {\r\n      sim.t = tGH;\r\n    }\r\n    var np = Math.floor(sim.t \/ CFG.dt) + 1;\r\n    while (gData.t.length < np && lastGT < sim.t) {\r\n      var nt = lastGT + CFG.dt;\r\n      if (nt <= sim.t) addGP(nt);\r\n      else { addGP(sim.t); break; }\r\n    }\r\n  } else {\r\n    sim.lastTs = null;\r\n  }\r\n  updateAll();\r\n  requestAnimationFrame(animLoop);\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ EVENT LISTENERS\r\n\/\/ =====================================================================\r\n\r\nfunction initEvents() {\r\n  \/\/ Controles de simulaci\u00f3n\r\n  document.getElementById('btn-play').addEventListener('click', play);\r\n  document.getElementById('btn-pause').addEventListener('click', pause);\r\n  document.getElementById('btn-reset').addEventListener('click', resetSim);\r\n  document.getElementById('btn-step').addEventListener('click', stepFwd);\r\n\r\n  \/\/ Selector de velocidad\r\n  document.querySelectorAll('.speed-btn').forEach(function(b) {\r\n    b.addEventListener('click', function() {\r\n      document.querySelectorAll('.speed-btn').forEach(function(x) { x.classList.remove('active'); });\r\n      b.classList.add('active');\r\n      sim.speed = parseFloat(b.dataset.speed);\r\n    });\r\n  });\r\n\r\n  \/\/ Sincronizaci\u00f3n de par\u00e1metros\r\n  syncP('input-y0', 'y0', 0, 200);\r\n  syncP('input-v0', 'v0', -50, 50);\r\n  syncP('input-g', 'g', -20, -0.1);\r\n  syncP('input-tmax', 'tMax', 0.5, 20);\r\n\r\n  \/\/ Presets\r\n  document.querySelectorAll('.preset-btn').forEach(function(b) {\r\n    b.addEventListener('click', function() { loadPreset(b.dataset.preset); });\r\n  });\r\n\r\n  \/\/ Opciones de visualizaci\u00f3n\r\n  document.getElementById('chk-vel').addEventListener('change', function(e) { sim.showVel = e.target.checked; });\r\n  document.getElementById('chk-acc').addEventListener('change', function(e) { sim.showAcc = e.target.checked; });\r\n  document.getElementById('chk-trail').addEventListener('change', function(e) { sim.showTrail = e.target.checked; });\r\n\r\n  \/\/ Modo experimento\r\n  document.getElementById('btn-register').addEventListener('click', regData);\r\n  document.getElementById('btn-delete-last').addEventListener('click', delLast);\r\n  document.getElementById('btn-clear-data').addEventListener('click', clearData);\r\n  document.getElementById('btn-export-csv').addEventListener('click', exportCSV);\r\n\r\n  \/\/ Comparaci\u00f3n\r\n  document.getElementById('btn-save-a').addEventListener('click', function() { saveCmp('a'); });\r\n  document.getElementById('btn-save-b').addEventListener('click', function() { saveCmp('b'); });\r\n  document.getElementById('btn-clear-cmp').addEventListener('click', clearCmp);\r\n\r\n  \/\/ Overlay pedag\u00f3gico\r\n  var pedOverlay = document.getElementById('ped-overlay');\r\n  document.getElementById('ped-header').addEventListener('click', function() {\r\n    pedOverlay.classList.add('open');\r\n  });\r\n  document.getElementById('ped-close').addEventListener('click', function() {\r\n    pedOverlay.classList.remove('open');\r\n  });\r\n  pedOverlay.addEventListener('click', function(e) {\r\n    if (e.target === pedOverlay) pedOverlay.classList.remove('open');\r\n  });\r\n\r\n  \/\/ Atajos de teclado\r\n  document.addEventListener('keydown', function(e) {\r\n    if (e.target.tagName === 'INPUT') return;\r\n    if (e.code === 'Space') { e.preventDefault(); sim.playing ? pause() : play(); }\r\n    if (e.code === 'ArrowRight') { e.preventDefault(); stepFwd(); }\r\n    if (e.code === 'KeyR') { e.preventDefault(); resetSim(); }\r\n  });\r\n\r\n  \/\/ Resize\r\n  window.addEventListener('resize', function() { resizeCanvas(); updateAll(); });\r\n}\r\n\r\n\/\/ =====================================================================\r\n\/\/ INICIALIZACI\u00d3N\r\n\/\/ =====================================================================\r\n\r\nfunction init() {\r\n  initCanvas();\r\n  initCharts();\r\n  initEvents();\r\n  addGP(0);\r\n  updateEquations();\r\n  updateAll();\r\n  requestAnimationFrame(animLoop);\r\n}\r\n\r\nif (document.readyState === 'loading') {\r\n  document.addEventListener('DOMContentLoaded', init);\r\n} else {\r\n  init();\r\n}\r\n\r\n<\/script>\r\n\r\n\r\n<style>\r\n  .btn-guia {\r\n    position: fixed;\r\n    bottom: 24px;\r\n    left: 24px;\r\n    z-index: 999;\r\n    background: #3fb950;\r\n    color: #0d1117;\r\n    font-family: 'Space Grotesk', sans-serif;\r\n    font-size: 0.82rem;\r\n    font-weight: 700;\r\n    letter-spacing: 1px;\r\n    text-transform: uppercase;\r\n    padding: 10px 18px;\r\n    border-radius: 8px;\r\n    border: none;\r\n    cursor: pointer;\r\n    box-shadow: 0 4px 20px rgba(63,185,80,0.35);\r\n    transition: transform 0.2s, box-shadow 0.2s;\r\n    display: flex;\r\n    align-items: center;\r\n    gap: 8px;\r\n  }\r\n  .btn-guia:hover {\r\n    transform: translateY(-2px);\r\n    box-shadow: 0 6px 24px rgba(63,185,80,0.5);\r\n  }\r\n  .btn-guia svg { width: 15px; height: 15px; flex-shrink: 0; }\r\n  .guia-panel {\r\n    position: fixed;\r\n    bottom: 72px;\r\n    left: 24px;\r\n    width: 400px;\r\n    max-height: 70vh;\r\n    z-index: 1000;\r\n    background: #ffffff;\r\n    border: 1px solid #d0d7de;\r\n    border-radius: 12px;\r\n    box-shadow: 0 16px 48px rgba(0,0,0,0.4);\r\n    display: none;\r\n    flex-direction: column;\r\n    overflow: hidden;\r\n    font-family: 'Space Grotesk', sans-serif;\r\n  }\r\n  .guia-panel.visible { display: flex; }\r\n  .guia-header {\r\n    display: flex;\r\n    justify-content: space-between;\r\n    align-items: center;\r\n    padding: 12px 16px;\r\n    border-bottom: 1px solid #d0d7de;\r\n    flex-shrink: 0;\r\n    background: #f6f8fa;\r\n  }\r\n  .guia-header-title {\r\n    display: flex;\r\n    align-items: center;\r\n    gap: 8px;\r\n    font-size: 0.85rem;\r\n    font-weight: 700;\r\n    color: #1f2328;\r\n  }\r\n  .guia-header-title svg { width: 15px; height: 15px; color: #3fb950; }\r\n  .guia-close {\r\n    background: none;\r\n    border: none;\r\n    color: #636c76;\r\n    font-size: 1rem;\r\n    cursor: pointer;\r\n    padding: 2px 7px;\r\n    border-radius: 4px;\r\n    transition: color 0.2s, background 0.2s;\r\n    line-height: 1;\r\n  }\r\n  .guia-close:hover { color: #1f2328; background: rgba(0,0,0,0.06); }\r\n  .guia-body {\r\n    overflow-y: auto;\r\n    padding: 18px 18px 20px;\r\n    color: #1f2328;\r\n    font-size: 0.83rem;\r\n    line-height: 1.65;\r\n    background: #ffffff;\r\n  }\r\n  .guia-body::-webkit-scrollbar { width: 5px; }\r\n  .guia-body::-webkit-scrollbar-track { background: transparent; }\r\n  .guia-body::-webkit-scrollbar-thumb { background: #d0d7de; border-radius: 3px; }\r\n  .guia-section-title {\r\n    font-size: 1rem;\r\n    font-weight: 700;\r\n    color: #1f2328;\r\n    margin: 0 0 12px 0;\r\n    padding-bottom: 8px;\r\n    border-bottom: 1px solid #d0d7de;\r\n  }\r\n  .guia-block { margin-bottom: 14px; }\r\n  .guia-label {\r\n    font-size: 0.72rem;\r\n    text-transform: uppercase;\r\n    letter-spacing: 1px;\r\n    color: #636c76;\r\n    margin-bottom: 4px;\r\n  }\r\n  .guia-text { color: #1f2328; }\r\n  .guia-formula {\r\n    background: #f6f8fa;\r\n    border: 1px solid #d0d7de;\r\n    border-radius: 6px;\r\n    padding: 10px 14px;\r\n    text-align: center;\r\n    font-family: 'JetBrains Mono', monospace;\r\n    font-size: 0.88rem;\r\n    color: #0550ae;\r\n    margin: 10px 0;\r\n  }\r\n  .guia-steps { list-style: none; margin: 0; padding: 0; counter-reset: steps; }\r\n  .guia-steps li {\r\n    counter-increment: steps;\r\n    display: flex;\r\n    gap: 10px;\r\n    margin-bottom: 8px;\r\n    align-items: flex-start;\r\n    color: #1f2328;\r\n  }\r\n  .guia-steps li::before {\r\n    content: counter(steps);\r\n    background: #dafbe1;\r\n    color: #1a7f37;\r\n    font-size: 0.72rem;\r\n    font-weight: 700;\r\n    width: 20px;\r\n    height: 20px;\r\n    border-radius: 50%;\r\n    display: flex;\r\n    align-items: center;\r\n    justify-content: center;\r\n    flex-shrink: 0;\r\n    margin-top: 1px;\r\n    border: 1px solid #1a7f37;\r\n  }\r\n  .guia-result {\r\n    background: #dafbe1;\r\n    border-left: 3px solid #1a7f37;\r\n    border-radius: 0 6px 6px 0;\r\n    padding: 10px 14px;\r\n    color: #1f2328;\r\n    font-size: 0.82rem;\r\n    margin-top: 10px;\r\n  }\r\n  .guia-params {\r\n    background: #f6f8fa;\r\n    border: 1px solid #d0d7de;\r\n    border-radius: 6px;\r\n    padding: 10px 14px;\r\n    font-size: 0.8rem;\r\n    color: #1f2328;\r\n  }\r\n  .guia-params span { color: #0550ae; font-family: 'JetBrains Mono', monospace; }\r\n  .var { font-family: 'JetBrains Mono', monospace; }\r\n  .var.pos { color: #cf222e; }\r\n  .var.neg { color: #0550ae; }\r\n  .var.neu { color: #7d4e00; }\r\n  .var.ok  { color: #1a7f37; }\r\n<\/style>\r\n\r\n<div class=\"guia-panel\" id=\"guiaPanel\">\r\n  <div class=\"guia-header\">\r\n    <div class=\"guia-header-title\">\r\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\"\/><path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\"\/><\/svg>\r\n      Gu\u00eda de uso\r\n    <\/div>\r\n    <button class=\"guia-close\" onclick=\"toggleGuia()\">\u2715<\/button>\r\n  <\/div>\r\n\r\n  <div class=\"guia-body\">\r\n\r\n    <!--\u2605 BORRAR DESDE AQUI \u2605-->\r\n\r\n<div style=\"font-family: Arial, sans-serif; line-height: 1.6; color: #333;\">\r\n    <h3 style=\"color: #0056b3;\">Laboratorio de Cinem\u00e1tica: Ca\u00edda Libre y Tiro Vertical<\/h3>\r\n\r\n    <p><strong>Objetivo:<\/strong> Estudiar el movimiento de los cuerpos bajo la acci\u00f3n exclusiva de la aceleraci\u00f3n de la gravedad, analizando la simetr\u00eda temporal del tiro vertical y la independencia de la masa en la ca\u00edda libre en ausencia de roce.<\/p>\r\n    \r\n    <p><strong>Descripci\u00f3n breve:<\/strong> Simulaci\u00f3n interactiva que permite explorar el comportamiento cinem\u00e1tico de objetos lanzados verticalmente o dejados caer. Facilita la comprensi\u00f3n del efecto de la aceleraci\u00f3n gravitatoria \\(g\\) sobre la velocidad y la altura, as\u00ed como la verificaci\u00f3n de las ecuaciones horarias que rigen este caso particular de MRUA:<\/p>\r\n\r\n    <div style=\"text-align: center; margin: 20px 0; background: #f0f4f8; padding: 10px; border-radius: 5px;\">\r\n        \\( y(t) = y_0 + v_0 t - \\frac{1}{2} g t^2 \\quad \\rightarrow \\quad v(t) = v_0 - g t \\)\r\n    <\/div>\r\n\r\n    <p><strong>C\u00f3mo usar (pasos):<\/strong><\/p>\r\n    <ul style=\"margin-left: 20px;\">\r\n        <li>Seleccionar el modo de simulaci\u00f3n (Ca\u00edda Libre dejando caer el objeto con \\(v_0 = 0\\) o Tiro Vertical especificando una velocidad inicial).<\/li>\r\n        <li>Definir la altura inicial (\\(y_0\\)) e ingresar la velocidad inicial (\\(v_0\\)) deseada.<\/li>\r\n        <li>Iniciar el movimiento y observar en tiempo real la trayectoria del cuerpo, los vectores de velocidad\/aceleraci\u00f3n y la generaci\u00f3n de los gr\u00e1ficos de altura, velocidad y aceleraci\u00f3n en funci\u00f3n del tiempo.<\/li>\r\n        <li>Utilizar las herramientas de pausa o seguimiento punto a punto para registrar la altura m\u00e1xima alcanzada (\\(y_{m\u00e1x}\\)), el tiempo de subida y el tiempo total de vuelo.<\/li>\r\n        <li>Comprobar la simetr\u00eda del movimiento comparando el tiempo de subida con el tiempo de bajada y la velocidad de lanzamiento con la velocidad de impacto.<\/li>\r\n    <\/ul>\r\n\r\n    <p><strong>Par\u00e1metros ajustables:<\/strong> Altura inicial (\\(m\\)), velocidad inicial (\\(m\/s\\)), valor de la aceleraci\u00f3n de la gravedad (\\(g\\)) y masa del objeto.<\/p>\r\n\r\n    <p style=\"background-color: #f8f9fa; padding: 15px; border-left: 5px solid #0056b3;\">\r\n        <strong>Resultados esperados \/ observaciones:<\/strong> En la altura m\u00e1xima, la velocidad instant\u00e1nea del objeto es exactamente cero, aunque la aceleraci\u00f3n sigue siendo \\(-g\\). Se verifica que el tiempo empleado en alcanzar la altura m\u00e1xima es igual al tiempo de retorno a la posici\u00f3n inicial, y que la masa del cuerpo no influye en la aceleraci\u00f3n ni en el tiempo de ca\u00edda.\r\n    <\/p>\r\n<\/div>\r\n\r\n   <!--\u2605 BORRAR HASTA AQUI \u2605-->\r\n\r\n  <\/div>\r\n<\/div>\r\n\r\n<button class=\"btn-guia\" onclick=\"toggleGuia()\">\r\n  <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z\"\/><path d=\"M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z\"\/><\/svg>\r\n  Gu\u00eda de uso\r\n<\/button>\r\n\r\n<script>\r\n  function toggleGuia() {\r\n    document.getElementById('guiaPanel').classList.toggle('visible');\r\n  }\r\n<\/script>\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n<\/body>\r\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>FISAR_SIM \u2013 Ca\u00edda Libre | F\u00edsica 2 \u2013 UNSL FICA \u2013 Universidad Nacional de San Luis Ca\u00edda libre de los cuerpos Simulaci\u00f3n interactiva sin rozamiento&hellip;<\/p>\n","protected":false},"author":7,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-8579","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.3 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>caida_libre - F\u00edsica 2<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/\" \/>\n<meta property=\"og:locale\" content=\"es_ES\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"caida_libre - F\u00edsica 2\" \/>\n<meta property=\"og:description\" content=\"FISAR_SIM \u2013 Ca\u00edda Libre | F\u00edsica 2 \u2013 UNSL FICA \u2013 Universidad Nacional de San Luis Ca\u00edda libre de los cuerpos Simulaci\u00f3n interactiva sin rozamiento&hellip;\" \/>\n<meta property=\"og:url\" content=\"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/\" \/>\n<meta property=\"og:site_name\" content=\"F\u00edsica 2\" \/>\n<meta property=\"article:modified_time\" content=\"2026-08-21T13:08:35+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Tiempo de lectura\" \/>\n\t<meta name=\"twitter:data1\" content=\"4 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/index.php\\\/caida_libre\\\/\",\"url\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/index.php\\\/caida_libre\\\/\",\"name\":\"caida_libre - F\u00edsica 2\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#website\"},\"datePublished\":\"2026-08-12T14:34:41+00:00\",\"dateModified\":\"2026-08-21T13:08:35+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/index.php\\\/caida_libre\\\/#breadcrumb\"},\"inLanguage\":\"es-AR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/index.php\\\/caida_libre\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/index.php\\\/caida_libre\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Inicio\",\"item\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"caida_libre\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#website\",\"url\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/\",\"name\":\"F\u00edsica 2\",\"description\":\"FICA - UNSL\",\"publisher\":{\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"es-AR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#organization\",\"name\":\"SAC- Secretar\u00eda General FICA\",\"url\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es-AR\",\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/SG-Logo.png\",\"contentUrl\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/wp-content\\\/uploads\\\/2021\\\/11\\\/SG-Logo.png\",\"width\":4483,\"height\":1231,\"caption\":\"SAC- Secretar\u00eda General FICA\"},\"image\":{\"@id\":\"https:\\\/\\\/fisica2.fica.unsl.edu.ar\\\/#\\\/schema\\\/logo\\\/image\\\/\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"caida_libre - F\u00edsica 2","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/","og_locale":"es_ES","og_type":"article","og_title":"caida_libre - F\u00edsica 2","og_description":"FISAR_SIM \u2013 Ca\u00edda Libre | F\u00edsica 2 \u2013 UNSL FICA \u2013 Universidad Nacional de San Luis Ca\u00edda libre de los cuerpos Simulaci\u00f3n interactiva sin rozamiento&hellip;","og_url":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/","og_site_name":"F\u00edsica 2","article_modified_time":"2026-08-21T13:08:35+00:00","twitter_card":"summary_large_image","twitter_misc":{"Tiempo de lectura":"4 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/","url":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/","name":"caida_libre - F\u00edsica 2","isPartOf":{"@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#website"},"datePublished":"2026-08-12T14:34:41+00:00","dateModified":"2026-08-21T13:08:35+00:00","breadcrumb":{"@id":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/#breadcrumb"},"inLanguage":"es-AR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/caida_libre\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https:\/\/fisica2.fica.unsl.edu.ar\/"},{"@type":"ListItem","position":2,"name":"caida_libre"}]},{"@type":"WebSite","@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#website","url":"https:\/\/fisica2.fica.unsl.edu.ar\/","name":"F\u00edsica 2","description":"FICA - UNSL","publisher":{"@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/fisica2.fica.unsl.edu.ar\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"es-AR"},{"@type":"Organization","@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#organization","name":"SAC- Secretar\u00eda General FICA","url":"https:\/\/fisica2.fica.unsl.edu.ar\/","logo":{"@type":"ImageObject","inLanguage":"es-AR","@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#\/schema\/logo\/image\/","url":"https:\/\/fisica2.fica.unsl.edu.ar\/wp-content\/uploads\/2021\/11\/SG-Logo.png","contentUrl":"https:\/\/fisica2.fica.unsl.edu.ar\/wp-content\/uploads\/2021\/11\/SG-Logo.png","width":4483,"height":1231,"caption":"SAC- Secretar\u00eda General FICA"},"image":{"@id":"https:\/\/fisica2.fica.unsl.edu.ar\/#\/schema\/logo\/image\/"}}]}},"_links":{"self":[{"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/pages\/8579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/comments?post=8579"}],"version-history":[{"count":40,"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/pages\/8579\/revisions"}],"predecessor-version":[{"id":8751,"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/pages\/8579\/revisions\/8751"}],"wp:attachment":[{"href":"https:\/\/fisica2.fica.unsl.edu.ar\/index.php\/wp-json\/wp\/v2\/media?parent=8579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}