{"id":1292,"date":"2026-08-14T18:41:54","date_gmt":"2026-08-14T16:41:54","guid":{"rendered":"https:\/\/www.blakmedia.cl\/?page_id=1292"},"modified":"2026-08-14T21:54:57","modified_gmt":"2026-08-14T19:54:57","slug":"ruta","status":"publish","type":"page","link":"https:\/\/www.blakmedia.cl\/index.php\/ruta\/","title":{"rendered":"ruta"},"content":{"rendered":"<div class=\"et_pb_section_0 et_pb_section et_section_regular et_flex_section\">\n<div class=\"et_pb_row_0 et_pb_row et_flex_row\">\n<div class=\"et_pb_column_0 et_pb_column et-last-child et_flex_column et_pb_css_mix_blend_mode_passthrough et_flex_column_24_24\">\n<div class=\"et_pb_code_0 et_pb_code et_pb_module\"><div class=\"et_pb_code_inner\">\n\n<!-- Ruta Google Maps + GSAP. Pega todo este archivo en un m\u00f3dulo C\u00f3digo de Divi. -->\n<style>\n  .rx-route,.rx-route * {\n    box-sizing:border-box\n  }\n  .rx-route {\n    --ink:#101828;\n    --muted:#667085;\n    --purple:#635bff;\n    --purple2:#7c3aed;\n    --cyan:#06b6d4;\n    --green:#10b981;\n    position:relative;\n    width:100%;\n    min-height:680px;\n    overflow:hidden;\n    border:1px solid rgba(15,23,42,.08);\n    border-radius:28px;\n    background:#e9eef5;\n    box-shadow:0 28px 80px rgba(15,23,42,.18);\n    font-family:Inter,ui-sans-serif,system-ui,-apple-system,\"Segoe UI\",sans-serif;\n    isolation:isolate\n  }\n  .rx-map {\n    width:100%;\n    height:720px;\n    min-height:680px\n  }\n  .rx-route:after {\n    position:absolute;\n    z-index:1;\n    inset:0;\n    content:\"\";\n    pointer-events:none;\n    background:linear-gradient(180deg,rgba(15,23,42,.06),transparent 24%),linear-gradient(0deg,rgba(15,23,42,.08),transparent 22%)\n  }\n  .rx-glass {\n    border:1px solid rgba(255,255,255,.72);\n    background:rgba(255,255,255,.9);\n    box-shadow:0 18px 45px rgba(15,23,42,.16);\n    backdrop-filter:blur(18px) saturate(145%);\n    -webkit-backdrop-filter:blur(18px) saturate(145%)\n  }\n  .rx-header {\n    position:absolute;\n    z-index:5;\n    top:20px;\n    right:20px;\n    left:20px;\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:18px;\n    min-height:76px;\n    padding:14px 16px 14px 18px;\n    border-radius:20px\n  }\n  .rx-brand {\n    display:flex;\n    align-items:center;\n    min-width:0;\n    gap:13px\n  }\n  .rx-brand-mark {\n    display:grid;\n    width:46px;\n    height:46px;\n    flex:0 0 46px;\n    place-items:center;\n    border-radius:15px;\n    color:#fff;\n    background:linear-gradient(145deg,var(--purple),#8b5cf6 58%,var(--cyan));\n    box-shadow:0 10px 24px rgba(99,91,255,.36)\n  }\n  .rx-brand-mark svg {\n    width:24px;\n    height:24px\n  }\n  .rx-eyebrow {\n    margin:0 0 2px;\n    color:#4f46e5;\n    font-size:11px;\n    font-weight:800;\n    letter-spacing:.14em;\n    line-height:1.2;\n    text-transform:uppercase\n  }\n  .rx-title {\n    overflow:hidden;\n    margin:0;\n    color:var(--ink);\n    font-size:clamp(17px,2vw,22px);\n    font-weight:800;\n    letter-spacing:-.025em;\n    line-height:1.15;\n    text-overflow:ellipsis;\n    white-space:nowrap\n  }\n  .rx-live {\n    display:inline-flex;\n    align-items:center;\n    flex:0 0 auto;\n    gap:8px;\n    min-height:40px;\n    padding:0 14px;\n    border:1px solid rgba(16,185,129,.18);\n    border-radius:999px;\n    color:#047857;\n    background:rgba(236,253,245,.92);\n    font-size:12px;\n    font-weight:800;\n    letter-spacing:.05em;\n    text-transform:uppercase\n  }\n  .rx-live-dot {\n    width:8px;\n    height:8px;\n    border-radius:50%;\n    background:var(--green);\n    animation:rxPulse 1.8s ease-out infinite\n  }\n  @keyframes rxPulse {\n    0% {\n      box-shadow:0 0 0 0 rgba(16,185,129,.35)\n    }\n    70% {\n      box-shadow:0 0 0 8px rgba(16,185,129,0)\n    }\n    100% {\n      box-shadow:0 0 0 0 rgba(16,185,129,0)\n    }\n  }\n  .rx-details {\n    position:absolute;\n    z-index:5;\n    top:112px;\n    left:20px;\n    width:min(330px,calc(100% - 40px));\n    padding:19px;\n    border-radius:22px\n  }\n  .rx-points {\n    display:grid;\n    grid-template-columns:34px minmax(0,1fr);\n    column-gap:12px\n  }\n  .rx-track {\n    position:relative;\n    display:grid;\n    min-height:62px;\n    place-items:start center\n  }\n  .rx-track:after {\n    position:absolute;\n    top:27px;\n    bottom:-1px;\n    left:50%;\n    width:2px;\n    content:\"\";\n    transform:translateX(-50%);\n    background:repeating-linear-gradient(to bottom,rgba(99,91,255,.45) 0 5px,transparent 5px 10px)\n  }\n  .rx-track.last:after {\n    display:none\n  }\n  .rx-point {\n    position:relative;\n    z-index:1;\n    display:grid;\n    width:28px;\n    height:28px;\n    place-items:center;\n    border-radius:10px;\n    color:#fff;\n    background:var(--purple);\n    box-shadow:0 6px 15px rgba(99,91,255,.28);\n    font-size:11px;\n    font-weight:900\n  }\n  .rx-point.end {\n    background:var(--cyan);\n    box-shadow:0 6px 15px rgba(6,182,212,.28)\n  }\n  .rx-point-copy {\n    min-width:0;\n    padding:1px 0 17px\n  }\n  .rx-point-copy small {\n    display:block;\n    margin-bottom:3px;\n    color:var(--muted);\n    font-size:11px;\n    font-weight:700;\n    letter-spacing:.08em;\n    text-transform:uppercase\n  }\n  .rx-point-copy strong {\n    display:block;\n    overflow:hidden;\n    color:var(--ink);\n    font-size:14px;\n    font-weight:750;\n    line-height:1.25;\n    text-overflow:ellipsis;\n    white-space:nowrap\n  }\n  .rx-stats {\n    display:grid;\n    grid-template-columns:repeat(3,1fr);\n    gap:8px;\n    margin-top:3px;\n    padding-top:14px;\n    border-top:1px solid rgba(100,116,139,.14)\n  }\n  .rx-stat {\n    min-width:0;\n    padding:9px 7px;\n    border-radius:13px;\n    text-align:center;\n    background:rgba(241,245,249,.8)\n  }\n  .rx-stat span {\n    display:block;\n    overflow:hidden;\n    color:var(--ink);\n    font-size:14px;\n    font-weight:850;\n    line-height:1.15;\n    text-overflow:ellipsis;\n    white-space:nowrap\n  }\n  .rx-stat small {\n    display:block;\n    margin-top:3px;\n    color:var(--muted);\n    font-size:9px;\n    font-weight:800;\n    letter-spacing:.08em;\n    text-transform:uppercase\n  }\n  .rx-console {\n    position:absolute;\n    z-index:5;\n    right:20px;\n    bottom:20px;\n    left:20px;\n    display:grid;\n    grid-template-columns:minmax(150px,1fr) auto;\n    align-items:center;\n    gap:18px;\n    min-height:88px;\n    padding:15px 16px 15px 20px;\n    border-radius:22px\n  }\n  .rx-progress-head {\n    display:flex;\n    align-items:center;\n    justify-content:space-between;\n    gap:12px;\n    margin-bottom:10px\n  }\n  .rx-progress-state {\n    color:var(--ink);\n    font-size:13px;\n    font-weight:800\n  }\n  .rx-progress-value {\n    color:#4f46e5;\n    font-size:12px;\n    font-weight:900\n  }\n  .rx-progress-track {\n    height:7px;\n    overflow:hidden;\n    border-radius:999px;\n    background:rgba(100,116,139,.16)\n  }\n  .rx-progress-bar {\n    width:0;\n    height:100%;\n    border-radius:inherit;\n    background:linear-gradient(90deg,var(--purple),#8b5cf6 52%,var(--cyan));\n    box-shadow:0 0 18px rgba(99,91,255,.4);\n    transition:width 80ms linear\n  }\n  .rx-actions {\n    display:flex;\n    align-items:center;\n    gap:8px\n  }\n  .rx-button {\n    display:inline-flex;\n    align-items:center;\n    justify-content:center;\n    gap:8px;\n    min-height:52px;\n    border:0;\n    border-radius:15px;\n    padding:0 17px;\n    color:#334155;\n    background:rgba(241,245,249,.9);\n    font:inherit;\n    font-size:13px;\n    font-weight:800;\n    cursor:pointer;\n    transition:transform 180ms ease,box-shadow 180ms ease,background 180ms ease\n  }\n  .rx-button:hover {\n    transform:translateY(-2px);\n    background:#fff;\n    box-shadow:0 10px 22px rgba(15,23,42,.12)\n  }\n  .rx-button:active {\n    transform:translateY(0) scale(.98)\n  }\n  .rx-button svg {\n    width:17px;\n    height:17px\n  }\n  .rx-button.primary {\n    min-width:122px;\n    color:#fff;\n    background:linear-gradient(135deg,var(--purple),var(--purple2));\n    box-shadow:0 11px 24px rgba(99,91,255,.3)\n  }\n  .rx-button.primary:hover {\n    background:linear-gradient(135deg,#574ff3,#6d28d9);\n    box-shadow:0 14px 28px rgba(99,91,255,.38)\n  }\n  .rx-status {\n    position:absolute;\n    z-index:12;\n    top:112px;\n    right:20px;\n    width:min(420px,calc(100% - 40px));\n    padding:15px 17px;\n    border:1px solid rgba(239,68,68,.22);\n    border-radius:16px;\n    color:#991b1b;\n    background:rgba(254,242,242,.96);\n    box-shadow:0 16px 35px rgba(127,29,29,.15);\n    font-size:13px;\n    font-weight:650;\n    line-height:1.45;\n    backdrop-filter:blur(14px)\n  }\n  .rx-status[hidden] {\n    display:none\n  }\n  .rx-vehicle {\n    position:relative;\n    display:grid;\n    width:38px;\n    height:38px;\n    place-items:center;\n    border:3px solid rgba(255,255,255,.96);\n    border-radius:10px;\n    color:#fff;\n    background:linear-gradient(145deg,#6d5dfc,#4338ca);\n    box-shadow:0 10px 22px rgba(67,56,202,.4),0 0 0 7px rgba(99,91,255,.14);\n    transform-origin:50% 50%;\n    will-change:transform\n  }\n  .rx-vehicle:before {\n    position:absolute;\n    top:-10px;\n    left:50%;\n    width:0;\n    height:0;\n    content:\"\";\n    transform:translateX(-50%);\n    border-right:7px solid transparent;\n    border-bottom:11px solid #635bff;\n    border-left:7px solid transparent;\n    filter:drop-shadow(0 -1px 0 #fff)\n  }\n  .rx-vehicle svg {\n    width:21px;\n    height:21px;\n    filter:drop-shadow(0 2px 2px rgba(15,23,42,.24))\n  }\n  .rx-endpoint {\n    display:grid;\n    width:34px;\n    height:34px;\n    place-items:center;\n    border:3px solid #fff;\n    border-radius:12px;\n    color:#fff;\n    background:var(--purple);\n    box-shadow:0 8px 16px rgba(15,23,42,.25);\n    font-size:11px;\n    font-weight:900\n  }\n  .rx-endpoint.end {\n    background:var(--cyan)\n  }\n  .rx-arrival-marker {\n    display:flex;\n    flex-direction:column;\n    align-items:center;\n    gap:8px;\n    opacity:0;\n    transform:translateY(16px) scale(.86);\n    transform-origin:50% 100%\n  }\n  .rx-arrival-message {\n    padding:10px 15px;\n    border:1px solid rgba(255,255,255,.9);\n    border-radius:999px;\n    color:#0f172a;\n    background:rgba(255,255,255,.96);\n    box-shadow:0 12px 30px rgba(15,23,42,.2);\n    font-size:13px;\n    font-weight:850;\n    white-space:nowrap;\n    backdrop-filter:blur(12px)\n  }\n  .rx-arrival-pin {\n    display:grid;\n    width:46px;\n    height:46px;\n    place-items:center;\n    border:3px solid #fff;\n    border-radius:50% 50% 50% 8px;\n    color:#fff;\n    background:linear-gradient(145deg,#06b6d4,#0f766e);\n    box-shadow:0 10px 22px rgba(6,182,212,.36);\n    transform:rotate(-45deg)\n  }\n  .rx-arrival-pin svg {\n    width:23px;\n    height:23px;\n    transform:rotate(45deg)\n  }\n  @media(max-width:760px) {\n    .rx-route,.rx-map {\n      min-height:670px\n    }\n    .rx-map {\n      height:76vh;\n      max-height:760px\n    }\n    .rx-header {\n      top:12px;\n      right:12px;\n      left:12px;\n      min-height:66px;\n      padding:10px 12px;\n      border-radius:17px\n    }\n    .rx-brand-mark {\n      width:42px;\n      height:42px;\n      flex-basis:42px;\n      border-radius:13px\n    }\n    .rx-live {\n      min-height:34px;\n      padding:0 10px;\n      font-size:10px\n    }\n    .rx-details {\n      top:90px;\n      left:12px;\n      width:min(292px,calc(100% - 24px));\n      padding:15px;\n      border-radius:18px\n    }\n    .rx-console {\n      right:12px;\n      bottom:12px;\n      left:12px;\n      grid-template-columns:1fr;\n      gap:13px;\n      padding:14px;\n      border-radius:18px\n    }\n    .rx-actions {\n      display:grid;\n      grid-template-columns:1.2fr 1fr 1fr\n    }\n    .rx-button {\n      min-width:0;\n      min-height:48px;\n      padding:0 10px;\n      font-size:12px\n    }\n    .rx-button.primary {\n      min-width:0\n    }\n    .rx-status {\n      top:90px;\n      right:12px;\n      width:calc(100% - 24px)\n    }\n  }\n  @media(max-width:440px) {\n    .rx-live span:last-child,.rx-button span {\n      display:none\n    }\n  }\n<\/style>\n\n<section class=\"rx-route\" id=\"rx-route\" aria-label=\"Recorrido animado en Google Maps\">\n  <div class=\"rx-map\" id=\"rx-map\"><\/div>\n  <header class=\"rx-header rx-glass\">\n    <div class=\"rx-brand\">\n      <div class=\"rx-brand-mark\" aria-hidden=\"true\">\n        <svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M5 17.5 9.2 13l3 2.7L19 7\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><circle cx=\"5\" cy=\"17.5\" r=\"2\" fill=\"currentColor\"\/><circle cx=\"19\" cy=\"7\" r=\"2\" fill=\"currentColor\"\/><\/svg>\n      <\/div>\n      <div><p class=\"rx-eyebrow\">Experiencia interactiva<\/p><h2 class=\"rx-title\">Ruta urbana en movimiento<\/h2><\/div>\n    <\/div>\n    <div class=\"rx-live\"><span class=\"rx-live-dot\"><\/span><span>Ruta disponible<\/span><\/div>\n  <\/header>\n\n  <aside class=\"rx-details rx-glass\">\n    <div class=\"rx-points\">\n      <div class=\"rx-track\"><span class=\"rx-point\">A<\/span><\/div>\n      <div class=\"rx-point-copy\"><small>Origen<\/small><strong id=\"rx-origin-label\">Centro de Santiago<\/strong><\/div>\n      <div class=\"rx-track last\"><span class=\"rx-point end\">B<\/span><\/div>\n      <div class=\"rx-point-copy\"><small>Destino<\/small><strong id=\"rx-destination-label\">Providencia<\/strong><\/div>\n    <\/div>\n    <div class=\"rx-stats\">\n      <div class=\"rx-stat\"><span id=\"rx-distance\">\u2014<\/span><small>Distancia<\/small><\/div>\n      <div class=\"rx-stat\"><span id=\"rx-duration\">\u2014<\/span><small>Tiempo<\/small><\/div>\n      <div class=\"rx-stat\"><span id=\"rx-percent-card\">0%<\/span><small>Avance<\/small><\/div>\n    <\/div>\n  <\/aside>\n\n  <div class=\"rx-console rx-glass\">\n    <div>\n      <div class=\"rx-progress-head\"><span class=\"rx-progress-state\" id=\"rx-progress-state\">Preparando recorrido\u2026<\/span><span class=\"rx-progress-value\" id=\"rx-progress-value\">0%<\/span><\/div>\n      <div class=\"rx-progress-track\"><div class=\"rx-progress-bar\" id=\"rx-progress-bar\"><\/div><\/div>\n    <\/div>\n    <div class=\"rx-actions\">\n      <button class=\"rx-button primary\" id=\"rx-start\" type=\"button\"><svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M8 5.5v13l10-6.5L8 5.5Z\"\/><\/svg><span>Iniciar<\/span><\/button>\n      <button class=\"rx-button\" id=\"rx-pause\" type=\"button\"><svg viewBox=\"0 0 24 24\" fill=\"currentColor\"><path d=\"M7 5h4v14H7V5Zm6 0h4v14h-4V5Z\"\/><\/svg><span>Pausar<\/span><\/button>\n      <button class=\"rx-button\" id=\"rx-reset\" type=\"button\"><svg viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M5.2 7.7A8 8 0 1 1 4 15\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\"\/><path d=\"M4 4v5h5\" stroke=\"currentColor\" stroke-width=\"2.2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg><span>Reiniciar<\/span><\/button>\n    <\/div>\n  <\/div>\n  <div class=\"rx-status\" id=\"rx-status\" hidden><\/div>\n<\/section>\n\n<!-- Bootstrap Icons car-front-fill, c\u00f3digo MIT: https:\/\/icons.getbootstrap.com\/icons\/car-front-fill\/ -->\n<template id=\"rx-vehicle-template\">\n  <div class=\"rx-vehicle\" aria-hidden=\"true\">\n    <svg viewBox=\"0 0 16 16\" fill=\"currentColor\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n      <path d=\"M2.52 3.515A2.5 2.5 0 0 1 4.82 2h6.362c1 0 1.904.596 2.298 1.515l.792 1.848c.075.175.21.319.38.404.5.25.855.715.965 1.262l.335 1.679q.05.242.049.49v.413c0 .814-.39 1.543-1 1.997V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.338c-1.292.048-2.745.088-4 .088s-2.708-.04-4-.088V13.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-1.892c-.61-.454-1-1.183-1-1.997v-.413a2.5 2.5 0 0 1 .049-.49l.335-1.68c.11-.546.465-1.012.964-1.261a.8.8 0 0 0 .381-.404l.792-1.848ZM3 10a1 1 0 1 0 0-2 1 1 0 0 0 0 2m10 0a1 1 0 1 0 0-2 1 1 0 0 0 0 2M6 8a1 1 0 0 0 0 2h4a1 1 0 1 0 0-2zM2.906 5.189a.51.51 0 0 0 .497.731c.91-.073 3.35-.17 4.597-.17s3.688.097 4.597.17a.51.51 0 0 0 .497-.731l-.956-1.913A.5.5 0 0 0 11.691 3H4.309a.5.5 0 0 0-.447.276L2.906 5.19Z\"\/>\n    <\/svg>\n  <\/div>\n<\/template>\n\n<template id=\"rx-arrival-template\">\n  <div class=\"rx-arrival-marker\" aria-live=\"polite\">\n    <div class=\"rx-arrival-message\">\u00a1Llegaste a tu destino!<\/div>\n    <div class=\"rx-arrival-pin\" aria-hidden=\"true\">\n      <svg viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n        <path d=\"m7.5 12 3 3 6-7\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\n      <\/svg>\n    <\/div>\n  <\/div>\n<\/template>\n\n<script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/gsap\/3.13.0\/gsap.min.js\"><\/script>\n<script>\n  \/* ================================================================\n     1. CONFIGURACI\u00d3N EDITABLE\n     ================================================================ *\/\n  const RX_GOOGLE_MAPS_API_KEY = \"AIzaSyAA0YLm931zacseqZ02rb1x9thR_LfCW9Y\"; \/\/ Pega aqu\u00ed tu clave.\n\n  const RX_ORIGIN = {\n    lat: -33.00079,\n    lng: -70.68725,\n  };\n\n  const RX_DESTINATION = {\n    lat: -32.816738,\n    lng: -70.691311,\n  };\n\n  const RX_ORIGIN_LABEL = \"Punto de inicio\";\n  const RX_DESTINATION_LABEL = \"Punto de t\u00e9rmino\";\n  const RX_ANIMATION_SECONDS = 15;\n  const RX_MAX_AUTO_ZOOM = 16; \/\/ N\u00famero mayor = mapa m\u00e1s cercano.\n  const RX_ARRIVAL_ZOOM_START = 0.6; \/\/ Comienza al completar el 60% de la ruta.\n  const RX_ARRIVAL_FINAL_ZOOM = 15; \/\/ Zoom final centrado en el destino.\n\n  \/* ================================================================\n     2. ESTADO INTERNO\n     ================================================================ *\/\n  let rxMap = null;\n  let rxVehicleMarker = null;\n  let rxVehicleElement = null;\n  let rxRoutePath = [];\n  let rxSegments = [];\n  let rxTotalDistance = 0;\n  let rxProgressLine = null;\n  let rxTween = null;\n  let rxRouteViewport = null;\n  let rxArrivalStartZoom = null;\n  let rxArrivalStartCenter = null;\n  let rxAdvancedMarkerElement = null;\n  let rxArrivalMarker = null;\n  let rxArrivalElement = null;\n\n  const rxAnimation = { progress: 0 };\n\n  \/* ================================================================\n     3. INTERFAZ Y FORMATO\n     ================================================================ *\/\n  function rxError(message) {\n    const element = document.getElementById(\"rx-status\");\n    element.textContent = message;\n    element.hidden = false;\n  }\n\n  function rxState(label) {\n    document.getElementById(\"rx-progress-state\").textContent = label;\n  }\n\n  function rxProgress(progress) {\n    const value = Math.round(progress * 100);\n    document.getElementById(\"rx-progress-value\").textContent = value + \"%\";\n    document.getElementById(\"rx-percent-card\").textContent = value + \"%\";\n    document.getElementById(\"rx-progress-bar\").style.width = value + \"%\";\n  }\n\n  function rxDistance(meters) {\n    if (!meters) return \"\u2014\";\n    if (meters < 1000) return Math.round(meters) + \" m\";\n    return (meters \/ 1000).toFixed(1).replace(\".\", \",\") + \" km\";\n  }\n\n  function rxDuration(milliseconds) {\n    if (!milliseconds) return \"\u2014\";\n    return Math.max(1, Math.round(milliseconds \/ 60000)) + \" min\";\n  }\n\n  window.gm_authFailure = function () {\n    rxError(\n      \"Google rechaz\u00f3 la clave. Revisa la facturaci\u00f3n y autoriza \" +\n      \"https:\/\/www.blakmedia.cl en las restricciones HTTP.\"\n    );\n  };\n\n  \/* ================================================================\n     4. GOOGLE MAPS Y C\u00c1LCULO DE LA RUTA\n     ================================================================ *\/\n  function rxLoadGoogle() {\n    return new Promise(function (resolve, reject) {\n      window.rxGoogleReady = resolve;\n\n      const parameters = new URLSearchParams({\n        key: RX_GOOGLE_MAPS_API_KEY,\n        v: \"weekly\",\n        language: \"es\",\n        loading: \"async\",\n        callback: \"rxGoogleReady\",\n      });\n\n      const script = document.createElement(\"script\");\n      script.src =\n        \"https:\/\/maps.googleapis.com\/maps\/api\/js?\" + parameters.toString();\n      script.async = true;\n      script.onerror = reject;\n      document.head.appendChild(script);\n    });\n  }\n\n  function rxCoordinate(point, property) {\n    const value = point[property];\n    return typeof value === \"function\" ? value.call(point) : value;\n  }\n\n  function rxBuildSegments(path, spherical) {\n    rxSegments = [];\n    rxTotalDistance = 0;\n\n    for (let index = 0; index < path.length - 1; index += 1) {\n      const distance = spherical.computeDistanceBetween(\n        path[index],\n        path[index + 1]\n      );\n\n      rxSegments.push({\n        start: rxTotalDistance,\n        distance: distance,\n        from: path[index],\n        to: path[index + 1],\n        pathIndex: index,\n      });\n\n      rxTotalDistance += distance;\n    }\n  }\n\n  function rxFitCompleteRoute() {\n    if (!rxMap) return;\n    if (!rxRouteViewport) return;\n\n    rxMap.fitBounds(rxRouteViewport, 76);\n\n    google.maps.event.addListenerOnce(rxMap, \"idle\", function () {\n      if (rxMap.getZoom() > RX_MAX_AUTO_ZOOM) {\n        rxMap.setZoom(RX_MAX_AUTO_ZOOM);\n      }\n    });\n  }\n\n  function rxCreateEndpoint(\n    AdvancedMarkerElement,\n    position,\n    letter,\n    isEnd\n  ) {\n    const element = document.createElement(\"div\");\n    element.className = isEnd ? \"rx-endpoint end\" : \"rx-endpoint\";\n    element.textContent = letter;\n\n    const marker = new AdvancedMarkerElement({\n      map: rxMap,\n      position: position,\n      anchorLeft: \"-50%\",\n      anchorTop: \"-50%\",\n      zIndex: 2,\n    });\n\n    marker.appendChild(element);\n  }\n\n  function rxHideArrivalPin() {\n    if (!rxArrivalMarker) return;\n\n    rxArrivalMarker.map = null;\n    rxArrivalMarker.remove();\n    rxArrivalMarker = null;\n    rxArrivalElement = null;\n  }\n\n  function rxShowArrivalPin() {\n    rxHideArrivalPin();\n    if (!rxAdvancedMarkerElement) return;\n    if (!rxRoutePath.length) return;\n\n    rxArrivalElement = document\n      .getElementById(\"rx-arrival-template\")\n      .content.firstElementChild.cloneNode(true);\n\n    rxArrivalMarker = new rxAdvancedMarkerElement({\n      map: rxMap,\n      position: rxRoutePath[rxRoutePath.length - 1],\n      anchorLeft: \"-50%\",\n      anchorTop: \"-100%\",\n      title: \"Llegaste a tu destino\",\n      zIndex: 20,\n    });\n    rxArrivalMarker.appendChild(rxArrivalElement);\n\n    gsap.to(rxArrivalElement, {\n      opacity: 1,\n      y: 0,\n      scale: 1,\n      duration: 0.75,\n      ease: \"back.out(1.7)\",\n    });\n  }\n\n  function rxRestoreVehicle() {\n    if (!rxVehicleMarker) return;\n    if (!rxVehicleElement) return;\n\n    gsap.killTweensOf(rxVehicleElement);\n    rxVehicleElement.style.opacity = \"1\";\n    rxVehicleMarker.map = rxMap;\n  }\n\n  function rxHideVehicleAndShowPin() {\n    if (!rxVehicleMarker) {\n      rxShowArrivalPin();\n      return;\n    }\n\n    gsap.to(rxVehicleElement, {\n      opacity: 0,\n      duration: 0.35,\n      ease: \"power2.in\",\n      onComplete: function () {\n        rxVehicleMarker.map = null;\n        rxShowArrivalPin();\n      },\n    });\n  }\n\n  \/* ================================================================\n     5. MOVIMIENTO DEL VEH\u00cdCULO\n     ================================================================ *\/\n  function rxUpdateVehicle(progress) {\n    const targetDistance = rxTotalDistance * progress;\n    let segment = rxSegments[rxSegments.length - 1];\n\n    for (let index = 0; index < rxSegments.length; index += 1) {\n      const candidate = rxSegments[index];\n\n      if (targetDistance <= candidate.start + candidate.distance) {\n        segment = candidate;\n        break;\n      }\n    }\n\n    const localProgress = segment.distance\n      ? (targetDistance - segment.start) \/ segment.distance\n      : 0;\n\n    const startLat = rxCoordinate(segment.from, \"lat\");\n    const startLng = rxCoordinate(segment.from, \"lng\");\n    const endLat = rxCoordinate(segment.to, \"lat\");\n    const endLng = rxCoordinate(segment.to, \"lng\");\n\n    const position = {\n      lat: startLat + (endLat - startLat) * localProgress,\n      lng: startLng + (endLng - startLng) * localProgress,\n    };\n\n    rxVehicleMarker.position = position;\n\n    const rotation =\n      Math.atan2(endLng - startLng, endLat - startLat) * (180 \/ Math.PI);\n    rxVehicleElement.style.transform = \"rotate(\" + rotation + \"deg)\";\n\n    const completedPath = rxRoutePath.slice(0, segment.pathIndex + 1);\n    completedPath.push(position);\n    rxProgressLine.setPath(completedPath);\n    rxProgress(progress);\n\n    \/* Acercamiento lento y progresivo durante el tramo final. *\/\n    if (progress >= RX_ARRIVAL_ZOOM_START) {\n      if (rxArrivalStartZoom === null) {\n        const currentCenter = rxMap.getCenter();\n        rxArrivalStartZoom = rxMap.getZoom();\n        rxArrivalStartCenter = {\n          lat: currentCenter.lat(),\n          lng: currentCenter.lng(),\n        };\n      }\n\n      const arrivalProgress =\n        (progress - RX_ARRIVAL_ZOOM_START) \/\n        (1 - RX_ARRIVAL_ZOOM_START);\n\n      \/* Curva suave: comienza y termina lentamente. *\/\n      const smoothProgress =\n        arrivalProgress * arrivalProgress * (3 - 2 * arrivalProgress);\n\n      const arrivalZoom =\n        rxArrivalStartZoom +\n        (RX_ARRIVAL_FINAL_ZOOM - rxArrivalStartZoom) * smoothProgress;\n\n      const arrivalCenter = {\n        lat:\n          rxArrivalStartCenter.lat +\n          (position.lat - rxArrivalStartCenter.lat) * smoothProgress,\n        lng:\n          rxArrivalStartCenter.lng +\n          (position.lng - rxArrivalStartCenter.lng) * smoothProgress,\n      };\n\n      rxMap.moveCamera({\n        center: arrivalCenter,\n        zoom: arrivalZoom,\n      });\n    }\n  }\n\n  function rxStart() {\n    if (!rxVehicleMarker) return;\n    if (!rxSegments.length) return;\n\n    if (rxTween) {\n      rxTween.play();\n      rxState(\"Recorrido en curso\");\n      return;\n    }\n\n    if (rxAnimation.progress >= 1) {\n      rxAnimation.progress = 0;\n      rxHideArrivalPin();\n      rxRestoreVehicle();\n      rxArrivalStartZoom = null;\n      rxArrivalStartCenter = null;\n      rxFitCompleteRoute();\n      rxUpdateVehicle(0);\n    }\n\n    rxState(\"Recorrido en curso\");\n\n    rxTween = gsap.to(rxAnimation, {\n      progress: 1,\n      duration: RX_ANIMATION_SECONDS,\n      ease: \"power1.inOut\",\n      onUpdate: function () {\n        rxUpdateVehicle(rxAnimation.progress);\n      },\n      onComplete: function () {\n        rxTween = null;\n        rxState(\"Destino alcanzado\");\n        rxHideVehicleAndShowPin();\n      },\n    });\n  }\n\n  function rxPause() {\n    if (!rxTween) return;\n    rxTween.pause();\n    rxState(\"Recorrido pausado\");\n  }\n\n  function rxReset() {\n    if (rxTween) {\n      rxTween.kill();\n      rxTween = null;\n    }\n\n    rxAnimation.progress = 0;\n    rxHideArrivalPin();\n    rxRestoreVehicle();\n    rxArrivalStartZoom = null;\n    rxArrivalStartCenter = null;\n    rxFitCompleteRoute();\n    if (rxVehicleMarker) rxUpdateVehicle(0);\n    rxState(\"Listo para comenzar\");\n  }\n\n  \/* ================================================================\n     6. INICIALIZACI\u00d3N\n     ================================================================ *\/\n  async function rxInitialize() {\n    document.getElementById(\"rx-origin-label\").textContent = RX_ORIGIN_LABEL;\n    document.getElementById(\"rx-destination-label\").textContent =\n      RX_DESTINATION_LABEL;\n\n    if (!RX_GOOGLE_MAPS_API_KEY.trim()) {\n      rxError(\"Falta pegar la clave en RX_GOOGLE_MAPS_API_KEY.\");\n      rxState(\"Configuraci\u00f3n pendiente\");\n      return;\n    }\n\n    if (typeof gsap === \"undefined\") {\n      rxError(\"No se pudo cargar GSAP.\");\n      return;\n    }\n\n    try {\n      await rxLoadGoogle();\n\n      const libraries = await Promise.all([\n        google.maps.importLibrary(\"maps\"),\n        google.maps.importLibrary(\"marker\"),\n        google.maps.importLibrary(\"routes\"),\n        google.maps.importLibrary(\"geometry\"),\n      ]);\n\n      const Map = libraries[0].Map;\n      const AdvancedMarkerElement = libraries[1].AdvancedMarkerElement;\n      const Route = libraries[2].Route;\n      const spherical = libraries[3].spherical;\n      rxAdvancedMarkerElement = AdvancedMarkerElement;\n\n      rxMap = new Map(document.getElementById(\"rx-map\"), {\n        center: RX_ORIGIN,\n        zoom: 13,\n        mapId: \"DEMO_MAP_ID\",\n        mapTypeControl: false,\n        streetViewControl: false,\n        fullscreenControl: true,\n        zoomControl: true,\n        clickableIcons: false,\n      });\n\n      const result = await Route.computeRoutes({\n        origin: RX_ORIGIN,\n        destination: RX_DESTINATION,\n        travelMode: \"DRIVING\",\n        fields: [\"path\", \"viewport\", \"distanceMeters\", \"durationMillis\"],\n      });\n\n      if (!result.routes) throw new Error(\"Google no devolvi\u00f3 rutas.\");\n      if (!result.routes.length) throw new Error(\"No se encontr\u00f3 una ruta.\");\n\n      const route = result.routes[0];\n      rxRoutePath = route.path;\n\n      new google.maps.Polyline({\n        map: rxMap,\n        path: rxRoutePath,\n        strokeColor: \"#4338ca\",\n        strokeOpacity: 0.16,\n        strokeWeight: 16,\n        clickable: false,\n        zIndex: 1,\n      });\n\n      new google.maps.Polyline({\n        map: rxMap,\n        path: rxRoutePath,\n        strokeColor: \"#6d5dfc\",\n        strokeOpacity: 0.72,\n        strokeWeight: 7,\n        clickable: false,\n        zIndex: 2,\n      });\n\n      rxProgressLine = new google.maps.Polyline({\n        map: rxMap,\n        path: [rxRoutePath[0]],\n        strokeColor: \"#06b6d4\",\n        strokeOpacity: 1,\n        strokeWeight: 7,\n        clickable: false,\n        zIndex: 3,\n      });\n\n      rxRouteViewport = route.viewport;\n      rxFitCompleteRoute();\n\n      rxBuildSegments(rxRoutePath, spherical);\n      rxCreateEndpoint(AdvancedMarkerElement, rxRoutePath[0], \"A\", false);\n      rxCreateEndpoint(\n        AdvancedMarkerElement,\n        rxRoutePath[rxRoutePath.length - 1],\n        \"B\",\n        true\n      );\n\n      rxVehicleElement = document\n        .getElementById(\"rx-vehicle-template\")\n        .content.firstElementChild.cloneNode(true);\n\n      rxVehicleMarker = new AdvancedMarkerElement({\n        map: rxMap,\n        position: rxRoutePath[0],\n        anchorLeft: \"-50%\",\n        anchorTop: \"-50%\",\n        title: \"Veh\u00edculo en recorrido\",\n        zIndex: 10,\n      });\n      rxVehicleMarker.appendChild(rxVehicleElement);\n\n      document.getElementById(\"rx-distance\").textContent = rxDistance(\n        route.distanceMeters\n      );\n      document.getElementById(\"rx-duration\").textContent = rxDuration(\n        route.durationMillis\n      );\n\n      document.getElementById(\"rx-start\").addEventListener(\"click\", rxStart);\n      document.getElementById(\"rx-pause\").addEventListener(\"click\", rxPause);\n      document.getElementById(\"rx-reset\").addEventListener(\"click\", rxReset);\n\n      rxProgress(0);\n      rxState(\"Listo para comenzar\");\n    } catch (error) {\n      console.error(error);\n      rxError(\"Error: \" + String(error.message || error));\n      rxState(\"No se pudo iniciar\");\n    }\n  }\n\n  rxInitialize();\n<\/script>\n<\/div><\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-template-blank.php","meta":{"footnotes":""},"class_list":["post-1292","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/pages\/1292","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/comments?post=1292"}],"version-history":[{"count":70,"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/pages\/1292\/revisions"}],"predecessor-version":[{"id":1433,"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/pages\/1292\/revisions\/1433"}],"wp:attachment":[{"href":"https:\/\/www.blakmedia.cl\/index.php\/wp-json\/wp\/v2\/media?parent=1292"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}