diff --git a/menu.html b/menu.html
index 0a7fe45..b3552ed 100644
--- a/menu.html
+++ b/menu.html
@@ -1,279 +1,280 @@
-
-
- Menú - IntegraRepara
-
-
-
+/* Ajuste limpio para el scroll sin barra abajo */
+.main-content { padding-bottom: calc(env(safe-area-inset-bottom) + 30px); }
+
+.app-card { box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.05); }
+
+/* Clases CSS dinámicas para aplicar las variables */
+.bg-primary-dynamic { background-color: var(--primary) !important; }
+.text-primary-dynamic { color: var(--primary) !important; }
+
-
+
-
+
+
+
+
+
Hola,
+
-
-
+
-
-
-
-
-
- Solicitar
Servicios
- 0 libres
-
+
+
-
+
+
+
+
+
+Buscar
Servicio
+
-
+await loadStatuses();
+await fetchUserData();
+fetchBadges();
+fetchChatNotifications(); // <-- Nueva función
+});
+
+async function fetchChatNotifications() {
+try {
+const res = await fetch(`${API_URL}/worker/notifications`, {
+headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
+});
+const data = await res.json();
+if (data.ok && data.unreadCount > 0) {
+const badge = document.getElementById('badgeChat');
+badge.innerText = data.unreadCount;
+badge.classList.remove('hidden');
+}
+} catch (e) {}
+}
+
+async function loadStatuses() {
+try {
+const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
+const data = await res.json();
+if (data.ok) systemStatuses = data.statuses;
+} catch (e) { console.error("Error cargando estados:", e); }
+}
+
+async function fetchUserData() {
+try {
+const res = await fetch(`${API_URL}/auth/me`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
+const data = await res.json();
+if (data.ok && data.user) {
+userZones = Array.isArray(data.user.zones) ? data.user.zones.map(z => z.cps) : [];
+const resG = await fetch(`${API_URL}/admin/users`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
+const dataG = await resG.json();
+if(dataG.ok) {
+const myUser = dataG.users.find(u => u.id === data.user.id);
+if(myUser) userGuilds = myUser.guilds || [];
+}
+}
+} catch(e) {}
+}
+
+async function fetchBadges() {
+try {
+const headers = { "Authorization": `Bearer ${localStorage.getItem("token")}` };
+const resActive = await fetch(`${API_URL}/services/active`, { headers });
+const dataActive = await resActive.json();
+const resReqs = await fetch(`${API_URL}/agenda/requests`, { headers });
+const dataReqs = await resReqs.json();
+
+if (dataActive.ok && dataReqs.ok) {
+let sinCitaCount = 0;
+sinCitaCount += dataReqs.requests.length;
+dataActive.services.forEach(s => {
+if (s.provider === 'SYSTEM_BLOCK') return;
+const raw = s.raw_data || {};
+if (raw.appointment_status === 'pending') return;
+if (!raw.scheduled_date || raw.scheduled_date.trim() === "") {
+if (raw.status_operativo) {
+const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
+if (st && st.is_final) return;
+}
+sinCitaCount++;
+}
+});
+if (sinCitaCount > 0) {
+const b1 = document.getElementById('badgeAsignados');
+b1.innerText = `${sinCitaCount} pendiente${sinCitaCount > 1 ? 's' : ''}`;
+b1.classList.remove('hidden');
+}
+}
+
+if (localStorage.getItem("role") === 'operario') {
+const resScraped = await fetch(`${API_URL}/providers/scraped`, { headers });
+const dataScraped = await resScraped.json();
+if (dataScraped.ok) {
+let bolsaCount = 0;
+let sinGremioCount = 0;
+dataScraped.services.forEach(s => {
+if(s.assigned_to || s.provider === 'SYSTEM_BLOCK') return;
+const raw = s.raw_data || {};
+const dbStatus = raw.status_operativo;
+if(s.status === 'archived' || (dbStatus && ['anulado', 'terminado', 'finalizado'].includes(dbStatus.toLowerCase()))) return;
+// 🆕 Mismo criterio que solicitar.html: si el aviso no tiene gremio asignado
+// de verdad, es visible para cualquier operario (antes esto no se contaba aquí,
+// por eso el número de la burbuja no cuadraba con lo que salía en la lista).
+const rawGuildId = s.guild_id ?? raw.guild_id;
+const hasGuild = rawGuildId !== undefined && rawGuildId !== null && String(rawGuildId).trim() !== "" && String(rawGuildId) !== "0";
+const sGuild = String(rawGuildId);
+const sCp = String(raw['Código Postal'] || raw['CP'] || "").trim();
+const matchGuild = hasGuild && (userGuilds.includes(Number(sGuild)) || userGuilds.includes(String(sGuild)));
+const matchZone = userZones.some(z => sCp.startsWith(z));
+const guildOk = !hasGuild || userGuilds.length === 0 || matchGuild;
+if(guildOk && (userZones.length === 0 || matchZone)) {
+bolsaCount++;
+if (!hasGuild) sinGremioCount++;
+}
+});
+if (bolsaCount > 0) {
+const b2 = document.getElementById('badgeBolsa');
+let texto = `${bolsaCount} servicio${bolsaCount > 1 ? 's' : ''}`;
+if (sinGremioCount > 0) texto += ` y ${sinGremioCount} sin gremio`;
+b2.innerText = texto;
+b2.classList.remove('hidden');
+}
+}
+}
+} catch (e) { console.log("Error cargando badges"); }
+}
+
+function logout() {
+localStorage.clear();
+window.location.href = "index.html";
+}
+
-
\ No newline at end of file
+