signalk-mareas-ihm 2.1.2 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "2.1.2",
3
- "timestamp": "20260609-0141",
2
+ "version": "2.1.4",
3
+ "timestamp": "20260609-0204",
4
4
  "gitHash": null,
5
5
  "gitDirty": true,
6
- "builtAt": "2026-06-08T23:41:36.818Z"
6
+ "builtAt": "2026-06-09T00:04:55.082Z"
7
7
  }
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ function isPositionValue(v) {
49
49
  // timestamp + git hash so we can verify exactly which build is running on the Pi
50
50
  // without ambiguity. ("¿Qué versión tengo deployada?" → /api/paths or landing.)
51
51
  const PLUGIN_VERSION = esmRequire("../package.json").version;
52
- const PLUGIN_REVISION = "Rev313";
52
+ const PLUGIN_REVISION = "Rev315";
53
53
  let _buildInfo = null;
54
54
  try {
55
55
  _buildInfo = esmRequire("./build-info.json");
package/dist/mobile.html CHANGED
@@ -755,6 +755,33 @@ body.mobile-ui #m-bottom-bar .m-bb-grade .ic{font-size:10px}
755
755
  body.mobile-ui #m-bottom-bar .m-bb-grade #m-bb-grade{width:28px!important;height:28px!important;font-size:18px!important}
756
756
  body.mobile-ui #m-bottom-bar #m-bb-pres-svg{width:50px!important;height:18px!important}
757
757
 
758
+ /* Rev315: estilos UNIFICADOS de back-button y título en TODAS las ventanas.
759
+ Estas clases utility se pueden aplicar a cualquier botón/título de un modal
760
+ para forzar la misma apariencia visual en todo el plugin. Color principal
761
+ var(--org) naranja, peso 900, tamaño 22px back y 28px título. */
762
+ .m-back-btn{
763
+ background:rgba(255,178,63,.15)!important;
764
+ border:1px solid var(--org)!important;
765
+ color:var(--org)!important;
766
+ font-size:18px!important;
767
+ font-weight:800!important;
768
+ padding:12px 18px!important;
769
+ border-radius:12px!important;
770
+ cursor:pointer!important;
771
+ min-height:48px!important;
772
+ display:inline-flex!important;align-items:center!important;gap:4px!important;
773
+ letter-spacing:-.2px!important;
774
+ }
775
+ .m-back-btn:active{opacity:.6!important;transform:scale(.95)!important}
776
+ .m-modal-title{
777
+ font-size:28px!important;
778
+ font-weight:900!important;
779
+ color:#fff!important;
780
+ letter-spacing:-.3px!important;
781
+ text-align:center!important;
782
+ margin:0!important;
783
+ }
784
+
758
785
  /* === MODALES fullscreen Rev173: contenido MUCHO más grande, sin pinch, sin scroll horizontal === */
759
786
  body.mobile-ui .popup-overlay{
760
787
  align-items:stretch!important;justify-content:stretch!important;
@@ -790,19 +817,24 @@ body.mobile-ui .m-modal-hdr{
790
817
  display:flex;align-items:center;justify-content:space-between;
791
818
  padding:0 12px;
792
819
  }
820
+ /* Rev315: m-back UNIFICADO (alineado con clase utility .m-back-btn). Ya no
821
+ necesita min-width:110px ni height fija — es más compacto y consistente
822
+ con todos los demás botones Back del proyecto (Anchor Calculation, etc). */
793
823
  body.mobile-ui .m-modal-hdr .m-back{
794
824
  background:rgba(255,178,63,.15);border:1px solid var(--org);color:var(--org);
795
- font-size:22px;font-weight:800;cursor:pointer;padding:14px 20px;
796
- display:flex;align-items:center;gap:6px;min-width:110px;height:56px;
797
- border-radius:12px;
825
+ font-size:18px;font-weight:800;cursor:pointer;padding:12px 18px;
826
+ display:flex;align-items:center;gap:4px;min-height:48px;
827
+ border-radius:12px;letter-spacing:-.2px;
798
828
  }
799
829
  body.mobile-ui .m-modal-hdr .m-back:active{opacity:.6;transform:scale(.95)}
830
+ /* Rev315: título UNIFICADO 28px (antes 38px). Coherente con la clase
831
+ utility .m-modal-title y con todos los modales del proyecto. */
800
832
  body.mobile-ui .m-modal-hdr .m-title{
801
- flex:1;text-align:center;font-size:38px;font-weight:900;color:#fff;
833
+ flex:1;text-align:center;font-size:28px;font-weight:900;color:#fff;
802
834
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
803
- padding:0 8px;letter-spacing:-.4px;
835
+ padding:0 8px;letter-spacing:-.3px;
804
836
  }
805
- body.mobile-ui .m-modal-hdr .m-close-spacer{min-width:110px}
837
+ body.mobile-ui .m-modal-hdr .m-close-spacer{min-width:80px}
806
838
 
807
839
  /* === Tipografía GIGANTE dentro de modales (legibilidad marítima de sol) ===
808
840
  Rev183: subimos todo +2-4 px más. El usuario insiste en que todavía
@@ -1320,30 +1352,30 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1320
1352
  mismas funciones que el desktop -->
1321
1353
  <div id="m-sidebar">
1322
1354
  <!-- Rev187: orden Echar, Centrar, Info, AIS, Cartas, Abrigo, Curvas, Mareas, Alarmas, Fondeo -->
1323
- <button class="m-sb-btn" id="m-sb-anchor" onclick="m_onAnchorBtn()" title="Fondear/Levar"><span class="ico">⚓</span><span id="m-sb-anchor-lbl" data-i18n="sb_echar">Echar</span></button>
1324
- <button class="m-sb-btn" id="m-sb-center" onclick="centerOnAnchor&&centerOnAnchor()||m_centerOnBoat()" title="Centrar mapa">
1355
+ <button class="m-sb-btn" id="m-sb-anchor" onclick="m_onAnchorBtn()" data-i18n-title="sb_anchor_tip" title="Fondear/Levar"><span class="ico">⚓</span><span id="m-sb-anchor-lbl" data-i18n="sb_echar">Echar</span></button>
1356
+ <button class="m-sb-btn" id="m-sb-center" onclick="centerOnAnchor&&centerOnAnchor()||m_centerOnBoat()" data-i18n-title="sb_centrar_tip" title="Centrar mapa">
1325
1357
  <span class="ico" style="display:inline-flex;align-items:center;justify-content:center"><svg viewBox="0 0 44 44" width="34" height="34"><circle cx="22" cy="22" r="18" fill="none" stroke="#f44336" stroke-width="2.5" stroke-dasharray="4 3"/><line x1="22" y1="0" x2="22" y2="44" stroke="#f44336" stroke-width="1.5"/><line x1="0" y1="22" x2="44" y2="22" stroke="#f44336" stroke-width="1.5"/><circle cx="22" cy="22" r="3" fill="#f44336"/><text x="22" y="27" text-anchor="middle" font-size="18" fill="#fff" style="filter:drop-shadow(0 0 2px #000)">⚓</text></svg></span>
1326
1358
  <span data-i18n="sb_centrar">Centrar</span>
1327
1359
  </button>
1328
- <button class="m-sb-btn" id="m-sb-info" onclick="m_openInfoModal()" title="Datos del fondeo">
1360
+ <button class="m-sb-btn" id="m-sb-info" onclick="m_openInfoModal()" data-i18n-title="sb_info_tip" title="Datos del fondeo">
1329
1361
  <span class="ico" style="background:#fff;color:#000;width:32px;height:32px;border-radius:50%;font-family:Georgia,serif;font-style:italic;font-weight:900;font-size:22px;display:inline-flex;align-items:center;justify-content:center;line-height:1">i</span>
1330
1362
  <span data-i18n="sb_info">Info</span>
1331
1363
  </button>
1332
- <button class="m-sb-btn" id="m-sb-ais" onclick="m_toggleAisList()" title="AIS en zona"><span class="ico">🚢</span><span id="m-sb-ais-lbl" data-i18n="sb_ais">AIS</span></button>
1333
- <button class="m-sb-btn" id="m-sb-panel" onclick="m_togglePanel()" title="Cartas y Capas"><span class="ico">🗺️</span><span data-i18n="sb_cartas">Cartas</span></button>
1334
- <button class="m-sb-btn" id="m-sb-abrigo" onclick="openShelterConfig()" title="Previsión abrigo"><span class="ico">🏔️</span><span data-i18n="sb_abrigo">Abrigo</span></button>
1364
+ <button class="m-sb-btn" id="m-sb-ais" onclick="m_toggleAisList()" data-i18n-title="ais_en_zona" title="AIS en zona"><span class="ico">🚢</span><span id="m-sb-ais-lbl" data-i18n="sb_ais">AIS</span></button>
1365
+ <button class="m-sb-btn" id="m-sb-panel" onclick="m_togglePanel()" data-i18n-title="panel_cartas_capas" title="Cartas y Capas"><span class="ico">🗺️</span><span data-i18n="sb_cartas">Cartas</span></button>
1366
+ <button class="m-sb-btn" id="m-sb-abrigo" onclick="openShelterConfig()" data-i18n-title="sb_abrigo_tip" title="Previsión abrigo"><span class="ico">🏔️</span><span data-i18n="sb_abrigo">Abrigo</span></button>
1335
1367
  <!-- Rev297: Curvas y Mareas se han movido al left bar (entre Favorito y KIP).
1336
1368
  Se quitan de aquí para no duplicar. -->
1337
- <button class="m-sb-btn" id="m-sb-alarm" onclick="toggleAlarmPanel()" title="Panel de alarmas"><span class="ico">🔔</span><span data-i18n="sb_alarmas">Alarmas</span></button>
1338
- <button class="m-sb-btn" id="m-sb-fondeo" onclick="openPopup('fondeo-pop');fetchFondeoData()" title="Cálculo fondeo"><span class="ico">⛓️</span><span data-i18n="sb_fondeo">Fondeo</span></button>
1369
+ <button class="m-sb-btn" id="m-sb-alarm" onclick="toggleAlarmPanel()" data-i18n-title="sb_alarmas_tip" title="Panel de alarmas"><span class="ico">🔔</span><span data-i18n="sb_alarmas">Alarmas</span></button>
1370
+ <button class="m-sb-btn" id="m-sb-fondeo" onclick="openPopup('fondeo-pop');fetchFondeoData()" data-i18n-title="sb_fondeo_tip" title="Cálculo fondeo"><span class="ico">⛓️</span><span data-i18n="sb_fondeo">Fondeo</span></button>
1339
1371
  </div>
1340
1372
 
1341
1373
  <!-- Bottom info bar (icon-led + mini sparkline presión + grade abrigo) -->
1342
1374
  <!-- Rev285: viento con flecha grande+kt dentro, junto a SOG.
1343
1375
  Donut de abrigo con letra dentro. SOG sin click. -->
1344
1376
  <div id="m-bottom-bar">
1345
- <div class="it" title="Velocidad sobre fondo"><span class="lb" data-i18n="bb_velocidad">Velocidad</span><span class="vl" id="m-bb-sog">—</span></div>
1346
- <div class="it m-bb-wind" title="Viento real (veleta) · flecha apunta hacia DONDE VA (TO) · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
1377
+ <div class="it" data-i18n-title="bb_velocidad_tip" title="Velocidad sobre fondo"><span class="lb" data-i18n="bb_velocidad">Velocidad</span><span class="vl" id="m-bb-sog">—</span></div>
1378
+ <div class="it m-bb-wind" data-i18n-title="bb_viento_tip" title="Viento real (veleta) · flecha apunta hacia DONDE VA (TO) · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
1347
1379
  <span class="lb" data-i18n="bb_viento">Viento</span>
1348
1380
  <!-- Rev295: alineación correcta con el resto de .vl. El SVG con margin
1349
1381
  negativo compensa el padding interno del viewBox 28 con flecha hasta
@@ -1357,10 +1389,10 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1357
1389
  <span class="vl" id="m-bb-wind-kt-txt">—<span style="font-size:11px;font-weight:700;opacity:.75;margin-left:2px">kts</span></span>
1358
1390
  </div>
1359
1391
  </div>
1360
- <div class="it" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop')"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
1361
- <div class="it" title="Cadena recomendada" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_cadena_rec">Cadena rec.</span><span class="vl" id="m-bb-cad">—</span></div>
1362
- <div class="it" title="Distancia al ancla" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_dist_ancla">Dist. ancla</span><span class="vl" id="m-bb-dist">—</span></div>
1363
- <div class="it m-bb-graph" title="Presión barométrica · línea naranja = AHORA · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
1392
+ <div class="it" data-i18n-title="bb_sonda_tip" title="Profundidad bajo quilla · Sonda" onclick="openPopup&&openPopup('sonda-pop')"><span class="lb" data-i18n="bb_sonda">Sonda</span><span class="vl" id="m-bb-sonda">—</span><span id="m-bb-sonda-status" style="display:none;font-size:9px;font-weight:800;margin-top:2px;text-align:center;line-height:1.1"></span></div>
1393
+ <div class="it" data-i18n-title="bb_cadena_tip" title="Cadena recomendada" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_cadena_rec">Cadena rec.</span><span class="vl" id="m-bb-cad">—</span></div>
1394
+ <div class="it" data-i18n-title="bb_dist_tip" title="Distancia al ancla" onclick="m_openInfoModal&&m_openInfoModal()"><span class="lb" data-i18n="bb_dist_ancla">Dist. ancla</span><span class="vl" id="m-bb-dist">—</span></div>
1395
+ <div class="it m-bb-graph" data-i18n-title="bb_presion_tip" title="Presión barométrica · línea naranja = AHORA · click → Meteo" onclick="toggleMeteo&&toggleMeteo()">
1364
1396
  <span class="lb" data-i18n="bb_presion">Presión</span>
1365
1397
  <!-- Rev288: viewBox extendido 0-80 para reservar espacio a la previsión
1366
1398
  (a la derecha del AHORA). El histórico ocupa 0-60 (línea AHORA en
@@ -1373,11 +1405,11 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1373
1405
  </svg>
1374
1406
  <span class="vl" id="m-bb-pres-val" style="font-size:13px!important">—</span>
1375
1407
  </div>
1376
- <div class="it m-bb-grade" title="Grado de abrigo · click → Abrigo" onclick="openShelterConfig&&openShelterConfig()">
1408
+ <div class="it m-bb-grade" data-i18n-title="bb_abrigo_tip" title="Grado de abrigo · click → Abrigo" onclick="openShelterConfig&&openShelterConfig()">
1377
1409
  <span class="lb" data-i18n="bb_abrigo">Abrigo</span>
1378
1410
  <span id="m-bb-grade" style="display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;border-radius:8px;background:rgba(120,120,120,.35);color:#fff;font-weight:900;font-size:18px;line-height:1">—</span>
1379
1411
  </div>
1380
- <div class="it m-bb-donut" title="Calidad de abrigo (gauge) · click → Abrigo" onclick="openShelterConfig&&openShelterConfig()">
1412
+ <div class="it m-bb-donut" data-i18n-title="bb_calidad_tip" title="Calidad de abrigo (gauge) · click → Abrigo" onclick="openShelterConfig&&openShelterConfig()">
1381
1413
  <span class="lb" data-i18n="bb_calidad">Calidad</span>
1382
1414
  <svg viewBox="0 0 36 36" width="38" height="38" style="display:block">
1383
1415
  <circle cx="18" cy="18" r="15.5" fill="none" stroke="rgba(255,255,255,.15)" stroke-width="3.5"/>
@@ -1777,14 +1809,14 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1777
1809
  <!-- Rev286: modal propio para guardar fondeo favorito (en vez del prompt() nativo cutre). -->
1778
1810
  <div class="popup-overlay" id="m-fav-save-pop" onclick="if(event.target.id==='m-fav-save-pop')closePopup('m-fav-save-pop')">
1779
1811
  <div class="popup-box" onclick="event.stopPropagation()">
1780
- <h4>❤ Guardar fondeo favorito</h4>
1781
- <p style="opacity:.85;margin:8px 0 14px">Nombre del favorito (vacío = nombre automático por geolocalización):</p>
1782
- <input type="text" id="m-fav-save-name" placeholder="p.ej. Cangas - Massó"
1812
+ <h4 data-i18n="fav_guardar_title">❤ Guardar fondeo favorito</h4>
1813
+ <p style="opacity:.85;margin:8px 0 14px" data-i18n="fav_guardar_hint">Nombre del favorito (vacío = nombre automático por geolocalización):</p>
1814
+ <input type="text" id="m-fav-save-name" data-i18n-placeholder="fav_placeholder" placeholder="p.ej. Cangas - Massó"
1783
1815
  style="width:100%;padding:14px 16px;font-size:20px;border-radius:10px;border:2px solid rgba(255,255,255,.25);background:rgba(0,0,0,.35);color:#fff;box-sizing:border-box"
1784
1816
  onkeydown="if(event.key==='Enter')m_favSaveConfirm();">
1785
1817
  <div style="display:flex;gap:10px;justify-content:flex-end;margin-top:18px">
1786
- <button onclick="closePopup('m-fav-save-pop')" style="padding:12px 22px;background:rgba(255,255,255,.15);color:#fff;border:none;border-radius:10px;cursor:pointer;font-size:17px;font-weight:700">Cancelar</button>
1787
- <button onclick="m_favSaveConfirm()" style="padding:12px 22px;background:var(--org);color:#0a1929;border:none;border-radius:10px;cursor:pointer;font-size:17px;font-weight:900">Guardar</button>
1818
+ <button onclick="closePopup('m-fav-save-pop')" style="padding:12px 22px;background:rgba(255,255,255,.15);color:#fff;border:none;border-radius:10px;cursor:pointer;font-size:17px;font-weight:700" data-i18n="m_cancelar">Cancelar</button>
1819
+ <button onclick="m_favSaveConfirm()" style="padding:12px 22px;background:var(--org);color:#0a1929;border:none;border-radius:10px;cursor:pointer;font-size:17px;font-weight:900" data-i18n="m_guardar">Guardar</button>
1788
1820
  </div>
1789
1821
  </div>
1790
1822
  </div>
@@ -1797,7 +1829,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1797
1829
  <div style="margin:4px 0;font-size:21px;display:flex;gap:10px;align-items:center;flex-wrap:wrap">
1798
1830
  <span style="opacity:.6;flex-shrink:0"><span data-i18n="meteo_modelo">Modelo:</span></span>
1799
1831
  <select id="meteo-model" style="background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.3);color:#fff;border-radius:4px;padding:4px 8px;font-size:21px;flex-shrink:0" onchange="fetchMeteo()">
1800
- <option value="" style="background:#1a2a3a;color:#fff">Mejor (automático)</option><option value="&models=icon_seamless" style="background:#1a2a3a;color:#fff">ICON (DWD)</option><option value="&models=gfs_seamless" style="background:#1a2a3a;color:#fff">GFS (NOAA)</option><option value="&models=ecmwf_ifs025" style="background:#1a2a3a;color:#fff">ECMWF IFS</option><option value="&models=meteofrance_seamless" style="background:#1a2a3a;color:#fff">Météo-France</option><option value="&models=gem_seamless" style="background:#1a2a3a;color:#fff">GEM (Canada)</option><option value="&models=jma_seamless" style="background:#1a2a3a;color:#fff">JMA (Japan)</option>
1832
+ <option value="" style="background:#1a2a3a;color:#fff" data-i18n="meteo_modelo_auto">Mejor (automático)</option><option value="&models=icon_seamless" style="background:#1a2a3a;color:#fff">ICON (DWD)</option><option value="&models=gfs_seamless" style="background:#1a2a3a;color:#fff">GFS (NOAA)</option><option value="&models=ecmwf_ifs025" style="background:#1a2a3a;color:#fff">ECMWF IFS</option><option value="&models=meteofrance_seamless" style="background:#1a2a3a;color:#fff">Météo-France</option><option value="&models=gem_seamless" style="background:#1a2a3a;color:#fff">GEM (Canada)</option><option value="&models=jma_seamless" style="background:#1a2a3a;color:#fff">JMA (Japan)</option>
1801
1833
  </select>
1802
1834
  <span id="meteo-model-desc" style="font-size:20px;color:#cde;font-weight:600;flex:1;min-width:200px;line-height:1.25;white-space:normal"></span>
1803
1835
  </div>
@@ -1806,7 +1838,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1806
1838
  como TEXTO HTML — se renderizaba tal cual hasta que fetchMeteo()
1807
1839
  reemplazaba el innerHTML. Ahora simplemente "Cargando…" en plano; JS
1808
1840
  lo sobreescribe en cuanto llega el primer fetch. -->
1809
- <div id="meteo-panel" style="overflow-y:auto;flex:1;padding:0"><div style="text-align:center;padding:20px;opacity:.5;font-size:15px">Cargando…</div></div>
1841
+ <div id="meteo-panel" style="overflow-y:auto;flex:1;padding:0"><div style="text-align:center;padding:20px;opacity:.5;font-size:15px" data-i18n="m_cargando">Cargando…</div></div>
1810
1842
  </div>
1811
1843
  </div>
1812
1844
  <div class="popup-overlay" id="ais-pop" onclick="closePopup('ais-pop')" style="background:transparent;pointer-events:none"><div class="popup-box" onclick="event.stopPropagation()" style="position:fixed;bottom:80px;left:10px;pointer-events:auto;width:321px;max-height:445px;overflow-y:auto;z-index:2001;padding:10px 12px;box-sizing:border-box"><button onclick="closePopup('ais-pop')" style="position:absolute;top:6px;right:8px;background:none;border:none;color:#aaa;font-size:20px;cursor:pointer;padding:2px 6px;line-height:1">✕</button><h4 id="ais-pop-title" style="margin-right:28px">🚢 Target AIS</h4><div id="ais-pop-actions"></div><div id="ais-pop-body"></div></div></div>
@@ -1814,7 +1846,7 @@ body.mobile-ui #shelter-hours .sh-hr{flex:0 0 60px!important}
1814
1846
  <div class="popup-box" onclick="event.stopPropagation()" style="width:520px;max-width:94vw;max-height:85vh;overflow-y:auto">
1815
1847
  <button onclick="closePopup('sonda-pop')" style="position:absolute;top:6px;right:10px;background:none;border:none;color:#aaa;font-size:22px;cursor:pointer;line-height:1">✕</button>
1816
1848
  <h4 style="font-size:18px" data-i18n="calc_sonda_title">📐 Cálculo de Sonda</h4>
1817
- <div id="sonda-content" style="margin-top:10px"><div style="text-align:center;padding:20px;opacity:.5">Cargando...</div></div>
1849
+ <div id="sonda-content" style="margin-top:10px"><div style="text-align:center;padding:20px;opacity:.5" data-i18n="m_cargando">Cargando…</div></div>
1818
1850
  </div>
1819
1851
  </div>
1820
1852
  <div class="popup-overlay" id="fondeo-pop" onclick="closePopup('fondeo-pop')">
@@ -2007,6 +2039,52 @@ var _i18n={
2007
2039
  m_calma:{es:'Calma',en:'Calm'},
2008
2040
  sh_detected:{es:'Detectado',en:'Detected'},
2009
2041
  sh_sectores:{es:'sectores abrigados',en:'sheltered sectors'},
2042
+ /* Rev313: tooltips bottom bar */
2043
+ bb_velocidad_tip:{es:'Velocidad sobre fondo',en:'Speed over ground'},
2044
+ bb_viento_tip:{es:'Viento real (veleta) · flecha apunta hacia DONDE VA (TO) · click → Meteo',en:'True wind (vane) · arrow points TO where it goes · click → Weather'},
2045
+ bb_sonda_tip:{es:'Profundidad bajo quilla · Sonda',en:'Depth under keel · Sounder'},
2046
+ bb_cadena_tip:{es:'Cadena recomendada',en:'Recommended chain'},
2047
+ bb_dist_tip:{es:'Distancia al ancla',en:'Distance to anchor'},
2048
+ bb_presion_tip:{es:'Presión barométrica · línea naranja = AHORA · click → Meteo',en:'Barometric pressure · orange line = NOW · click → Weather'},
2049
+ bb_abrigo_tip:{es:'Grado de abrigo · click → Abrigo',en:'Shelter grade · click → Shelter'},
2050
+ bb_calidad_tip:{es:'Calidad de abrigo (gauge) · click → Abrigo',en:'Shelter quality (gauge) · click → Shelter'},
2051
+ /* Rev313: tooltips sidebar derecha */
2052
+ sb_anchor_tip:{es:'Fondear / Levar',en:'Drop / Lift anchor'},
2053
+ sb_centrar_tip:{es:'Centrar mapa',en:'Center map'},
2054
+ sb_info_tip:{es:'Datos del fondeo',en:'Anchorage data'},
2055
+ sb_abrigo_tip:{es:'Previsión abrigo',en:'Shelter forecast'},
2056
+ sb_alarmas_tip:{es:'Panel de alarmas',en:'Alarms panel'},
2057
+ sb_fondeo_tip:{es:'Cálculo fondeo',en:'Anchorage calculator'},
2058
+ /* Rev315: meteo panel AHORA + row labels */
2059
+ met_viento:{es:'VIENTO',en:'WIND'},
2060
+ met_aire:{es:'AIRE',en:'AIR'},
2061
+ met_mar:{es:'MAR',en:'SEA'},
2062
+ met_resumen:{es:'Resumen',en:'Summary'},
2063
+ met_row_aire:{es:'Aire',en:'Air'},
2064
+ met_row_presion:{es:'Presión',en:'Pressure'},
2065
+ met_row_lluvia:{es:'Lluvia',en:'Rain'},
2066
+ met_row_tmar:{es:'T. Mar',en:'Sea T.'},
2067
+ met_row_ola:{es:'Ola',en:'Wave'},
2068
+ met_row_periodo:{es:'Período',en:'Period'},
2069
+ met_row_dirola:{es:'Dir ola',en:'Wave dir'},
2070
+ /* Rev315: shelter NOW box */
2071
+ sh_veleta:{es:'Veleta',en:'Vane'},
2072
+ sh_sensor:{es:'Sensor',en:'Sensor'},
2073
+ sh_racha:{es:'racha',en:'gust'},
2074
+ sh_aire:{es:'aire',en:'air'},
2075
+ sh_agua:{es:'agua',en:'water'},
2076
+ m_ahora_low:{es:'ahora',en:'now'},
2077
+ /* Rev315: labels flotantes visor (mapa) */
2078
+ m_viento:{es:'Viento',en:'Wind'},
2079
+ m_olas:{es:'Olas',en:'Waves'},
2080
+ m_mar_calma:{es:'Mar en calma',en:'Calm sea'},
2081
+ /* Rev313: modal guardar favorito */
2082
+ fav_guardar_title:{es:'❤ Guardar fondeo favorito',en:'❤ Save favourite anchorage'},
2083
+ fav_guardar_hint:{es:'Nombre del favorito (vacío = nombre automático por geolocalización):',en:'Favourite name (empty = auto name by geolocation):'},
2084
+ fav_placeholder:{es:'p.ej. Cangas - Massó',en:'e.g. Cangas - Massó'},
2085
+ m_cancelar:{es:'Cancelar',en:'Cancel'},
2086
+ m_guardar:{es:'Guardar',en:'Save'},
2087
+ meteo_modelo_auto:{es:'Mejor (automático)',en:'Best (automatic)'},
2010
2088
  otras_capas:{es:'Otras capas base',en:'Other base layers'},
2011
2089
  cartas_mbt:{es:'Cartas MBTiles',en:'MBTiles Charts'},
2012
2090
  cartas_sk:{es:'Cartas registradas (Signal K)',en:'Registered charts (Signal K)'},
@@ -2226,6 +2304,15 @@ function applyLang(){
2226
2304
  var k=el.getAttribute('data-i18n-html');
2227
2305
  var t=T(k);if(t)el.innerHTML=t;
2228
2306
  });
2307
+ /* Rev313: traducir atributos title (tooltips on hover) y placeholders. */
2308
+ document.querySelectorAll('[data-i18n-title]').forEach(function(el){
2309
+ var k=el.getAttribute('data-i18n-title');
2310
+ var t=T(k);if(t)el.setAttribute('title',t);
2311
+ });
2312
+ document.querySelectorAll('[data-i18n-placeholder]').forEach(function(el){
2313
+ var k=el.getAttribute('data-i18n-placeholder');
2314
+ var t=T(k);if(t)el.setAttribute('placeholder',t);
2315
+ });
2229
2316
  /* Special elements */
2230
2317
  var bl=document.getElementById('ais-borneo-label');if(bl)bl.textContent=T('colision');
2231
2318
  /* Flag active state */
@@ -5092,7 +5179,11 @@ function _shelterRenderPopup(){
5092
5179
  if(typeof _boatWave.heightSigM==='number'&&_boatWave.heightSigM>=0.05){
5093
5180
  waveLblTxt=_boatWave.heightSigM.toFixed(1).replace('.',',')+' m'+(_wavePer?' '+_wavePer:'');
5094
5181
  }else{
5095
- var _waveIntShort={calma:'Cal',rizada:'Riz',moderada:'Mod',agitada:'Agi',fuerte:'Fue'}[_boatWave.intensity||'']||(_boatWave.intensity||'');
5182
+ /* Rev313: i18n abreviaturas intensidad ola. */
5183
+ var _waveIntMap=(_lang==='en')
5184
+ ? {calma:'Calm',rizada:'Rip',moderada:'Mod',agitada:'Rou',fuerte:'Str'}
5185
+ : {calma:'Cal',rizada:'Riz',moderada:'Mod',agitada:'Agi',fuerte:'Fue'};
5186
+ var _waveIntShort=_waveIntMap[_boatWave.intensity||'']||(_boatWave.intensity||'');
5096
5187
  waveLblTxt=_waveIntShort+(_wavePer?' '+_wavePer:'');
5097
5188
  }
5098
5189
  waveLblHalfW=Math.max(30, waveLblTxt.length*4+8);
@@ -5288,8 +5379,11 @@ function _shelterRenderPopup(){
5288
5379
  temperaturas. Sin sensor → forecast Open-Meteo (sin badge).
5289
5380
  _BADGE_VELETA y _BADGE_SENSOR son verdes (#3aa856), mismo tono que
5290
5381
  el badge "IMU" pre-existente para coherencia visual. */
5291
- var _BADGE_VELETA=' <span style="color:#3aa856;font-size:11px;font-weight:800;margin-left:4px">Veleta</span>';
5292
- var _BADGE_SENSOR=' <span style="color:#3aa856;font-size:10px;font-weight:800;margin-left:3px">Sensor</span>';
5382
+ /* Rev315: badges Veleta/Sensor i18n. */
5383
+ var _badgeVeletaTxt=(typeof T==='function')?T('sh_veleta','Veleta'):'Veleta';
5384
+ var _badgeSensorTxt=(typeof T==='function')?T('sh_sensor','Sensor'):'Sensor';
5385
+ var _BADGE_VELETA=' <span style="color:#3aa856;font-size:11px;font-weight:800;margin-left:4px">'+_badgeVeletaTxt+'</span>';
5386
+ var _BADGE_SENSOR=' <span style="color:#3aa856;font-size:10px;font-weight:800;margin-left:3px">'+_badgeSensorTxt+'</span>';
5293
5387
  var _useSensorWind=isNow&&_boatWind;
5294
5388
  var _useSensorEnv=isNow&&_envSensors;
5295
5389
  var stWindKt,stDirDeg,windBadge;
@@ -5445,7 +5539,7 @@ function _shelterRenderPopup(){
5445
5539
  var axis=
5446
5540
  '<text x="'+x_24h.toFixed(1)+'" y="'+(H-6)+'" text-anchor="start" fill="rgba(255,255,255,.78)" font-size="13" font-weight="700">-24 h</text>'+
5447
5541
  '<text x="'+x_12h.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="rgba(255,255,255,.65)" font-size="13" font-weight="700">-12 h</text>'+
5448
- '<text x="'+nowX.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="#ffeb3b" font-size="13" font-weight="900">ahora</text>'+
5542
+ '<text x="'+nowX.toFixed(1)+'" y="'+(H-6)+'" text-anchor="middle" fill="#ffeb3b" font-size="13" font-weight="900">'+((typeof T==='function')?T('m_ahora_low','ahora'):'ahora')+'</text>'+
5449
5543
  '<text x="'+x_max.toFixed(1)+'" y="'+(H-6)+'" text-anchor="end" fill="rgba(255,235,59,.85)" font-size="13" font-weight="800">+'+maxFutureH+' h</text>'+
5450
5544
  '<text x="2" y="'+(padTop+10)+'" text-anchor="start" fill="rgba(255,255,255,.6)" font-size="11" font-weight="700">'+Math.round(pMax)+'</text>'+
5451
5545
  '<text x="2" y="'+(H-padBot-2)+'" text-anchor="start" fill="rgba(255,255,255,.6)" font-size="11" font-weight="700">'+Math.round(pMin)+'</text>';
@@ -5489,11 +5583,13 @@ function _shelterRenderPopup(){
5489
5583
  /* Rev128: reorden infobox — wind, gust, wave, TEMPS, pressure-num, sparkline.
5490
5584
  La sparkline queda al final para visual cleanliness (es el elemento más
5491
5585
  alto y rompía si quedaba en medio). */
5586
+ /* Rev315: labels i18n (racha/aire/agua). */
5587
+ var _ts=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
5492
5588
  statusBody.innerHTML=
5493
5589
  '<div>🌬 <b style="font-size:24px">'+Math.round(stWindKt)+' kt</b> <span style="color:#9ad">'+stDir+'</span>'+windBadge+'</div>'+
5494
- '<div>💨 racha <b style="font-size:21px">'+Math.round(stGustKt)+' kt</b>'+gustBadge+'</div>'+
5590
+ '<div>💨 '+_ts('sh_racha','racha')+' <b style="font-size:21px">'+Math.round(stGustKt)+' kt</b>'+gustBadge+'</div>'+
5495
5591
  '<div>'+waveLine+'</div>'+
5496
- '<div>🌡 aire <b style="font-size:20px">'+tempA+'</b>'+tempABadge+' &nbsp;&nbsp; 🌊 agua <b style="font-size:20px">'+tempW+'</b>'+tempWBadge+'</div>'+
5592
+ '<div>🌡 '+_ts('sh_aire','aire')+' <b style="font-size:20px">'+tempA+'</b>'+tempABadge+' &nbsp;&nbsp; 🌊 '+_ts('sh_agua','agua')+' <b style="font-size:20px">'+tempW+'</b>'+tempWBadge+'</div>'+
5497
5593
  pressureNumLine+
5498
5594
  sparkRow;
5499
5595
  /* Botón AHORA: resaltado si NO hay hora seleccionada */
@@ -5860,7 +5956,7 @@ function _wxMapMarkerUpdate(){
5860
5956
  if(_wDeg!==null){
5861
5957
  var wEdge=geoEdge(pos.lat,pos.lng,alarmR,_wDeg);
5862
5958
  var wDirTxt=_wxC16?_wxC16(_wDeg):'';
5863
- var wLabel='Viento '+(wDirTxt?wDirTxt+' ':'')+Math.round(_wKt)+' kt';
5959
+ var wLabel=((typeof T==='function')?T('m_viento','Viento'):'Viento')+' '+(wDirTxt?wDirTxt+' ':'')+Math.round(_wKt)+' kt';
5864
5960
  var html=_wxMarkerHtml('#ffeb3b',wLabel,_wDeg,'wind',_labOffW);
5865
5961
  var icon=L.divIcon({html:html,iconSize:[1,1],iconAnchor:[0,0],className:'wx-map-marker'});
5866
5962
  if(_wxMapMarkers.wind){_wxMapMarkers.wind.setIcon(icon);_wxMapMarkers.wind.setLatLng(wEdge);_wxMapMarkers.wind.setZIndexOffset(300);if(!map.hasLayer(_wxMapMarkers.wind))_wxMapMarkers.wind.addTo(map);}
@@ -5874,13 +5970,12 @@ function _wxMapMarkerUpdate(){
5874
5970
  var oEdge=geoEdge(pos.lat,pos.lng,alarmR,_vDirEff);
5875
5971
  var oDirTxt=_wxC16?_wxC16(_vDirEff):'';
5876
5972
  var oLabel;
5973
+ /* Rev315: labels "Olas" y "Mar en calma" i18n. */
5974
+ var _tw=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
5877
5975
  if(_boatWave){
5878
- /* Rev128: si la ola es despreciable (intensidad calma), no marear con
5879
- direcciones y períodos cortos sin sentido — decir simplemente
5880
- "Mar en calma". Si hay movimiento medible, mostrar altura+dir+período. */
5881
5976
  var _vbCalm=(_boatWave.intensity==='calma')&&(typeof _boatWave.heightSigM!=='number'||_boatWave.heightSigM<0.05);
5882
5977
  if(_vbCalm){
5883
- oLabel='Mar en calma';
5978
+ oLabel=_tw('m_mar_calma','Mar en calma');
5884
5979
  }else{
5885
5980
  var _vbPer=(typeof _boatWave.periodSec==='number'&&_boatWave.periodSec>0)?Math.round(_boatWave.periodSec)+'s':'';
5886
5981
  var _vbMain;
@@ -5889,10 +5984,10 @@ function _wxMapMarkerUpdate(){
5889
5984
  }else{
5890
5985
  _vbMain=(_boatWave.intensity||'').charAt(0).toUpperCase()+(_boatWave.intensity||'').slice(1);
5891
5986
  }
5892
- oLabel='Olas '+(oDirTxt?oDirTxt+' ':'')+_vbMain+(_vbPer?' · '+_vbPer:'');
5987
+ oLabel=_tw('m_olas','Olas')+' '+(oDirTxt?oDirTxt+' ':'')+_vbMain+(_vbPer?' · '+_vbPer:'');
5893
5988
  }
5894
5989
  }else{
5895
- oLabel='Olas '+(oDirTxt?oDirTxt+' ':'')+h.waveHeightM.toFixed(1).replace('.',',')+' m'+(h.wavePeriodSec>0?' · '+Math.round(h.wavePeriodSec)+' s':'');
5990
+ oLabel=_tw('m_olas','Olas')+' '+(oDirTxt?oDirTxt+' ':'')+h.waveHeightM.toFixed(1).replace('.',',')+' m'+(h.wavePeriodSec>0?' · '+Math.round(h.wavePeriodSec)+' s':'');
5896
5991
  }
5897
5992
  var oHtml=_wxMarkerHtml('#4dd0ff',oLabel,_vDirEff,'wave',_labOffV);
5898
5993
  var oIcon=L.divIcon({html:oHtml,iconSize:[1,1],iconAnchor:[0,0],className:'wx-map-marker'});
@@ -8997,12 +9092,14 @@ function m_renderMeteoWindy(d, seaD){
8997
9092
  var nowColW = (nowWindKt!=null) ? wColor(nowWindKt) : '#fff';
8998
9093
  var nowColG = (nowGustKt!=null) ? wColor(nowGustKt) : '#fff';
8999
9094
  /* Rev238: panel AHORA — sin iconos en labels (limpio). */
9095
+ /* Rev315: AHORA panel — labels i18n. */
9096
+ var _tn = function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
9000
9097
  var nowPanelHtml =
9001
9098
  '<div style="display:flex;flex-direction:column;height:100%;background:var(--bg);border:3px solid var(--org);box-sizing:border-box">' +
9002
- '<div style="background:var(--org);color:#000;font-size:22px;font-weight:900;text-align:center;padding:4px 4px;letter-spacing:2px;line-height:1">AHORA</div>' +
9099
+ '<div style="background:var(--org);color:#000;font-size:22px;font-weight:900;text-align:center;padding:4px 4px;letter-spacing:2px;line-height:1">'+_tn('m_ahora','AHORA')+'</div>' +
9003
9100
  '<div style="display:grid;grid-template-columns:1fr 1fr;grid-template-rows:1fr 1fr;flex:1;gap:0;color:#fff;text-align:center">' +
9004
9101
  '<div style="border-right:1px solid rgba(255,255,255,.4);border-bottom:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
9005
- '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">VIENTO'+srcBadge(srcWind)+'</div>' +
9102
+ '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_viento','VIENTO').toUpperCase()+srcBadge(srcWind)+'</div>' +
9006
9103
  '<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05;display:flex;align-items:center;justify-content:center;gap:4px">'+
9007
9104
  '<span>'+fmtKt(nowWindKt)+'</span>'+
9008
9105
  '<span style="font-size:14px;color:#9aa;font-weight:700">kt</span>'+
@@ -9010,15 +9107,15 @@ function m_renderMeteoWindy(d, seaD){
9010
9107
  '</div>' +
9011
9108
  '</div>' +
9012
9109
  '<div style="border-bottom:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
9013
- '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">RACHA'+srcBadge(srcGust)+'</div>' +
9110
+ '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_racha','RACHA').toUpperCase()+srcBadge(srcGust)+'</div>' +
9014
9111
  '<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtKt(nowGustKt)+'<span style="font-size:14px;color:#9aa;font-weight:700"> kt</span></div>' +
9015
9112
  '</div>' +
9016
9113
  '<div style="border-right:1px solid rgba(255,255,255,.4);padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
9017
- '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">AIRE'+srcBadge(srcAir)+'</div>' +
9114
+ '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_aire','AIRE').toUpperCase()+srcBadge(srcAir)+'</div>' +
9018
9115
  '<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtT(nowTAir)+'</div>' +
9019
9116
  '</div>' +
9020
9117
  '<div style="padding:4px 4px;display:flex;flex-direction:column;justify-content:center">' +
9021
- '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">MAR'+srcBadge(srcSea)+'</div>' +
9118
+ '<div style="font-size:16px;color:var(--org);font-weight:700;letter-spacing:1px;line-height:1">'+_tn('met_mar','MAR').toUpperCase()+srcBadge(srcSea)+'</div>' +
9022
9119
  '<div style="font-size:32px;font-weight:900;color:#fff;line-height:1.05">'+fmtT(nowTSea)+'</div>' +
9023
9120
  '</div>' +
9024
9121
  '</div>' +
@@ -9167,32 +9264,27 @@ function m_renderMeteoWindy(d, seaD){
9167
9264
  if (kn == null) return '–';
9168
9265
  return '<span style="color:'+wColor(kn)+';font-size:32px">'+kn.toFixed(0)+'</span>';
9169
9266
  });
9170
- /* Aire */
9171
- if (h.temperature_2m) rows += rowMetric('Aire','🌡️','#ffb23f','°C',
9267
+ /* Rev315: row labels i18n. */
9268
+ var _tr=function(k,fb){ return (typeof T==='function')?T(k,fb):fb; };
9269
+ if (h.temperature_2m) rows += rowMetric(_tr('met_row_aire','Aire'),'🌡️','#ffb23f','°C',
9172
9270
  function(col){ return getVal(h.temperature_2m, col); },
9173
9271
  function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(0)+'°</span>'; });
9174
- /* Presión */
9175
- if (h.pressure_msl || h.surface_pressure) rows += rowMetric('Presión','🔵','#9ad','mbar',
9272
+ if (h.pressure_msl || h.surface_pressure) rows += rowMetric(_tr('met_row_presion','Presión'),'🔵','#9ad','mbar',
9176
9273
  function(col){ return getVal(h.pressure_msl || h.surface_pressure, col); },
9177
9274
  function(v){ return v==null?'–':'<span style="font-size:26px">'+Math.round(v)+'</span>'; });
9178
- /* Lluvia */
9179
- if (h.precipitation || h.rain) rows += rowMetric('Lluvia','💧','#4dd0ff','mm',
9275
+ if (h.precipitation || h.rain) rows += rowMetric(_tr('met_row_lluvia','Lluvia'),'💧','#4dd0ff','mm',
9180
9276
  function(col){ return getVal(h.precipitation || h.rain, col); },
9181
9277
  function(v){ return (v==null||v===0)?'–':'<span style="font-size:28px">'+v.toFixed(1)+'</span>'; });
9182
- /* T. Mar */
9183
- if (seaH.sea_surface_temperature) rows += rowMetric('T. Mar','🐟','#4dd0ff','°C',
9278
+ if (seaH.sea_surface_temperature) rows += rowMetric(_tr('met_row_tmar','T. Mar'),'🐟','#4dd0ff','°C',
9184
9279
  function(col){ return getSeaVal(seaH.sea_surface_temperature, col); },
9185
9280
  function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(0)+'°</span>'; });
9186
- /* Ola altura */
9187
- if (seaH.wave_height) rows += rowMetric('Ola','🌊','#4dd0ff','m',
9281
+ if (seaH.wave_height) rows += rowMetric(_tr('met_row_ola','Ola'),'🌊','#4dd0ff','m',
9188
9282
  function(col){ return getSeaVal(seaH.wave_height, col); },
9189
9283
  function(v){ return v==null?'–':'<span style="font-size:30px">'+v.toFixed(1)+'</span>'; });
9190
- /* Período */
9191
- if (seaH.wave_period) rows += rowMetric('Período','⏱️','#9ad','s',
9284
+ if (seaH.wave_period) rows += rowMetric(_tr('met_row_periodo','Período'),'⏱️','#9ad','s',
9192
9285
  function(col){ return getSeaVal(seaH.wave_period, col); },
9193
9286
  function(v){ return v==null?'–':'<span style="font-size:28px">'+Math.round(v)+'</span>'; });
9194
- /* Dir ola número de grados + flecha rotada pequeña (Rev228). */
9195
- if (seaH.wave_direction) rows += rowMetric('Dir ola','🧭','#4dd0ff','°',
9287
+ if (seaH.wave_direction) rows += rowMetric(_tr('met_row_dirola','Dir ola'),'🧭','#4dd0ff','°',
9196
9288
  function(col){ return getSeaVal(seaH.wave_direction, col); },
9197
9289
  function(v){
9198
9290
  if (v == null) return '–';
@@ -9302,7 +9394,7 @@ function m_renderMeteoWindy(d, seaD){
9302
9394
  try { collapsed = localStorage.getItem('m-met-summary-collapsed') === '1'; } catch(_){}
9303
9395
  return '<div id="m-met-summary" class="' + (collapsed ? 'collapsed' : '') + '" style="background:rgba(20,40,60,.7);border-top:2px solid var(--org);color:#dde;flex-shrink:0">' +
9304
9396
  '<button id="m-met-summary-toggle" onclick="m_toggleMeteoSummary()" style="display:flex;align-items:center;justify-content:space-between;width:100%;background:none;border:none;color:var(--org);font-size:28px;font-weight:900;padding:18px 24px;cursor:pointer;text-align:left">' +
9305
- '<span>📰 Resumen</span>' +
9397
+ '<span>📰 '+((typeof T==='function')?T('met_resumen','Resumen'):'Resumen')+'</span>' +
9306
9398
  '<span id="m-met-summary-chev" style="font-size:24px">' + (collapsed ? '▼' : '▲') + '</span>' +
9307
9399
  '</button>' +
9308
9400
  '<div id="m-met-summary-body" style="padding:0 24px 18px;font-size:24px;line-height:1.55;' + (collapsed ? 'display:none' : '') + '">' +
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "signalk-mareas-ihm",
3
- "version": "2.1.2",
3
+ "version": "2.1.4",
4
4
  "description": "Complete SignalK anchor watch & marine safety webapp. Voice alarms for anchor drag, AIS collision risk and grounding. Shelter / anchorage forecast (wind exposure + wave history). Weather forecast (Open-Meteo). Depth & scope calculator. Multi-source nautical charts (Esri Satellite, IHM ENC S-52, bathymetry, MBTiles offline, OpenStreetMap, OpenSeaMap). Favourite anchorages. Multi-device real-time sync (SSE). Includes official IHM Spain tide predictions. Mobile-first UI in ES/EN.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",