vibespot 0.7.1 → 0.9.0

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.
package/ui/setup.js CHANGED
@@ -101,10 +101,8 @@ async function initSetup() {
101
101
  return;
102
102
  }
103
103
 
104
- // Show fetch section if hs is installed
105
- if (info.hsInstalled) {
106
- document.getElementById("section-fetch").classList.remove("hidden");
107
- }
104
+ // Reset panel state
105
+ remoteThemesLoaded = false;
108
106
 
109
107
  } catch (err) {
110
108
  showError("Could not connect to server. Is vibeSpot running?");
@@ -853,35 +851,226 @@ function hideError() {
853
851
  document.getElementById("setup-error").classList.add("hidden");
854
852
  }
855
853
 
854
+ // ---------------------------------------------------------------------------
855
+ // Action button panel toggling
856
+ // ---------------------------------------------------------------------------
857
+
858
+ let activePanel = null;
859
+ let remoteThemesLoaded = false;
860
+
861
+ function togglePanel(action) {
862
+ const panels = document.querySelectorAll(".setup__panel");
863
+ const buttons = document.querySelectorAll(".setup__action-btn");
864
+
865
+ // Close if same panel clicked
866
+ if (activePanel === action) {
867
+ panels.forEach((p) => p.classList.add("hidden"));
868
+ buttons.forEach((b) => b.classList.remove("active"));
869
+ activePanel = null;
870
+ return;
871
+ }
872
+
873
+ // Hide all, show target
874
+ panels.forEach((p) => p.classList.add("hidden"));
875
+ buttons.forEach((b) => b.classList.remove("active"));
876
+
877
+ const panelMap = { new: "panel-new", continue: "panel-continue", download: "panel-download", convert: "panel-convert" };
878
+ const panel = document.getElementById(panelMap[action]);
879
+ if (panel) {
880
+ panel.classList.remove("hidden");
881
+ activePanel = action;
882
+ }
883
+
884
+ // Mark button active
885
+ const btn = document.querySelector(`.setup__action-btn[data-action="${action}"]`);
886
+ if (btn) btn.classList.add("active");
887
+
888
+ // Focus input if applicable
889
+ if (action === "new") setTimeout(() => document.getElementById("new-theme-name")?.focus(), 50);
890
+ if (action === "convert") setTimeout(() => document.getElementById("import-url")?.focus(), 50);
891
+
892
+ // Load remote themes on first open
893
+ if (action === "download" && !remoteThemesLoaded) loadDownloadPanel();
894
+
895
+ // Populate continue panel
896
+ if (action === "continue") populateContinuePanel();
897
+ }
898
+
899
+ function populateContinuePanel() {
900
+ const container = document.getElementById("continue-projects");
901
+ const empty = document.getElementById("continue-empty");
902
+ if (!container) return;
903
+
904
+ // Gather projects from the rail
905
+ const railItems = document.querySelectorAll(".project-rail__item");
906
+ if (railItems.length === 0) {
907
+ container.innerHTML = "";
908
+ empty.classList.remove("hidden");
909
+ return;
910
+ }
911
+
912
+ empty.classList.add("hidden");
913
+ container.innerHTML = "";
914
+
915
+ railItems.forEach((item) => {
916
+ const name = item.dataset.name || item.querySelector(".project-rail__name")?.textContent || "";
917
+ const sessionId = item.dataset.sessionId || "";
918
+ const meta = item.querySelector(".project-rail__meta")?.textContent || "";
919
+
920
+ const pill = document.createElement("button");
921
+ pill.className = "setup__pill";
922
+ pill.innerHTML = `<span>${esc(name)}</span>${meta ? `<span class="setup__pill__meta">${esc(meta)}</span>` : ""}`;
923
+ pill.addEventListener("click", () => {
924
+ if (sessionId) {
925
+ resumeSession(sessionId);
926
+ } else {
927
+ openTheme(name);
928
+ }
929
+ });
930
+ container.appendChild(pill);
931
+ });
932
+ }
933
+
934
+ async function loadDownloadPanel() {
935
+ const accountEl = document.getElementById("dl-account");
936
+ const accountName = document.getElementById("dl-account-name");
937
+ const switchArea = document.getElementById("dl-account-switch");
938
+ const inputRow = document.getElementById("dl-input-row");
939
+ const hint = document.getElementById("dl-hint");
940
+ const noAccount = document.getElementById("dl-no-account");
941
+
942
+ accountEl.classList.add("hidden");
943
+ switchArea.classList.add("hidden");
944
+ inputRow.classList.add("hidden");
945
+ hint.classList.add("hidden");
946
+ noAccount.classList.add("hidden");
947
+
948
+ // Fetch active account info to show portal name
949
+ try {
950
+ const statusRes = await fetch("/api/settings/status");
951
+ const statusData = await statusRes.json();
952
+ const accounts = statusData.config?.hubspotAccounts || [];
953
+ const activeId = statusData.config?.activeHubSpotAccount;
954
+ const active = accounts.find((a) => a.portalId === activeId) || accounts[0];
955
+
956
+ if (active) {
957
+ accountName.textContent = `${active.portalName || active.portalId} (${active.portalId})`;
958
+ accountEl.classList.remove("hidden");
959
+ inputRow.classList.remove("hidden");
960
+ hint.classList.remove("hidden");
961
+
962
+ // Wire up change button
963
+ initDlAccountSwitch(accounts, activeId);
964
+ remoteThemesLoaded = true;
965
+ } else {
966
+ noAccount.classList.remove("hidden");
967
+ }
968
+ } catch {
969
+ noAccount.classList.remove("hidden");
970
+ }
971
+ }
972
+
973
+ function initDlAccountSwitch(accounts, activeId) {
974
+ const changeBtn = document.getElementById("dl-account-change");
975
+ const switchArea = document.getElementById("dl-account-switch");
976
+
977
+ // Remove old listener by replacing the element
978
+ const newBtn = changeBtn.cloneNode(true);
979
+ changeBtn.parentNode.replaceChild(newBtn, changeBtn);
980
+
981
+ newBtn.addEventListener("click", () => {
982
+ if (!switchArea.classList.contains("hidden")) { switchArea.classList.add("hidden"); return; }
983
+ switchArea.classList.remove("hidden");
984
+
985
+ let html = '<div style="display:flex;flex-direction:column;gap:6px">';
986
+ for (const acct of accounts) {
987
+ const isActive = acct.portalId === activeId;
988
+ html += `<button class="btn btn--${isActive ? "primary" : "secondary"} dl-acct-btn" data-portal="${esc(acct.portalId)}" style="text-align:left;padding:6px 12px;font-size:13px">${esc(acct.portalName || acct.portalId)} (${esc(acct.portalId)})${isActive ? " ✓" : ""}</button>`;
989
+ }
990
+ html += `<button class="btn btn--secondary dl-acct-btn" data-portal="__new" style="text-align:left;padding:6px 12px;font-size:13px">+ Add another account</button>`;
991
+ html += '</div>';
992
+ switchArea.innerHTML = html;
993
+
994
+ switchArea.querySelectorAll(".dl-acct-btn").forEach((btn) => {
995
+ btn.addEventListener("click", async () => {
996
+ const pid = btn.dataset.portal;
997
+ if (pid === "__new") {
998
+ switchArea.classList.add("hidden");
999
+ if (typeof openSettings === "function") openSettings();
1000
+ return;
1001
+ }
1002
+ if (pid === activeId) { switchArea.classList.add("hidden"); return; }
1003
+ await fetch("/api/settings/hs-switch", {
1004
+ method: "POST",
1005
+ headers: { "Content-Type": "application/json" },
1006
+ body: JSON.stringify({ portalId: pid }),
1007
+ });
1008
+ switchArea.classList.add("hidden");
1009
+ // Reload themes for the new account
1010
+ remoteThemesLoaded = false;
1011
+ loadDownloadPanel();
1012
+ });
1013
+ });
1014
+ });
1015
+ }
1016
+
1017
+ async function downloadThemeByName() {
1018
+ const input = document.getElementById("dl-theme-name");
1019
+ const name = input.value.trim();
1020
+ if (!name) { showError("Enter a theme name."); return; }
1021
+
1022
+ showLoading(`Downloading ${name} from HubSpot...`);
1023
+
1024
+ try {
1025
+ const res = await fetch("/api/setup/fetch", {
1026
+ method: "POST",
1027
+ headers: { "Content-Type": "application/json" },
1028
+ body: JSON.stringify({ name }),
1029
+ });
1030
+ const data = await res.json();
1031
+
1032
+ if (data.error) { showError(data.error); return; }
1033
+ showApp(data.themeName);
1034
+ } catch (err) {
1035
+ showError("Failed to download theme: " + err.message);
1036
+ }
1037
+ }
1038
+
856
1039
  // ---------------------------------------------------------------------------
857
1040
  // Event listeners
858
1041
  // ---------------------------------------------------------------------------
859
1042
 
1043
+ // Action buttons
1044
+ document.querySelectorAll(".setup__action-btn").forEach((btn) => {
1045
+ btn.addEventListener("click", () => togglePanel(btn.dataset.action));
1046
+ });
1047
+
1048
+ // New theme
860
1049
  document.getElementById("btn-create-theme").addEventListener("click", createTheme);
861
1050
  document.getElementById("new-theme-name").addEventListener("keydown", (e) => {
862
1051
  if (e.key === "Enter") { e.preventDefault(); createTheme(); }
863
1052
  });
864
1053
 
865
- document.getElementById("btn-fetch-theme").addEventListener("click", fetchTheme);
866
- document.getElementById("fetch-theme-name").addEventListener("keydown", (e) => {
867
- if (e.key === "Enter") { e.preventDefault(); fetchTheme(); }
1054
+ // Download from HubSpot
1055
+ document.getElementById("btn-fetch-theme").addEventListener("click", downloadThemeByName);
1056
+ document.getElementById("dl-theme-name").addEventListener("keydown", (e) => {
1057
+ if (e.key === "Enter") { e.preventDefault(); downloadThemeByName(); }
868
1058
  });
869
1059
 
870
- document.getElementById("btn-open-theme").addEventListener("click", () => {
871
- openTheme(document.getElementById("open-theme-path").value.trim());
872
- });
873
- document.getElementById("open-theme-path").addEventListener("keydown", (e) => {
874
- if (e.key === "Enter") { e.preventDefault(); document.getElementById("btn-open-theme").click(); }
875
- });
1060
+ // Settings link in download panel
1061
+ const dlSettingsLink = document.getElementById("dl-open-settings");
1062
+ if (dlSettingsLink) {
1063
+ dlSettingsLink.addEventListener("click", (e) => { e.preventDefault(); openSettings(); });
1064
+ }
876
1065
 
877
- // Import from GitHub (on setup screen)
1066
+ // Import from GitHub / Lovable
878
1067
  document.getElementById("import-btn").addEventListener("click", async () => {
879
1068
  const urlInput = document.getElementById("import-url");
880
1069
  const url = urlInput.value.trim();
881
1070
  if (!url) return;
882
1071
 
883
1072
  // Extract repo name to use as theme name
884
- const repoMatch = url.match(/github\.com\/[\w.-]+\/([\w.-]+)/);
1073
+ const repoMatch = url.match(/(?:github\.com|lovable\.dev)\/[\w.-]+\/([\w.-]+)/);
885
1074
  const themeName = repoMatch ? repoMatch[1].replace(/\.git$/, "") : "imported-project";
886
1075
 
887
1076
  showLoading(`Importing ${themeName}...`);
@@ -893,7 +1082,7 @@ document.getElementById("import-btn").addEventListener("click", async () => {
893
1082
  const setupRes = await fetch("/api/setup/create", {
894
1083
  method: "POST",
895
1084
  headers: { "Content-Type": "application/json" },
896
- body: JSON.stringify({ themeName }),
1085
+ body: JSON.stringify({ name: themeName }),
897
1086
  });
898
1087
  const setupData = await setupRes.json();
899
1088
  if (setupData.error) {
@@ -932,8 +1121,6 @@ document.getElementById("import-url").addEventListener("keydown", (e) => {
932
1121
  if (e.key === "Enter") { e.preventDefault(); document.getElementById("import-btn").click(); }
933
1122
  });
934
1123
 
935
- // API key is now handled in the settings panel (settings.js)
936
-
937
1124
  // ---------------------------------------------------------------------------
938
1125
  // Helpers
939
1126
  // ---------------------------------------------------------------------------
package/ui/styles.css CHANGED
@@ -270,6 +270,124 @@ body { display: flex; }
270
270
  margin-top: 6px;
271
271
  }
272
272
 
273
+ /* Action buttons grid */
274
+ .setup__actions {
275
+ display: grid;
276
+ grid-template-columns: repeat(4, 1fr);
277
+ gap: 10px;
278
+ margin-bottom: 16px;
279
+ }
280
+ .setup__action-btn {
281
+ position: relative;
282
+ display: flex;
283
+ flex-direction: column;
284
+ align-items: center;
285
+ gap: 6px;
286
+ padding: 16px 8px;
287
+ background: var(--bg-input);
288
+ border: 1px solid var(--border);
289
+ border-radius: var(--radius);
290
+ color: var(--text);
291
+ font-family: var(--font);
292
+ font-size: 13px;
293
+ cursor: pointer;
294
+ transition: border-color 0.2s, background 0.2s;
295
+ }
296
+ .setup__action-btn:hover {
297
+ border-color: var(--accent);
298
+ background: var(--bg-card);
299
+ }
300
+ .setup__action-btn.active {
301
+ border-color: var(--accent);
302
+ background: var(--accent-bg, rgba(232, 97, 58, 0.08));
303
+ }
304
+ .setup__action-icon {
305
+ font-size: 20px;
306
+ line-height: 1;
307
+ opacity: 0.7;
308
+ }
309
+ .setup__action-btn.active .setup__action-icon { opacity: 1; }
310
+ .setup__action-badge {
311
+ position: absolute;
312
+ top: -6px;
313
+ right: -6px;
314
+ font-size: 9px;
315
+ font-weight: 600;
316
+ text-transform: uppercase;
317
+ letter-spacing: 0.5px;
318
+ padding: 1px 6px;
319
+ border-radius: 8px;
320
+ background: var(--accent);
321
+ color: #fff;
322
+ line-height: 1.4;
323
+ }
324
+
325
+ /* Expandable panels */
326
+ .setup__panel {
327
+ padding: 12px 0;
328
+ animation: panelSlideIn 0.15s ease-out;
329
+ }
330
+ @keyframes panelSlideIn {
331
+ from { opacity: 0; transform: translateY(-6px); }
332
+ to { opacity: 1; transform: translateY(0); }
333
+ }
334
+ .setup__panel-account {
335
+ font-size: 13px;
336
+ color: var(--text-muted);
337
+ margin-bottom: 10px;
338
+ }
339
+ .setup__panel-account span { color: var(--text); font-weight: 500; }
340
+ .setup__panel-loading {
341
+ display: flex;
342
+ align-items: center;
343
+ gap: 8px;
344
+ color: var(--text-muted);
345
+ font-size: 13px;
346
+ }
347
+
348
+ /* Theme pills for Continue panel */
349
+ .setup__pills {
350
+ display: flex;
351
+ flex-wrap: wrap;
352
+ gap: 8px;
353
+ }
354
+ .setup__pill {
355
+ display: flex;
356
+ align-items: center;
357
+ gap: 6px;
358
+ padding: 8px 14px;
359
+ background: var(--bg-input);
360
+ border: 1px solid var(--border);
361
+ border-radius: 20px;
362
+ color: var(--text);
363
+ font-size: 13px;
364
+ cursor: pointer;
365
+ transition: border-color 0.2s, background 0.2s;
366
+ }
367
+ .setup__pill:hover {
368
+ border-color: var(--accent);
369
+ background: var(--bg-card);
370
+ }
371
+ .setup__pill__meta {
372
+ font-size: 11px;
373
+ color: var(--text-muted);
374
+ }
375
+
376
+ /* Select dropdown */
377
+ .setup__select {
378
+ flex: 1;
379
+ padding: 10px 14px;
380
+ background: var(--bg-input);
381
+ border: 1px solid var(--border);
382
+ border-radius: var(--radius);
383
+ color: var(--text);
384
+ font-family: var(--font);
385
+ font-size: 14px;
386
+ outline: none;
387
+ cursor: pointer;
388
+ }
389
+ .setup__select:focus { border-color: var(--accent-glow); }
390
+
273
391
  .setup__cards {
274
392
  display: flex;
275
393
  flex-wrap: wrap;
@@ -1017,6 +1135,18 @@ body { display: flex; }
1017
1135
  gap: 8px;
1018
1136
  }
1019
1137
 
1138
+ .btn-link {
1139
+ background: none;
1140
+ border: none;
1141
+ color: var(--accent);
1142
+ cursor: pointer;
1143
+ font-size: 12px;
1144
+ padding: 0;
1145
+ margin-left: 6px;
1146
+ text-decoration: underline;
1147
+ }
1148
+ .btn-link:hover { opacity: 0.8; }
1149
+
1020
1150
  /* HubSpot setup dialog (deferred install/auth) */
1021
1151
  .hs-setup__steps {
1022
1152
  margin: 12px 0 16px;
@@ -1686,6 +1816,130 @@ body { display: flex; }
1686
1816
  padding: 12px 16px;
1687
1817
  border-top: 1px solid var(--border);
1688
1818
  background: var(--bg-panel-solid);
1819
+ position: relative;
1820
+ }
1821
+
1822
+ /* File chips (pending attachments) */
1823
+ .chat__file-chips {
1824
+ display: none;
1825
+ flex-wrap: wrap;
1826
+ gap: 6px;
1827
+ margin-bottom: 8px;
1828
+ }
1829
+ .chat__file-chips.visible {
1830
+ display: flex;
1831
+ }
1832
+
1833
+ .file-chip {
1834
+ display: inline-flex;
1835
+ align-items: center;
1836
+ gap: 5px;
1837
+ padding: 3px 8px;
1838
+ border-radius: 6px;
1839
+ font-size: 11px;
1840
+ line-height: 1.4;
1841
+ }
1842
+ .file-chip--image {
1843
+ background: rgba(74, 255, 74, 0.1);
1844
+ border: 1px solid rgba(74, 255, 74, 0.3);
1845
+ color: #4aff4a;
1846
+ }
1847
+ .file-chip--doc {
1848
+ background: rgba(180, 120, 255, 0.1);
1849
+ border: 1px solid rgba(180, 120, 255, 0.3);
1850
+ color: #b478ff;
1851
+ }
1852
+ .file-chip__icon { font-size: 13px; }
1853
+ .file-chip__name {
1854
+ max-width: 120px;
1855
+ overflow: hidden;
1856
+ text-overflow: ellipsis;
1857
+ white-space: nowrap;
1858
+ }
1859
+ .file-chip__size {
1860
+ color: var(--text-muted);
1861
+ font-size: 10px;
1862
+ }
1863
+ .file-chip__remove {
1864
+ background: none;
1865
+ border: none;
1866
+ color: #ff6b6b;
1867
+ cursor: pointer;
1868
+ font-size: 14px;
1869
+ padding: 0 2px;
1870
+ line-height: 1;
1871
+ }
1872
+ .file-chip__remove:hover { color: #ff4444; }
1873
+
1874
+ .file-chip--sent {
1875
+ padding: 3px 8px;
1876
+ font-size: 11px;
1877
+ background: rgba(255, 255, 255, 0.1);
1878
+ border-color: rgba(255, 255, 255, 0.25);
1879
+ color: var(--text);
1880
+ }
1881
+ .file-chip--sent.file-chip--image {
1882
+ background: rgba(74, 255, 74, 0.12);
1883
+ border-color: rgba(74, 255, 74, 0.35);
1884
+ color: #5cff5c;
1885
+ }
1886
+ .file-chip--sent.file-chip--doc {
1887
+ background: rgba(180, 120, 255, 0.12);
1888
+ border-color: rgba(180, 120, 255, 0.35);
1889
+ color: #c49aff;
1890
+ }
1891
+ .file-chip--sent .file-chip__icon { font-size: 12px; }
1892
+ .file-chip--sent .file-chip__name {
1893
+ max-width: 150px;
1894
+ }
1895
+
1896
+ .chat-msg__files {
1897
+ display: flex;
1898
+ flex-wrap: wrap;
1899
+ gap: 5px;
1900
+ margin-bottom: 6px;
1901
+ }
1902
+
1903
+ /* Drop overlay */
1904
+ .chat__drop-overlay {
1905
+ position: absolute;
1906
+ inset: 0;
1907
+ background: rgba(74, 158, 255, 0.08);
1908
+ border: 2px dashed var(--accent);
1909
+ border-radius: 12px;
1910
+ display: flex;
1911
+ align-items: center;
1912
+ justify-content: center;
1913
+ z-index: 10;
1914
+ }
1915
+ .chat__drop-overlay.hidden { display: none; }
1916
+ .chat__drop-overlay-content {
1917
+ display: flex;
1918
+ flex-direction: column;
1919
+ align-items: center;
1920
+ gap: 8px;
1921
+ color: var(--accent);
1922
+ font-size: 14px;
1923
+ }
1924
+
1925
+ /* Toast notification */
1926
+ .toast {
1927
+ position: fixed;
1928
+ bottom: 20px;
1929
+ left: 50%;
1930
+ transform: translateX(-50%) translateY(20px);
1931
+ background: #333;
1932
+ color: #fff;
1933
+ padding: 8px 16px;
1934
+ border-radius: 8px;
1935
+ font-size: 13px;
1936
+ opacity: 0;
1937
+ transition: opacity 0.3s, transform 0.3s;
1938
+ z-index: 9999;
1939
+ }
1940
+ .toast.visible {
1941
+ opacity: 1;
1942
+ transform: translateX(-50%) translateY(0);
1689
1943
  }
1690
1944
 
1691
1945
  .chat__input-wrapper {
@@ -1997,6 +2251,95 @@ body { display: flex; }
1997
2251
  }
1998
2252
  .settings__close:hover { background: var(--bg-hover); color: var(--text); }
1999
2253
 
2254
+ .settings__tabs {
2255
+ display: flex;
2256
+ border-bottom: 1px solid var(--border);
2257
+ padding: 0 20px;
2258
+ flex-shrink: 0;
2259
+ }
2260
+ .settings__tab {
2261
+ padding: 10px 16px;
2262
+ background: none;
2263
+ border: none;
2264
+ color: var(--text-muted);
2265
+ cursor: pointer;
2266
+ border-bottom: 2px solid transparent;
2267
+ font-size: 13px;
2268
+ font-weight: 500;
2269
+ transition: color 0.15s, border-color 0.15s;
2270
+ }
2271
+ .settings__tab:hover { color: var(--text); }
2272
+ .settings__tab.active { color: var(--accent); border-bottom-color: var(--accent); }
2273
+
2274
+ .settings__description {
2275
+ color: var(--text-muted);
2276
+ font-size: 12px;
2277
+ margin: 4px 0 12px;
2278
+ line-height: 1.4;
2279
+ }
2280
+
2281
+ .settings__toggle-row {
2282
+ display: flex;
2283
+ align-items: center;
2284
+ justify-content: space-between;
2285
+ padding: 8px 0;
2286
+ }
2287
+ .settings__toggle-label {
2288
+ font-size: 13px;
2289
+ color: var(--text);
2290
+ }
2291
+ .settings__toggle-label-sub {
2292
+ font-size: 11px;
2293
+ color: var(--text-muted);
2294
+ margin-top: 2px;
2295
+ }
2296
+ .settings__toggle {
2297
+ position: relative;
2298
+ width: 36px;
2299
+ height: 20px;
2300
+ background: var(--border);
2301
+ border-radius: 10px;
2302
+ border: none;
2303
+ cursor: pointer;
2304
+ transition: background 0.2s;
2305
+ flex-shrink: 0;
2306
+ }
2307
+ .settings__toggle.active { background: var(--accent); }
2308
+ .settings__toggle::after {
2309
+ content: "";
2310
+ position: absolute;
2311
+ top: 2px;
2312
+ left: 2px;
2313
+ width: 16px;
2314
+ height: 16px;
2315
+ background: white;
2316
+ border-radius: 50%;
2317
+ transition: transform 0.2s;
2318
+ }
2319
+ .settings__toggle.active::after { transform: translateX(16px); }
2320
+
2321
+ .settings__mode-pills {
2322
+ display: flex;
2323
+ gap: 0;
2324
+ border: 1px solid var(--border);
2325
+ border-radius: var(--radius);
2326
+ overflow: hidden;
2327
+ margin-bottom: 12px;
2328
+ }
2329
+ .settings__mode-pill {
2330
+ flex: 1;
2331
+ padding: 8px 16px;
2332
+ background: none;
2333
+ border: none;
2334
+ color: var(--text-muted);
2335
+ font-size: 12px;
2336
+ cursor: pointer;
2337
+ transition: background 0.15s, color 0.15s;
2338
+ }
2339
+ .settings__mode-pill:not(:last-child) { border-right: 1px solid var(--border); }
2340
+ .settings__mode-pill:hover { background: var(--bg-hover); color: var(--text); }
2341
+ .settings__mode-pill.active { background: var(--accent); color: white; }
2342
+
2000
2343
  .settings__body {
2001
2344
  overflow-y: auto;
2002
2345
  padding: 20px;