{"id":719,"date":"2026-02-11T18:21:04","date_gmt":"2026-02-11T23:21:04","guid":{"rendered":"https:\/\/onetapdock.com\/?page_id=719"},"modified":"2026-02-11T19:38:35","modified_gmt":"2026-02-12T00:38:35","slug":"tu-restaurante","status":"publish","type":"page","link":"https:\/\/onetapdock.com\/en\/tu-restaurante\/","title":{"rendered":"Carta"},"content":{"rendered":"<!-- ===== OneTap Dock: Reproductor TTS minimalista (Apple-like + Liquid Glass) =====\n     - Lee TODO el texto visible de la p\u00e1gina (o de #tts-content si existe)\n     - UI tipo Apple: glass, blur, bordes sutiles, botones grandes\n     - Siguiente \/ Atr\u00e1s por \u201cbloques\u201d (t\u00edtulos, p\u00e1rrafos, \u00edtems)\n     - Resaltado sutil del bloque le\u00eddo\n\n     \u2705 Uso:\n     1) Pega este bloque en tu p\u00e1gina.\n     2) (Opcional recomendado) Encierra lo que quieres que lea:\n        <div id=\"tts-content\"> ...contenido... <\/div>\n-->\n<style>\n  :root{\n    --glass-bg: rgba(255,255,255,.12);\n    --glass-bg2: rgba(255,255,255,.08);\n    --glass-border: rgba(255,255,255,.22);\n    --glass-border2: rgba(255,255,255,.10);\n    --text: rgba(255,255,255,.92);\n    --muted: rgba(255,255,255,.65);\n\n    --shadow: 0 16px 40px rgba(0,0,0,.28);\n    --radius: 22px;\n\n    \/* Accent (sutil, no \u201cne\u00f3n\u201d) *\/\n    --accent: rgba(255,255,255,.92);\n    --accent2: rgba(255,255,255,.55);\n\n    \/* sizes *\/\n    --h: 64px;\n    --pad: 10px;\n  }\n\n  \/* Contenedor sticky tipo \u201cmini player\u201d *\/\n  .tts-apple{\n    position: sticky;\n    top: 10px;\n    z-index: 99999;\n    padding: 10px 12px;\n  }\n\n  .tts-apple *{ box-sizing:border-box; }\n\n  .tts-card{\n    border-radius: var(--radius);\n    border: 1px solid var(--glass-border2);\n    background:\n      linear-gradient(180deg, rgba(255,255,255,.16), rgba(255,255,255,.06)),\n      radial-gradient(900px 180px at 20% 0%, rgba(255,255,255,.16), transparent 60%),\n      rgba(255,255,255,.06);\n    box-shadow: var(--shadow);\n    backdrop-filter: blur(18px) saturate(140%);\n    -webkit-backdrop-filter: blur(18px) saturate(140%);\n    overflow:hidden;\n    position:relative;\n  }\n\n  \/* \u201cLiquid glass rim\u201d *\/\n  .tts-card::before{\n    content:\"\";\n    position:absolute;\n    inset:0;\n    pointer-events:none;\n    border-radius: var(--radius);\n    border: 1px solid rgba(255,255,255,.18);\n    mask: linear-gradient(#000, transparent 70%);\n    opacity:.75;\n  }\n\n  .tts-bar{\n    display:flex;\n    align-items:center;\n    gap: 10px;\n    padding: 10px;\n    min-height: var(--h);\n  }\n\n  .tts-pill{\n    flex: 1 1 auto;\n    min-width: 220px;\n    padding: 10px 12px;\n    border-radius: 16px;\n    border: 1px solid rgba(255,255,255,.10);\n    background: rgba(0,0,0,.12);\n    backdrop-filter: blur(10px);\n    -webkit-backdrop-filter: blur(10px);\n    color: var(--text);\n    display:flex;\n    flex-direction:column;\n    justify-content:center;\n    gap: 4px;\n    overflow:hidden;\n  }\n\n  .tts-title{\n    font-size: 13px;\n    letter-spacing: .02em;\n    color: var(--muted);\n    white-space: nowrap;\n    overflow:hidden;\n    text-overflow: ellipsis;\n  }\n  .tts-sub{\n    font-size: 14px;\n    font-weight: 700;\n    color: var(--text);\n    white-space: nowrap;\n    overflow:hidden;\n    text-overflow: ellipsis;\n  }\n\n  .tts-controls{\n    display:flex;\n    gap: 8px;\n    align-items:center;\n  }\n\n  .tts-btn{\n    width: 52px;\n    height: 52px;\n    border-radius: 16px;\n    border: 1px solid rgba(255,255,255,.12);\n    background: rgba(0,0,0,.18);\n    color: var(--text);\n    display:grid;\n    place-items:center;\n    cursor:pointer;\n    user-select:none;\n    transition: transform .08s ease, background .18s ease, border-color .18s ease;\n    backdrop-filter: blur(10px);\n    -webkit-backdrop-filter: blur(10px);\n  }\n  .tts-btn:hover{\n    background: rgba(0,0,0,.26);\n    border-color: rgba(255,255,255,.18);\n  }\n  .tts-btn:active{ transform: scale(.98); }\n  .tts-btn:focus-visible{\n    outline: 3px solid rgba(255,255,255,.35);\n    outline-offset: 3px;\n  }\n\n  .tts-btn.primary{\n    background: rgba(255,255,255,.90);\n    color: rgba(0,0,0,.90);\n    border-color: rgba(255,255,255,.65);\n  }\n  .tts-btn.primary:hover{\n    background: rgba(255,255,255,.98);\n  }\n\n  \/* Iconos simples (sin librer\u00edas) *\/\n  .tts-ico{\n    width: 22px;\n    height: 22px;\n    display:block;\n  }\n\n  \/* Drawer: velocidad *\/\n  .tts-drawer{\n    border-top: 1px solid rgba(255,255,255,.10);\n    padding: 10px 12px 12px;\n    display:flex;\n    gap: 12px;\n    align-items:center;\n  }\n\n  .tts-rate{\n    flex: 1 1 auto;\n    display:flex;\n    gap: 10px;\n    align-items:center;\n    border: 1px solid rgba(255,255,255,.10);\n    background: rgba(0,0,0,.12);\n    border-radius: 16px;\n    padding: 10px 12px;\n    color: var(--text);\n  }\n  .tts-rate label{\n    font-size: 13px;\n    color: var(--muted);\n    font-weight: 700;\n    white-space: nowrap;\n  }\n  .tts-rate input[type=\"range\"]{\n    width: 100%;\n    accent-color: rgba(255,255,255,.90);\n  }\n  .tts-val{\n    min-width: 52px;\n    text-align:right;\n    font-variant-numeric: tabular-nums;\n    font-weight: 800;\n    color: var(--text);\n  }\n\n  \/* Highlight del bloque actual *\/\n  .tts-reading{\n    outline: 2px solid rgba(255,255,255,.35);\n    outline-offset: 6px;\n    border-radius: 12px;\n    scroll-margin-top: 120px;\n  }\n\n  \/* Mobile *\/\n  @media (max-width: 560px){\n    .tts-apple{ top: 6px; padding: 8px 10px; }\n    .tts-bar{ padding: 10px; gap: 8px; }\n    .tts-pill{ min-width: 150px; }\n    .tts-btn{ width: 48px; height: 48px; border-radius: 15px; }\n  }\n\n  @media (prefers-reduced-motion: reduce){\n    .tts-btn{ transition:none; }\n  }\n<\/style>\n\n<div class=\"tts-apple\" role=\"region\" aria-label=\"Reproductor de lectura\">\n  <div class=\"tts-card\">\n    <div class=\"tts-bar\">\n      <div class=\"tts-pill\" aria-live=\"polite\" aria-atomic=\"true\">\n        <div class=\"tts-title\" id=\"ttsAppleTitle\">Lectura<\/div>\n        <div class=\"tts-sub\" id=\"ttsAppleSub\">Listo. Presiona play para leer la p\u00e1gina.<\/div>\n      <\/div>\n\n      <div class=\"tts-controls\" aria-label=\"Controles\">\n        <button class=\"tts-btn\" id=\"ttsApplePrev\" type=\"button\" title=\"Atr\u00e1s\">\n          <svg class=\"tts-ico\" viewBox=\"0 0 24 24\" fill=\"none\">\n            <path d=\"M6 5v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/>\n            <path d=\"M19 6l-9 6 9 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\n          <\/svg>\n        <\/button>\n\n        <button class=\"tts-btn primary\" id=\"ttsApplePlay\" type=\"button\" title=\"Reproducir todo\">\n          <svg class=\"tts-ico\" viewBox=\"0 0 24 24\" fill=\"none\" id=\"ttsApplePlayIcon\">\n            <path d=\"M9 7l10 5-10 5V7z\" fill=\"currentColor\"\/>\n          <\/svg>\n        <\/button>\n\n        <button class=\"tts-btn\" id=\"ttsAppleNext\" type=\"button\" title=\"Siguiente\">\n          <svg class=\"tts-ico\" viewBox=\"0 0 24 24\" fill=\"none\">\n            <path d=\"M18 5v14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/>\n            <path d=\"M5 6l9 6-9 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/>\n          <\/svg>\n        <\/button>\n\n        <button class=\"tts-btn\" id=\"ttsAppleStop\" type=\"button\" title=\"Detener\">\n          <svg class=\"tts-ico\" viewBox=\"0 0 24 24\" fill=\"none\">\n            <rect x=\"7\" y=\"7\" width=\"10\" height=\"10\" rx=\"2\" fill=\"currentColor\"\/>\n          <\/svg>\n        <\/button>\n      <\/div>\n    <\/div>\n\n    <div class=\"tts-drawer\">\n      <div class=\"tts-rate\" role=\"group\" aria-label=\"Velocidad de lectura\">\n        <label for=\"ttsAppleRate\">Velocidad<\/label>\n        <input id=\"ttsAppleRate\" type=\"range\" min=\"0.7\" max=\"1.4\" step=\"0.1\" value=\"1.0\" \/>\n        <div class=\"tts-val\" id=\"ttsAppleRateVal\">1.0\u00d7<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n<\/div>\n\n<script>\n(function(){\n  const $ = (s, r=document) => r.querySelector(s);\n\n  const uiTitle = $(\"#ttsAppleTitle\");\n  const uiSub   = $(\"#ttsAppleSub\");\n\n  const btnPlay = $(\"#ttsApplePlay\");\n  const btnPrev = $(\"#ttsApplePrev\");\n  const btnNext = $(\"#ttsAppleNext\");\n  const btnStop = $(\"#ttsAppleStop\");\n\n  const rate = $(\"#ttsAppleRate\");\n  const rateVal = $(\"#ttsAppleRateVal\");\n\n  \/\/ \u2705 Si existe #tts-content, lee solo eso. Si no, lee el body completo.\n  const root = document.getElementById(\"tts-content\") || document.body;\n\n  const SELECTORS = \"h1,h2,h3,h4,li,p,blockquote,dt,dd\";\n  const MIN_CHARS = 28;\n\n  let blocks = [];\n  let idx = 0;\n  let playAll = false;\n  let utter = null;\n\n  function isVisible(el){\n    const st = window.getComputedStyle(el);\n    if(st.display === \"none\" || st.visibility === \"hidden\" || st.opacity === \"0\") return false;\n    const r = el.getBoundingClientRect();\n    return (r.width > 0 && r.height > 0);\n  }\n\n  function cleanText(t){\n    return (t || \"\")\n      .replace(\/\\s+\/g,\" \")\n      .replace(\/\\u00A0\/g,\" \")\n      .trim();\n  }\n\n  function setStatus(title, sub){\n    if(title != null) uiTitle.textContent = title;\n    if(sub != null) uiSub.textContent = sub;\n  }\n\n  function clearHighlight(){\n    document.querySelectorAll(\".tts-reading\").forEach(el => el.classList.remove(\"tts-reading\"));\n  }\n\n  function highlightCurrent(){\n    clearHighlight();\n    const b = blocks[idx];\n    if(!b?.el) return;\n    b.el.classList.add(\"tts-reading\");\n    b.el.scrollIntoView({ behavior: \"smooth\", block: \"center\" });\n  }\n\n  function buildBlocks(){\n    const els = Array.from(root.querySelectorAll(SELECTORS))\n      .filter(el => isVisible(el))\n      .filter(el => !el.closest(\".tts-apple\")); \/\/ no leer el player\n\n    let out = els\n      .map(el => ({ el, text: cleanText(el.innerText) }))\n      .filter(x => x.text.length >= MIN_CHARS);\n\n    if(out.length < 6){\n      \/\/ fallback\n      const all = Array.from(root.querySelectorAll(\"*\"))\n        .filter(el => isVisible(el))\n        .filter(el => !el.closest(\".tts-apple\"));\n      out = all\n        .map(el => ({ el, text: cleanText(el.innerText) }))\n        .filter(x => x.text.length >= MIN_CHARS)\n        .slice(0, 160);\n    }\n\n    \/\/ dedupe\n    const seen = new Set();\n    out = out.filter(x => {\n      const key = x.text.toLowerCase();\n      if(seen.has(key)) return false;\n      seen.add(key);\n      return true;\n    });\n\n    blocks = out;\n    idx = 0;\n  }\n\n  function stopSpeech(msg){\n    if(\"speechSynthesis\" in window) window.speechSynthesis.cancel();\n    utter = null;\n    playAll = false;\n    clearHighlight();\n    if(msg) setStatus(\"Lectura\", msg);\n  }\n\n  function chooseVoice(){\n    const voices = window.speechSynthesis?.getVoices ? window.speechSynthesis.getVoices() : [];\n    return voices.find(v => (v.lang || \"\").toLowerCase().startsWith(\"es\")) || null;\n  }\n\n  function speakAt(i){\n    if(!(\"speechSynthesis\" in window)){\n      setStatus(\"Lectura\", \"Tu navegador no soporta lectura por voz (TTS).\");\n      return;\n    }\n\n    if(!blocks.length){\n      buildBlocks();\n      if(!blocks.length){\n        setStatus(\"Lectura\", \"No encontr\u00e9 texto suficiente para leer en esta p\u00e1gina.\");\n        return;\n      }\n    }\n\n    idx = (i + blocks.length) % blocks.length;\n    highlightCurrent();\n\n    const b = blocks[idx];\n    const text = b?.text || \"\";\n    if(!text){\n      if(playAll) return speakAt(idx + 1);\n      return;\n    }\n\n    \/\/ cancel previous\n    window.speechSynthesis.cancel();\n\n    utter = new SpeechSynthesisUtterance(text);\n    utter.lang = \"es-PE\";\n    utter.rate = Number(rate.value || 1);\n    utter.pitch = 1;\n\n    const v = chooseVoice();\n    if(v) utter.voice = v;\n\n    utter.onstart = () => {\n      setStatus(\"Reproduciendo\", `Secci\u00f3n ${idx+1} de ${blocks.length} \u00b7 ${utter.rate.toFixed(1)}\u00d7`);\n    };\n\n    utter.onend = () => {\n      if(playAll) speakAt(idx + 1);\n      else setStatus(\"Lectura\", \"Listo. Presiona play para continuar.\");\n    };\n\n    utter.onerror = () => {\n      if(playAll) speakAt(idx + 1);\n      else setStatus(\"Lectura\", \"No se pudo reproducir el audio. Intenta nuevamente.\");\n    };\n\n    window.speechSynthesis.speak(utter);\n  }\n\n  \/\/ UI events\n  rateVal.textContent = `${Number(rate.value).toFixed(1)}\u00d7`;\n  rate.addEventListener(\"input\", () => {\n    rateVal.textContent = `${Number(rate.value).toFixed(1)}\u00d7`;\n    if(window.speechSynthesis && window.speechSynthesis.speaking){\n      const keep = playAll;\n      speakAt(idx);\n      playAll = keep;\n    } else {\n      setStatus(\"Lectura\", `Velocidad: ${Number(rate.value).toFixed(1)}\u00d7`);\n    }\n  });\n\n  btnPlay.addEventListener(\"click\", () => {\n    \/\/ Safari\/iOS requiere gesto del usuario: este click sirve para \u201cdesbloquear\u201d la voz.\n    buildBlocks();\n    playAll = true;\n    speakAt(0);\n  });\n\n  btnStop.addEventListener(\"click\", () => stopSpeech(\"Detenido.\"));\n  btnNext.addEventListener(\"click\", () => { playAll = false; speakAt(idx + 1); });\n  btnPrev.addEventListener(\"click\", () => { playAll = false; speakAt(idx - 1); });\n\n  \/\/ Atajos teclado\n  document.addEventListener(\"keydown\", (e) => {\n    if(e.target && (e.target.tagName === \"INPUT\" || e.target.tagName === \"TEXTAREA\")) return;\n    if(e.key === \"Escape\") stopSpeech(\"Detenido.\");\n    if(e.key === \"ArrowRight\") { playAll = false; speakAt(idx + 1); }\n    if(e.key === \"ArrowLeft\")  { playAll = false; speakAt(idx - 1); }\n    if(e.key === \" \") { e.preventDefault(); playAll = false; speakAt(idx); }\n  });\n\n  \/\/ voces (algunos navegadores las cargan luego)\n  if(\"speechSynthesis\" in window && window.speechSynthesis.onvoiceschanged !== undefined){\n    window.speechSynthesis.onvoiceschanged = () => {};\n  }\n\n  \/\/ Init\n  buildBlocks();\n  setStatus(\"Lectura\", \"Listo. Presiona play para leer la p\u00e1gina.\");\n})();\n<\/script><p class=\"wp-block-paragraph\"><\/p>","protected":false},"excerpt":{"rendered":"<p>Lectura Listo. Presiona play para leer la p\u00e1gina. Velocidad 1.0\u00d7<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"page-with-title","meta":{"_uag_custom_page_level_css":"","footnotes":""},"class_list":["post-719","page","type-page","status-publish","hentry"],"uagb_featured_image_src":{"full":false,"thumbnail":false,"medium":false,"medium_large":false,"large":false,"1536x1536":false,"2048x2048":false,"trp-custom-language-flag":false,"mailpoet_newsletter_max":false,"woocommerce_thumbnail":false,"woocommerce_single":false,"woocommerce_gallery_thumbnail":false},"uagb_author_info":{"display_name":"OneTapDockWordpress","author_link":"https:\/\/onetapdock.com\/en\/author\/onetapdockwordpress\/"},"uagb_comment_info":0,"uagb_excerpt":"Lectura Listo. Presiona play para leer la p\u00e1gina. Velocidad 1.0\u00d7","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/pages\/719","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/comments?post=719"}],"version-history":[{"count":11,"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/pages\/719\/revisions"}],"predecessor-version":[{"id":747,"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/pages\/719\/revisions\/747"}],"wp:attachment":[{"href":"https:\/\/onetapdock.com\/en\/wp-json\/wp\/v2\/media?parent=719"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}