PageSourceSearch

https://smartcone.ai/src/shell/types/appbuilder/styles.js

js smartcone.ai collected 2026-10-05 15:08:14 UTC 57,296 bytes, 1,009 lines download raw bytes

1// appbuilder/styles.js — All AppBuilder CSS, injected once on first mount.
2import { ensureToolbarBaseStyles } from "../../common/toolbar.js";
3
4let _stylesInjected = false;
5export function ensureStyles() {
6  if (_stylesInjected) return;
7  _stylesInjected = true;
8  // Inject shared toolbar base CSS (toolbar-ab, btn-ab, toolbar-section-ab, etc.)
9  ensureToolbarBaseStyles();
10  const style = document.createElement("style");
11  style.textContent = `
12/* ── App Builder shell ─────────────────────────────────────────────────── */
13.shell-ab {
14  display: flex; flex-direction: row;
15  width: 100%; height: 100%; overflow: hidden;
16  background: #0f172a;
17  font-family: system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
18}
19/* ── Vertical Left Toolbar ──────────────────────────────────────────────── */
20.shell-ab > .toolbar-ab {
21  flex-direction: column; align-items: stretch;
22  width: 132px; min-width: 132px; flex-shrink: 0;
23  height: 100%; border-right: 1px solid #334155; border-bottom: none;
24  padding: 0; overflow-y: auto; overflow-x: hidden; gap: 0;
25}
26.shell-ab > .toolbar-ab .toolbar-title-ab {
27  text-align: center; margin: 0; padding: 7px 4px 6px;
28  border-bottom: 1px solid #334155; font-size: 10px;
29  align-self: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
30}
31/* Vertical toolbar sections */
32.shell-ab > .toolbar-ab .toolbar-section-ab {
33  border-left: none; border-top: 1px solid #334155;
34  padding: 0; width: 100%; gap: 0; align-items: stretch;
35}
36.shell-ab > .toolbar-ab .toolbar-section-ab:first-of-type { border-top: none; }
37/* Section label as collapsible toggle header */
38.shell-ab > .toolbar-ab .toolbar-section-label-ab {
39  display: flex; align-items: center; justify-content: space-between;
40  width: 100%; padding: 4px 6px 3px; cursor: pointer;
41  pointer-events: auto; box-sizing: border-box;
42}
43.shell-ab > .toolbar-ab .toolbar-section-label-ab:hover {
44  color: #94a3b8; background: rgba(255,255,255,0.04);
45}
46.shell-ab > .toolbar-ab .section-toggle-ab {
47  font-size: 8px; color: #475569; transition: transform 0.15s; flex-shrink: 0;
48}
49.shell-ab > .toolbar-ab .toolbar-section-ab.section-collapsed-ab .section-toggle-ab {
50  transform: rotate(-90deg);
51}
52.shell-ab > .toolbar-ab .toolbar-section-ab.section-collapsed-ab .toolbar-section-row-ab {
53  display: none;
54}
55/* Section body: 2-column button grid */
56.shell-ab > .toolbar-ab .toolbar-section-row-ab {
57  width: 100%;
58  display: grid;
59  grid-template-columns: repeat(2, minmax(0, 1fr));
60  gap: 4px;
61  padding: 2px 0 4px;
62}
63.shell-ab > .toolbar-ab .palette-ab {
64  grid-column: 1 / -1;
65  width: 100%;
66  min-width: 0;
67  border-right: none;
68  background: transparent;
69  padding: 0;
70  overflow: visible;
71}
72.shell-ab > .toolbar-ab .palette-ab .palette-section {
73  padding: 2px 0 4px;
74}
75.shell-ab > .toolbar-ab .palette-ab .palette-grid {
76  grid-template-columns: repeat(2, minmax(0, 1fr));
77}
78/* Vertical buttons: icon above, label below */
79.shell-ab > .toolbar-ab .btn-ab {
80  flex-direction: column; align-items: center; justify-content: center;
81  padding: 5px 2px; gap: 2px; width: 100%; min-width: 0; font-size: 9px;
82}
83.shell-ab > .toolbar-ab .btn-emoji-ab {
84  font-size: 14px; line-height: 1;
85}
86.shell-ab > .toolbar-ab .btn-ab .btn-label-ab {
87  display: block;
88  font-size: 9px; text-align: center; white-space: normal;
89  overflow: hidden; text-overflow: ellipsis; line-height: 1.15;
90}
91/* Theme select fits the sidebar width */
92.shell-ab > .toolbar-ab .theme-select-ab {
93  grid-column: 1 / -1;
94  width: 100%; min-width: unset; font-size: 10px; padding: 3px 4px;
95}
96/* Hide spacer in vertical layout */
97.shell-ab > .toolbar-ab .spacer-ab { flex: 0; display: none; }
98/* ── Theme selector (non-toolbar context) ───────────────────────────────── */
99.theme-select-ab {
100  padding: 4px 8px; border: 1px solid #475569; border-radius: 5px;
101  background: #1e293b; color: #cbd5e1; font: 12px/1.4 system-ui;
102  cursor: pointer; outline: none; min-width: 120px;
103}
104.theme-select-ab:hover { background:#334155; color:#f1f5f9; }
105.theme-select-ab:focus { border-color:#60a5fa; }
106/* ── Mode toggle (non-toolbar context) ─────────────────────────────────── */
107/* ── Body ───────────────────────────────────────────────────────────────── */
108.body-ab { display:flex; flex:1; overflow:hidden; }
109/* ── Canvas toolbar (inline above canvas) ────────────────────────────────── */
110.canvas-toolbar-ab {
111  display:flex; align-items:center; gap:3px; flex-shrink:0;
112  padding:4px 8px; background:#e2e8f0; border-bottom:1px solid #cbd5e1;
113  flex-wrap:wrap;
114}
115.canvas-toolbar-ab .btn-ab {
116  background:#f8fafc; border-color:#cbd5e1; color:#64748b;
117  padding:3px 8px; font-size:11px;
118}
119.canvas-toolbar-ab .btn-ab:hover { background:#e2e8f0; color:#334155; }
120.canvas-toolbar-ab .btn-ab:disabled { opacity:.3; }
121.canvas-toolbar-ab .btn-active-ab { background:#dbeafe; border-color:#3b82f6; color:#1d4ed8; }
122.canvas-toolbar-ab .sep-ab { background:#cbd5e1; }
123.ct-label-ab { font:600 10px/1 system-ui; color:#94a3b8; letter-spacing:.4px; margin-right:2px; }
124/* ── Canvas wrapper ─────────────────────────────────────────────────────── */
125.canvas-wrap-ab { flex:1; overflow:auto; background:#e2e8f0; display:flex; flex-direction:column; }
126.canvas-ab { flex:1; overflow:auto; background:#e2e8f0; position:relative; padding:0 20px 20px; touch-action: none; }
127/* ── Canvas page (the "artboard") ──────────────────────────────────────── */
128.canvas-page-ab {
129  position:relative; background:#fff;
130  border-radius:4px; box-shadow:0 2px 20px rgba(0,0,0,.12);
131  margin:0 auto; overflow:visible;
132}
133.canvas-page-ab.grid-on-ab {
134  background-image: linear-gradient(rgba(0,0,0,.06) 1px, transparent 1px),
135    linear-gradient(90deg, rgba(0,0,0,.06) 1px, transparent 1px);
136  background-size: var(--grid-size-ab,10px) var(--grid-size-ab,10px);
137}
138/* ── Component wrapper (absolute positioned) ───────────────────────────── */
139.comp-wrap-ab {
140  position:absolute; box-sizing:border-box;
141  border-radius:3px; outline:none;
142}
143.comp-wrap-ab .live-inner-ab {
144  width:100%; height:100%; overflow:hidden; pointer-events:none;
145  box-sizing:border-box;
146}
147.comp-wrap-ab:hover > .comp-hover-ring-ab { display:block; }
148.comp-hover-ring-ab {
149  display:none; position:absolute; inset:-2px;
150  border:1px dashed #93c5fd; border-radius:4px; pointer-events:none; z-index:1;
151}
152.comp-wrap-ab.selected-ab > .comp-hover-ring-ab { display:none; }
153.comp-wrap-ab.selected-ab { outline:2px solid #2563eb; outline-offset:1px; }
154.comp-wrap-ab.locked-ab { outline-color:#f59e0b !important; }
155.comp-wrap-ab.locked-ab .comp-hover-ring-ab { border-color:#fcd34d; }
156.comp-wrap-ab.group-wrap-ab {
157  outline:2px dashed #6366f1 !important; outline-offset:3px;
158  background:rgba(99,102,241,.04);
159}
160.comp-wrap-ab.comp-moving-ab { opacity:.7; z-index:99999 !important; }
161.comp-wrap-ab.drop-target-ab { outline:2px solid #7c3aed !important; outline-offset:2px; background:rgba(124,58,237,.08) !important; }
162/* A container holding docked children fades its own placeholder template so the
163   docked items read as the real content instead of overlapping mock UI. */
164.comp-wrap-ab.has-docked-children-ab { outline:1px dashed #94a3b8; outline-offset:2px; }
165.container-template-ab { opacity:.18 !important; pointer-events:none !important; }
166/* ── Resize handles ─────────────────────────────────────────────────────── */
167.handle-ab {
168  position:absolute; width:10px; height:10px;
169  background:#2563eb; border:2px solid #fff;
170  border-radius:2px; z-index:10; box-shadow:0 1px 4px rgba(0,0,0,.3);
171}
172.handle-ab[data-dir="nw"] { top:-5px; left:-5px; cursor:nwse-resize; }
173.handle-ab[data-dir="n"]  { top:-5px; left:calc(50% - 5px); cursor:ns-resize; }
174.handle-ab[data-dir="ne"] { top:-5px; right:-5px; cursor:nesw-resize; }
175.handle-ab[data-dir="e"]  { top:calc(50% - 5px); right:-5px; cursor:ew-resize; }
176.handle-ab[data-dir="se"] { bottom:-5px; right:-5px; cursor:nwse-resize; }
177.handle-ab[data-dir="s"]  { bottom:-5px; left:calc(50% - 5px); cursor:ns-resize; }
178.handle-ab[data-dir="sw"] { bottom:-5px; left:-5px; cursor:nesw-resize; }
179.handle-ab[data-dir="w"]  { top:calc(50% - 5px); left:-5px; cursor:ew-resize; }
180/* ── Label / lock badge on selected component ───────────────────────────── */
181.comp-badge-ab {
182  position:absolute; top:-22px; left:0;
183  background:#2563eb; color:#fff; border-radius:3px 3px 0 0;
184  font:600 10px/1 system-ui; padding:2px 6px; white-space:nowrap;
185  pointer-events:none; z-index:10; letter-spacing:.2px;
186}
187.locked-ab .comp-badge-ab { background:#d97706; }
188.comp-lock-icon-ab { position:absolute; top:3px; right:3px; font-size:11px; z-index:5; pointer-events:none; opacity:.7; }
189/* ── Alignment guide lines ──────────────────────────────────────────────── */
190.guide-ab {
191  position:absolute; background:#ef4444; z-index:99998; pointer-events:none;
192}
193.guide-h-ab { height:1px; left:0; right:0; box-shadow:0 0 4px rgba(239,68,68,.6); }
194.guide-v-ab { width:1px; top:0; bottom:0; box-shadow:0 0 4px rgba(239,68,68,.6); }
195/* ── Marquee selection rectangle ────────────────────────────────────────── */
196.marquee-ab {
197  position:absolute; border:1.5px dashed #2563eb;
198  background:rgba(37,99,235,.08); pointer-events:none; z-index:99999;
199  box-sizing:border-box; border-radius:2px;
200}
201/* ── Touch palette drag ghost ───────────────────────────────────────────── */
202.palette-drag-ghost-ab {
203  position:fixed; pointer-events:none; z-index:99999;
204  transform:translate(-50%,-50%);
205  background:#1e293b; color:#e2e8f0;
206  border:1.5px dashed #3b82f6; border-radius:6px;
207  padding:6px 12px; font:600 12px system-ui; white-space:nowrap;
208  box-shadow:0 4px 12px rgba(0,0,0,.4); opacity:.9;
209}
210/* ── Canvas empty state ─────────────────────────────────────────────────── */
211.canvas-empty-ab {
212  position:absolute; inset:0; display:flex; flex-direction:column;
213  align-items:center; justify-content:center; color:#94a3b8;
214  pointer-events:none; text-align:center; gap:10px;
215}
216.canvas-empty-icon-ab { font-size:40px; opacity:.35; }
217.canvas-empty-text-ab { font:13px/1.6 system-ui; max-width:280px; }
218/* ── Settings modal ─────────────────────────────────────────────────────── */
219.settings-overlay-ab { position:fixed; inset:0; z-index:49000; display:grid; place-items:center; background:rgba(2,6,23,.75); backdrop-filter:blur(4px); }
220.settings-panel-ab { width:min(540px,92vw); max-height:85vh; overflow-y:auto; background:#0f172a; border:1px solid rgba(148,163,184,.22); border-radius:12px; box-shadow:0 20px 60px rgba(0,0,0,.55); color:#f8fafc; }
221.settings-header-ab { display:flex; align-items:center; justify-content:space-between; padding:14px 18px; border-bottom:1px solid rgba(148,163,184,.15); position:sticky; top:0; background:#0f172a; z-index:1; }
222.settings-title-ab { font:700 14px/1 system-ui; letter-spacing:.4px; }
223.settings-close-ab { background:none; border:none;
223 color:rgba(148,163,184,.8); font-size:16px; cursor:pointer; padding:2px 6px; border-radius:4px; line-height:1; }
224.settings-close-ab:hover { color:#f1f5f9; background:rgba(255,255,255,.08); }
225.settings-body-ab { padding:16px 18px 20px; }
226.settings-row-ab { display:flex; flex-direction:column; gap:4px; margin-bottom:14px; }
227.settings-row-ab label { font:600 10px/1 system-ui; letter-spacing:.7px; text-transform:uppercase; color:rgba(100,116,139,.9); }
228.settings-row-ab input[type="text"], .settings-row-ab input[type="number"], .settings-row-ab select { padding:6px 10px; border:1px solid #334155; border-radius:6px; background:#1e293b; color:#e2e8f0; font:13px system-ui; outline:none; }
229.settings-row-ab input[type="text"]:focus, .settings-row-ab input[type="number"]:focus, .settings-row-ab select:focus { border-color:#60a5fa; }
230.settings-input-ab { width:100%; box-sizing:border-box; padding:7px 10px; border:1px solid #334155; border-radius:6px; background:#1e293b; color:#e2e8f0; font:13px system-ui; outline:none; }
231.settings-input-ab:focus { border-color:#7c3aed; }
232.settings-row-ab input[type="checkbox"] { width:16px; height:16px; cursor:pointer; }
233.settings-row-ab .settings-check-row-ab { display:flex; align-items:center; gap:8px; }
234.settings-row-ab .settings-check-row-ab span { font:13px system-ui; color:#cbd5e1; }
235.settings-section-title-ab { font:700 11px/1 system-ui; letter-spacing:.5px; text-transform:uppercase; color:#64748b; margin:18px 0 10px; padding-bottom:6px; border-bottom:1px solid #1e293b; }
236.settings-footer-ab { display:flex; justify-content:flex-end; gap:8px; margin-top:20px; padding-top:14px; border-top:1px solid #1e293b; }
237/* ── Live component styles ──────────────────────────────────────────────── */
238.lc-btn-ab { display:inline-flex; align-items:center; justify-content:center; padding:8px 18px; background:#0d6efd; color:#fff; border:none; border-radius:6px; font:600 14px/1.4 system-ui; cursor:default; }
239.lc-btn-sec-ab { background:#6c757d; }
240.lc-btn-suc-ab { background:#198754; }
241.lc-btn-dan-ab { background:#dc3545; }
242.lc-btn-out-ab { background:transparent; color:#0d6efd; border:2px solid #0d6efd; }
243.lc-label-ab { display:block; font:600 13px system-ui; color:#374151; margin-bottom:4px; }
244.lc-input-ab { display:block; width:100%; padding:8px 12px; box-sizing:border-box; border:1px solid #ced4da; border-radius:6px; font:14px system-ui; color:#374151; background:#fff; outline:none; }
245.lc-textarea-ab { display:block; width:100%; padding:8px 12px; box-sizing:border-box; border:1px solid #ced4da; border-radius:6px; font:14px system-ui; color:#374151; background:#fff; resize:vertical; min-height:80px; outline:none; }
246.lc-select-ab { display:block; width:100%; padding:8px 12px; box-sizing:border-box; border:1px solid #ced4da; border-radius:6px; font:14px system-ui; color:#374151; background:#fff; }
247.lc-checkbox-row-ab { display:flex; align-items:center; gap:8px; font:14px system-ui; color:#374151; }
248.lc-range-ab { width:100%; accent-color:#0d6efd; margin:6px 0; }
249.lc-toggle-row-ab { display:flex; align-items:center; gap:8px; font:14px system-ui; color:#374151; }
250.lc-toggle-track-ab { width:44px; height:24px; background:#dee2e6; border-radius:12px; position:relative; flex-shrink:0; }
251.lc-toggle-track-ab.on { background:#0d6efd; }
252.lc-toggle-thumb-ab { position:absolute; top:3px; left:3px; width:18px; height:18px; background:#fff; border-radius:50%; transition:left .15s; }
253.lc-toggle-track-ab.on .lc-toggle-thumb-ab { left:23px; }
254.lc-card-ab { background:#fff; border:1px solid #dee2e6; border-radius:8px; overflow:hidden; box-shadow:0 1px 4px rgba(0,0,0,.07); }
255.lc-card-header-ab { padding:10px 16px; background:#f8f9fa; border-bottom:1px solid #dee2e6; font:600 14px system-ui; color:#212529; }
256.lc-card-body-ab { padding:16px; font:14px/1.6 system-ui; color:#495057; }
257.lc-title-ab { font:700 26px/1.3 system-ui; color:#111827; margin:0 0 4px; }
258.lc-subtitle-ab { font:600 17px/1.4 system-ui; color:#6b7280; margin:0 0 2px; }
259.lc-text-ab { font:14px/1.6 system-ui; color:#374151; margin:0; }
260/* Tabs. The strip scrolls rather than wrapping: the Android TabRow keeps every tab
261   on one line, and a tab bar that reflows moves the operator's target between
262   renders. The pointer-events:auto below re-enables hit-testing on the tab buttons
263   alone - .live-inner-ab is inert, so the component stays selectable and draggable
264   by its body while only the tabs themselves take the click.
265   NOTE: this whole stylesheet is a JS template literal - no backticks in here. */
266.lc-tabs-ab { border-bottom:2px solid var(--ab-comp-border, #dee2e6); display:flex; gap:0;
267  overflow-x:auto; overflow-y:hidden; scrollbar-width:none; flex:0 0 auto;
268  /* Pinned to tabModel.TAB_STRIP_H - a docked child is positioned against the pane
269     top, so the strip cannot be allowed to size itself off its own font metrics. */
270  height:40px; box-sizing:border-box; align-items:stretch; }
271.lc-tabs-ab::-webkit-scrollbar { display:none; }
272.lc-tab-ab { padding:0 18px; font:14px system-ui; color:var(--ab-text-muted, #6b7280); cursor:pointer;
273  display:flex; align-items:center;
274  border-bottom:3px solid transparent; margin-bottom:-2px; white-space:nowrap; pointer-events:auto;
275  max-width:180px; overflow:hidden; text-overflow:ellipsis; }
276.lc-tab-ab:hover { color:var(--ab-comp-text, #334155); background:rgba(148,163,184,.12); }
277.lc-tab-ab.active { color:var(--ab-comp-accent, #0d6efd); border-bottom-color:var(--ab-comp-accent, #0d6efd); font-weight:600; }
278.lc-tab-pane-ab { position:relative; }
279/* A tabs container with docked children keeps its strip (like a card keeps its
280   title) — only the empty-pane hint goes away, and only for tabs that have one. */
281.comp-wrap-ab[data-comp-type="tabs"].has-docked-children-ab { outline:none; }
282.comp-wrap-ab[data-comp-type="tabs"] .lc-tab-hint-ab { display:none; }
283.comp-wrap-ab[data-comp-type="tabs"].tab-pane-empty-ab .lc-tab-hint-ab {
284  display:block; margin:8px 0 0; padding:0 4px; font:13px/1.5 system-ui;
285  color:var(--ab-text-muted, #6b7280); opacity:.75; }
286.lc-nav-ab { display:flex; align-items:center; background:#212529; padding:0 16px; height:52px; border-radius:6px; gap:4px; }
287.lc-nav-brand-ab { color:#fff; font:700 16px system-ui; margin-right:20px; flex-shrink:0; }
288.lc-nav-link-ab { color:#adb5bd; font:14px system-ui; padding:0 12px; }
289.lc-nav-link-ab.active { color:#fff; }
290.lc-stackedmenu-ab { background:#fff; border:1px solid #dee2e6; border-radius:8px; overflow:hidden; }
291.lc-smenu-item-ab { display:flex; align-items:center; gap:10px; padding:10px 14px; font:14px system-ui; color:#374151; border-bottom:1px solid #f3f4f6; cursor:default; }
292.lc-smenu-item-ab:last-child { border-bottom:none; }
293.lc-smenu-item-ab i { color:#6b7280; font-size:14px; }
294.lc-img-ph-ab { width:100%; background:#f1f5f9; display:flex; align-items:center; justify-content:center; border:1px dashed #cbd5e1; border-radius:6px; color:#94a3b8; font:13px system-ui; gap:6px; height:100%; }
295.lc-media-ph-ab { width:100%; background:#0f172a; display:flex; flex-direction:column; align-items:center; justify-content:center; border-radius:6px; color:#64748b; gap:6px; height:100%; }
296.lc-media-ph-ab i { font-size:32px; }
297.lc-media-ph-ab span { font:12px system-ui; }
298.lc-chart-ph-ab { width:100%; min-height:160px; background:#f8fafc; display:flex; flex-direction:column; align-items:center; justify-content:center; border:1px solid #e2e8f0; border-radius:8px; gap:6px; color:#94a3b8; height:100%; }
299.lc-chart-ph-ab i { font-size:36px; opacity:.45; }
300.lc-chart-ph-ab .lc-chart-label-ab { font:13px system-ui; color:#64748b; }
301.lc-table-ab { width:100%; border-collapse:collapse; font:13px system-ui; }
302.lc-table-ab th { padding:8px 12px; background:#f1f5f9; border:1px solid #e2e8f0; font:600 13px system-ui; color:#374151; text-align:left; }
303.lc-table-ab td { padding:7px 12px; border:1px solid #e2e8f0; color:#6b7280; }
304.lc-counter-ab { display:flex; flex-direction:column; align-items:flex-start; padding:16px 20px; background:#fff; border-radius:8px; border:1px solid #e2e8f0; box-shadow:0 1px 4px rgba(0,0,0,.06); }
305.lc-counter-val-ab { font:700 36px/1 system-ui; color:#0f172a; }
306.lc-counter-label-ab { font:12px system-ui; color:#64748b; margin-top:4px; }
307/* ── Badge (Controls) — its own component, not a metric-card internal ── */
308.lc-badge-ab { display:inline-block; font-size:10px; font-weight:700; padding:2px 8px; border-radius:999px; background:#eef2f6; color:#475569; letter-spacing:.02em; }
309/* ── Progress Bar (Controls) — its own component ── */
310.lc-progress-row-ab { display:flex; align-items:center; gap:8px; font:600 11px system-ui; color:#475569; width:100%; }
311.lc-progress-ab { height:9px; background:#e8edf3; border-radius:999px; overflow:hidden; flex:1; }
312.lc-progress-fill-ab { display:block; height:100%; border-radius:999px; background:linear-gradient(90deg,#34d399,#10b981); transition:width .3s; }
313.lc-progress-lbl-ab { white-space:nowrap; }
314/* Unbound data part — visibly flagged; NEVER shows plausible fake data (safety readout) */
315.lc-unbound-ab { outline:1px dashed #f59e0b; outline-offset:1px; color:#b45309 !important; background:#fffbeb !important; font-style:italic; }
316/* Alert rule tripped (live value crossed the band) — the safety RED state */
317.lc-alert-on-ab { box-shadow:0 0 0 2px #ef4444 inset; border-radius:8px; background:#fef2f2; }
318.lc-alert-on-ab .lc-counter-val-ab, .lc-alert-on-ab .lc-badge-ab, .lc-alert-on-ab .lc-card-header-ab { color:#dc2626 !important; }
319/* Reuse the existing "card" control as a card container: when parts dock into it, keep
320   the card shell on the wrap (the placeholder body fades via container-template-ab). */
321/* Card as a container: it keeps its OWN shell (.lc-card-ab) + title; just drop the
322   dashed dock outline and hide the empty-body hint once real children are inside. */
323.comp-wrap-ab[data-comp-type="card"].has-docked-children-ab { outline:none; }
324.comp-wrap-ab[data-comp-type="card"].has-docked-children-ab .lc-card-body-ab { display:none; }
325.lc-status-ab { display:inline-flex; align-items:center; gap:6px; padding:5px 12px; background:#f0fdf4; border-radius:20px; border:1px solid #bbf7d0; font:13px/1 system-ui; color:#15803d; }
326.lc-status-dot-ab { width:8px; height:8px; background:#22c55e; border-radius:50%; flex-shrink:0; }
327.lc-gauge-ab { display:flex; flex-direction:column; align-items:center; padding:14px 20px; background:#fff; border-radius:8px; border:1px solid #e2e8f0; box-shadow:0 1px 4px rgba(0,0,0,.06); }
328.lc-gauge-arc-ab { width:100px; height:55px; border:8px solid #e2e8f0; border-bottom:none; border-radius:60px 60px 0 0; position:relative; }
329.lc-gauge-arc-fill-ab { position:absolute; inset:-8px; bottom:0; border:8px solid #0d6efd; border-bottom:none; border-radius:60px 60px 0 0; clip-path:polygon(0 100%,55% 0%,100% 100%); }
330.lc-gauge-val-ab { font:700 20px/1 system-ui; color:#0f172a; margin-top:6px; }
331.lc-gauge-label-ab { font:11px system-ui; color:#64748b; margin-top:2px; }
332.lc-stream-card-ab { background:#fff; border:1px solid #e2e8f0; border-radius:8px; padding:12px 16px; display:flex; align-items:center; gap:12px; box-shadow:0 1px 4px rgba(0,0,0,.06); }
333.lc-stream-icon-ab { font-size:24px; color:#0d6efd; flex-shrink:0; }
334.lc-stream-info-ab { flex:1; }
335.lc-stream-title-ab { font:600 13px system-ui; color:#374151; }
336.lc-stream-val-ab { font:700 20px/1.2 system-ui; color:#0f172a; }
337.lc-window-ab { background:#fff; border:1px solid #dee2e6; border-radius:8px; overflow:hidden; box-shadow:0 4px 16px rgba(0,0,0,.08); height:100%; }
338.lc-window-bar-ab { display:flex; align-items:center; gap:6px; padding:8px 12px; background:#f8f9fa; border-bottom:1px solid #dee2e6; user-select:none; }
339.lc-window-dot-ab { width:10px; height:10px; border-radius:50%; }
340.lc-window-body-ab { padding:16px; min-height:60px; font:14px/1.6 system-ui; color:#374151; }
341.lc-form-ab { background:#fff; border:1px solid #dee2e6; border-radius:8px; overflow:hidden; height:100%; }
342.lc-form-header-ab { padding:10px 16px; background:#f8f9fa; border-bottom:1px solid #dee2e6; font:600 14px system-ui; color:#374151; }
343.lc-form-body-ab { padding:16px; display:flex; flex-direction:column; gap:12px; overflow:auto; }
344.lc-collapsible-ab { border:1px solid #dee2e6; border-radius:6px; overflow:hidden; }
345.lc-coll-head-ab { display:flex; align-items:center; justify-content:space-between; padding:10px 14px; background:#f8f9fa; font:600 14px system-ui; color:#374151; cursor:default; }
346.lc-coll-body-ab { padding:12px 14px; font:14px system-ui; color:#6b7280; }
347.lc-comment-ab { background:#fefce8; border:1px solid #fde68a; border-radius:8px; padding:12px 16px; font:14px/1.5 system-ui; color:#78350f; }
348.lc-radial-ab { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:12px; background:#fff; border-radius:50%; width:120px; height:120px; border:1px solid #dee2e6; justify-content:center; color:#6b7280; font:12px system-ui; text-align:center; }
349.lc-menu-item-ab { display:flex; align-items:center; gap:8px; padding:8px 12px; background:#f8f9fa; border:1px solid #e2e8f0; border-radius:6px; font:13px system-ui; color:#374151; }
350.lc-menu-item-ab i { color:#6b7280; }
351.lc-carousel-ab { width:100%; min-height:160px; background:#e9ecef; display:flex; align-items:center; justify-content:space-between; border-radius:8px; padding:16px; box-sizing:border-box; gap:12px; color:#6b7280; height:100%; }
352.lc-carousel-arrow-ab { font-size:24px; color:#adb5bd; flex-shrink:0; }
353.lc-carousel-content-ab { flex:1; display:flex; flex-direction:column; align-items:center; gap:6px; font:13px system-ui; }
354.lc-carousel-content-ab i { font-size:32px; opacity:.5; }
355.lc-group-outline-ab { border:2px dashed #a5b4fc; border-radius:6px; min-height:60px; background:rgba(99,102,241,.04); padding:4px; }
356/* ── Live data indicator (edit mode) ─────────────────────────────────── */
357.live-data-active-ab {
358  position:relative;
359}
360.live-data-active-ab::after {
361  content:'';
362  position:absolute;
363  top:2px; right:2px;
364  width:6px; height:6px;
365  background:#22c55e;
366  border-radius:50%;
367  box-shadow:0 0 4px rgba(34,197,94,.6);
368  animation:live-pulse-ab 2s ease-in-out infinite;
369}
370@keyframes live-pulse-ab {
371  0%, 100% { opacity:1; transform:scale(1); }
372  50% { opacity:.6; transform:scale(0.85); }
373}
374/* ── Mapping diagnostics panel ─────────────────────────────────────────── */
375.mapping-diagnostics-ab {
376  border-bottom:1px solid #cbd5e1; background:#f8fafc; padding:6px 8px;
377  max-height:120px; overflow:auto;
378}
379.mapping-diag-ok-ab { font:600 11px/1.4 system-ui; color:#166534; }
380.mapping-diag-list-ab { display:flex; flex-direction:column; gap:4px; }
381.mapping-diag-row-ab {
382  font:600 11px/1.4 system-ui; padding:4px 6px; border-radius:4px; border:1px solid transparent;
383}
384.mapping-diag-row-ab.is-warning { color:#92400e; background:#fffbeb; border-color:#fcd34d; }
385.mapping-diag-row-ab.is-error { color:#991b1b; background:#fef2f2; border-color:#fca5a5; }
386
387/* ── Widget mapping status badge ───────────────────────────────────────── */
388.mapping-health-badge-ab {
389  position:absolute; top:4px; right:4px; z-index:9;
390  border-radius:999px; padding:2px 6px; font:700 9px/1 system-ui;
391  border:1px solid #cbd5e1; background:#fff; color:#334155;
392}
393.mapping-health-badge-ab[data-status="connected"] { background:#ecfdf5; border-color:#86efac; color:#166534; }
394.mapping-health-badge-ab[data-status="connecting"] { background:#eff6ff; border-color:#93c5fd; color:#1d4ed8; }
395.mapping-health-badge-ab[data-status="error"] { background:#fef2f2; border-color:#fca5a5; color:#b91c1c; }
396
397/* ── Data binding strip (edit mode only) ──────────────────────────────── */
398.bind-strip-ab {
399  position:absolute; bottom:0; left:0; right:0; z-index:8;
400  background:rgba(37,99,235,.82); color:#fff; font:600 9px/1 system-ui;
401  display:flex; align-items:center; gap:4px; padding:2px 5px;
402  border-radius:0 0 3px 3px; letter-spacing:.2px; pointer-events:auto;
403  backdrop-filter:blur(2px); min-height:16px; overflow:hidden;
404}
405.bind-strip-ab i { font-size:9px; opacity:.85; flex-shrink:0; }
406.bind-strip-ab .bind-key-ab { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
407.bind-strip-ab .bind-btn-ab {
408  flex-shrink:0; background:rgba(255,255,255,.2); border:none; border-radius:3px;
409  color:#fff; font:600 8px system-ui; cursor:pointer; padding:1px 5px; line-height:1.6;
410  transition:background .1s;
411}
412.bind-strip-ab .bind-btn-ab:hover { background:rgba(255,255,255,.38); }
413/* ── Data binding modal ───────────────────────────────────────────────── */
414.bind-modal-overlay-ab {
415  position:fixed; inset:0; z-index:52000; display:grid; place-items:center;
416  background:rgba(2,6,23,.72); backdrop-filter:blur(4px);
417}
418.bind-modal-ab {
419  width:min(420px,90vw); background:#0f172a; border:1px solid rgba(148,163,184,.22);
420  border-radius:10px; box-shadow:0 20px 60px rgba(0,0,0,.55); color:#f8fafc;
421  font:14px system-ui; overflow:hidden; max-height:min(92vh,860px); display:flex; flex-direction:column;
422}
423.bind-modal-header-ab {
424  display:flex; align-items:center; justify-content:space-between;
425  padding:12px 16px; background:#1e293b; border-bottom:1px solid rgba(148,163,184,.15);
426}
427.bind-modal-title-ab { font:700 13px/1 system-ui; letter-spacing:.3px; }
428.bind-modal-close-ab {
429  background:none; border:none; color:rgba(148,163,184,.8); font-size:16px;
430  cursor:pointer; padding:2px 6px; border-radius:4px; line-height:1;
431}
432.bind-modal-close-ab:hover { color:#f1f5f9; background:rgba(255,255,255,.08); }
433.bind-modal-body-ab {
434  padding:16px; display:flex; flex-direction:column; gap:10px;
435  overflow-y:auto; overscroll-behavior:contain;
436}
437.bind-modal-body-ab label { font:600 10px/1 system-ui; letter-spacing:.6px; text-transform:uppercase; color:rgba(100,116,139,.9); }
438.bind-modal-body-ab input {
439  padding:7px 10px; border:1px solid #334155; border-radius:6px;
440  background:#1e293b; color:#e2e8f0; font:13px system-ui; outline:none; width:100%; box-sizing:border-box;
441}
442.bind-modal-body-ab input:focus { border-color:#60a5fa; }
443.bind-modal-body-ab input[type="checkbox"] {
444  width:auto; justify-self:flex-start; accent-color:#3b82f6;
445}
446.bind-map-select-ab {
447  width:100%; padding:7px 10px; border:1px solid #334155; border-radius:6px;
448  background:#1e293b; color:#e2e8f0; font:13px system-ui; outline:none; box-sizing:border-box;
449  transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
450}
451.bind-map-select-ab:focus { border-color:#60a5fa; }
452.bind-map-drop-target-ab { border-color:#38bdf8 !important; box-shadow:0 0 0 2px rgba(56,189,248,.28); background:#1b2a44; }
453.bind-modal-hint-ab { font:11px/1.5 system-ui; color:#64748b; }
454.bind-modal-section-ab {
455  border:1px solid rgba(148,163,184,.22);
456  border-radius:8px;
457  background:rgba(15,23,42,.6);
458}
459.bind-modal-section-title-ab {
460  list-style:none;
461  cursor:pointer;
462  padding:10px 12px;
463  font:700 11px/1 system-ui;
464  letter-spacing:.5px;
465  text-transform:uppercase;
466  color:#cbd5e1;
467  display:flex;
468  align-items:center;
469  gap:8px;
470}
471.bind-modal-section-title-ab::-webkit-details-marker { display:none; }
472.bind-modal-section-title-ab::before {
473  content:"▸";
474  color:#60a5fa;
475  font-size:11px;
476  transform:translateY(-.5px);
477}
478.bind-modal-section-ab[open] .bind-modal-section-title-ab::before { content:"▾"; }
479.bind-modal-section-content-ab {
480  padding:0 12px 12px;
481  display:grid;
482  grid-template-columns:repeat(2, minmax(0, 1fr));
483  gap:10px 12px;
484}
485.bind-modal-field-row-ab {
486  display:flex;
487  flex-direction:column;
488  gap:6px;
489  min-width:0;
490}
491.bind-modal-field-row-full-ab { grid-column:1 / -1; }
492.bind-modal-field-row-checkbox-ab input[type="checkbox"] {
493  margin-top:2px;
494  justify-self:flex-start;
495}
496.bind-modal-footer-ab {
497  display:flex; justify-content:flex-end; gap:8px;
498  padding:12px 16px; border-top:1px solid #1e293b;
499}
500@media (max-width:700px) {
501  .bind-modal-section-content-ab { grid-template-columns:1fr; }
502}
503/* ── FeBe field picker ────────────────────────────────────────────────── */
504.bind-field-picker-ab {
505  max-height:180px; overflow-y:auto; border:1px solid #334155; border-radius:6px;
506  background:#1e293b; margin-top:4px;
507}
508.bind-field-picker-ab::-webkit-scrollbar { width:6px; }
509.bind-field-picker-ab::-webkit-scrollbar-track { background:#1e293b; border-radius:3px; }
510.bind-field-picker-ab::-webkit-scrollbar-thumb { background:#475569; border-radius:3px; }
511.bind-stream-header-ab {
512  font:600 10px/1 system-ui; letter-spacing:.5px; text-transform:uppercase;
513  color:#94a3b8; padding:8px 10px 4px; border-bottom:1px solid #334155;
514  background:#0f172a; position:sticky; top:0; z-index:1;
515}
516.bind-field-item-ab {
517  padding:6px 12px; cursor:pointer; font:12px system-ui; color:#cbd5e1;
518  border-bottom:1px solid rgba(51,65,85,.5); display:flex; align-items:center; gap:6px;
519  transition:background .1s;
520}
521.bind-field-item-ab:last-child { border-bottom:none; }
522.bind-field-item-ab:hover { background:rgba(96,165,250,.15); color:#f8fafc; }
523.bind-field-item-ab i { font-size:10px; color:#60a5fa; }
524.bind-no-streams-ab {
525  padding:16px; text-align:center; color:#64748b; font:12px system-ui;
526}
527.bind-no-streams-ab i { font-size:20px; display:block; margin-bottom:6px; opacity:.5; }
528/* ── Pivot chart placeholder ──────────────────────────────────────────── */
529.lc-pivot-ph-ab {
530  width:100%; height:100%; background:#f8fafc; border:1px solid #e2e8f0;
531  border-radius:8px; display:flex; flex-direction:column;
532  align-items:stretch; overflow:hidden; font:12px system-ui; color:#374151;
533}
534.lc-pivot-header-ab {
535  background:#1e293b; color:#94a3b8; font:600 10px/1 system-ui;
536  padding:6px 10px; display:flex; align-items:center; gap:6px; flex-shrink:0;
537}
538.lc-pivot-header-ab i { font-size:12px; color:#60a5fa; }
539.lc-pivot-lib-label-ab { margin-left:auto; font:10px system-ui; opacity:.6; }
540.lc-pivot-body-ab { flex:1; display:grid; overflow:hidden; }
541.lc-pivot-table-ab { width:100%; border-collapse:collapse; font:11px system-ui; }
542.lc-pivot-table-ab th {
543  padding:5px 8px; background:#e2e8f0; border:1px solid #cbd5e1;
544  font:600 10px system-ui; color:#475569; text-align:center; white-space:nowrap;
545}
546.lc-pivot-table-ab td {
547  padding:4px 8px; border:1px solid #e2e8f0; color:#374151;
548  text-align:right; font:11px system-ui;
549}
550.lc-pivot-table-ab tr:nth-child(even) td { background:#f1f5f9; }
551.lc-pivot-table-ab td.pivot-row-hdr-ab {
552  text-align:left; font-weight:600; color:#475569; background:#f8fafc;
553}
554/* ── Map placeholder ──────────────────────────────────────────────────── */
555.lc-map-ph-ab {
556  width:100%; height:100%; position:relative; border-radius:8px;
557  overflow:hidden; background:#c8d9e6; border:1px solid #b0c4d8;
558  display:flex; flex-direction:column;
559}
560.lc-map-tiles-ab {
561  flex:1; position:relative; overflow:hidden;
562  background:repeating-linear-gradient(
563    0deg, rgba(255,255,255,.12) 0px, rgba(255,255,255,.12) 1px, transparent 1px, transparent 40px
564  ),
565  repeating-linear-gradient(
566    90deg, rgba(255,255,255,.12) 0px, rgba(255,255,255,.12) 1px, transparent 1px, transparent 40px
567  ),
568  linear-gradient(135deg, #a8c5da 0%, #c8d9e6 40%, #b5cfe0 70%, #9fbcd4 100%);
569}
570.lc-map-road-h-ab {
571  position:absolute; top:40%; left:0; right:0; height:6px;
572  background:rgba(255,255,255,.7); border-radius:1px;
573}
574.lc-map-road-v-ab {
575  position:absolute; top:0; bottom:0; left:35%; width:6px;
576  background:rgba(255,255,255,.7); border-radius:1px;
577}
578.lc-map-block-ab {
579  position:absolute; background:rgba(150,170,140,.55);
580  border:1px solid rgba(120,145,110,.4); border-radius:2px;
581}
582.lc-map-pin-ab {
583  position:absolute; width:18px; height:22px;
584  display:flex; flex-direction:column; align-items:center;
585}
586.lc-map-pin-head-ab {
587  width:14px; height:14px; border-radius:50% 50% 50% 0;
588  transform:rotate(-45deg); border:2px solid #fff;
589  box-shadow:0 1px 4px rgba(0,0,0,.3);
590}
591.lc-map-pin-tail-ab { width:2px; height:6px; margin-top:1px; border-radius:0 0 2px 2px; }
592.lc-map-cluster-ab {
593  position:absolute; width:24px; height:24px; border-radius:50%;
594  background:rgba(37,99,235,.8); border:2px solid #fff; color:#fff;
595  font:700 9px system-ui; display:flex; align-items:center; justify-content:center;
596  box-shadow:0 1px 6px rgba(0,0,0,.3);
597}
598.lc-map-ctrl-bar-ab {
599  position:absolute; top:6px; right:6px; display:flex; flex-direction:column; gap:3px;
600}
601.lc-map-ctrl-btn-ab {
602  width:22px; height:22px; background:rgba(255,255,255,.9);
603  border:1px solid rgba(0,0,0,.2); border-radius:3px; display:flex;
604  align-items:center; justify-content:center; font-size:10px; color:#374151;
605  cursor:default; box-shadow:0 1px 3px rgba(0,0,0,.15);
606}
607.lc-map-zoom-bar-ab {
608  position:absolute; bottom:6px; right:6px; display:flex; flex-direction:column;
609  background:rgba(255,255,255,.9); border:1px solid rgba(0,0,0,.2);
610  border-radius:3px; overflow:hidden; box-shadow:0 1px 3px rgba(0,0,0,.15);
611}
612.lc-map-zoom-btn-ab {
613  width:22px; height:22px; display:flex; align-items:center;
614  justify-content:center; font:700 14px system-ui; color:#374151;
615  cursor:default; line-height:1;
616}
617.lc-map-zoom-btn-ab + .lc-map-zoom-btn-ab { border-top:1px solid rgba(0,0,0,.15); }
618.lc-map-draw-bar-ab {
619  position:absolute; top:6px; left:6px; display:flex; flex-direction:column; gap:3px;
620}
621.lc-map-attr-ab {
622  flex-shrink:0; padding:2px 6px; background:rgba(255,255,255,.75);
623  font:9px system-ui; color:#374151; text-align:right;
624}
625.lc-map-badge-ab {
626  position:absolute; bottom:24px; left:6px; background:rgba(0,0,0,.6);
627  color:#fff; font:600 9px system-ui; padding:2px 6px; border-radius:3px;
628  letter-spacing:.3px;
629}
630/* ── Toast notifications ──────────────────────────────────────────────── */
631.toast-host-ab {
632  position:fixed; bottom:20px; right:20px; z-index:99999;
633  display:flex; flex-direction:column; gap:8px; pointer-events:none;
634  max-width:380px;
635}
636.toast-ab {
637  display:flex; align-items:flex-start; gap:8px;
638  padding:10px 12px; border-radius:7px; border:1px solid transparent;
639  font:13px/1.45 system-ui; box-shadow:0 4px 18px rgba(0,0,0,.35);
640  pointer-events:all; opacity:0; transform:translateY(8px);
641  transition:opacity .22s ease, transform .22s ease;
642  min-width:220px;
643}
644.toast-ab.toast-visible-ab { opacity:1; transform:translateY(0); }
645.toast-error-ab   { background:#1c0b0b; border-color:#dc2626; color:#fca5a5; }
646.toast-warning-ab { background:#1c1408; border-color:#d97706; color:#fcd34d; }
647.toast-success-ab { background:#071c0f; border-color:#16a34a; color:#86efac; }
648.toast-info-ab    { background:#0c1730; border-color:#2563eb; color:#93c5fd; }
649.toast-icon-ab { font-size:14px; flex-shrink:0; margin-top:1px; }
650.toast-msg-ab  { flex:1; word-break:break-word; }
651.toast-close-ab {
652  background:none; border:none; cursor:pointer; font-size:12px;
653  opacity:.6; padding:0 2px; margin-top:1px; color:inherit; flex-shrink:0;
654  line-height:1;
655}
656.toast-close-ab:hover { opacity:1; }
657/* ── Diagnostics panel (enhanced) ─────────────────────────────────────── */
658.mapping-diagnostics-ab {
659  font:11px system-ui; background:#0f172a; border-bottom:1px solid #1e293b;
660  padding:0; flex-shrink:0; overflow:hidden;
661  transition:max-height .2s ease;
662  max-height:200px;
663}
664.mapping-diagnostics-ab.diag-collapsed-ab { max-height:28px; }
665.diag-header-ab {
666  display:flex; align-items:center; gap:6px; padding:4px 10px;
667  background:#1e293b; border-bottom:1px solid #334155;
668  cursor:pointer; user-select:none;
669}
670.diag-header-title-ab {
671  font:700 10px system-ui; color:#94a3b8; letter-spacing:.05em;
672  text-transform:uppercase; flex:1;
673}
674.diag-header-status-ab {
675  font:10px system-ui; color:#64748b;
676}
677.diag-toggle-ab {
678  background:none; border:none;
678 color:#64748b; cursor:pointer;
679  font-size:10px; padding:0 2px;
680}
681.diag-body-ab {
682  overflow-y:auto; max-height:172px; padding:4px 0;
683}
684.diag-body-ab::-webkit-scrollbar { width:4px; }
685.diag-body-ab::-webkit-scrollbar-thumb { background:#334155; border-radius:2px; }
686.diag-section-ab {
687  padding:3px 10px;
688}
689.diag-section-title-ab {
690  font:700 9px system-ui; color:#475569; text-transform:uppercase;
691  letter-spacing:.05em; padding:2px 0;
692}
693.mapping-diag-ok-ab {
694  padding:6px 10px; color:#22c55e; font:11px system-ui;
695}
696.mapping-diag-list-ab { }
697.mapping-diag-row-ab {
698  padding:3px 10px 3px 12px; font:11px/1.4 system-ui;
699  border-left:2px solid transparent; cursor:default;
700  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
701}
702.mapping-diag-row-ab.is-error   { color:#f87171; border-left-color:#dc2626; }
703.mapping-diag-row-ab.is-warning { color:#fcd34d; border-left-color:#d97706; }
704.diag-server-row-ab {
705  display:flex; align-items:center; gap:6px;
706  padding:2px 10px; font:11px/1.4 system-ui; color:#94a3b8;
707}
708.diag-server-dot-ab {
709  width:7px; height:7px; border-radius:50%; flex-shrink:0;
710  background:#64748b;
711}
712.diag-server-dot-ok-ab      { background:#22c55e; }
713.diag-server-dot-error-ab   { background:#ef4444; }
714.diag-server-dot-pending-ab { background:#f59e0b; }
715/* ── Undo/Redo toolbar buttons ─────────────────────────────────────────── */
716.btn-undo-ab, .btn-redo-ab {
717  padding:4px 8px; font-size:13px;
718}
719.btn-undo-ab:disabled, .btn-redo-ab:disabled {
720  opacity:.25; cursor:default; pointer-events:none;
721}
722/* ── Chart customization modal ──────────────────────────────────────────── */
723.chart-custom-overlay-ab {
724  position:fixed; inset:0; z-index:52000; display:grid; place-items:center;
725  background:rgba(2,6,23,.72); backdrop-filter:blur(4px);
726}
727.chart-custom-modal-ab {
728  width:min(480px,94vw); background:#0f172a; border:1px solid rgba(148,163,184,.22);
729  border-radius:10px; box-shadow:0 20px 60px rgba(0,0,0,.55); color:#f8fafc;
730  font:14px system-ui; overflow:hidden; max-height:min(92vh,780px); display:flex; flex-direction:column;
731}
732.chart-custom-header-ab {
733  display:flex; align-items:center; justify-content:space-between;
734  padding:12px 16px; background:#1e293b; border-bottom:1px solid rgba(148,163,184,.15);
735}
736.chart-custom-title-ab { font:700 13px/1 system-ui; letter-spacing:.3px; }
737.chart-custom-close-ab {
738  background:none; border:none; color:rgba(148,163,184,.8); font-size:16px;
739  cursor:pointer; padding:2px 6px; border-radius:4px; line-height:1;
740}
741.chart-custom-close-ab:hover { color:#f1f5f9; background:rgba(255,255,255,.08); }
742.chart-custom-body-ab {
743  padding:16px; display:flex; flex-direction:column; gap:12px;
744  overflow-y:auto; overscroll-behavior:contain;
745}
746.chart-custom-field-ab { display:flex; flex-direction:column; gap:5px; }
747.chart-custom-field-ab label {
748  font:600 10px/1 system-ui; letter-spacing:.6px; text-transform:uppercase;
749  color:rgba(100,116,139,.9);
750}
751.chart-custom-field-ab input[type="text"],
752.chart-custom-field-ab input[type="number"],
753.chart-custom-field-ab textarea,
754.chart-custom-field-ab select {
755  padding:7px 10px; border:1px solid #334155; border-radius:6px;
756  background:#1e293b; color:#e2e8f0; font:13px system-ui; outline:none;
757  width:100%; box-sizing:border-box;
758}
759.chart-custom-field-ab input[type="text"]:focus,
760.chart-custom-field-ab input[type="number"]:focus,
761.chart-custom-field-ab textarea:focus,
762.chart-custom-field-ab select:focus { border-color:#60a5fa; }
763.chart-custom-field-ab textarea {
764  min-height:132px; resize:vertical; font:12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
765}
766.chart-custom-row2-ab { display:grid; grid-template-columns:1fr 1fr; gap:12px; }
767.chart-custom-series-group-ab { display:flex; flex-direction:column; gap:10px; }
768.chart-custom-series-card-ab {
769  display:flex; flex-direction:column; gap:8px; padding:10px;
770  border:1px solid rgba(148,163,184,.15); border-radius:8px; background:rgba(15,23,42,.45);
771}
772.chart-custom-series-heading-ab {
773  font:700 11px/1 system-ui; letter-spacing:.4px; text-transform:uppercase; color:#cbd5e1;
774}
775.chart-custom-series-row-ab { display:grid; grid-template-columns:repeat(auto-fit,minmax(180px,1fr)); gap:12px; }
776.chart-custom-checkbox-row-ab { display:flex; align-items:center; gap:8px; }
777.chart-custom-checkbox-row-ab input[type="checkbox"] {
778  accent-color:#3b82f6; width:14px; height:14px; flex-shrink:0;
779}
780.chart-custom-checkbox-row-ab label {
781  font:12px system-ui; color:#cbd5e1; text-transform:none; letter-spacing:0;
782}
783.chart-custom-hint-ab { font:11px/1.5 system-ui; color:#64748b; }
784.chart-custom-footer-ab {
785  display:flex; justify-content:flex-end; gap:8px;
786  padding:12px 16px; border-top:1px solid #1e293b;
787}
788/* chart style button inside the per-component bind strip */
789.bind-strip-ab .chart-style-btn-ab {
790  flex-shrink:0; background:rgba(16,185,129,.25); border:none; border-radius:3px;
791  color:#6ee7b7; font:600 8px system-ui; cursor:pointer; padding:1px 5px; line-height:1.6;
792  transition:background .1s;
793}
794.bind-strip-ab .chart-style-btn-ab:hover { background:rgba(16,185,129,.45); }
795@media (max-width:700px) {
796  .chart-custom-row2-ab { grid-template-columns:1fr; }
797  .chart-custom-series-row-ab { grid-template-columns:1fr; }
798}
799
800/* ── RoadDefender tablet shelf ─────────────────────────────────────────────
801   Every value is traced to zim-mobile/RoadDefender (branch \`live\`):
802   ui/theme/RoadDefenderTheme.kt for the tokens, app/src/tablet/.../zim for the
803   composables. Each colour reads a --rd-* variable with the RoadDefenderTheme.kt
804   value as its FALLBACK, so the shelf renders under the four generic themes too —
805   picking the RoadDefender theme is what makes it match the device rather than
806   merely resemble it.
807   Density note: the tablet is a dark, high-contrast, tight-radius surface. The
808   generic AppBuilder widgets are light with 8px radii and roomy padding, which is
809   why an AppBuilder mock reads as "close but not it". These use RdShapes (8/12/18)
810   and RdSpacing (4/8/12/16/24) exactly.                                        */
811
812.rd-section-ab {
813  width:100%; height:100%; box-sizing:border-box; position:relative;
814  background:var(--ab-comp-bg,#000); color:var(--ab-comp-text,#fff);
815  border:1px solid var(--ab-comp-border,#2a2a2a);
816  border-radius:var(--rd-radius,12px); padding:var(--rd-md,12px) var(--rd-lg,16px);
817  font:14px/1.35 system-ui;
818}
819.rd-section-title-ab {
820  font:600 12px system-ui; letter-spacing:.06em; text-transform:uppercase;
821  color:var(--ab-text-muted,#9e9e9e); margin-bottom:var(--rd-sm,8px);
822}
823.rd-section-body-ab { min-height:24px; }
824.rd-section-rule-ab {
825  position:absolute; left:var(--rd-lg,16px); right:var(--rd-lg,16px);
826  bottom:var(--rd-md,12px); height:1px; background:rgba(255,255,255,.12);
827}
828
829/* Stat / CompactStat / LiveStat — label, value, unit, AGE. */
830.rd-stat-ab {
831  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
832  justify-content:center; gap:2px; padding:var(--rd-sm,8px) var(--rd-md,12px);
833  background:var(--ab-comp-bg,#000); border:1px solid var(--ab-comp-border,#2a2a2a);
834  border-radius:var(--rd-radius-sm,8px); font-family:system-ui;
835}
836.rd-stat-label-ab {
837  font:600 10px system-ui; letter-spacing:.08em; text-transform:uppercase;
838  color:var(--ab-text-muted,#9e9e9e);
839}
840.rd-stat-row-ab { display:flex; align-items:baseline; gap:4px; }
841.rd-stat-val-ab { font:600 26px/1 system-ui; color:var(--ab-comp-text,#fff); }
842.rd-stat-unit-ab { font:500 12px system-ui; color:var(--ab-text-muted,#9e9e9e); }
843.rd-stat-age-ab { font:400 10px system-ui; color:var(--rd-neutral,#9e9e9e); opacity:.8; }
844.rd-stat-ab[data-sev="danger"]  .rd-stat-val-ab { color:var(--rd-danger,#e53935); }
845.rd-stat-ab[data-sev="caution"] .rd-stat-val-ab { color:var(--rd-caution,#ffa000); }
846.rd-stat-ab[data-sev="hazard"]  .rd-stat-val-ab { color:var(--rd-hazard,#ff9800); }
847.rd-stat-ab[data-sev="safe"]    .rd-stat-val-ab { color:var(--rd-safe,#4caf50); }
848.rd-stat-ab[data-sev="info"]    .rd-stat-val-ab { color:var(--rd-info,#2196f3); }
849
850/* SpeedHeadline — the one number you read from across the cab. */
851.rd-headline-ab {
852  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
853  align-items:center; justify-content:center; gap:var(--rd-xs,4px);
854  background:var(--ab-comp-bg,#000); border-radius:var(--rd-radius,12px);
855  font-family:system-ui;
856}
857.rd-headline-val-ab {
858  font:700 56px/1 system-ui; color:var(--rd-scheme2,#ffa000);
859  display:flex; align-items:baseline; gap:6px;
860}
861.rd-headline-unit-ab { font:600 18px system-ui; color:var(--ab-text-muted,#9e9e9e); }
862.rd-headline-label-ab {
863  font:600 11px system-ui; letter-spacing:.12em; text-transform:uppercase;
864  color:var(--ab-text-muted,#9e9e9e);
865}
866
867/* FilterChip row — Material3 chips: pill, 1px outline, filled when selected. */
868.rd-chiprow-ab {
869  width:100%; height:100%; box-sizing:border-box; display:flex; align-items:center;
870  flex-wrap:wrap; gap:var(--rd-sm,8px); font-family:system-ui;
871}
872.rd-chip-ab {
873  display:inline-flex; align-items:center; height:32px; padding:0 var(--rd-md,12px);
874  border:1px solid var(--ab-comp-border,#2a2a2a); border-radius:var(--rd-radius-sm,8px);
875  font:500 13px system-ui; color:var(--ab-comp-text,#fff); white-space:nowrap;
876}
877.rd-chip-on-ab {
878  background:var(--rd-scheme2,#ffa000); border-color:var(--rd-scheme2,#ffa000);
879  color:#000; font-weight:600;
880}
881
882/* InvalidationBanner / OfflineBanner / Warn. */
883.rd-banner-ab {
884  width:100%; height:100%; box-sizing:border-box; display:flex; align-items:center;
885  gap:var(--rd-sm,8px); padding:0 var(--rd-md,12px); border-radius:var(--rd-radius,12px);
886  font:600 13px system-ui;
887}
888.rd-banner-icon-ab { font-size:14px; flex-shrink:0; opacity:.9; }
889.rd-banner-text-ab { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
890.rd-banner-ab[data-sev="caution"] { background:rgba(255,160,0,.16);  color:var(--rd-caution,#ffa000); }
891.rd-banner-ab[data-sev="danger"]  { background:rgba(229,57,53,.16);  color:var(--rd-danger,#e53935); }
892.rd-banner-ab[data-sev="hazard"]  { background:rgba(255,152,0,.16);
892  color:var(--rd-hazard,#ff9800); }
893.rd-banner-ab[data-sev="safe"]    { background:rgba(76,175,80,.16);  color:var(--rd-safe,#4caf50); }
894.rd-banner-ab[data-sev="info"]    { background:rgba(33,150,243,.16); color:var(--rd-info,#2196f3); }
895.rd-banner-ab[data-sev="neutral"] { background:rgba(158,158,158,.16); color:var(--rd-neutral,#9e9e9e); }
896
897/* SeverityCard — the state itself is the widget. */
898.rd-sevcard-ab {
899  width:100%; height:100%; box-sizing:border-box; display:flex; align-items:center;
900  justify-content:center; border-radius:var(--rd-radius,12px);
901}
902.rd-sevcard-label-ab { font:700 22px system-ui; letter-spacing:.06em; }
903.rd-sevcard-ab[data-sev="caution"] { background:var(--rd-caution,#ffa000); color:#000; }
904.rd-sevcard-ab[data-sev="danger"]  { background:var(--rd-danger,#e53935);  color:#fff; }
905.rd-sevcard-ab[data-sev="hazard"]  { background:var(--rd-hazard,#ff9800);  color:#000; }
906.rd-sevcard-ab[data-sev="safe"]    { background:var(--rd-safe,#4caf50);    color:#000; }
907.rd-sevcard-ab[data-sev="info"]    { background:var(--rd-info,#2196f3);    color:#000; }
908.rd-sevcard-ab[data-sev="neutral"] { background:var(--rd-neutral,#9e9e9e); color:#000; }
909
910/* ViLadderCard — rungs top-down, most severe first. */
911.rd-ladder-ab {
912  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
913  gap:var(--rd-xs,4px); padding:var(--rd-md,12px);
914  background:var(--ab-comp-bg,#000); border:1px solid var(--ab-comp-border,#2a2a2a);
915  border-radius:var(--rd-radius,12px); font-family:system-ui; overflow:hidden;
916}
917.rd-ladder-title-ab {
918  font:600 11px system-ui; letter-spacing:.1em; text-transform:uppercase;
919  color:var(--ab-text-muted,#9e9e9e); margin-bottom:var(--rd-xs,4px);
920}
921.rd-rung-ab {
922  display:flex; align-items:center; gap:var(--rd-sm,8px); flex:1; min-height:28px;
923  padding:0 var(--rd-sm,8px); border-radius:var(--rd-radius-sm,8px);
924  font:600 13px system-ui; color:var(--ab-text-muted,#9e9e9e);
925  border:1px solid transparent;
926}
927.rd-rung-dot-ab { width:10px; height:10px; border-radius:50%; background:currentColor; flex-shrink:0; opacity:.45; }
928.rd-rung-ab[data-sev="danger"]  { color:var(--rd-danger,#e53935); }
929.rd-rung-ab[data-sev="caution"] { color:var(--rd-caution,#ffa000); }
930.rd-rung-ab[data-sev="hazard"]  { color:var(--rd-hazard,#ff9800); }
931.rd-rung-ab[data-sev="safe"]    { color:var(--rd-safe,#4caf50); }
932.rd-rung-ab[data-sev="neutral"] { color:var(--rd-neutral,#9e9e9e); }
933.rd-rung-on-ab { border-color:currentColor; background:rgba(255,255,255,.06); }
934.rd-rung-on-ab .rd-rung-dot-ab { opacity:1; }
935
936/* TrailBar — a segmented history meter, not a single-value progress bar. */
937.rd-trail-ab {
938  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
939  gap:var(--rd-xs,4px); justify-content:center; font-family:system-ui;
940}
941.rd-trail-label-ab {
942  font:600 10px system-ui; letter-spacing:.08em; text-transform:uppercase;
943  color:var(--ab-text-muted,#9e9e9e);
944}
945.rd-trail-track-ab { display:flex; gap:2px; height:16px; }
946.rd-trail-seg-ab {
947  flex:1; border-radius:2px; background:rgba(255,255,255,.10);
948  border:1px solid var(--ab-comp-border,#2a2a2a);
949}
950
951/* SettingRow / LimitRow / Field — a key/value line, not a table cell. */
952.rd-setrow-ab {
953  width:100%; height:100%; box-sizing:border-box; display:flex; align-items:center;
954  justify-content:space-between; gap:var(--rd-md,12px); padding:0 var(--rd-md,12px);
955  border-bottom:1px solid var(--ab-comp-border,#2a2a2a); font:14px system-ui;
956  color:var(--ab-comp-text,#fff);
957}
958.rd-setrow-label-ab { color:var(--ab-text-muted,#9e9e9e); }
959.rd-setrow-val-ab { font-weight:600; display:inline-flex; align-items:baseline; gap:4px; }
960.rd-setrow-unit-ab { font:500 11px system-ui; color:var(--ab-text-muted,#9e9e9e); }
961
962/* AppTile — the TabletLauncherActivity launcher tile. */
963.rd-apptile-ab {
964  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
965  align-items:center; justify-content:center; gap:var(--rd-sm,8px);
966  background:var(--ab-comp-bg,#000); border:1px solid var(--ab-comp-border,#2a2a2a);
967  border-radius:var(--rd-radius-lg,18px); font-family:system-ui;
968  min-height:var(--rd-touch,48px);
969}
970.rd-apptile-icon-ab { font-size:30px; color:var(--rd-scheme1,#2196f3); }
971.rd-apptile-label-ab {
972  font:600 13px system-ui; letter-spacing:.04em; color:var(--ab-comp-text,#fff);
973}
974
975/* ConfirmDialog. */
976.rd-dialog-ab {
977  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
978  gap:var(--rd-sm,8px); padding:var(--rd-lg,16px);
979  background:var(--ab-surface,#141414); border:1px solid var(--ab-comp-border,#2a2a2a);
980  border-radius:var(--rd-radius-lg,18px); box-shadow:0 12px 32px rgba(0,0,0,.55);
981  font-family:system-ui; color:var(--ab-comp-text,#fff);
982}
983.rd-dialog-title-ab { font:600 16px system-ui; }
984.rd-dialog-msg-ab { font:14px/1.4 system-ui; color:var(--ab-text-muted,#9e9e9e); flex:1; }
985.rd-dialog-actions-ab { display:flex; justify-content:flex-end; gap:var(--rd-sm,8px); }
986.rd-dialog-cancel-ab, .rd-dialog-confirm-ab {
987  display:inline-flex; align-items:center; height:36px; padding:0 var(--rd-lg,16px);
988  border-radius:var(--rd-radius-sm,8px); font:600 13px system-ui; letter-spacing:.04em;
989}
990.rd-dialog-cancel-ab { color:var(--ab-text-muted,#9e9e9e); border:1px solid var(--ab-comp-border,#2a2a2a); }
991.rd-dialog-confirm-ab { background:var(--rd-caution,#ffa000); color:#000; }
992
993/* LoadingState. */
994.rd-loading-ab {
995  width:100%; height:100%; box-sizing:border-box; display:flex; flex-direction:column;
996  align-items:center; justify-content:center; gap:var(--rd-sm,8px); font-family:system-ui;
997  color:var(--ab-text-muted,#9e9e9e);
998}
999.rd-loading-spin-ab {
1000  width:26px; height:26px; border-radius:50%;
1001  border:3px solid var(--ab-comp-border,#2a2a2a);
1002  border-top-color:var(--rd-caution,#ffa000);
1003  animation:rd-spin-ab .9s linear infinite;
1004}
1005.rd-loading-msg-ab { font:500 13px system-ui; }
1006@keyframes rd-spin-ab { to { transform:rotate(360deg); } }
1007@media (prefers-reduced-motion:reduce) { .rd-loading-spin-ab { animation:none; } }`;
1008  document.head.appendChild(style);
1009}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.