尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證正在載入完整 README。 以下為已儲存的摘錄。
Landing comercial de TapCar: la operación de tus vehículos a un toque. Cada vehículo guarda sus papeles (Permiso de Circulación, Revisión Técnica, SOAP, etc.), registra quién lo usa con un PIN y abre su ficha pública desde un chip NFC —pegado al parabrisas o de llavero—. Sirve tanto para un auto particular como para una flota de empresa; el precio es por vehículo. Sitio estático en HTML/CSS/JS, implementado a partir de un diseño de Claude Design.檢視完整 README ↗
Landing comercial de TapCar: los documentos de tus vehículos a un toque. Cada vehículo guarda sus papeles (Permiso de Circulación, Revisión Técnica, SOAP, etc.), abre su ficha pública desde un chip NFC —pegado al parabrisas o de llavero— y, en el plan Flotas, registra quién lo usa con un PIN.
Uso particular es gratis con 1 vehículo, con TapCar+, el plan de pago por vehículo al año; Flotas se cobra por vehículo en UF. Hay perfiles aparte para automotoras, aseguradoras y gestorías.
Sitio estático en HTML/CSS/JS, implementado a partir de un diseño de Claude Design.
| Página | Archivo | Contenido |
|---|---|---|
| Inicio | index.html | Hero con la insignia "Gratis con 1 auto", el titular con rotor de palabra, un solo CTA ("Crear cuenta gratis") más "Ver planes", y la escena Tap; franja de prueba social con el contador (+600 vehículos); "Así de simple" (3 pasos con mini escenas); "¿Para quién es?" (Uso particular, Flotas, empresas del rubro); 4 beneficios; CTA final |
| ¿Cómo funciona? | como-funciona/index.html | Paso a paso en dos fases (puesta en marcha 01–04, día a día 05–08), con los pasos de Flotas marcados; bloque compacto "Con conductores, el mismo Tap controla la operación" (6 ítems de Flotas); enlace a ¿Es legal?; FAQ de 9 preguntas; CTA |
| Planes | planes/index.html | Pills de plan (Uso particular / Flotas). Uso particular: dos tarjetas (Gratis, $0 con 1 vehículo; TapCar+, $5.000 al año por vehículo), franja "Calcula tu TapCar+" con slider 1–10, el total anual y un botón secundario "Empezar con TapCar+", nota de chips. /planes/#flotas abre directo la pestaña Flotas (¿Cómo funciona? enlaza ahí). Flotas: toggle mensual/anual con el ahorro, sliders de vehículos y cuentas de conductor, formulario sobre 100 vehículos. Franja de empresas del rubro, FAQ de 7 preguntas, CTA |
| Empresas del rubro | socios/index.html | Los tres perfiles en tarjetas cortas (automotoras y aseguradoras con Convenio, gestorías con Envíos), tres pasos para empezar (el tercero, las condiciones, se conversa), FAQ de 4 preguntas y formulario de contacto. Enlazada desde el nav ("Socios"), la tarjeta de empresas del rubro del inicio, la franja de Planes y el footer |
| Términos de uso y privacidad | terminos/index.html | Términos del sitio y cómo trata los datos que se dejan en él; las secciones 6 (datos) y 7 (cookies, ancla #cookies, a la que enlaza el banner) describen Google Analytics y el consentimiento. El servicio tiene sus propios términos, que la app pide aceptar al crear la cuenta según el tipo de cuenta. Enlazada solo desde el footer, no desde el nav |
| ¿Es legal? | legal/index.html | Tabla documento por documento, las cuatro bases normativas, los tres requisitos de validez, qué pasa en un control, referencias oficiales y FAQ de 8 preguntas. Es la página pensada para captar búsqueda: "¿es legal mostrar los documentos del auto en digital?" es una consulta real y poco respondida |
.
├── index.html # Home / Landing (autónoma, CSS incrustado)
├── como-funciona/index.html
├── planes/index.html
├── socios/index.html
├── legal/index.html
├── terminos/index.html
├── styles.css # Design system compartido (fuente editable)
├── assets/ # Imágenes publicadas y el script de analítica
│ ├── analitica.js # Google Analytics 4: consentimiento, banner de cookies y eventos
│ ├── chip-llavero.webp # Foto del llavero NFC
│ ├── impulse-ai.webp # Lockup de la matriz, para el footer (3x)
│ └── og-tapcar.png # Tarjeta 1200×630 al compartir el link
├── favicon.svg
├── robots.txt # Bloquea /_design_src/, apunta al sitemap
├── sitemap.xml
├── llms.txt # Resumen del sitio para motores generativos
├── precios.json # Fuente única de las tarifas (publicada, es contrato con la app)
├── api/contacto.js # Función serverless: formularios de flota y de socios -> Resend
├── tools/precios.py # Revisa las cifras del sitio contra precios.json; --generar escribe la calculadora
├── tools/schema.py # Regenera el JSON-LD desde el contenido visible
├── tools/test_contacto.js # Pruebas de la función de contacto, sin red
├── tools/test_analitica.js # Pruebas de assets/analitica.js, con un DOM falso
├── tools/test_html.js # Pruebas del HTML de las seis páginas para la analítica
└── _design_src/ # Archivos originales del diseño y fotos sin procesar
_design_src/ está versionado pero no es parte del sitio: si lo publicas tal cual, esas rutas quedan accesibles. Por eso robots.txt las excluye.
styles.css es la fuente editable. El mismo CSS va incrustado dentro del <style> de cada .html para que las páginas funcionen de forma autónoma (abrir con doble clic, sin servidor).
Tras editar styles.css hay que volver a incrustarlo en las seis páginas:
python -c "import io; css=io.open('styles.css',encoding='utf-8').read().rstrip(); [io.open(f,'w',encoding='utf-8',newline='').write(h[:h.index('<style>')+7]+'\n'+css+'\n'+h[h.index(' </style>'):]) for f in ['index.html','planes/index.html','legal/index.html','como-funciona/index.html','terminos/index.html','socios/index.html'] for h in [io.open(f,encoding='utf-8').read()]]"
Editar el <style> de un .html a mano hace que ese cambio se pierda en la siguiente sincronización.
Abre index.html en el navegador, o sirve la carpeta con cualquier servidor estático:
python -m http.server 4310
Sírvela en vez de abrir el archivo si vas a probar la navegación: los enlaces entre páginas son absolutos (/planes/) y las imágenes viven en /assets/.
El sistema visual se toma de la app, no se inventa: los tokens viven en :root de styles.css y copian app/globals.css y docs/ui.md del repo nfc.
display=swap). Caveat ya no se carga: solo la usaba la anotación manuscrita de Planes, que desapareció con el rediseño.tinta #16191f, acero #5b6573, linea #e3e6ea, lienzo #f6f7f9, superficie #fff, azul #2952e6 (marca) con azul-press, azul-50 y azul-100, y ambar. Los estados (Vigente, Por vencer, Vencido) tienen sus variantes -texto y -suave: un color de estado nunca va solo —siempre con texto o ícono—, y como texto siempre la variante -texto, que pasa 4,5:1.ease-salir (lo que entra), ease-entrar (lo que se va) y duraciones 120/200/320ms. Toda animación vive bajo @media (prefers-reduced-motion: no-preference) y mueve solo transform y opacity. El estado base es el final: con movimiento reducido, sin JavaScript o sin soporte de animación se ve todo armado y quieto, nunca a medio armar. Como regla, no más de uno o dos elementos animándose a la vez en una vista..btn, alto mínimo 44px), tarjeta (.card), chip de ícono (.icon-chip), pill de estado (.badge, siempre con texto e ícono), placa de patente (.placa). Íconos SVG de trazo, familia Lucide como la app. Sin emoji.HTML + SVG + CSS, autónoma, con todo bajo el prefijo tap- (bloque <escena-tap> de styles.css). Un llavero NFC —el mismo dibujo de components/brand/Llavero.tsx de la app— entra desde la izquierda y toca el celular; nacen las ondas NFC y en la pantalla se abre la ficha pública: encabezado, placa GJ·KP·47, "Toyota Hilux · 2022", el resumen "1 por vencer" y tres filas escalonadas (Permiso de Circulación Vigente, Revisión Técnica Por vencer, SOAP Vigente), cada pill con un pequeño rebote. Pausa de lectura, se desvanece y vuelve el reposo. Ciclo de 6 s en bucle; la línea de tiempo está comentada al inicio del bloque.
IntersectionObserver en el script del inicio le pone .tap-pausada a la escena cuando sale del viewport, y esa clase congela todo con animation-play-state: paused. Sin IntersectionObserver simplemente sigue corriendo.aria-hidden="true"): lo que muestra ya lo dice el subtítulo del hero.data-reveal lo que aparece, y sube 8px con fundido, 320ms, escalonado 40ms entre hermanos (ver "Comportamientos con JS")..is-visible al paso. Cada paso espera su turno (--paso-t), 700ms después del anterior, para que nunca haya más de dos moviéndose a la vez. El turno lo reparte el script del inicio en orden de entrada —en celular, en una columna, los pasos entran de a uno o de a varios según el alto de la pantalla y la velocidad del scroll—; los nth-child del CSS (150/850/1550ms) son el respaldo para cuando el script no corre, y calzan con el caso de escritorio.Cada página lleva su script incrustado al final del <body>:
pathname, y cierra con Escape, con clic fuera o al volver a escritorio. En escritorio la barra lleva los cinco enlaces, "Ingresar" y "Crear cuenta gratis"; bajo 860px "Ingresar" se queda visible en la barra (no entra al panel) y "Crear cuenta gratis" pasa a vivir solo dentro del panel.h1 lleva un aria-label fijo con la frase completa y el rotor se desactiva con prefers-reduced-motion.MutationObserver mira cuándo cada paso recibe .is-visible y le asigna --paso-t en orden de entrada. Ver "Aparición al hacer scroll y turnos de los pasos".data-reveal lo que aparece y sube 8px con fundido, 320ms, escalonado 40ms entre hermanos. Va con animation (no transition, que chocaba con el hover de las tarjetas) y solo bajo prefers-reduced-motion: no-preference: sin JS o con movimiento reducido todo se ve en su estado final.data-valor y el texto del span, las dos), así que sin JS se ve igual, solo que sin animar. data-prefijo es lo que va pegado delante (hoy +).n × precio anual, sin selector mensual/anual porque el plan es solo anual) y la fórmula ("2 vehículos × $5.000"). Escribe además el despacho de Uso particular desde ENVIO_PARTICULAR. Desde 5 vehículos muestra un puente al plan Flotas ("también puedes tomar Flotas"), y en 10 avisa que es el tope.minmax(0, 1fr), no 1fr: las fichas con texto que no envuelve (patentes, documentos) estiraban la columna y sacaban scroll lateral.html lleva text-size-adjust: 100% y overflow-x: clip (no hidden, que rompe el nav sticky) como red de seguridad contra el desborde horizontal en móviles reales.<br class="lp-hero__nl"> que está activo en todos los anchos: el titular se lee siempre en dos líneas, "Tu vehículo / a un Tap.". Antes solo existía bajo 560px, y en escritorio el h1 cabía en una línea con "auto" y pasaba a dos con "camioneta" (y entre 1024 y 1100px también con "vehículo"); como la grilla del hero centra en vertical, todo el hero bajaba y subía ~30px cada 2,6 s, con cada vuelta del rotor. Con el salto forzado el alto del titular no depende de la palabra. Si agregas palabras al rotor, ninguna debería ser tan larga que parta también la primera línea ("Tu camioneta" es la más larga hoy).Cada página lleva su bloque de canonical + Open Graph + Twitter Card, con URLs absolutas a https://tapcar.cl. og:image no admite rutas relativas, así que si cambia el dominio hay que actualizar los seis bloques a mano.
La tarjeta al compartir es assets/og-tapcar.png (1200×630). Está generada con Pillow usando Segoe UI, no Geist —las fuentes de marca no están instaladas localmente—, así que la tipografía no es exacta. Sirve, pero es candidata a rehacerse con las fuentes reales.
precios.json manda. Es la única fuente de las tarifas, y se sirve en https://tapcar.cl/precios.json porque el sitio es estático y cualquier archivo del repo queda publicado. Esa URL es el contrato que app.tapcar.cl verifica contra su configuración de cobro.
Es un contrato para verificar, no una fuente de cobro en vivo. Si la app lo leyera en runtime para facturar, un error de edición acá se cobraría de inmediato y sin revisión.
Su forma desde el 2026-10-20:
planes.particular: gratis: { vehiculos: 1 } (lo que cubre el plan gratis) y vehiculo: { anio: 5000 }. No hay mes: TapCar+ es solo anual y cobra cada vehículo, incluido el primero. Sigue con vehiculos: { min: 1, max: 10 }, moneda: "CLP" e iva_incluido: true.planes.flota: sin cambios (vehiculo y cuenta_conductor con mes y anio en UF, más IVA, de 5 a 100 vehículos desde la web).envio_chips.particular: { hasta: 10, precio: 3000 }: el despacho propio de Uso particular, un monto fijo de 1 a 10 chips. Los tramos ($2.000 / $5.000 / $10.000) son para los demás tipos de cuenta.Tiene que ser idéntico byte a byte a lib/contrato/precios.tapcar.json del repo nfc. El workflow contrato-precios.yml de ese repo baja todos los lunes el archivo publicado y lo compara con diff contra su copia; si no calzan, falla. Por eso un cambio de precios se publica el mismo día que la app: si la web sale antes, promete un precio que la app todavía no cobra (y el contrato falla); si sale después, al revés. La copia de la app se hace copiando este archivo tal cual, no reescribiéndolo.
Tras editarlo:
python tools/precios.py --generar && python tools/schema.py
Para comprobar que el sitio calza con él:
python tools/precios.py
tools/precios.py hace dos cosas distintas a propósito:
--generar escribe las constantes de la calculadora en planes/index.html, entre los marcadores // <precios> y // </precios>: PLANES (con particular: { gratis: 1, veh: { anio: 5000 }, min, max } y flota: { veh: { mes, anio }, cuenta: { mes, anio }, min, max }), ENVIO_PARTICULAR (el despacho fijo de Uso particular, una constante y no una función) y envio(chips) con los tramos, que usa Flotas. Eso es dato estructurado y se genera sin riesgo. Solo datos: la lógica que los usa —descuento(), IVA, chip()— vive fuera de los marcadores, o el generador la borraría.llms.txt y este README) toda cifra con forma de precio y avisa si aparece alguna que precios.json no explica. Las cifras legítimas de Uso particular son el $0 del plan gratis, el precio anual y los totales de 1 a 10 vehículos ($5.000 a $50.000), más su despacho; ya no hay mensual, descuento ni ahorro en Uso particular. Sale con código 1 si encuentra algo.canonicas_por_archivo), no en el sitio entero: Planes tiene que decir el $0, el precio anual por vehículo, el despacho de Uso particular, los tramos de despacho, las dos tarifas anuales de Flotas en UF y el descuento del anual; ¿Cómo funciona? tiene que decir el despacho de Uso particular en el paso del chip. Con un conjunto global, borrar todos los $5.000 de Planes no avisaba, porque $5.000 es también el tramo de 3 a 50 chips y otro archivo lo seguía diciendo.ANCLAS): el monto de la tarjeta Gratis tiene que ser exactamente $0, el de la tarjeta TapCar+ el precio anual por vehículo y el [data-envio-part] el despacho de Uso particular. Que la cifra esté en el archivo no basta: con la tarjeta diciendo $10.000 (el total de 2 vehículos) o el despacho en $2.000 (un tramo válido) la revisión por archivo pasaba. Cada ancla tiene que encontrar su nodo una sola vez; si cambias la clase o el atributo, actualiza ANCLAS o la revisión falla.<head> se revisa pero no cuenta. Una cifra desconocida en el <title> o en una meta se marca igual (es lo que sale en Google), pero una cifra conocida ahí no satisface a la canónica: si se borra la tarjeta de precio, que la meta description todavía diga $5.000 no la tapa.No genera la prosa a propósito: las frases del FAQ y de los términos llevan las cifras metidas en oraciones, y un generador que las escriba produce copy peor y un fuente ilegible. Revisar atrapa el mismo error —cambiaste un número y se te quedó uno viejo— sin pretender escribir español.
Lo que el revisor no ve: que el JavaScript siga funcionando. Solo compara cifras. Después de tocar el bloque generado, hay que abrir la página.
tools/schema.py también lee precios.json, así que el JSON-LD no es una segunda fuente.
Cada página lleva un bloque <script type="application/ld+json" data-schema> con Organization, WebSite, WebPage y BreadcrumbList; el inicio y Planes suman SoftwareApplication con un AggregateOffer de cuatro ofertas (Uso particular gratis, TapCar+ anual por vehículo, Flotas por vehículo y cuenta de conductor fijo), ¿Cómo funciona? suma HowTo (8 pasos) y FAQPage (9 preguntas), Planes suma FAQPage (7), ¿Es legal? un FAQPage de 8 y Empresas del rubro un FAQPage de 4.
No se edita a mano. Lo genera tools/schema.py, que lee las preguntas y los pasos del propio HTML:
python tools/schema.py
Es idempotente: borra el bloque anterior y escribe uno nuevo. Hay que volver a correrlo cada vez que cambien el FAQ, los pasos de ¿Cómo funciona?, los precios, los títulos o las descriptions, o el marcado empieza a prometer cosas que la página ya no dice —que es exactamente lo que Google penaliza—.
Dos mediciones en paralelo, en las seis páginas:
G-VQ42VL3F5T, propiedad "TapCar" de la cuenta [email protected]. Lo carga assets/analitica.js, un solo archivo compartido (<script src="/assets/analitica.js">, sin defer, al final del <body>).El ID se cambia en un solo lugar: la constante ID al principio de assets/analitica.js.
Modo de consentimiento de Google v2, modo avanzado:
analytics_storage, ad_storage, ad_user_data, ad_personalization). En ese estado gtag no escribe cookies y manda señales sin identificador, que GA usa para modelar.analytics_storage. Los tres permisos de publicidad quedan siempre denegados, también al aceptar: no hacemos publicidad y los Términos lo prometen._ga y _ga_<ID> que hubieran quedado de una aceptación anterior.localStorage, clave tapcar-consentimiento ({ v: 1, analitica: true|false, fecha }). Sin decisión válida —o si localStorage falla— el banner vuelve a aparecer y gtag sigue denegado: nunca se concede por un dato que no se pudo leer.[data-preferencias-cookies]) lo vuelve a abrir.Cada evento va a GA4 (gtag) y a Vercel (window.va). Los formularios y la calculadora los mandan con window.tapcarEvento(nombre, params), que nunca lanza; los clics en enlaces (también con el botón del medio, vía auxclick) se miden por delegación en document, sin frenar la navegación.
Ojo con Vercel: los eventos personalizados solo existen en los planes Pro y Enterprise; en Hobby va('event') no registra nada. En Pro cada evento admite 2 propiedades como máximo, por eso ningún evento manda más de dos.
| Evento | Cuándo | Parámetros |
|---|---|---|
clic_crear_cuenta | Clic en un enlace a https://app.tapcar.cl que no es "Ingresar" | ubicacion, texto (texto visible del enlace, hasta 60 caracteres) |
clic_ingresar | Clic en "Ingresar" (.nav__login o data-evento="ingresar") | ubicacion |
clic_correo | Clic en un mailto: | ubicacion |
enviar_formulario | Respuesta OK de /api/contacto | formulario: flota o socios |
usar_calculadora | Primera vez por página que se mueve un slider de Planes | calculadora: particular o flotas |
ubicacion sale del atributo data-ubicacion del enlace. Valores en uso: nav, nav-movil, hero, cta-final, tarjeta-gratis, tarjeta-pago, calculadora-particular, calculadora-flotas, formulario-flota, formulario-socios, footer-contacto, footer-soporte, terminos-derechos, terminos-contacto.
Todo enlace nuevo a app.tapcar.cl o mailto: debe llevar data-ubicacion. Si falta, el evento sale con sin-ubicacion: se nota en los informes, pero ya no se sabe de dónde vino el clic.
node tools/test_analitica.js
node tools/test_html.js
Sin dependencias: prueba las funciones puras (leer y guardar la decisión, el estado de consentimiento, el orden de los comandos de gtag, qué cookies borrar, qué evento corresponde a cada enlace) y iniciar(win) contra un DOM falso. test_html.js, también sin dependencias, revisa las seis páginas: data-ubicacion en todo enlace a la app o mailto:, que carguen analitica.js y tengan "Preferencias de cookies", que enviar_formulario vaya dentro de la rama de éxito de /api/contacto y que usar_calculadora conserve su marca de "una vez por página".
Las secciones 6 y 7 de terminos/index.html describen exactamente lo que hace assets/analitica.js: qué cookies, cuándo, qué se mide, que la publicidad va siempre denegada. Cualquier cambio en el tag (otra herramienta, otro permiso, otro evento con datos nuevos) se publica en el mismo cambio que el texto de los Términos, nunca antes ni después, y si cambia lo que se pregunta se sube VERSION en el script para que todos vuelvan a decidir.
Las 6 páginas llevan al final del <body> la variante html de la documentación de Web Analytics: la cola window.va y el <script defer>.
No se instala el paquete npm @vercel/analytics. La guía de Vercel tiene un selector de framework; el import { Analytics } from '@vercel/analytics/next' que se ve por defecto corresponde al selector nextjs. En el selector html el paso de instalación aparece vacío: no hay npm. Acá no habría dónde importarlo — el sitio es HTML estático sin bundler — e instalarlo dejaría package.json y node_modules para un paquete que el navegador nunca cargaría.
La cola window.va no es opcional: el script va con defer, así que cualquier evento disparado antes de que cargue se perdería sin ella.
Dos condiciones para que mida:
/<unique-path>/script.js) además de /_vercel/insights/script.js. La ruta única resiste mejor a los bloqueadores de anuncios; si el dashboard te da una, cambiar el src es una línea en cada página.llms.txt resume el sitio para motores generativos (ChatGPT, Perplexity, Google AI Overviews): qué resuelve TapCar, qué no es —no es GPS, no emite documentos, no reemplaza los originales—, los precios, la base legal y cómo citar la marca. Es el archivo que evita que un modelo describa mal el producto. Hay que actualizarlo cuando cambien los precios o el alcance. Hoy además declara lo que el sitio no promete —un período de prueba del plan de pago (el plan gratis de Uso particular existe, pero es otra cosa: permanente y con 1 vehículo), el análisis de daños—, para que un modelo no lo invente.
api/contacto.js es la única pieza de servidor del repo. Recibe dos formularios: el del tramo "más de 100 vehículos" de /planes/ y el de empresas del rubro de /socios/, que se distingue por el campo tipo. Los manda a [email protected] a través de Resend.
Vercel toma cualquier .js dentro de /api como función serverless, sin configuración. Está escrita en CommonJS y usa fetch nativo a propósito: un .js con import necesitaría un package.json con "type": "module", y el SDK de Resend obligaría a instalar npm. Ninguna de las dos cosas entra acá.
Dos decisiones que conviene no deshacer:
text de Resend, nunca html). Con HTML habría que escapar cada valor antes de interpolarlo o se puede inyectar markup en el correo que llega.El remitente es [email protected]. El dominio verificado en Resend es el subdominio notifications.tapcar.cl, no el apex tapcar.cl: mandar desde @tapcar.cl lo rechaza, porque el apex solo tiene los registros de Google Workspace. Si algún día se verifica otro dominio, hay que cambiar la constante REMITENTE de la función.
Contra el spam hay un campo trampa (sitio), topes de largo en todos los campos y rechazo de todo lo que no sea POST. No hay captcha a propósito: agrega fricción a la consulta más valiosa del sitio y el destinatario fijo ya acota el daño. Si aparece abuso real, lo que corresponde es activar rate limiting en el WAF de Vercel desde el panel — disponible desde el plan Pro en adelante, no en Hobby — porque entre invocaciones serverless no hay estado compartido para implementarlo a mano.
node tools/test_contacto.js
Sin dependencias y sin red: las pruebas reemplazan el fetch global por uno falso, así que no hace falta ninguna API key para correrlas. Cubren el método no permitido, el JSON malformado, cada regla de validación, la trampa, la key ausente, el error de Resend, el caso feliz y el campo tipo del formulario de socios.
Lo que las pruebas no pueden comprobar es que el correo llegue de verdad. Eso solo se ve desplegado.
| Variable | Dónde | Para qué |
|---|---|---|
RESEND_API_KEY | Vercel → el proyecto → Settings → Environment Variables | Autentica la llamada a Resend |
Nunca va al repo. Si falta, la función responde 500 con un mensaje claro y deja el detalle en los logs de Vercel; el formulario muestra el error y el enlace mailto: que siempre queda visible.
Cuando algo falla —la key expira, se cae la verificación DNS del dominio, se agota la cuota de Resend— cada consulta se pierde con un mensaje amable y el único rastro queda en los logs de Vercel. Hoy no hay alerta: conviene revisarlos de vez en cuando, o montar un log drain si el formulario empieza a traer volumen.
notifications.tapcar.cl; el apex tapcar.cl no sirve como remitente.RESEND_API_KEY, en los tres entornos./planes/ y otra desde /socios/, y confirmar que las dos llegan a [email protected].La landing no tiene staging. main publica directo en tapcar.cl; lo que se revisa antes de mergear es la vista previa de Vercel del PR. Un cambio que dependa de la app (como los precios) se mergea el mismo día que la app sale a producción, con el OK del dueño del proyecto.
/terminos/ no declara domicilio. La sociedad ya está identificada (IMPULSE AI SpA, RUT 78.479.762-7) pero falta la dirección; en un documento legal conviene tenerla.
El documento no pasó por revisión legal. Está escrito en lenguaje simple y, desde el 2026-09-24, cubre solo el sitio (el servicio tiene sus propios términos en la app), pero conviene que un abogado lo revise.
Las fechas de vencimiento de las fichas de ejemplo son ilustrativas a propósito, no tienen que cuadrar con la fecha real.
/legal/ afirma cosas sobre normativa chilena y lleva fecha. Dice "vigente a agosto de 2026" en el disclaimer. Los cuatro enlaces oficiales están verificados uno por uno (BCN 196640 = Ley 19.799, BCN 1007469 = texto refundido de la Ley de Tránsito, el dictamen E71389/2021 de Contraloría y CONASET). Si la normativa cambia, hay que revisar la página y mover esa fecha.
app.tapcar.cl dejó de cobrar el chip el 2026-09-17./terminos/ y en el legalName del schema; el pie lleva © 2026 TapCar y la pertenencia la comunica el lockup de Impulse AI, para no repetir la matriz dos veces en el mismo footer.fetch a /api/contacto. Botón deshabilitado mientras viaja, confirmación con foco al terminar y, si falla, un mensaje de error. El enlace mailto: queda visible siempre, así que ni sin JavaScript ni con la función caída alguien se queda sin forma de escribir.tipo (automotora, aseguradora, gestoría u otra) y sin cantidad de vehículos.https://www.impulseai.cl/<text>font-familyassets/impulse-ai.webpindex.html, no viene de la app: hay que actualizarla cuando cambie o queda desfasada sin que nada avise..plan-tag. Flotas incluye todo lo de TapCar+.