Install or repair your pool heater and enjoy your pool 365 days a year. We service all major brands across all of Florida.
🏊 Installation & Repair · All Brands · Free Estimate
Add Photo
Click to upload a photo from our gallery
Add Photo
Click to upload a photo from our gallery
We visit your property, assess your pool size and layout, and recommend the best heating solution at no cost.
We walk you through your options — heat pump, gas, solar — with honest pros, cons, and cost comparisons.
Our certified techs install your system correctly the first time, including all plumbing and electrical connections.
We test the full system, confirm target temperatures, and show you how to operate and maintain your heater.
en cualquier sitio: * <" + "script src="aaj-widget.js">" + "script> * * No requiere frameworks. Vanilla JS. Preferencias guardadas en localStorage. */ (function () { "use strict"; const STORAGE_KEY = "aaj-web-prefs"; /** * Configuración por sitio. Cada cliente define esto ANTES de cargar * aaj-widget.js, por ejemplo: * * window.AAJ_CONFIG = { * siteId: "cliente-001", * simplifyEndpoint: "https://us-central1-TU-PROYECTO.cloudfunctions.net/aajSimplify" * }; * (luego cargar aaj-widget.js) * * Si no se define simplifyEndpoint, el botón "Simplificar" se oculta. */ const config = Object.assign( { siteId: null, simplifyEndpoint: null }, window.AAJ_CONFIG || {} ); const defaultPrefs = { fontScale: 1, // 1 = 100% lineSpacing: 1, // 1 = normal, 1.5 / 2 = aumentado dyslexiaFont: false, highContrast: false, mode: "default", // "default" | "zen" | "gamer" }; let prefs = loadPrefs(); // ---------- Persistencia ---------- function loadPrefs() { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return { ...defaultPrefs }; return { ...defaultPrefs, ...JSON.parse(raw) }; } catch (e) { return { ...defaultPrefs }; } } function savePrefs() { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs)); } catch (e) { /* almacenamiento no disponible, continuar sin persistencia */ } } // ---------- Estilos ---------- function injectStyles() { const style = document.createElement("style"); style.id = "aaj-web-styles"; style.textContent = ` #aaj-toggle { position: fixed; bottom: 20px; left: 20px; width: 56px; height: 56px; border-radius: 50%; background: linear-gradient(135deg, #8aff5e, #5ec6ff); border: none; box-shadow: 0 4px 14px rgba(0,0,0,0.25); cursor: pointer; z-index: 2147483000; display: flex; align-items: center; justify-content: center; font-size: 26px; line-height: 1; transition: transform 0.2s ease; } #aaj-toggle:hover { transform: scale(1.08); } #aaj-toggle:focus-visible { outline: 3px solid #222; outline-offset: 2px; } #aaj-panel { position: fixed; bottom: 86px; right: 20px; width: 300px; max-width: calc(100vw - 40px); max-height: 70vh; overflow-y: auto; background: #14171c; color: #f2f2f2; border-radius: 14px; box-shadow: 0 8px 30px rgba(0,0,0,0.35); font-family: Arial, Helvetica, sans-serif; font-size: 15px; padding: 16px; z-index: 2147483000; display: none; } #aaj-panel.aaj-open { display: block; } #aaj-panel h2 { font-size: 16px; margin: 0 0 12px 0; display: flex; align-items: center; gap: 8px; } #aaj-panel .aaj-section { margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid rgba(255,255,255,0.08); } #aaj-panel .aaj-section:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; } #aaj-panel label { display: block; margin-bottom: 6px; font-weight: bold; } #aaj-panel .aaj-row { display: flex; gap: 8px; flex-wrap: wrap; } #aaj-panel button.aaj-btn { flex: 1; min-width: 60px; padding: 8px 10px; border-radius: 8px; border: 1px solid rgba(255,255,255,0.15); background: #20252d; color: #f2f2f2; cursor: pointer; font-size: 14px; } #aaj-panel button.aaj-btn:hover { background: #2c333d; } #aaj-panel button.aaj-btn.aaj-active { background: #5ec6ff; color: #0a0a0a; border-color: #5ec6ff; font-weight: bold; } #aaj-panel .aaj-toggle-row { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; } #aaj-panel .aaj-footer { font-size: 11px; opacity: 0.6; text-align: center; margin-top: 4px; } /* ---- Estados aplicados al sitio ---- */ html.aaj-dyslexia-font, html.aaj-dyslexia-font * { font-family: "OpenDyslexic", "Comic Sans MS", Arial, sans-serif !important; } html.aaj-high-contrast, html.aaj-high-contrast body { background: #000 !important; color: #fff !important; } html.aaj-high-contrast a { color: #ffd400 !important; } html.aaj-high-contrast img { filter: grayscale(40%) contrast(1.1); } /* Modo Zen (filtro Franz): tonos suaves, baja saturación */ html.aaj-mode-zen { filter: saturate(0.75) brightness(0.97); } html.aaj-mode-zen, html.aaj-mode-zen body { background-color: #f4f1ec !important; } /* Modo Gamer (filtro Kalman): contraste y saturación elevados */ html.aaj-mode-gamer { filter: saturate(1.25) contrast(1.05); } #aaj-reading-highlight { outline: 2px solid #5ec6ff; outline-offset: 2px; border-radius: 3px; } `; document.head.appendChild(style); } // ---------- UI ---------- function buildUI() { // Botón flotante const toggle = document.createElement("button"); toggle.id = "aaj-toggle"; toggle.type = "button"; toggle.setAttribute("aria-label", "Abrir opciones de accesibilidad AimAndJoy"); toggle.setAttribute("aria-expanded", "false"); toggle.innerHTML = "♿"; // Panel const panel = document.createElement("div"); panel.id = "aaj-panel"; panel.setAttribute("role", "dialog"); panel.setAttribute("aria-label", "Opciones de accesibilidad AimAndJoy"); panel.innerHTML = `
`;
document.body.appendChild(toggle);
document.body.appendChild(panel);
toggle.addEventListener("click", () => {
const isOpen = panel.classList.toggle("aaj-open");
toggle.setAttribute("aria-expanded", String(isOpen));
});
panel.addEventListener("click", handlePanelClick);
return { toggle, panel };
}
// ---------- Lógica de acciones ----------
function handlePanelClick(e) {
const btn = e.target.closest("button[data-action]");
if (!btn) return;
const action = btn.dataset.action;
switch (action) {
case "font-inc":
prefs.fontScale = Math.min(2, +(prefs.fontScale + 0.1).toFixed(2));
break;
case "font-dec":
prefs.fontScale = Math.max(0.8, +(prefs.fontScale - 0.1).toFixed(2));
break;
case "font-reset":
prefs.fontScale = 1;
break;
case "spacing-normal":
prefs.lineSpacing = 1;
break;
case "spacing-comfortable":
prefs.lineSpacing = 1.5;
break;
case "spacing-wide":
prefs.lineSpacing = 2;
break;
case "toggle-dyslexia":
prefs.dyslexiaFont = !prefs.dyslexiaFont;
break;
case "toggle-contrast":
prefs.highContrast = !prefs.highContrast;
break;
case "mode-default":
prefs.mode = "default";
break;
case "mode-zen":
prefs.mode = "zen";
break;
case "mode-gamer":
prefs.mode = "gamer";
break;
case "read-page":
readPageAloud();
return; // no necesita re-render del panel
case "stop-read":
stopReadingAloud();
return;
case "simplify":
simplifyPage(btn);
return;
}
savePrefs();
applyPrefs();
syncPanelState();
}
function applyPrefs() {
const html = document.documentElement;
// Tamaño de texto
html.style.fontSize = (prefs.fontScale * 100) + "%";
// Espaciado de línea
if (prefs.lineSpacing === 1) {
html.style.removeProperty("--aaj-line-height");
document.body.style.removeProperty("line-height");
} else {
document.body.style.lineHeight = String(prefs.lineSpacing);
}
// Fuente para dislexia
html.classList.toggle("aaj-dyslexia-font", !!prefs.dyslexiaFont);
// Alto contraste
html.classList.toggle("aaj-high-contrast", !!prefs.highContrast);
// Modo visual (Zen / Gamer / Original)
html.classList.remove("aaj-mode-zen", "aaj-mode-gamer");
if (prefs.mode === "zen") html.classList.add("aaj-mode-zen");
if (prefs.mode === "gamer") html.classList.add("aaj-mode-gamer");
}
function syncPanelState() {
const panel = document.getElementById("aaj-panel");
if (!panel) return;
const dyslexiaBtn = panel.querySelector("#aaj-dyslexia");
dyslexiaBtn.classList.toggle("aaj-active", !!prefs.dyslexiaFont);
dyslexiaBtn.textContent = prefs.dyslexiaFont ? "Desactivar" : "Activar";
const contrastBtn = panel.querySelector("#aaj-contrast");
contrastBtn.classList.toggle("aaj-active", !!prefs.highContrast);
contrastBtn.textContent = prefs.highContrast ? "Desactivar" : "Activar";
const spacingMap = { 1: "spacing-normal", 1.5: "spacing-comfortable", 2: "spacing-wide" };
highlightGroup(panel, ["spacing-normal", "spacing-comfortable", "spacing-wide"], spacingMap[prefs.lineSpacing]);
const modeMap = { default: "mode-default", zen: "mode-zen", gamer: "mode-gamer" };
highlightGroup(panel, ["mode-default", "mode-zen", "mode-gamer"], modeMap[prefs.mode]);
}
function highlightGroup(panel, actions, activeAction) {
actions.forEach((action) => {
const btn = panel.querySelector(`[data-action="${action}"]`);
if (btn) btn.classList.toggle("aaj-active", action === activeAction);
});
}
// ---------- Lectura en voz alta ----------
function readPageAloud() {
if (!("speechSynthesis" in window)) {
alert("Tu navegador no soporta lectura en voz alta.");
return;
}
stopReadingAloud();
const text = getReadableText();
if (!text.trim()) return;
const utterance = new SpeechSynthesisUtterance(text);
utterance.lang = document.documentElement.lang || "es-ES";
window.speechSynthesis.speak(utterance);
}
function stopReadingAloud() {
if ("speechSynthesis" in window) {
window.speechSynthesis.cancel();
}
}
function getReadableText() {
// Extrae texto visible del