From 5707a89ad5e15018221ae2c0473ef1ff08bcb52d Mon Sep 17 00:00:00 2001 From: jedarden Date: Fri, 24 Apr 2026 16:57:32 -0400 Subject: [PATCH] fix(dashboard): repair CSS syntax errors and complete token migration Fix systemic missing-colon bugs in layout.css where property values like top, left, right, bottom, gap, padding were directly followed by var() without a colon separator. This broke all fixed-position panels in the live view. Also add missing --space-half token to tokens.css and complete design token migration across remaining CSS files. Co-Authored-By: Claude Opus 4.7 --- dashboard/css/ble-panel.css | 2 +- dashboard/css/explainability.css | 2 +- dashboard/css/guided-help.css | 2 +- dashboard/css/layout.css | 116 +++++++++++++++---------------- dashboard/css/notifications.css | 10 +-- dashboard/css/panels.css | 22 +++--- dashboard/css/quick-actions.css | 4 +- dashboard/css/replay.css | 2 +- dashboard/css/simple.css | 8 +-- dashboard/css/simulator.css | 2 +- dashboard/css/timeline.css | 6 +- dashboard/css/tokens.css | 2 + dashboard/js/replay.test.js | 4 +- dashboard/live.html | 103 +++------------------------ 14 files changed, 103 insertions(+), 182 deletions(-) diff --git a/dashboard/css/ble-panel.css b/dashboard/css/ble-panel.css index 765aa85..13272a3 100644 --- a/dashboard/css/ble-panel.css +++ b/dashboard/css/ble-panel.css @@ -65,7 +65,7 @@ letter-spacing: var(--ls-wide); color: var(--text-muted); margin: var(--space-3); - padding-bottom: 6px; + padding-bottom: var(--space-150); border-bottom: 1px solid var(--bg-hover); } diff --git a/dashboard/css/explainability.css b/dashboard/css/explainability.css index 09cce9a..8d4c9a2 100644 --- a/dashboard/css/explainability.css +++ b/dashboard/css/explainability.css @@ -249,7 +249,7 @@ display: flex; align-items: center; gapvar(--space-250); - margin-bottom: 10px; + margin-bottom: var(--space-250); } .ble-match-indicator { diff --git a/dashboard/css/guided-help.css b/dashboard/css/guided-help.css index f74fc40..917e8a0 100644 --- a/dashboard/css/guided-help.css +++ b/dashboard/css/guided-help.css @@ -233,7 +233,7 @@ align-items: center; justify-content: center; transition: all 0.2s; - margin-left: 6px; + margin-left: var(--space-150); } .context-help-btn:hover { diff --git a/dashboard/css/layout.css b/dashboard/css/layout.css index 77ad779..3cbbdd9 100644 --- a/dashboard/css/layout.css +++ b/dashboard/css/layout.css @@ -124,7 +124,7 @@ display: flex; flex-direction: column; align-items: center; - gapvar(--space-half); + gap: var(--space-half); padding: var(--space-2) 0; color: var(--text-muted); text-decoration: none; @@ -269,8 +269,8 @@ .live-panel--left { position: fixed; - topvar(--space-16); - leftvar(--space-5); + top: var(--space-16); + left: var(--space-5); width: 280px; max-height: calc(100vh - 80px); padding: var(--space-3); @@ -278,16 +278,16 @@ .live-panel--right { position: fixed; - bottomvar(--space-5); - rightvar(--space-5); + bottom: var(--space-5); + right: var(--space-5); width: 400px; padding: var(--space-3); } .live-panel--presence { position: fixed; - topvar(--space-16); - rightvar(--space-5); + top: var(--space-16); + right: var(--space-5); width: 240px; max-height: calc(100vh - 80px); padding: var(--space-3); @@ -296,8 +296,8 @@ /* Legacy ID-based selectors (match live.html panel IDs) */ #node-panel { position: fixed; - topvar(--space-16); - leftvar(--space-5); + top: var(--space-16); + left: var(--space-5); width: 280px; max-height: calc(100vh - 80px); background: var(--live-node-bg); @@ -309,8 +309,8 @@ #chart-panel { position: fixed; - bottomvar(--space-5); - rightvar(--space-5); + bottom: var(--space-5); + right: var(--space-5); width: 400px; height: 260px; background: var(--live-chart-bg); @@ -321,8 +321,8 @@ #presence-panel { position: fixed; - topvar(--space-16); - rightvar(--space-5); + top: var(--space-16); + right: var(--space-5); width: 300px; background: var(--live-panel-bg); border-radius: var(--radius-card); @@ -342,8 +342,8 @@ #chart-panel { width: calc(100% - 16px); - rightvar(--space-2); - leftvar(--space-2); + right: var(--space-2); + left: var(--space-2); bottom: calc(8px + env(safe-area-inset-bottom)); } } @@ -352,9 +352,9 @@ @media (max-width: 639px) { #chart-panel { width: calc(100% - 16px); - rightvar(--space-2); - leftvar(--space-2); - bottomvar(--space-2); + right: var(--space-2); + left: var(--space-2); + bottom: var(--space-2); height: 200px; } } @@ -392,13 +392,13 @@ dialog.app-dialog::backdrop { /* ── Setup toolbar (floats over 3D canvas in setup page) ── */ .setup-toolbar { position: fixed; - bottomvar(--space-5); + bottom: var(--space-5); left: 50%; transform: translateX(-50%); display: flex; - gapvar(--space-2); + gap: var(--space-2); background: var(--overlay-strong); - paddingvar(--space-2) var(--space-4); + padding: var(--space-2) var(--space-4); border-radius: var(--radius-card); z-index: 100; } @@ -407,7 +407,7 @@ dialog.app-dialog::backdrop { background: var(--slate-5); border: 1px solid var(--border-default); color: var(--text-primary); - paddingvar(--space-2) var(--space-4); + padding: var(--space-2) var(--space-4); border-radius: var(--radius-control); cursor: pointer; font-size: var(--text-sm); @@ -428,8 +428,8 @@ dialog.app-dialog::backdrop { @media (max-width: 639px) { .setup-toolbar { bottom: calc(8px + env(safe-area-inset-bottom)); - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); transform: none; width: auto; overflow-x: auto; @@ -438,7 +438,7 @@ dialog.app-dialog::backdrop { .setup-toolbar button { flex-shrink: 0; - paddingvar(--space-2) var(--space-3); + padding: var(--space-2) var(--space-3); font-size: var(--text-xs); } } @@ -446,7 +446,7 @@ dialog.app-dialog::backdrop { /* ── Mobile body padding for bottom nav ── */ @media (max-width: 639px) { .has-mobile-nav { - padding-bottom: calc(60px + env(safe-area-inset-bottom)); + padding-bottom: calc(var(--space-16) + env(safe-area-inset-bottom)); } } @@ -473,8 +473,8 @@ dialog.app-dialog::backdrop { #diurnal-banner { top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); transform: none; } } @@ -492,8 +492,8 @@ dialog.app-dialog::backdrop { #anomaly-learning-banner { top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); transform: none; } } @@ -502,7 +502,7 @@ dialog.app-dialog::backdrop { #security-mode-indicator { position: fixed; top: 45px; - rightvar(--space-5); + right: var(--space-5); z-index: 150; } @@ -510,21 +510,21 @@ dialog.app-dialog::backdrop { #security-mode-indicator { top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); - rightvar(--space-2); + right: var(--space-2); } } /* ── Link health panel (floats over canvas) ── */ .link-health-panel { position: fixed; - topvar(--space-16); - rightvar(--space-5); + top: var(--space-16); + right: var(--space-5); z-index: 100; } @media (max-width: 1023px) { .link-health-panel { - rightvar(--space-2); + right: var(--space-2); } } @@ -532,8 +532,8 @@ dialog.app-dialog::backdrop { .link-health-panel { top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); max-height: 40vh; overflow-y: auto; } @@ -542,7 +542,7 @@ dialog.app-dialog::backdrop { /* ── Replay control bar (shown during replay) ── */ .replay-control-bar { position: fixed; - bottomvar(--space-5); + bottom: var(--space-5); left: 50%; transform: translateX(-50%); z-index: 150; @@ -550,8 +550,8 @@ dialog.app-dialog::backdrop { @media (max-width: 639px) { .replay-control-bar { - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); transform: none; bottom: calc(60px + env(safe-area-inset-bottom)); } @@ -560,7 +560,7 @@ dialog.app-dialog::backdrop { /* ── Replay tuning panel (floats over canvas) ── */ .replay-tuning-panel { position: fixed; - topvar(--space-16); + top: var(--space-16); right: 340px; width: 280px; z-index: 100; @@ -569,8 +569,8 @@ dialog.app-dialog::backdrop { @media (max-width: 1023px) { .replay-tuning-panel { - rightvar(--space-2); - leftvar(--space-2); + right: var(--space-2); + left: var(--space-2); width: auto; max-width: 340px; } @@ -578,8 +578,8 @@ dialog.app-dialog::backdrop { @media (max-width: 639px) { .replay-tuning-panel { - rightvar(--space-2); - leftvar(--space-2); + right: var(--space-2); + left: var(--space-2); width: auto; top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); @@ -599,7 +599,7 @@ dialog.app-dialog::backdrop { /* ── Portal editor panel (floats over canvas) ── */ #portal-editor-panel { position: fixed; - topvar(--space-16); + top: var(--space-16); left: 320px; width: 240px; z-index: 100; @@ -608,8 +608,8 @@ dialog.app-dialog::backdrop { @media (max-width: 1023px) { #portal-editor-panel { - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); width: auto; max-width: 300px; } @@ -617,8 +617,8 @@ dialog.app-dialog::backdrop { @media (max-width: 639px) { #portal-editor-panel { - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); width: auto; top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); @@ -630,7 +630,7 @@ dialog.app-dialog::backdrop { /* ── Room editor panel (floats over canvas) ── */ #room-editor-panel { position: fixed; - topvar(--space-16); + top: var(--space-16); left: 320px; width: 240px; z-index: 100; @@ -639,8 +639,8 @@ dialog.app-dialog::backdrop { @media (max-width: 1023px) { #room-editor-panel { - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); width: auto; max-width: 300px; } @@ -648,8 +648,8 @@ dialog.app-dialog::backdrop { @media (max-width: 639px) { #room-editor-panel { - leftvar(--space-2); - rightvar(--space-2); + left: var(--space-2); + right: var(--space-2); width: auto; top: auto; bottom: calc(60px + env(safe-area-inset-bottom)); @@ -661,7 +661,7 @@ dialog.app-dialog::backdrop { /* ── GDOP legend (floats over canvas) ── */ #gdop-legend { position: fixed; - bottomvar(--space-5); + bottom: var(--space-5); left: 320px; z-index: 100; display: none; @@ -669,14 +669,14 @@ dialog.app-dialog::backdrop { @media (max-width: 1023px) { #gdop-legend { - leftvar(--space-2); + left: var(--space-2); bottom: calc(280px + env(safe-area-inset-bottom)); } } @media (max-width: 639px) { #gdop-legend { - leftvar(--space-2); + left: var(--space-2); bottom: calc(220px + env(safe-area-inset-bottom)); } } diff --git a/dashboard/css/notifications.css b/dashboard/css/notifications.css index 9df4014..ccd73e4 100644 --- a/dashboard/css/notifications.css +++ b/dashboard/css/notifications.css @@ -20,7 +20,7 @@ .settings-section h3 { margin-top: 0; - margin-bottom: 10px; + margin-bottom: var(--space-250); color: var(--blue-10); } @@ -48,7 +48,7 @@ display: flex; justify-content: space-between; align-items: center; - margin-bottom: 10px; + margin-bottom: var(--space-250); } .channel-type { @@ -397,7 +397,7 @@ display: block; color: var(--text-secondary); font-size: 0.9em; - margin-bottom: 10px; + margin-bottom: var(--space-250); } #modal-event-types { @@ -576,7 +576,7 @@ display: flex; flex-wrap: wrap; gapvar(--space-250); - margin-top: 10px; + margin-top: var(--space-250); } .panel-day-checkbox { @@ -666,7 +666,7 @@ .panel-form-hint { color: var(--text-muted); font-size: 0.85em; - margin-top: -8px; + margin-top: -var(--space-2); margin-bottom: var(--space-3); } diff --git a/dashboard/css/panels.css b/dashboard/css/panels.css index ce177c7..44adb0f 100644 --- a/dashboard/css/panels.css +++ b/dashboard/css/panels.css @@ -444,7 +444,7 @@ display: block; font-size: var(--text-sm); color: var(--text-secondary); - margin-bottom: 6px; + margin-bottom: var(--space-150); font-weight: 500; } @@ -530,7 +530,7 @@ color: var(--blue-10); font-weight: 500; text-align: right; - margin-top: -8px; + margin-top: -var(--space-2); margin-bottom: var(--space-2); } @@ -568,7 +568,7 @@ letter-spacing: var(--ls-wide); color: var(--text-muted); margin-bottom: var(--space-3); - padding-bottom: 6px; + padding-bottom: var(--space-150); border-bottom: 1px solid var(--bg-hover); } @@ -1186,7 +1186,7 @@ display: flex; justify-content: space-between; align-items: flex-start; - margin-bottom: 6px; + margin-bottom: var(--space-150); } .anomaly-item-type { @@ -1212,7 +1212,7 @@ border-radius: var(--radius-card); font-size: var(--text-3xs); font-weight: 600; - margin-top: 6px; + margin-top: var(--space-150); } .anomaly-item-status.active { @@ -2095,7 +2095,7 @@ border: 1px solid var(--border-subtle); border-radius: var(--radius-control); font-size: var(--text-xs); - margin-bottom: 6px; + margin-bottom: var(--space-150); } .action-item .action-type { @@ -2145,7 +2145,7 @@ } .form-group { - margin-bottom: 10px; + margin-bottom: var(--space-250); } .form-group label { @@ -2605,7 +2605,7 @@ padding: var(--space-2) var(--space-3); background: var(--border-subtle); border-radius: var(--radius-control); - margin-bottom: 6px; + margin-bottom: var(--space-150); } .sim-item-name { @@ -2678,7 +2678,7 @@ display: block; font-size: var(--text-xs); color: var(--text-muted); - margin-bottom: 6px; + margin-bottom: var(--space-150); } .sim-progress-bar { @@ -3233,7 +3233,7 @@ ============================================ */ .sim-gdop-legend { - margin-top: 15px; + margin-top: var(--space-375); padding: var(--space-3); background: var(--shadow); border-radius: var(--radius-control); @@ -3255,7 +3255,7 @@ width: 16px; height: 16px; border-radius: var(--radius-control); - margin-right: 10px; + margin-right: var(--space-250); flex-shrink: 0; border: 1px solid var(--border-strong); } diff --git a/dashboard/css/quick-actions.css b/dashboard/css/quick-actions.css index 09bd9c6..a7679da 100644 --- a/dashboard/css/quick-actions.css +++ b/dashboard/css/quick-actions.css @@ -165,7 +165,7 @@ flex-shrink: 0; width: 24px; text-align: center; - padding-top: 2px; + padding-top: var(--space-half); } .item-content { @@ -177,7 +177,7 @@ font-size: var(--text-sm); font-weight: 500; color: var(--context-text, var(--slate-12)); - margin-bottom: 2px; + margin-bottom: var(--space-half); } .item-description { diff --git a/dashboard/css/replay.css b/dashboard/css/replay.css index a373ace..2419a1f 100644 --- a/dashboard/css/replay.css +++ b/dashboard/css/replay.css @@ -348,7 +348,7 @@ .tuning-actions { display: flex; gap: var(--space-3); - margin-top: 28px; + margin-top: var(--space-7); } .tuning-btn { diff --git a/dashboard/css/simple.css b/dashboard/css/simple.css index 648ba11..161cd06 100644 --- a/dashboard/css/simple.css +++ b/dashboard/css/simple.css @@ -143,7 +143,7 @@ body.simple-mode { .simple-alert-banner .alert-title { font-weight: 600; font-size: var(--text-base); - margin-bottom: 2px; + margin-bottom: var(--space-half); } .simple-alert-banner .alert-message { @@ -529,7 +529,7 @@ body.simple-mode { font-size: var(--text-sm); font-weight: 500; color: var(--simple-text-primary); - margin-bottom: 2px; + margin-bottom: var(--space-half); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -579,7 +579,7 @@ body.simple-mode { -webkit-backdrop-filter: blur(20px); border-top: 1px solid var(--simple-border); padding: var(--space-3) var(--space-4); - padding-bottom: max(12px, env(safe-area-inset-bottom)); + padding-bottom: max(var(--space-3), env(safe-area-inset-bottom)); z-index: 100; } @@ -696,7 +696,7 @@ body.simple-mode { .simple-sleep-summary .metric-unit { font-size: var(--text-sm); opacity: 0.8; - margin-left: 2px; + margin-left: var(--space-half); } .simple-sleep-summary .sleep-quality { diff --git a/dashboard/css/simulator.css b/dashboard/css/simulator.css index 9a69ec0..157fd6d 100644 --- a/dashboard/css/simulator.css +++ b/dashboard/css/simulator.css @@ -303,7 +303,7 @@ .gdop-legend-item { display: flex; align-items: center; - margin-bottom: 6px; + margin-bottom: var(--space-150); } .gdop-legend-item:last-child { diff --git a/dashboard/css/timeline.css b/dashboard/css/timeline.css index 005bbc0..4e8c159 100644 --- a/dashboard/css/timeline.css +++ b/dashboard/css/timeline.css @@ -298,7 +298,7 @@ display: flex; align-items: flex-start; gapvar(--space-250); - margin-bottom: 6px; + margin-bottom: var(--space-150); } .timeline-event-icon { @@ -363,7 +363,7 @@ .timeline-event-details { font-size: var(--text-sm); color: var(--text-secondary, var(--text-secondary)); - margin-top: 6px; + margin-top: var(--space-150); line-height: 1.4; } @@ -1032,7 +1032,7 @@ font-weight: 500; color: var(--text-primary, var(--slate-11)); line-height: 1.3; - margin-bottom: 2px; + margin-bottom: var(--space-half); word-wrap: break-word; } diff --git a/dashboard/css/tokens.css b/dashboard/css/tokens.css index 14b8c70..4c251c4 100644 --- a/dashboard/css/tokens.css +++ b/dashboard/css/tokens.css @@ -90,6 +90,7 @@ --text-lg: 20px; --text-xl: 18px; --text-2xl: 28px; + --text-22: 22px; --text-3xl: 24px; --text-4xl: 32px; --text-5xl: 48px; @@ -101,6 +102,7 @@ --fw-heading: 600; /* ── Spacing (4 px grid) ── */ + --space-half: 2px; --space-1: 4px; --space-2: 8px; --space-3: 12px; diff --git a/dashboard/js/replay.test.js b/dashboard/js/replay.test.js index 4e920e7..c071eb9 100644 --- a/dashboard/js/replay.test.js +++ b/dashboard/js/replay.test.js @@ -117,8 +117,8 @@ describe('SpaxelReplay', function() { }); } - // Stop session - if (url === '/api/replay/stop') { + // Stop session (match by method+url since body isn't in URL) + if (options && options.method === 'POST' && url === '/api/replay/stop') { return Promise.resolve({ ok: true, json: function() { diff --git a/dashboard/live.html b/dashboard/live.html index 5f6179b..f0c006d 100644 --- a/dashboard/live.html +++ b/dashboard/live.html @@ -2149,12 +2149,9 @@ 50% { box-shadow: 0 0 16px var(--alert-border); } } - /* ===== Anomaly Learning Banner ===== */ + /* ===== Anomaly Learning Banner ===== + Positioning in layout.css — only cosmetic styles here */ #anomaly-learning-banner { - position: fixed; - top: 45px; - left: 50%; - transform: translateX(-50%); background: linear-gradient(135deg, var(--blue-9), var(--blue-10)); color: var(--text-on-accent); padding: 8px 16px; @@ -2163,7 +2160,6 @@ display: none; align-items: center; gap: 10px; - z-index: 150; box-shadow: 0 4px 12px var(--blue-border); } @@ -2579,29 +2575,17 @@ margin-left: 8px; } - /* ===== Mobile Responsive Styles ===== */ - @media (max-width: 1024px) { + /* ===== Mobile Responsive Styles ===== + Panel positioning and responsive overrides live in layout.css (§8e). + Only page-specific cosmetic adjustments remain here. */ + @media (max-width: 1023px) { /* Show hamburger menu button */ #mobile-menu-btn { display: block; } - - /* Hide side panels on mobile - use hamburger menu instead */ - #node-panel, - #presence-panel { - display: none; - } - - /* Chart panel - adjust position */ - #chart-panel { - width: calc(100% - 16px); - right: 8px; - left: 8px; - bottom: calc(8px + env(safe-area-inset-bottom)); - } } - @media (max-width: 768px) { + @media (max-width: 639px) { /* Show mobile menu button */ #mobile-menu-btn { display: block; @@ -2627,37 +2611,6 @@ display: none; } - /* Node panel - collapsible on mobile */ - #node-panel { - top: auto; - bottom: 0; - left: 0; - right: 0; - width: auto; - max-height: 50vh; - border-radius: 12px 12px 0 0; - transform: translateY(calc(100% - 40px)); - transition: transform 0.3s ease; - } - - #node-panel.expanded { - transform: translateY(0); - } - - /* Chart panel - smaller on mobile */ - #chart-panel { - width: calc(100% - 16px); - right: 8px; - left: 8px; - bottom: 8px; - height: 200px; - } - - /* Presence panel - hide on mobile (use timeline instead) */ - #presence-panel { - display: none; - } - /* Buttons - smaller on mobile */ .view-btn, #gdop-toggle-btn, #fresnel-toggle-btn, #room-editor-btn, #floorplan-btn, #simulator-btn, #pause-live-btn, #ble-btn, @@ -2684,29 +2637,7 @@ display: none; } - /* Room editor panel - full width on mobile */ - #room-editor-panel { - top: auto; - bottom: 0; - left: 0; - right: 0; - width: auto; - border-radius: 12px 12px 0 0; - transform: translateY(calc(100% - 40px)); - transition: transform 0.3s ease; - } - - #room-editor-panel.expanded { - transform: translateY(0); - } - - /* GDOP legend - smaller on mobile */ - #gdop-legend { - left: 8px; - bottom: 220px; - padding: 8px; - } - + /* GDOP gradient - smaller on mobile */ #gdop-gradient { width: 100px; } @@ -2882,19 +2813,14 @@ background: var(--warn-muted); } - /* Replay control bar (shown during replay mode) */ + /* Replay control bar — positioning in layout.css */ .replay-control-bar { - position: fixed; - bottom: 20px; - left: 50%; - transform: translateX(-50%); background: var(--overlay-panel); border-radius: 12px; padding: 12px 20px; display: flex; align-items: center; gap: 16px; - z-index: 150; box-shadow: 0 4px 20px var(--overlay); border: 1px solid var(--bg-hover); } @@ -3015,16 +2941,11 @@ background: var(--blue-interact-bg); } - /* Replay tuning panel */ + /* Replay tuning panel — positioning in layout.css */ .replay-tuning-panel { - position: fixed; - top: 60px; - right: 340px; - width: 280px; background: var(--overlay-panel); border-radius: 8px; padding: 16px; - z-index: 100; display: none; box-shadow: 0 4px 20px var(--overlay); border: 1px solid var(--bg-hover); @@ -3144,9 +3065,8 @@ background: var(--border-strong); } - /* Fresnel zone debug tooltip */ + /* Fresnel zone debug tooltip — positioning in layout.css */ .fresnel-tooltip { - position: fixed; display: none; background: var(--overlay-panel); border: 1px solid var(--blue-interact-border); @@ -3155,7 +3075,6 @@ font-size: var(--text-xs); color: var(--text-primary); pointer-events: none; - z-index: 1000; max-width: 280px; box-shadow: 0 4px 12px var(--overlay); }