@sailingrotevista/rotevista-dash 2.0.22 → 3.0.1

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.
Files changed (4) hide show
  1. package/app.js +65 -11
  2. package/index.html +90 -160
  3. package/package.json +1 -1
  4. package/style.css +347 -222
package/app.js CHANGED
@@ -308,10 +308,14 @@ function startDisplayLoop() {
308
308
  if (tick % 2 === 0) { refreshGraph('stw'); refreshGraph(displayModeSog === 'VMG' ? 'vmg' : 'sog'); refreshGraph('depth'); refreshGraph('tws'); }
309
309
 
310
310
  // SLOW TIER (3s)
311
- if (tick % 3 === 0) {
312
- let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, 30000, false), cObj = getCircularAverageFromBuffer(store.longBuf.cog, 30000, false),
313
- awObj = getCircularAverageFromBuffer(store.longBuf.awa, 30000, true), twObj = getCircularAverageFromBuffer(store.longBuf.twa, 30000, true),
314
- twdObj = getCircularAverageFromBuffer(store.longBuf.twd, 30000, false);
311
+ if (tick % 3 === 0) {
312
+ // Utilizziamo CONFIG.averages.longWindow (che ora è 30000ms)
313
+ // In questo modo, se cambi il tempo su Signal K, la dashboard si aggiorna da sola.
314
+ let hObj = getCircularAverageFromBuffer(store.longBuf.hdg, CONFIG.averages.longWindow, false),
315
+ cObj = getCircularAverageFromBuffer(store.longBuf.cog, CONFIG.averages.longWindow, false),
316
+ awObj = getCircularAverageFromBuffer(store.longBuf.awa, CONFIG.averages.longWindow, true),
317
+ twObj = getCircularAverageFromBuffer(store.longBuf.twa, CONFIG.averages.longWindow, true),
318
+ twdObj = getCircularAverageFromBuffer(store.longBuf.twd, CONFIG.averages.longWindow, false);
315
319
 
316
320
  const upUI = (el, obj, isCompass = false) => {
317
321
  if (!obj || obj.val === null) { el.innerHTML = "---°"; el.classList.remove('unstable-data'); }
@@ -418,22 +422,72 @@ function drawGraph(d, id, min, max, isTws, isHercules) {
418
422
  // 8. INTERAZIONI E RETE
419
423
  // ==========================================================================
420
424
  function toggleFocusMode(type, element) {
421
- const container = document.querySelector('.main-container'); const parentPanel = element.closest('.side-panel'); const isLeft = parentPanel.classList.contains('left-panel');
425
+ const container = document.querySelector('.main-container');
426
+
427
+ // Con la nuova struttura Flat HTML, non abbiamo più '.left-panel'.
428
+ // Dobbiamo determinare il lato guardando il tipo di box.
429
+ const leftBoxes = ['stw', 'sog', 'hdg', 'cog', 'tack'];
430
+ const isLeft = leftBoxes.includes(type);
431
+
422
432
  isFocusActive = !isFocusActive;
423
- if (isFocusActive) { container.classList.add('focus-active', isLeft ? 'focus-side-left' : 'focus-side-right'); parentPanel.classList.add('has-focus'); element.classList.add('is-focused'); }
424
- else { container.classList.remove('focus-active', 'focus-side-left', 'focus-side-right'); document.querySelectorAll('.side-panel').forEach(p => p.classList.remove('has-focus')); document.querySelectorAll('.data-box').forEach(b => b.classList.remove('is-focused')); }
433
+
434
+ if (isFocusActive) {
435
+ container.classList.add('focus-active', isLeft ? 'focus-side-left' : 'focus-side-right');
436
+ element.classList.add('is-focused');
437
+ } else {
438
+ container.classList.remove('focus-active', 'focus-side-left', 'focus-side-right');
439
+ document.querySelectorAll('.data-box').forEach(b => b.classList.remove('is-focused'));
440
+ }
425
441
  }
426
442
 
427
443
  ['stw', 'sog', 'tws', 'depth'].forEach(type => {
444
+ // Risale dal grafico al contenitore principale (funziona con la nuova struttura)
428
445
  const el = document.getElementById(type + '-graph').closest('.data-box');
429
446
  let lastTapTime = 0, tapTimeout, isLongPressActive = false;
430
- el.addEventListener('pointerdown', (e) => { isLongPressActive = false; pressTimer = setTimeout(() => { if (!isFocusActive) { isLongPressActive = true; toggleFocusMode(type, el); lastTapTime = 0; } }, 1000); });
447
+
448
+ el.addEventListener('pointerdown', (e) => {
449
+ isLongPressActive = false;
450
+ pressTimer = setTimeout(() => {
451
+ if (!isFocusActive) {
452
+ isLongPressActive = true;
453
+ toggleFocusMode(type, el);
454
+ lastTapTime = 0;
455
+ }
456
+ }, 1000);
457
+ });
458
+
431
459
  el.addEventListener('pointerup', (e) => {
432
- clearTimeout(pressTimer); if (isLongPressActive) return;
460
+ clearTimeout(pressTimer);
461
+ if (isLongPressActive) return; // Se era focus, ignora l'up
462
+
433
463
  const currentTime = new Date().getTime(), tapDelay = currentTime - lastTapTime;
434
- if (tapDelay < 300 && tapDelay > 0) { clearTimeout(tapTimeout); if (!isFocusActive) { graphModes[type] = graphModes[type] === 'standard' ? 'hercules' : 'standard'; localStorage.setItem('mode_' + type, graphModes[type]); } lastTapTime = 0; }
435
- else { lastTapTime = currentTime; tapTimeout = setTimeout(() => { if (isFocusActive && el.classList.contains('is-focused')) toggleFocusMode(type, el); else if (!isFocusActive && type === 'sog') { displayModeSog = (displayModeSog === 'SOG') ? 'VMG' : 'SOG'; el.style.backgroundColor = "rgba(0, 0, 0, 0.05)"; setTimeout(() => el.style.backgroundColor = "", 150); } }, 250); }
464
+
465
+ // DOPPIO TAP: Toggle Hercules Mode (Funziona anche in Focus)
466
+ if (tapDelay < 300 && tapDelay > 0) {
467
+ clearTimeout(tapTimeout); // Cancella l'uscita dal Focus
468
+ graphModes[type] = graphModes[type] === 'standard' ? 'hercules' : 'standard';
469
+ localStorage.setItem('mode_' + type, graphModes[type]);
470
+ refreshGraph(type); // Mostra istantaneamente
471
+ lastTapTime = 0;
472
+ }
473
+ // TAP SINGOLO (con ritardo di 250ms per aspettare l'eventuale doppio tap)
474
+ else {
475
+ lastTapTime = currentTime;
476
+ tapTimeout = setTimeout(() => {
477
+ // Uscita Focus
478
+ if (isFocusActive && el.classList.contains('is-focused')) {
479
+ toggleFocusMode(type, el);
480
+ }
481
+ // Toggle SOG/VMG (solo se non in focus)
482
+ else if (!isFocusActive && type === 'sog') {
483
+ displayModeSog = (displayModeSog === 'SOG') ? 'VMG' : 'SOG';
484
+ el.style.backgroundColor = "rgba(0, 0, 0, 0.05)";
485
+ setTimeout(() => el.style.backgroundColor = "", 150);
486
+ }
487
+ }, 250); // Attesa critica per il doppio tap
488
+ }
436
489
  });
490
+
437
491
  el.addEventListener('pointerleave', () => clearTimeout(pressTimer));
438
492
  });
439
493
 
package/index.html CHANGED
@@ -12,80 +12,54 @@
12
12
  <body>
13
13
 
14
14
  <div class="main-container">
15
-
16
- <!-- ======================================================= -->
17
- <!-- COLONNA SINISTRA: Dati Rotta e Velocità -->
18
- <!-- ======================================================= -->
19
- <div class="side-panel left-panel">
20
-
21
- <!-- STW: Velocità attraverso l'acqua -->
22
- <div class="data-box">
23
- <div class="label-row">
24
- <span class="label">STW</span>
25
- <span class="unit">kts</span>
26
- </div>
27
- <span class="value" id="stw">0.0</span>
28
- <div class="graph-wrapper">
29
- <svg class="sparkline" id="stw-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
30
- <div class="scale-labels" id="stw-scale"></div>
31
- </div>
32
- </div>
15
+ <!-- STATO CONNESSIONE -->
16
+ <div id="status" class="offline">OFFLINE</div>
33
17
 
34
- <!-- SOG / VMG: Velocità sul fondo o verso il vento -->
35
- <div class="data-box">
36
- <div class="label-row">
37
- <span class="label" id="sog-vmg-label">SOG</span>
38
- <span class="unit">kts</span>
39
- </div>
40
- <span class="value" id="sog">0.0</span>
41
- <div class="graph-wrapper">
42
- <svg class="sparkline" id="sog-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
43
- <div class="scale-labels" id="sog-scale"></div>
44
- </div>
18
+ <!-- COLONNA SINISTRA -->
19
+ <div class="data-box box-stw">
20
+ <div class="label-row"><span class="label">STW</span><span class="unit">kts</span></div>
21
+ <span class="value" id="stw">0.0</span>
22
+ <div class="graph-wrapper">
23
+ <svg class="sparkline" id="stw-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
24
+ <div class="scale-labels" id="stw-scale"></div>
45
25
  </div>
26
+ </div>
46
27
 
47
- <!-- HEADING: Prua Bussola (Media 60s) -->
48
- <div class="data-box">
49
- <div class="label-row">
50
- <span class="label">HEADING (MEAN)</span>
51
- </div>
52
- <span class="value value-large" id="hdg">000&deg;</span>
28
+ <div class="data-box box-sog">
29
+ <div class="label-row"><span class="label" id="sog-vmg-label">SOG</span><span class="unit">kts</span></div>
30
+ <span class="value" id="sog">0.0</span>
31
+ <div class="graph-wrapper">
32
+ <svg class="sparkline" id="sog-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
33
+ <div class="scale-labels" id="sog-scale"></div>
53
34
  </div>
35
+ </div>
54
36
 
55
- <!-- COG: Rotta sul fondo (Media 60s) -->
56
- <div class="data-box">
57
- <div class="label-row">
58
- <span class="label">COG (MEAN)</span>
59
- </div>
60
- <span class="value value-large" id="cog">000&deg;</span>
61
- </div>
37
+ <div class="data-box box-hdg">
38
+ <div class="label-row"><span class="label">HEADING (MEAN)</span></div>
39
+ <span class="value-large" id="hdg">000&deg;</span>
40
+ </div>
41
+
42
+ <div class="data-box box-cog">
43
+ <div class="label-row"><span class="label">COG (MEAN)</span></div>
44
+ <span class="value-large" id="cog">000&deg;</span>
45
+ </div>
62
46
 
63
- <!-- TACK: Calcolo mure opposte (Previsione) -->
64
- <div class="data-box">
65
- <div class="label-row">
66
- <span class="label">TACK</span>
47
+ <div class="data-box box-tack">
48
+ <div class="label-row"><span class="label">TACK</span></div>
49
+ <div class="dual-value-container">
50
+ <div class="dual-value-col tack-hdg-col">
51
+ <span class="dual-label">HDG</span>
52
+ <span class="value dual-val" id="tack-hdg">---&deg;</span>
67
53
  </div>
68
- <div class="dual-value-container">
69
- <div class="dual-value-col">
70
- <span class="dual-label">HDG</span>
71
- <span class="value dual-val" id="tack-hdg">---&deg;</span>
72
- </div>
73
- <div class="dual-value-col right-col">
74
- <span class="dual-label">COG</span>
75
- <span class="value dual-val" id="tack-cog">---&deg;</span>
76
- </div>
54
+ <div class="dual-value-col tack-cog-col">
55
+ <span class="dual-label">COG</span>
56
+ <span class="value dual-val" id="tack-cog">---&deg;</span>
77
57
  </div>
78
58
  </div>
79
-
80
59
  </div>
81
60
 
82
- <!-- ======================================================= -->
83
- <!-- CENTRO: Strumento Vento SVG (Pannello di Controllo) -->
84
- <!-- ======================================================= -->
85
- <div class="center-panel">
86
- <!-- Stato Connessione integrato nell'angolo del pannello centrale -->
87
- <div id="status" class="offline">OFFLINE</div>
88
-
61
+ <!-- BUSSOLA CENTRALE -->
62
+ <div class="box-gauge">
89
63
  <svg id="wind-gauge" viewBox="35 38 330 395" preserveAspectRatio="xMidYMid meet">
90
64
  <defs>
91
65
  <clipPath id="boat-clip"><circle cx="200" cy="200" r="50" /></clipPath>
@@ -116,26 +90,21 @@
116
90
  <circle cx="200" cy="200" r="160" fill="#050505" />
117
91
  <circle cx="200" cy="200" r="125" fill="#121212" />
118
92
 
119
- <path d="M 82.0 101.0 A 154 154 0 0 1 142.3 57.2" fill="none" stroke="#ff0000" stroke-width="12" opacity="1"/>
120
- <path d="M 257.7 57.2 A 154 154 0 0 1 318.0 101.0" fill="none" stroke="#00ff00" stroke-width="12" opacity="1"/>
121
- <path d="M 265.1 339.6 A 154 154 0 0 1 134.9 339.6" fill="none" stroke="#ff8800" stroke-width="12" opacity="1"/>
93
+ <path d="M 82.0 101.0 A 154 154 0 0 1 142.3 57.2" fill="none" stroke="#ff0000" stroke-width="12" />
94
+ <path d="M 257.7 57.2 A 154 154 0 0 1 318.0 101.0" fill="none" stroke="#00ff00" stroke-width="12" />
95
+ <path d="M 265.1 339.6 A 154 154 0 0 1 134.9 339.6" fill="none" stroke="#ff8800" stroke-width="12" />
122
96
 
123
97
  <g id="ticks"></g>
124
98
 
125
- <!-- Etichette riposizionate con raggio ridotto per un contatto perfetto con la ghiera -->
126
99
  <g id="tick-labels" fill="#bbb" text-anchor="middle" dominant-baseline="middle" font-family="Arial" font-weight="bold">
127
- <!-- Etichette Principali (Distanza 126px) -->
128
100
  <text font-size="16" transform="translate(200, 74)">0</text>
129
101
  <text font-size="16" transform="translate(326, 200) rotate(90)">90</text>
130
102
  <text font-size="16" transform="translate(74, 200) rotate(-90)">90</text>
131
103
  <text font-size="16" transform="translate(200, 326) rotate(180)">180</text>
132
-
133
- <!-- Etichette Intermedie (Distanza 125px) -->
134
104
  <text font-size="11" transform="translate(262.5, 91.7) rotate(30)">30</text>
135
105
  <text font-size="11" transform="translate(308.3, 137.5) rotate(60)">60</text>
136
106
  <text font-size="11" transform="translate(308.3, 262.5) rotate(120)">120</text>
137
107
  <text font-size="11" transform="translate(262.5, 308.3) rotate(150)">150</text>
138
-
139
108
  <text font-size="11" transform="translate(137.5, 91.7) rotate(-30)">30</text>
140
109
  <text font-size="11" transform="translate(91.7, 137.5) rotate(-60)">60</text>
141
110
  <text font-size="11" transform="translate(91.7, 262.5) rotate(-120)">120</text>
@@ -149,131 +118,92 @@
149
118
  <circle id="fullscreen-hotspot" cx="200" cy="200" r="55" fill="#181818" stroke="#333" stroke-width="1" filter="url(#center-glow)" cursor="pointer" />
150
119
 
151
120
  <path id="boat-icon" d="M200,150 Q165,185 170,250 Q165,190 200,173 Q235,190 230,250 Q235,185 200,150 Z"
152
- fill="url(#axiom-grad)"
153
- transform="translate(0, 5)"
154
- clip-path="url(#boat-clip)"
155
- style="pointer-events: none;" />
121
+ fill="url(#axiom-grad)" transform="translate(0, 5)" clip-path="url(#boat-clip)" style="pointer-events: none;" />
156
122
 
157
123
  <g id="aws-display-group" transform="translate(200, 265)">
158
124
  <text x="0" y="0" fill="#777" font-size="10" font-weight="bold" text-anchor="middle" text-transform="uppercase">Apparent Wind kts</text>
159
125
  <text id="aws-val-svg" x="0" y="42" fill="#fff" font-size="52" font-weight="bold" text-anchor="middle">0.0</text>
160
126
  </g>
161
127
 
162
- <!-- Lancetta Apparente (AWA) - Versione allungata -->
163
128
  <g id="awa-pointer" transform="rotate(0, 200, 200)" opacity="0.9">
164
- <!-- Ho portato 75 -> 70 (fuori) e 145 -> 155 (dentro) -->
165
- <path d="M 200,70 L 213,95 L 200,145 L 187,95 Z"
166
- fill="#ff8c00" stroke="#000" stroke-width="1" />
129
+ <path d="M 200,70 L 213,95 L 200,145 L 187,95 Z" fill="#ff8c00" stroke="#000" stroke-width="1" />
167
130
  <text x="200" y="90" fill="#000" font-size="10" font-weight="900" text-anchor="middle" font-family="Arial Black">A</text>
168
131
  </g>
169
132
 
170
- <!-- Lancetta Reale (TWA) - FORMA A GOCCIA (Secondaria/Piccola) -->
171
133
  <g id="twa-pointer" transform="rotate(0, 200, 200)" opacity="0.9">
172
- <!-- Goccia piccola: inizia a y=92, punta a y=128 -->
173
134
  <path d="M 200,92 A 8,8 0 0 1 208,100 C 208,108 200,128 200,128 C 200,128 192,108 192,100 A 8,8 0 0 1 200,92 Z"
174
135
  fill="#ffff00" stroke="#000" stroke-width="0.8" />
175
136
  <text x="200" y="106" fill="#000" font-size="9" font-weight="900" text-anchor="middle" font-family="Arial Black">T</text>
176
-
177
- <!-- Pallini Trend (Sempre agganciati alla rotazione del TWA) -->
178
- <circle id="trend-gauge-cw" cx="215" cy="110" r="4" fill="#ffffff" />
179
- <circle id="trend-gauge-ccw" cx="185" cy="110" r="4" fill="#ffffff" />
137
+ <circle id="trend-gauge-cw" cx="215" cy="110" r="4" fill="#bbb" />
138
+ <circle id="trend-gauge-ccw" cx="185" cy="110" r="4" fill="#bbb" />
180
139
  </g>
181
140
 
182
141
  <g transform="translate(75, 395)">
183
142
  <text x="125" y="-12" id="leeway-val" fill="#aaa" font-size="11" text-anchor="middle" font-weight="bold">LEEWAY: 0.0&deg;</text>
184
143
  <rect x="0" y="0" width="250" height="12" fill="#222" rx="3" />
185
144
  <rect x="0" y="0" width="250" height="12" fill="url(#leeway-grad)" clip-path="url(#leeway-clip)" rx="3" />
186
-
187
- <!-- RIPRISTINO SCALA: Tacche ogni 5 gradi (31.25px per intervallo) -->
188
145
  <g stroke="#555" stroke-width="1">
189
- <line x1="0" y1="-2" x2="0" y2="14" /> <!-- -20° -->
190
- <line x1="31.25" y1="2" x2="31.25" y2="10" /> <!-- -15° -->
191
- <line x1="62.5" y1="2" x2="62.5" y2="10" /> <!-- -10° -->
192
- <line x1="93.75" y1="3" x2="93.75" y2="9" /> <!-- -5° -->
193
- <line x1="125" y1="-2" x2="125" y2="14" /> <!-- 0° centrale -->
194
- <line x1="156.25" y1="3" x2="156.25" y2="9" /> <!-- +5° -->
195
- <line x1="187.5" y1="2" x2="187.5" y2="10" /> <!-- +10° -->
196
- <line x1="218.75" y1="2" x2="218.75" y2="10" /> <!-- +15° -->
197
- <line x1="250" y1="-2" x2="250" y2="14" /> <!-- +20° -->
146
+ <line x1="0" y1="-2" x2="0" y2="14" /><line x1="62.5" y1="2" x2="62.5" y2="10" />
147
+ <line x1="125" y1="-2" x2="125" y2="14" /><line x1="187.5" y1="2" x2="187.5" y2="10" />
148
+ <line x1="250" y1="-2" x2="250" y2="14" />
198
149
  </g>
199
-
200
- <!-- Etichette numeriche riallineate -->
201
150
  <g fill="#555" font-size="8" text-anchor="middle" font-weight="bold">
202
- <text x="0" y="24">-20&deg;</text>
203
- <text x="62.5" y="24">-10</text>
204
- <text x="125" y="24">0&deg;</text>
205
- <text x="187.5" y="24">10</text>
151
+ <text x="0" y="24">-20&deg;</text><text x="62.5" y="24">-10</text>
152
+ <text x="125" y="24">0&deg;</text><text x="187.5" y="24">10</text>
206
153
  <text x="250" y="24">20&deg;</text>
207
154
  </g>
208
155
  </g>
209
156
  </svg>
210
157
  </div>
211
158
 
212
- <!-- ======================================================= -->
213
- <!-- COLONNA DESTRA: Profondità e Vento Reale -->
214
- <!-- ======================================================= -->
215
- <div class="side-panel right-panel">
216
-
217
- <div class="data-box">
218
- <div class="label-row">
219
- <span class="unit">m</span>
220
- <span class="label">DEPTH</span>
221
- </div>
222
- <span class="value" id="depth">--.-</span>
223
- <div class="graph-wrapper">
224
- <div class="scale-labels" id="depth-scale"></div>
225
- <svg class="sparkline" id="depth-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
226
- </div>
159
+ <!-- COLONNA DESTRA -->
160
+ <!-- NOTA: Etichetta a SX e Unità a DX nel codice, il CSS usa row-reverse per specchiarli sul bordo -->
161
+ <div class="data-box box-depth">
162
+ <div class="label-row"><span class="label">DEPTH</span><span class="unit">m</span></div>
163
+ <span class="value" id="depth">--.-</span>
164
+ <div class="graph-wrapper">
165
+ <div class="scale-labels" id="depth-scale"></div>
166
+ <svg class="sparkline" id="depth-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
227
167
  </div>
168
+ </div>
228
169
 
229
- <div class="data-box">
230
- <div class="label-row">
231
- <span class="unit">kts</span>
232
- <span class="label">TWS</span>
233
- </div>
234
- <span class="value" id="tws">0.0</span>
235
- <div class="graph-wrapper">
236
- <div class="scale-labels" id="tws-scale"></div>
237
- <svg class="sparkline" id="tws-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
238
- </div>
170
+ <div class="data-box box-tws">
171
+ <div class="label-row"><span class="label">TWS</span><span class="unit">kts</span></div>
172
+ <span class="value" id="tws">0.0</span>
173
+ <div class="graph-wrapper">
174
+ <div class="scale-labels" id="tws-scale"></div>
175
+ <svg class="sparkline" id="tws-graph" viewBox="0 0 200 40" preserveAspectRatio="none"></svg>
239
176
  </div>
177
+ </div>
240
178
 
241
- <div class="data-box">
242
- <div class="label-row">
243
- <span class="label">TWA (MEAN)</span>
244
- </div>
245
- <span class="value value-large" id="twa-avg">---&deg;</span>
246
- </div>
179
+ <div class="data-box box-twa">
180
+ <div class="label-row"><span class="label">TWA (MEAN)</span></div>
181
+ <span class="value-large" id="twa-avg">---&deg;</span>
182
+ </div>
247
183
 
248
- <div class="data-box">
249
- <div class="label-row">
250
- <span class="label">AWA (MEAN)</span>
251
- </div>
252
- <span class="value value-large" id="awa-avg">---&deg;</span>
253
- </div>
184
+ <div class="data-box box-awa">
185
+ <div class="label-row"><span class="label">AWA (MEAN)</span></div>
186
+ <span class="value-large" id="awa-avg">---&deg;</span>
187
+ </div>
254
188
 
255
- <div class="data-box">
256
- <div class="label-row">
257
- <span class="label">TWD (MEAN)</span>
258
- </div>
259
- <div class="value-with-compass">
260
- <svg class="mini-compass" viewBox="0 0 40 40">
261
- <circle cx="20" cy="20" r="18.5" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1.2"/>
262
- <text x="20" y="8" fill="#e74c3c" font-size="5.5" text-anchor="middle" font-weight="900">N</text>
263
- <text x="20" y="36" fill="rgba(255,255,255,0.3)" font-size="5.5" text-anchor="middle" font-weight="900">S</text>
264
- <g id="twd-boat-wrap" transform="rotate(0, 20, 20)">
265
- <path d="M 20,17 L 17,26 L 20,24.5 L 23,26 Z" fill="white" opacity="0.2" />
266
- </g>
267
- <g id="twd-arrow" transform="rotate(0, 20, 20)">
268
- <path id="twd-wind-chevron" d="M 17,5 L 20,7.5 L 23,5" fill="none" stroke="#ffff00" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
269
- <circle id="trend-dot-cw" cx="24" cy="7.5" r="1.5" fill="#ffffff" opacity="0.3" />
270
- <circle id="trend-dot-ccw" cx="16" cy="7.5" r="1.5" fill="#ffffff" opacity="0.3" />
271
- </g>
272
- </svg>
273
- <span class="value value-large" id="twd-avg">---&deg;</span>
274
- </div>
189
+ <div class="data-box box-twd">
190
+ <div class="label-row"><span class="label">TWD (MEAN)</span></div>
191
+ <div class="value-with-compass">
192
+ <svg class="mini-compass" viewBox="0 0 40 40">
193
+ <circle cx="20" cy="20" r="18.5" fill="none" stroke="rgba(0,0,0,0.1)" stroke-width="1.2"/>
194
+ <text x="20" y="8" fill="#e74c3c" font-size="5.5" text-anchor="middle" font-weight="900">N</text>
195
+ <text x="20" y="36" fill="#000" font-size="5.5" text-anchor="middle" font-weight="900">S</text>
196
+ <g id="twd-boat-wrap" transform="rotate(0, 20, 20)">
197
+ <path d="M 20,17 L 17,26 L 20,24.5 L 23,26 Z" fill="#000" opacity="0.3" />
198
+ </g>
199
+ <g id="twd-arrow" transform="rotate(0, 20, 20)">
200
+ <path id="twd-wind-chevron" d="M 17,5 L 20,7.5 L 23,5" fill="none" stroke="#000" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" />
201
+ <circle id="trend-dot-cw" cx="24" cy="7.5" r="1.5" fill="#bbb" />
202
+ <circle id="trend-dot-ccw" cx="16" cy="7.5" r="1.5" fill="#bbb" />
203
+ </g>
204
+ </svg>
205
+ <span class="value-large" id="twd-avg">---&deg;</span>
275
206
  </div>
276
-
277
207
  </div>
278
208
  </div>
279
209
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sailingrotevista/rotevista-dash",
3
- "version": "2.0.22",
3
+ "version": "3.0.1",
4
4
  "description": "Public Wind Dashboard with navigation and course aids",
5
5
  "main": "index.js",
6
6
  "publishConfig": {
package/style.css CHANGED
@@ -1,222 +1,352 @@
1
1
  /* ==========================================================================
2
- 1. BASE E RESET DI SISTEMA (GIORNO: SFONDO CHIARO)
2
+ 1. BASE E RESET DI SISTEMA
3
3
  ========================================================================== */
4
4
  body {
5
5
  background-color: #fff;
6
6
  color: #000;
7
7
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
8
- margin: 0;
9
- padding: 0;
10
- height: 100vh;
11
- width: 100vw;
8
+ margin: 0; padding: 0;
9
+ height: 100vh; width: 100vw;
12
10
  overflow: hidden;
13
- -webkit-touch-callout: none;
14
- -webkit-user-select: none;
15
- user-select: none;
16
- touch-action: none;
11
+ -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; touch-action: none;
17
12
  }
18
13
 
19
14
  /* ==========================================================================
20
- 2. LAYOUT PRINCIPALE (LIQUID GRID)
15
+ 2. LAYOUT PRINCIPALE (GRID AREAS)
21
16
  ========================================================================== */
22
17
  .main-container {
23
18
  display: grid;
24
- width: 100%;
25
- height: 100%;
26
- padding: 5px;
27
- box-sizing: border-box;
28
- gap: 8px;
29
- grid-template-columns: minmax(180px, 1fr) minmax(auto, 1.5fr) minmax(180px, 1fr);
30
- grid-template-rows: 100%;
19
+ width: 100%; height: 100%;
20
+ padding: 5px; box-sizing: border-box; gap: 8px;
21
+ grid-template-areas:
22
+ "stw gauge depth"
23
+ "sog gauge tws"
24
+ "hdg gauge twa"
25
+ "cog gauge awa"
26
+ "tack gauge twd";
27
+ grid-template-rows: 1.75fr 1.75fr 0.75fr 0.75fr 1.25fr;
28
+ grid-template-columns: 1fr 1.5fr 1fr;
31
29
  transition: all 0.5s cubic-bezier(0.4, 0, 0.2, 1);
32
- justify-content: stretch;
33
- }
34
-
35
- .side-panel {
36
- display: flex;
37
- flex-direction: column;
38
- background: rgba(0, 0, 0, 0.03);
39
- border-radius: 12px;
40
- height: 100%;
41
- overflow: hidden;
42
- gap: 2px;
43
- }
44
-
45
- .left-panel { grid-column: 1; }
46
-
47
- .center-panel {
48
- grid-column: 2;
49
- position: relative;
50
- display: flex;
51
- flex-direction: column;
52
- align-items: center;
53
- justify-content: center;
54
- height: 100%;
55
- overflow: hidden;
56
30
  }
57
31
 
58
- .right-panel {
59
- grid-column: 3;
60
- align-items: flex-end;
61
- text-align: right;
62
- }
32
+ .box-stw { grid-area: stw; }
33
+ .box-sog { grid-area: sog; }
34
+ .box-hdg { grid-area: hdg; }
35
+ .box-cog { grid-area: cog; }
36
+ .box-tack { grid-area: tack; }
37
+ .box-gauge { grid-area: gauge; display: flex; align-items: center; justify-content: center; position: relative; }
38
+ .box-depth { grid-area: depth; }
39
+ .box-tws { grid-area: tws; }
40
+ .box-twa { grid-area: twa; }
41
+ .box-awa { grid-area: awa; }
42
+ .box-twd { grid-area: twd; }
63
43
 
64
44
  /* ==========================================================================
65
- 3. DATA-BOX E TIPOGRAFIA
45
+ 3. DATA-BOX: STRUTTURA E ALLINEAMENTO (Bordi Schermo & Display Curvi)
66
46
  ========================================================================== */
67
47
  .data-box {
68
- position: relative;
69
- border-bottom: 1px solid #eee;
70
- padding: 2px 4px;
71
- display: flex;
72
- flex-direction: column;
73
- width: 100%;
74
- box-sizing: border-box;
75
- container-type: size;
76
- flex: 1 1 0px;
77
- min-height: 0;
78
- overflow: hidden;
48
+ position: relative; border-bottom: 1px solid #eee;
49
+ /* Padding alto (1.2rem) per non far toccare il numero al titolo assoluto */
50
+ padding: 1.2rem 8px 4px 8px;
51
+ display: flex; flex-direction: column; width: 100%;
52
+ box-sizing: border-box; container-type: size;
53
+ min-height: 0; overflow: hidden;
54
+ background: rgba(0, 0, 0, 0.03); border-radius: 12px;
79
55
  transition: background-color 0.2s ease;
56
+ /* Forza la centratura verticale del valore per display curvi */
57
+ justify-content: center !important;
80
58
  }
81
59
 
82
- .left-panel .data-box { align-items: flex-start; text-align: left; }
83
- .right-panel .data-box { align-items: flex-end; text-align: right; }
60
+ /* Titoli ancorati sempre in alto */
61
+ .label-row {
62
+ position: absolute; top: 4px; left: 8px; right: 8px;
63
+ display: flex; align-items: baseline; z-index: 10;
64
+ }
84
65
 
85
- .label-row { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 2px; width: 100%; }
86
66
  .label { color: #888; font-size: 0.65rem; font-weight: bold; text-transform: uppercase; }
87
67
  .unit { color: #aaa; font-size: 0.6rem; font-weight: bold; }
88
68
 
89
- .value {
90
- color: #000;
91
- font-size: clamp(1.2rem, 22cqh, 3rem);
92
- font-weight: 600;
93
- line-height: 0.9;
94
- letter-spacing: -1px;
95
- padding-bottom: 5px;
69
+ /* --- ALLINEAMENTO SPECULARE RIGIDO --- */
70
+
71
+ /* COLONNA SINISTRA: Titolo a SX (bordo), Unità a DX */
72
+ .box-stw, .box-sog, .box-hdg, .box-cog, .box-tack { align-items: flex-start !important; text-align: left !important; }
73
+ .box-stw .label-row, .box-sog .label-row, .box-hdg .label-row, .box-cog .label-row, .box-tack .label-row {
74
+ flex-direction: row !important; justify-content: space-between !important;
96
75
  }
97
76
 
98
- .value-large, .dual-value-container, .value-with-compass {
99
- margin-top: auto;
100
- margin-bottom: auto;
77
+ /* COLONNA DESTRA: Titolo a DX (bordo), Unità a SX */
78
+ .box-depth, .box-tws, .box-twa, .box-awa, .box-twd { align-items: flex-end !important; text-align: right !important; }
79
+ .box-depth .label-row, .box-tws .label-row, .box-twa .label-row, .box-awa .label-row, .box-twd .label-row {
80
+ flex-direction: row-reverse !important; justify-content: space-between !important;
101
81
  }
102
82
 
103
- .value-large { font-size: clamp(1.5rem, 35cqh, 4.5rem); line-height: 0.85; }
83
+ /* ==========================================================================
84
+ 4. TIPOGRAFIA GERARCHICA (REGOLE BASE)
85
+ ========================================================================== */
86
+ .value { color: #000; font-size: clamp(1.2rem, 32cqw, 3.5rem); font-weight: 600; line-height: 0.9; letter-spacing: -1.5px; }
104
87
 
105
- .value-with-compass {
106
- display: flex;
107
- justify-content: space-between;
108
- align-items: center;
109
- width: 100%;
110
- align-self: stretch;
111
- gap: 5px;
88
+ /* Valore MASSIVO (Dati reali): 55% altezza box, larghezza protetta a 22% schermo */
89
+ .value-large {
90
+ font-size: clamp(1.5rem, 55cqh, 22cqw) !important;
91
+ font-weight: 600; line-height: 0.8; margin: 0 !important;
112
92
  }
113
93
 
114
- .dual-value-container { display: flex; justify-content: space-between; width: 100%; }
115
- .dual-value-col { display: flex; flex-direction: column; width: 48%; }
116
- .dual-label { color: #888; font-size: 0.55rem; font-weight: bold; text-transform: uppercase; margin-bottom: 2px; }
117
- .value.dual-val { font-size: clamp(1rem, 22cqh, 2rem); padding-bottom: 0; line-height: 0.9; }
94
+ /* BOX TWD: Bussola a SX, Valore a DX, centrati verticalmente */
95
+ .value-with-compass { display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 100%; height: 85%; }
96
+ .box-twd .mini-compass { height: 85cqh !important; width: auto !important; aspect-ratio: 1/1; flex-shrink: 0; }
97
+ .box-twd #twd-avg { font-size: clamp(1.5rem, 55cqh, 20cqw) !important; text-align: right; line-height: 1; }
98
+
99
+ /* BOX TACK: HDG a SX, COG a DX, Titoli sopra (Default Landscape/Portrait) */
100
+ .box-tack .dual-value-container { display: flex; flex-direction: row; justify-content: space-between; align-items: center; width: 100%; height: 100%; }
101
+ .box-tack .dual-value-col { display: flex; flex-direction: column; justify-content: center; }
102
+ .box-tack .dual-value-col:first-child { align-items: flex-start; }
103
+ .box-tack .dual-value-col:last-child { align-items: flex-end; }
104
+ .box-tack .dual-label { color: #888; font-size: 0.5rem; font-weight: 800; margin-bottom: -2px; }
105
+ .box-tack .value.dual-val { font-size: clamp(1rem, 35cqh, 12cqw) !important; line-height: 0.9; }
106
+
107
+ /* Tag HERCULES: Allineato all'unità (lato interno schermo) */
108
+ .box-hercules { background: rgba(255, 0, 0, 0.05) !important; }
109
+ .box-hercules .unit::before { font-size: 7px; color: #ff4444; font-weight: 900; letter-spacing: 1px; text-transform: uppercase; content: "HERCULES "; }
118
110
 
119
111
  /* ==========================================================================
120
- 4. TACTICAL FOCUS MODE
112
+ 5. TACTICAL FOCUS MODE
121
113
  ========================================================================== */
122
- .focus-active .side-panel:not(.has-focus) { display: none !important; }
123
- .focus-active.focus-side-left { grid-template-columns: 3fr 2fr !important; }
124
- .focus-active.focus-side-left .side-panel.has-focus { grid-column: 1 !important; }
125
- .focus-active.focus-side-left .center-panel { grid-column: 2 !important; justify-content: center !important; align-items: center !important; }
126
- .focus-active.focus-side-right { grid-template-columns: 2fr 3fr !important; }
127
- .focus-active.focus-side-right .center-panel { grid-column: 1 !important; justify-content: center !important; align-items: center !important; }
128
- .focus-active.focus-side-right .side-panel.has-focus { grid-column: 2 !important; }
129
-
130
- .focus-active .has-focus .data-box:not(.is-focused) { display: none !important; }
131
- .focus-active .has-focus .data-box.is-focused {
114
+ .focus-active .data-box:not(.is-focused),
115
+ .focus-active .box-gauge { display: none !important; }
116
+
117
+ .focus-active.focus-side-left {
118
+ grid-template-columns: 3fr 2fr !important;
119
+ grid-template-areas: "focused gauge" !important;
120
+ grid-template-rows: 100% !important;
121
+ }
122
+ .focus-active.focus-side-right {
123
+ grid-template-columns: 2fr 3fr !important;
124
+ grid-template-areas: "gauge focused" !important;
125
+ grid-template-rows: 100% !important;
126
+ }
127
+
128
+ .focus-active .is-focused {
129
+ grid-area: focused !important;
132
130
  height: 100vh !important;
133
- border: none;
134
- background: rgba(0, 0, 0, 0.02);
131
+ background: rgba(0, 0, 0, 0.02) !important;
135
132
  padding: 2vw 3vw !important;
136
- display: flex;
137
- flex-direction: column;
133
+ justify-content: center !important;
138
134
  }
139
135
 
140
- .focus-active .is-focused .value { font-size: clamp(4rem, 25cqh, 10rem) !important; margin-top: 15px; }
141
- .focus-active .is-focused .scale-labels { font-size: clamp(14px, 1.8vw, 22px) !important; min-width: 50px !important; }
142
- .focus-active .is-focused .label-row .label { font-size: 2rem !important; }
143
- .focus-active .is-focused .label-row .unit { font-size: 2rem !important; }
144
- .focus-active .is-focused .sparkline path { stroke-width: 2.5px !important; }
145
- .focus-active .center-panel svg#wind-gauge { max-width: 95% !important; max-height: 85vh !important; }
136
+ /* PRIORITÀ COLORE HERCULES IN FOCUS:
137
+ Forza lo sfondo rosso anche quando il box è ingrandito */
138
+ .focus-active .is-focused.box-hercules {
139
+ background: rgba(255, 0, 0, 0.12) !important;
140
+ box-shadow: inset 0 0 30px rgba(255, 0, 0, 0.1);
141
+ }
142
+
143
+ .focus-active .is-focused .value { font-size: clamp(4rem, 25cqh, 15rem) !important; margin-top: 20px; }
144
+ .focus-active .box-gauge { display: flex !important; grid-area: gauge !important; }
146
145
 
147
146
  /* ==========================================================================
148
- 5. GRAFICI, SCALE E HERCULES MODE
149
- ========================================================================= */
147
+ 6. VIEWPORT QUADRATA (U-SHAPE) - LOGICA ANTI-COLLISIONE
148
+ ========================================================================== */
149
+ @media screen and (min-aspect-ratio: 0.86/1) and (max-aspect-ratio: 1.25/1) {
150
+ .main-container {
151
+ grid-template-columns: 1fr 1fr 1fr 1fr !important;
152
+ grid-template-areas:
153
+ "stw gauge gauge depth"
154
+ "sog gauge gauge tws"
155
+ "hdg gauge gauge twa"
156
+ "cog tack twd awa" !important;
157
+ grid-template-rows: 1.75fr 1.75fr 0.75fr 1.25fr !important;
158
+ }
159
+
160
+ /* Sfondo per distinguere la riga tattica inferiore */
161
+ .box-cog, .box-tack, .box-twd, .box-awa {
162
+ border-top: 1px solid #eee;
163
+ background: rgba(0, 0, 0, 0.04) !important;
164
+ }
165
+
166
+ /* --- TACK: Impilamento Verticale Rigido in U-Shape --- */
167
+ .box-tack .dual-value-container {
168
+ flex-direction: column !important; /* Forza HDG sopra COG */
169
+ justify-content: center !important;
170
+ align-items: flex-start !important; /* Tutto a sinistra */
171
+ gap: 8px !important; /* Spazio tra i due blocchi */
172
+ }
173
+
174
+ .box-tack .dual-value-col {
175
+ display: flex !important;
176
+ flex-direction: column !important; /* Forza Titolo sopra Numero */
177
+ align-items: flex-start !important;
178
+ text-align: left !important;
179
+ width: 100% !important;
180
+ }
181
+
182
+ /* Font calibrato per non uscire dal box stretto */
183
+ .box-tack .value.dual-val {
184
+ font-size: clamp(1rem, 30cqh, 15cqw) !important;
185
+ line-height: 1 !important;
186
+ }
187
+
188
+ .box-tack .dual-label {
189
+ margin-bottom: 0px !important;
190
+ }
191
+
192
+ /* --- TWD: Calibrazione per box stretto --- */
193
+ .box-twd .value-with-compass { justify-content: center; gap: 5px; }
194
+ .box-twd .mini-compass { height: 60cqh !important; }
195
+ .box-twd #twd-avg { font-size: clamp(1.2rem, 50cqh, 20cqw) !important; }
196
+
197
+ /* NOTA: COG e AWA non vengono toccati qui, così restano massivi
198
+ ereditando la regola generale della Sezione 4 */
199
+ }
200
+
201
+ /* ==========================================================================
202
+ 7. GRAFICI, SCALE E TAG HERCULES
203
+ ========================================================================== */
150
204
  .graph-wrapper {
151
- position: relative; width: 100%; flex-grow: 1; min-height: 0; margin-top: 4px;
152
- display: flex; align-items: stretch; background: rgba(0, 0, 0, 0.04);
153
- border-radius: 4px; gap: 0px !important;
205
+ position: relative;
206
+ width: 100%;
207
+ flex-grow: 1;
208
+ min-height: 0;
209
+ margin-top: 5px;
210
+ display: flex;
211
+ flex-direction: row; /* Forza l'allineamento orizzontale dei componenti */
212
+ align-items: stretch;
213
+ background: rgba(0, 0, 0, 0.04);
214
+ border-radius: 4px;
154
215
  }
155
216
 
156
- .left-panel .graph-wrapper { margin-right: -6px; }
157
- .right-panel .graph-wrapper { margin-left: -6px; }
217
+ .sparkline {
218
+ flex-grow: 1;
219
+ height: 100%;
220
+ width: 100% !important;
221
+ background: transparent !important;
222
+ }
158
223
 
159
- .sparkline { flex-grow: 1; height: 100%; width: 100% !important; background: transparent !important; display: block; }
160
- .sparkline path, .sparkline line { stroke-width: 1px !important; transition: all 0.3s ease; }
224
+ /* Forza tutte le linee dei grafici (linee dati e griglie) a 1px per un look chirurgico */
225
+ .sparkline path,
226
+ .sparkline line {
227
+ stroke-width: 1px !important;
228
+ transition: all 0.3s ease;
229
+ }
161
230
 
162
231
  .scale-labels {
163
- display: flex; flex-direction: column; justify-content: space-between;
164
- font-size: 8px; color: #444; font-weight: bold; min-width: 12px;
165
- height: 100%; line-height: 1; padding: 0; border: none !important;
232
+ display: flex;
233
+ flex-direction: column;
234
+ justify-content: space-between;
235
+ font-size: 9px;
236
+ color: #666;
237
+ font-weight: 800;
238
+ min-width: 22px;
239
+ height: 100%;
240
+ line-height: 1;
241
+ z-index: 5;
242
+ }
243
+
244
+ /* --- COLONNA SINISTRA (STW, SOG) --- */
245
+ /* Ordine desiderato: [Grafico] [Scala] -> Scala verso il centro schermo */
246
+ .box-stw .sparkline, .box-sog .sparkline {
247
+ order: 1;
248
+ }
249
+ .box-stw .scale-labels, .box-sog .scale-labels {
250
+ order: 2;
251
+ text-align: left;
252
+ padding-left: 4px;
166
253
  }
167
254
 
168
- .left-panel .scale-labels { order: 2; text-align: left; padding-left: 4px; }
169
- .left-panel .sparkline { order: 1; }
170
- .right-panel .scale-labels { order: 1; text-align: right; padding-right: 4px; }
171
- .right-panel .sparkline { order: 2; }
255
+ /* --- COLONNA DESTRA (DEPTH, TWS) --- */
256
+ /* Ordine desiderato: [Scala] [Grafico] -> Scala verso il centro schermo */
257
+ .box-depth .scale-labels, .box-tws .scale-labels {
258
+ order: 1;
259
+ text-align: right;
260
+ padding-right: 4px;
261
+ }
262
+ .box-depth .sparkline, .box-tws .sparkline {
263
+ order: 2;
264
+ }
172
265
 
173
- .line-hercules { filter: drop-shadow(0 0 5px #ff0000); stroke-width: 1.8px !important; }
266
+ /* --- REGOLE HERCULES (Zoom) --- */
174
267
  .box-hercules { background: rgba(255, 0, 0, 0.05) !important; }
175
268
  .box-hercules .scale-labels { color: #ff5555; }
176
- .box-hercules .unit::before, .box-hercules .unit::after, .box-hercules .label::before {
177
- font-size: 7px; color: #ff4444; font-weight: 900; letter-spacing: 1px; text-transform: uppercase;
269
+ .box-hercules .unit::before {
270
+ font-size: 7px; color: #ff4444; font-weight: 900;
271
+ letter-spacing: 1px; text-transform: uppercase; content: "HERCULES ";
178
272
  }
179
- .left-panel .box-hercules .unit::before { content: "HERCULES "; }
180
- .right-panel .box-hercules .unit::after { content: " HERCULES"; }
181
- .right-panel .box-hercules .label:only-child::after { content: " HERCULES"; }
182
273
 
183
- #stw-graph { stroke: #2ecc71; fill: rgba(46, 204, 113, 0.12); }
184
- #sog-graph { stroke: #f39c12; fill: rgba(243, 156, 18, 0.12); }
185
- #depth-graph { stroke: #3498db; fill: rgba(52, 152, 219, 0.12); }
186
- #tws-graph { stroke: #000000; fill: rgba(0, 0, 0, 0.08); }
274
+ /* Anche in modalità Hercules manteniamo 1px per coerenza, affidandoci al filtro per lo stacco visivo */
275
+ .line-hercules {
276
+ filter: drop-shadow(0 0 5px #ff0000);
277
+ stroke-width: 1px !important;
278
+ }
187
279
 
188
280
  /* ==========================================================================
189
- 6. WIDGETS E STATUS (GIORNO)
281
+ 8. RESPONSIVE (PORTRAIT MODE)
190
282
  ========================================================================== */
191
- #status {
192
- position: absolute; top: 10px; right: 10px;
193
- font-size: 0.6rem; font-weight: 900;
194
- text-transform: uppercase; z-index: 1000;
195
- letter-spacing: 1px; background: rgba(0,0,0,0.05);
196
- padding: 2px 6px; border-radius: 4px;
283
+ @media screen and (max-aspect-ratio: 0.85/1) {
284
+ .main-container {
285
+ grid-template-areas:
286
+ "gauge gauge"
287
+ "stw depth"
288
+ "sog tws"
289
+ "hdg twa"
290
+ "cog awa"
291
+ "tack twd" !important;
292
+ grid-template-columns: 1fr 1fr !important;
293
+ grid-template-rows: 40vh 1.75fr 1.75fr 0.75fr 0.75fr 1.25fr !important;
294
+ }
295
+
296
+ /* Impedisce ai box di collassare in altezza in verticale */
297
+ .data-box { height: auto !important; }
298
+
299
+ /* --- FOCUS MODE IN VERTICALE (STACKED) --- */
300
+ /* Quando siamo in verticale e attiviamo il focus, impiliamo Sopra/Sotto */
301
+ .main-container.focus-active {
302
+ grid-template-columns: 1fr !important; /* Colonna singola */
303
+ grid-template-rows: 65vh 35vh !important; /* 65% Altezza al dato, 35% alla bussola */
304
+ grid-template-areas:
305
+ "focused"
306
+ "gauge" !important;
307
+ }
308
+
309
+ /* Correzione dimensioni box in Focus Verticale */
310
+ .focus-active .is-focused {
311
+ height: 65vh !important;
312
+ width: 100vw !important;
313
+ padding: 5vh 5vw !important; /* Più respiro per display curvi */
314
+ }
315
+
316
+ /* Correzione Bussola in Focus Verticale */
317
+ .focus-active .box-gauge {
318
+ height: 35vh !important;
319
+ width: 100vw !important;
320
+ display: flex !important;
321
+ align-items: center;
322
+ justify-content: center;
323
+ }
324
+
325
+ /* Ridimensionamento font per Focus Verticale (per non sbordare) */
326
+ .focus-active .is-focused .value {
327
+ font-size: clamp(4rem, 20cqh, 12rem) !important;
328
+ }
329
+
330
+ /* In verticale il box TACK è largo: forziamo i due valori ai bordi */
331
+ .box-tack .dual-value-container {
332
+ flex-direction: row !important;
333
+ justify-content: space-between !important;
334
+ align-items: center !important;
335
+ }
197
336
  }
337
+
338
+ /* ==========================================================================
339
+ 9. WIDGETS E OVERRIDE SVG (GIORNO)
340
+ ========================================================================== */
341
+ #status { position: absolute; top: 10px; right: 10px; font-size: 0.6rem; font-weight: 900; text-transform: uppercase; z-index: 1000; background: rgba(0,0,0,0.05); padding: 2px 6px; border-radius: 4px; }
198
342
  .online { color: #27ae60; opacity: 0.8; }
199
343
  .offline { color: #c0392b; font-weight: bold; }
200
344
 
201
- .mini-compass {
202
- width: min(80cqh, 42cqw); height: min(80cqh, 42cqw); aspect-ratio: 1 / 1;
203
- flex-shrink: 0; background: #f0f0f0;
204
- border-radius: 50%; border: 1.5px solid #ddd;
205
- box-shadow: inset 0 0 10px rgba(0,0,0,0.05); transition: all 0.4s ease;
206
- }
207
-
208
- /* Inversione elementi Mini Bussola TWD - Giorno */
209
- .mini-compass text:last-of-type { fill: #000 !important; opacity: 0.4; } /* La "S" nera */
210
- #twd-boat-wrap path { fill: #000 !important; opacity: 0.3; } /* Barca interna nera */
211
- #twd-wind-chevron { stroke: #000 !important; } /* Forza il triangolino vento a nero in giorno */
212
-
213
- #awa-pointer, #twa-pointer, #track-pointer, #twd-arrow, #twd-boat-wrap {
214
- transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1);
215
- }
345
+ .mini-compass { background: #f0f0f0; border-radius: 50%; border: 1.5px solid #ddd; }
346
+ .mini-compass text:last-of-type { fill: #000 !important; opacity: 0.4; }
347
+ #twd-boat-wrap path { fill: #000 !important; opacity: 0.3; }
348
+ #twd-wind-chevron { stroke: #000 !important; }
216
349
 
217
- #wind-gauge { width: 100%; height: 100%; max-height: 100%; object-fit: contain; }
218
-
219
- /* Quadrante Vento Centrale - Giorno */
220
350
  #wind-gauge circle[fill="#050505"] { fill: #fcfcfc; }
221
351
  #wind-gauge circle[fill="#121212"] { fill: #f0f0f0; }
222
352
  #boat-icon { fill: #000 !important; opacity: 1; }
@@ -226,104 +356,99 @@ body {
226
356
  #fullscreen-hotspot { fill: #eee !important; stroke: #ccc !important; }
227
357
  #ticks line[stroke="#fff"] { stroke: #000 !important; }
228
358
  #ticks line[stroke="#666"] { stroke: #bbb !important; }
229
-
230
- /* Leeway Container - Giorno */
231
359
  rect[fill="#222"] { fill: #eee !important; }
232
360
  #leeway-val { color: #000 !important; }
233
361
 
234
- /* ==========================================================================
235
- 7. STATI DI ALLARME
236
- ========================================================================== */
237
- .unstable-data { animation: blink-unstable 1.5s infinite ease-in-out; color: #e67e22 !important; }
238
- .alarm-warning { color: #f39c12 !important; }
239
- .alarm-danger { color: #e74c3c !important; font-weight: 900; animation: blink-unstable 1s infinite; }
240
- @keyframes blink-unstable { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } }
241
-
242
- /* ==========================================================================
243
- 8. RESPONSIVE
244
- ========================================================================== */
245
- @media (max-aspect-ratio: 0.9 / 1) {
246
- .main-container { grid-template-columns: 1fr 1fr !important; grid-template-rows: 45vh calc(55vh - 8px) !important; }
247
- .center-panel { grid-row: 1 !important; grid-column: 1 / span 2 !important; }
248
- .left-panel { grid-row: 2 !important; grid-column: 1 !important; }
249
- .right-panel { grid-row: 2 !important; grid-column: 2 !important; }
250
- .main-container.focus-active { display: flex !important; flex-direction: column !important; }
251
- .focus-active .center-panel { flex: 0 0 45vh !important; }
252
- .focus-active .side-panel.has-focus { flex: 0 0 calc(55vh - 8px) !important; display: flex !important; }
253
- .data-box { height: auto !important; flex: 1 !important; }
254
- }
362
+ #awa-pointer, #twa-pointer, #track-pointer, #twd-arrow, #twd-boat-wrap { transition: all 0.8s cubic-bezier(0.4, 0, 0.2, 1); }
363
+ #wind-gauge { width: 100%; height: 100%; max-height: 100%; object-fit: contain; }
255
364
 
256
365
  /* ==========================================================================
257
- 9. NIGHT MODE (TACTICAL RED)
366
+ 10. NIGHT MODE (TACTICAL RED - INTEGRALE E RIFINITO)
258
367
  ========================================================================== */
259
368
  body.night-mode { background-color: #000 !important; color: #ff0000 !important; }
260
- .night-mode .side-panel { background: rgba(20, 0, 0, 0.4) !important; border: 1px solid #330000; }
261
- .night-mode .data-box { border-bottom-color: #260000; }
262
- .night-mode .label, .night-mode .unit, .night-mode .dual-label { color: #800000 !important; }
263
- .night-mode .value, .night-mode .value-large { color: #ff3333 !important; text-shadow: 0 0 8px rgba(255, 0, 0, 0.4); }
264
369
 
265
- .night-mode .graph-wrapper { background: rgba(30, 0, 0, 0.3) !important; }
266
- .night-mode .sparkline path:first-of-type { display: none !important; }
267
- .night-mode .sparkline path { fill: none !important; stroke: #ff3333 !important; stroke-width: 1.8px !important; opacity: 1 !important; filter: drop-shadow(0 0 2px rgba(255, 0, 0, 0.4)); }
370
+ /* Pannelli e Box */
371
+ .night-mode .data-box { background: rgba(20, 0, 0, 0.4) !important; border-color: #330000; }
372
+ .night-mode .label,
373
+ .night-mode .unit,
374
+ .night-mode .dual-label { color: #800000 !important; }
375
+
376
+ .night-mode .value,
377
+ .night-mode .value-large {
378
+ color: #ff3333 !important;
379
+ text-shadow: 0 0 8px rgba(255, 0, 0, 0.4);
380
+ }
268
381
 
269
- .night-mode .sparkline line { stroke: rgba(255, 0, 0, 0.15) !important; stroke-width: 0.7px !important; }
270
- .night-mode #tws-graph line:not([stroke*="rgba"]) { stroke: #ff3333 !important; stroke-width: 1.8px !important; }
382
+ /* --- GRAFICI NIGHT MODE: Solo linee, zero abbagliamento --- */
383
+ .night-mode .graph-wrapper { background: rgba(30, 0, 0, 0.3) !important; border: 1px solid #330000; }
384
+ .night-mode .sparkline path:first-of-type { display: none !important; }
385
+ .night-mode .sparkline path {
386
+ fill: none !important;
387
+ stroke: #ff3333 !important;
388
+ stroke-width: 1px !important;
389
+ filter: drop-shadow(0 0 2px rgba(255, 0, 0, 0.4));
390
+ }
391
+ .night-mode .sparkline line { stroke: rgba(150, 0, 0, 0.1) !important; }
392
+ .night-mode #tws-graph line:not([stroke*="rgba"]) {
393
+ stroke: #ff3333 !important;
394
+ stroke-width: 1px !important;
395
+ }
271
396
  .night-mode .scale-labels { color: #660000 !important; }
272
397
 
398
+ /* Hercules in Night Mode */
273
399
  .night-mode .box-hercules { background: rgba(60, 0, 0, 0.2) !important; }
274
400
  .night-mode .line-hercules { filter: drop-shadow(0 0 6px #ff0000) !important; }
275
401
 
276
- /* Fix Bussola Centrale Night */
277
- .night-mode #wind-gauge circle { stroke: #330000; }
278
- .night-mode #wind-gauge circle[fill="#050505"] { fill: #050505 !important; }
279
- .night-mode #wind-gauge circle[fill="#121212"] { fill: #121212 !important; }
402
+ /* --- WIND GAUGE NIGHT: Scurimento settori e icone --- */
403
+ .night-mode #wind-gauge circle[fill="#050505"],
404
+ .night-mode #wind-gauge circle[fill="#fcfcfc"] { fill: #050505 !important; }
405
+
406
+ .night-mode #wind-gauge circle[fill="#121212"],
407
+ .night-mode #wind-gauge circle[fill="#f0f0f0"] { fill: #0a0000 !important; }
408
+
409
+ /* Glow e Hotspot centrale */
280
410
  .night-mode #fullscreen-hotspot { fill: #000 !important; stroke: #330000 !important; }
411
+ .night-mode #center-glow feDropShadow { flood-color: #ff0000 !important; flood-opacity: 0.6 !important; }
281
412
 
282
- .night-mode #ticks line { stroke: #4d0000 !important; }
283
- .night-mode #tick-labels { fill: #800000 !important; }
284
413
  .night-mode #boat-icon { fill: #330000 !important; opacity: 0.6 !important; }
285
414
  .night-mode #aws-val-svg { fill: #ff3333 !important; }
286
- .night-mode #aws-display-group text { fill: #ff3333 !important; }
287
- .night-mode #center-glow feDropShadow { flood-color: #ff0000 !important; flood-opacity: 0.6 !important; }
415
+ .night-mode #aws-display-group text { fill: #660000 !important; }
288
416
 
289
- /* Bussola TWD Night */
290
- .night-mode .mini-compass { border-color: #330000; background: #000; }
291
- .night-mode .mini-compass text { fill: #800000 !important; }
292
- .night-mode .mini-compass text:last-of-type { fill: #800000 !important; opacity: 1; } /* La "S" torna rossa */
293
- .night-mode #twd-boat-wrap path { fill: #ff3333 !important; opacity: 0.4 !important; } /* Barca torna rossa */
294
- .night-mode #twd-arrow #twd-wind-chevron { stroke: #ff3333 !important; stroke-width: 3px !important; opacity: 1 !important; filter: drop-shadow(0 0 4px #ff0000); transform-origin: center; }
417
+ /* SCALA GRADUATA BUSSOLA: Tacche e numeri rosso cupo */
418
+ .night-mode #ticks line { stroke: #4d0000 !important; }
419
+ .night-mode #tick-labels { fill: #800000 !important; }
295
420
 
296
- /* Vento e Leeway Night */
421
+ /* SETTORI VENTO: Il verde diventa un rosso scuro trattteggiato */
297
422
  .night-mode #wind-gauge path[stroke="#ff0000"] { stroke: #660000 !important; opacity: 0.8; }
298
- .night-mode #wind-gauge path[stroke="#00ff00"] { stroke: #660000 !important; stroke-dasharray: 4, 3; opacity: 0.8; }
423
+ .night-mode #wind-gauge path[stroke="#00ff00"] { stroke: #440000 !important; stroke-dasharray: 4, 3; opacity: 0.6; }
299
424
  .night-mode #wind-gauge path[stroke="#ff8800"] { stroke: #330000 !important; stroke-width: 8; }
425
+
426
+ /* --- LEEWAY & POINTERS NIGHT --- */
300
427
  .night-mode rect[fill="url(#leeway-grad)"] { fill: url(#leeway-night-grad) !important; }
301
428
  .night-mode #leeway-val { fill: #ff3333 !important; }
429
+ .night-mode rect[fill="#222"], .night-mode rect[fill="#eee"] { fill: #0a0000 !important; stroke: #200000; }
430
+
431
+ /* Scala graduata Leeway: tacche e numeri */
302
432
  .night-mode g[stroke="#555"] line { stroke: #4d0000 !important; }
303
433
  .night-mode g[fill="#555"] text { fill: #660000 !important; }
304
- .night-mode rect[fill="#222"] { fill: #0a0000 !important; stroke: #200000; }
305
434
 
435
+ /* Lancette */
306
436
  .night-mode #awa-pointer path { fill: #ff0000; stroke: #000; }
307
437
  .night-mode #twa-pointer path { fill: #800000; stroke: #000; }
308
438
  .night-mode #track-pointer path { fill: #ff0000; stroke: #fff; stroke-width: 0.5; }
309
439
 
440
+ /* --- MINI BUSSOLA TWD NIGHT --- */
441
+ .night-mode .mini-compass { border-color: #330000; background: #000; }
442
+ .night-mode .mini-compass text { fill: #800000 !important; }
443
+ .night-mode .mini-compass text:last-of-type { fill: #800000 !important; opacity: 1; }
444
+ .night-mode #twd-boat-wrap path { fill: #ff3333 !important; opacity: 0.4 !important; }
445
+ .night-mode #twd-arrow #twd-wind-chevron { stroke: #ff3333 !important; filter: drop-shadow(0 0 4px #ff0000); }
446
+
310
447
  /* ==========================================================================
311
- 10. TREND VENTO E ALLARME STRAMBATA
448
+ 11. TREND E ANIMAZIONI
312
449
  ========================================================================== */
313
450
  @keyframes blink-trend { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
314
-
315
- #trend-dot-cw, #trend-dot-ccw, #trend-gauge-cw, #trend-gauge-ccw {
316
- opacity: 0.3;
317
- transition: opacity 0.3s ease;
318
- }
319
-
320
- .is-trending {
321
- opacity: 1 !important;
322
- animation: blink-trend 1s infinite !important;
323
- }
324
-
325
- .is-gybing {
326
- opacity: 1 !important;
327
- animation: none !important;
328
- filter: drop-shadow(0 0 8px #ff0000) !important;
329
- }
451
+ #trend-dot-cw, #trend-dot-ccw, #trend-gauge-cw, #trend-gauge-ccw { opacity: 0.3; transition: opacity 0.3s ease; }
452
+ .is-trending { opacity: 1 !important; animation: blink-trend 1s infinite !important; }
453
+ .is-gybing { opacity: 1 !important; animation: none !important; filter: drop-shadow(0 0 8px #ff0000) !important; }
454
+ .unstable-data { animation: blink-trend 1.5s infinite ease-in-out; color: #e67e22 !important; }