@the-portland-company/shell 0.67.0 → 0.68.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.
@@ -131,14 +131,24 @@ function clearPendingThemeChoice() {
131
131
  writeSharedCookie(THEME_PENDING_COOKIE, "", 0);
132
132
  }
133
133
  function resolveTheme(pref) {
134
+ if (pref === "dark") return "dark";
135
+ if (pref === "system") {
136
+ try {
137
+ if (typeof window !== "undefined" && typeof window.matchMedia === "function") {
138
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
139
+ }
140
+ } catch {
141
+ }
142
+ }
134
143
  return "light";
135
144
  }
136
145
  function applyThemePreference(pref) {
137
146
  if (typeof document === "undefined") return;
138
- const resolved = resolveTheme();
147
+ const resolved = resolveTheme(pref);
139
148
  const d = document.documentElement;
140
- d.classList.remove("dark");
149
+ d.classList.toggle("dark", resolved === "dark");
141
150
  try {
151
+ d.setAttribute("data-theme", resolved);
142
152
  d.style.colorScheme = resolved;
143
153
  } catch {
144
154
  }
@@ -152,11 +162,10 @@ var ls=null;try{ls=localStorage.getItem(KEY);}catch(e){}
152
162
  var pref=cookie||ls||"system";
153
163
  if(VALID.indexOf(pref)===-1)pref="system";
154
164
  if(cookie&&cookie!==ls){try{localStorage.setItem(KEY,cookie);}catch(e){}}
155
- // Dark Mode retired platform-wide 2026-09-21 \u2014 always light, and strip any
156
- // stale dark class a pre-retirement cookie/localStorage value left behind.
165
+ var dark=pref==="dark"||(pref==="system"&&!!(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches));
157
166
  var d=document.documentElement;
158
- d.classList.remove("dark");
159
- try{d.style.colorScheme="light";}catch(e){}
167
+ if(dark)d.classList.add("dark");else d.classList.remove("dark");
168
+ try{d.setAttribute("data-theme",dark?"dark":"light");d.style.colorScheme=dark?"dark":"light";}catch(e){}
160
169
  }catch(e){}})();`;
161
170
  var CORE_APP_ORIGIN = "https://app.politogyvrm.com";
162
171
  var CORE_BILLING_PATH = "/organization-settings/subscription-billing/text-email";
@@ -177,12 +186,13 @@ function resolveOrgLogoUrl(url) {
177
186
  if (/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(raw)) return raw;
178
187
  return `${CORE_APP_ORIGIN}${raw.startsWith("/") ? "" : "/"}${raw}`;
179
188
  }
189
+ var TOPBAR_GLYPH_SIZE = 17;
180
190
  function topBarIconButtonClass(tone = "default") {
181
191
  const toneClass = tone === "danger" || tone === "warning" ? "text-[color:var(--chrome-danger)] hover:text-[color:var(--chrome-danger)]" : "text-[color:var(--topbar-icon-color,var(--muted))] hover:text-[color:var(--topbar-icon-hover-color,var(--foreground))] hover:bg-[color:var(--topbar-icon-hover-bg,transparent)]";
182
192
  return [
183
193
  // `group/tbtip` powers the shared <TopBarTooltip> rendered inside the trigger.
184
- "group/tbtip relative flex items-center justify-center rounded-full border border-[color:var(--line)] transition",
185
- "h-[var(--topbar-icon-button-size,32px)] w-[var(--topbar-icon-button-size,32px)]",
194
+ "group/tbtip relative flex items-center justify-center rounded-[var(--topbar-icon-button-radius,5px)] border border-[color:var(--line)] transition",
195
+ "h-[var(--topbar-icon-button-size,34px)] w-[var(--topbar-icon-button-size,34px)]",
186
196
  toneClass
187
197
  ].join(" ");
188
198
  }
@@ -205,7 +215,7 @@ function useAnyTopBarPanelOpen() {
205
215
  }
206
216
  function TopBarCountBadge({ count }) {
207
217
  if (!(count > 0)) return null;
208
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--chrome-danger)] px-1 text-[10px] font-normal leading-none text-white", children: count > 99 ? "99+" : count });
218
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-[5px] -right-[5px] flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--chrome-danger)] px-1 text-[10px] font-bold leading-4 text-white", children: count > 99 ? "99+" : count });
209
219
  }
210
220
  function TopBarTooltip({
211
221
  title,
@@ -391,7 +401,7 @@ function ValueSkeleton({ className }) {
391
401
  function SlotGlyph({
392
402
  slot,
393
403
  fallback: Fallback,
394
- size = 20
404
+ size = TOPBAR_GLYPH_SIZE
395
405
  }) {
396
406
  const glyphName = useTopBarIconSlotGlyph(slot ?? "apps");
397
407
  const BrandGlyph = slot ? resolveBrandGlyph(glyphName) : null;
@@ -483,7 +493,7 @@ function TopBarPopover({
483
493
  ) : null
484
494
  ] });
485
495
  }
486
- function BatteryGlyph({ size = 20, className }) {
496
+ function BatteryGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
487
497
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
488
498
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "1", y: "6", width: "18", height: "12", rx: "2", ry: "2" }),
489
499
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "23", y1: "10", x2: "23", y2: "14" }),
@@ -491,7 +501,7 @@ function BatteryGlyph({ size = 20, className }) {
491
501
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "9", y1: "10", x2: "9", y2: "14" })
492
502
  ] });
493
503
  }
494
- function BellGlyph({ size = 20, className }) {
504
+ function BellGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
495
505
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
496
506
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9" }),
497
507
  /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M13.73 21a2 2 0 0 1-3.46 0" })
@@ -506,7 +516,7 @@ function BellOffGlyph({ size = 14, className }) {
506
516
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23" })
507
517
  ] });
508
518
  }
509
- function LifeBuoyGlyph({ size = 20, className }) {
519
+ function LifeBuoyGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
510
520
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
511
521
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }),
512
522
  /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }),
@@ -516,7 +526,7 @@ function LifeBuoyGlyph({ size = 20, className }) {
516
526
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "4.93", y1: "19.07", x2: "9.17", y2: "14.83" })
517
527
  ] });
518
528
  }
519
- function LayoutGlyph({ size = 20, className }) {
529
+ function LayoutGlyph({ size = TOPBAR_GLYPH_SIZE, className }) {
520
530
  return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round", className, "aria-hidden": true, children: [
521
531
  /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
522
532
  /* @__PURE__ */ jsxRuntime.jsx("line", { x1: "3", y1: "9", x2: "21", y2: "9" }),
@@ -1534,7 +1544,7 @@ function SpecOverlayAction({ orgId, orgName } = {}) {
1534
1544
  className: topBarIconButtonClass(badgeTone),
1535
1545
  onClick: () => setOpen(true),
1536
1546
  children: [
1537
- /* @__PURE__ */ jsxRuntime.jsx(LayoutGlyph, { size: 20, className: "text-current" }),
1547
+ /* @__PURE__ */ jsxRuntime.jsx(LayoutGlyph, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
1538
1548
  badgeLabel ? /* @__PURE__ */ jsxRuntime.jsx(
1539
1549
  "span",
1540
1550
  {
@@ -1699,38 +1709,6 @@ var NAV_DISPLAY_OPTIONS = [
1699
1709
  { value: "tabs", label: "Tabs Only" },
1700
1710
  { value: "both", label: "Both" }
1701
1711
  ];
1702
- function NavDisplayAction({
1703
- value,
1704
- onChange
1705
- }) {
1706
- return /* @__PURE__ */ jsxRuntime.jsx(
1707
- TopBarPopover,
1708
- {
1709
- label: "Navigation Display",
1710
- description: "Show navigation as a sub-menu, tabs, or both.",
1711
- fallbackIcon: LayoutGlyph,
1712
- panelClassName: "w-44",
1713
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-1", role: "radiogroup", "aria-label": "Navigation display", children: NAV_DISPLAY_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
1714
- "button",
1715
- {
1716
- type: "button",
1717
- role: "radio",
1718
- "aria-checked": value === opt.value,
1719
- onClick: () => onChange(opt.value),
1720
- className: [
1721
- "flex w-full items-center justify-between px-4 py-2 text-left text-sm font-medium",
1722
- value === opt.value ? "text-[color:var(--color-accent)]" : "text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
1723
- ].join(" "),
1724
- children: [
1725
- opt.label,
1726
- value === opt.value ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" }) : null
1727
- ]
1728
- },
1729
- opt.value
1730
- )) })
1731
- }
1732
- );
1733
- }
1734
1712
  function useThemePreference() {
1735
1713
  const ctx = react.useContext(NavChromeContext);
1736
1714
  const fetchJson = ctx?.fetchJson ?? null;
@@ -1745,7 +1723,7 @@ function useThemePreference() {
1745
1723
  }
1746
1724
  const pref = cookie ?? (isThemePreference(ls) ? ls : null) ?? "system";
1747
1725
  setValueState(pref);
1748
- applyThemePreference();
1726
+ applyThemePreference(pref);
1749
1727
  }, []);
1750
1728
  const meCtx = react.useContext(NavChromeContext);
1751
1729
  react.useEffect(() => {
@@ -1754,7 +1732,7 @@ function useThemePreference() {
1754
1732
  if (pending) {
1755
1733
  if (meCtx?.me) reconciled.current = true;
1756
1734
  setValueState(pending);
1757
- applyThemePreference();
1735
+ applyThemePreference(pending);
1758
1736
  writeThemeCookie(pending);
1759
1737
  try {
1760
1738
  localStorage.setItem(THEME_STORAGE_KEY, pending);
@@ -1773,7 +1751,7 @@ function useThemePreference() {
1773
1751
  if (isThemePreference(pref)) {
1774
1752
  reconciled.current = true;
1775
1753
  setValueState(pref);
1776
- applyThemePreference();
1754
+ applyThemePreference(pref);
1777
1755
  writeThemeCookie(pref);
1778
1756
  try {
1779
1757
  localStorage.setItem(THEME_STORAGE_KEY, pref);
@@ -1792,7 +1770,7 @@ function useThemePreference() {
1792
1770
  const cookie = readThemeCookie();
1793
1771
  if (cookie) {
1794
1772
  setValueState((prev) => {
1795
- if (cookie !== prev) applyThemePreference();
1773
+ if (cookie !== prev) applyThemePreference(cookie);
1796
1774
  return cookie;
1797
1775
  });
1798
1776
  }
@@ -1814,14 +1792,14 @@ function useThemePreference() {
1814
1792
  } catch {
1815
1793
  return;
1816
1794
  }
1817
- const onChange = () => applyThemePreference();
1795
+ const onChange = () => applyThemePreference("system");
1818
1796
  mq.addEventListener("change", onChange);
1819
1797
  return () => mq.removeEventListener("change", onChange);
1820
1798
  }, [value]);
1821
1799
  const setValue = react.useCallback(
1822
1800
  (next) => {
1823
1801
  setValueState(next);
1824
- applyThemePreference();
1802
+ applyThemePreference(next);
1825
1803
  writeThemeCookie(next);
1826
1804
  try {
1827
1805
  localStorage.setItem(THEME_STORAGE_KEY, next);
@@ -1856,6 +1834,16 @@ var SIGNS_ORIGIN = "https://signs.politogyvrm.com/";
1856
1834
  var POLLS_ORIGIN = "https://polls.politogyvrm.com/";
1857
1835
  var CONTACTS_TRIBES = "https://contacts.politogyvrm.com/tribes";
1858
1836
  var PETITION_MODE_ORIGIN = "https://petition-mode.politogyvrm.com/";
1837
+ function applyHiddenItems(items, hidden) {
1838
+ if (!hidden || hidden.length === 0) return items;
1839
+ const drop = new Set(hidden.map((h) => h.trim().toLowerCase()));
1840
+ const kept = items.filter((i) => i.kind === "section" || !drop.has(i.label.toLowerCase()));
1841
+ return kept.filter((item, idx) => {
1842
+ if (item.kind !== "section") return true;
1843
+ const next = kept[idx + 1];
1844
+ return !!next && next.kind !== "section";
1845
+ });
1846
+ }
1859
1847
  var CONTACTS_APP = "https://contacts.politogyvrm.com";
1860
1848
  var APP_CONTACTS_CHILDREN = [
1861
1849
  { label: "Dashboard", icon: "grid", href: `${CONTACTS_APP}/` },
@@ -1970,7 +1958,8 @@ function standardPolitogyNav(opts) {
1970
1958
  label: "Contacts",
1971
1959
  icon: icon("users"),
1972
1960
  href: opts.contactsItem.href,
1973
- children: opts.contactsItem.children
1961
+ children: opts.contactsItem.children,
1962
+ badge: opts.contactsItem.count ?? void 0
1974
1963
  } : {
1975
1964
  label: "Contacts",
1976
1965
  icon: icon("users"),
@@ -1982,10 +1971,11 @@ function standardPolitogyNav(opts) {
1982
1971
  }))
1983
1972
  };
1984
1973
  return [
1974
+ { kind: "section", label: "Relationship" },
1985
1975
  { label: "Dashboard", icon: icon("home"), href: withOrg(abs("/relationship/dashboard")) },
1986
1976
  contacts,
1987
1977
  {
1988
- label: "Email Broadcast",
1978
+ label: "Email broadcast",
1989
1979
  icon: icon("mail"),
1990
1980
  href: withOrg(`${EMAIL_APP}/dashboard`),
1991
1981
  children: [
@@ -1993,20 +1983,19 @@ function standardPolitogyNav(opts) {
1993
1983
  ...leaves([EMAIL_V1_LEGACY])
1994
1984
  ]
1995
1985
  },
1996
- // SMS/MMS Blast IS a2p (Specs/A2P) — the standalone delivery engine
1997
- // shared with Campaign Mode, not a core-hosted surface (the old
1998
- // `/relationship/sms-mms-blast/*` core routes are dead; core no longer
1999
- // hosts them). Every link carries `mode=relationship` so a2p's nav
2000
- // resolves the Relationship tree instead of its own Campaign default
2001
- // (resolveActiveMode, mode-preference.ts).
2002
1986
  {
2003
- label: "SMS/MMS Blast",
1987
+ // SMS/MMS Blast is the a2p app entered in RELATIONSHIP mode (a2p is
1988
+ // dual-mode; Specs/A2P). The `?mode=relationship` param pins the
1989
+ // destination's tree so the row that got you there is the row that
1990
+ // lights up on arrival.
1991
+ label: "SMS/MMS blast",
2004
1992
  icon: icon("messageSquare"),
2005
1993
  href: withOrg(`${A2P_ORIGIN}?mode=relationship`),
2006
1994
  children: appLeaves(A2P_ORIGIN, APP_A2P_CHILDREN, "relationship")
2007
1995
  },
2008
- // These two carry no top-level href in VRM core (parent row only toggles).
2009
- { label: "Calls to Action", icon: icon("layout"), children: leaves(CTA_CHILDREN) },
1996
+ { kind: "section", label: "Collect" },
1997
+ // Carries no top-level href in VRM core (parent row only toggles).
1998
+ { label: "Calls to action", icon: icon("layout"), children: leaves(CTA_CHILDREN) },
2010
1999
  {
2011
2000
  label: "Forms",
2012
2001
  icon: icon("activity"),
@@ -2031,9 +2020,12 @@ function standardPolitogyNav(opts) {
2031
2020
  href: withOrg("https://ctas.politogyvrm.com/"),
2032
2021
  children: APP_CTAS_CHILDREN.map((c) => ({ label: c.label, icon: icon(c.icon), href: withOrg(c.href) }))
2033
2022
  },
2023
+ { kind: "section", label: "Account" },
2024
+ // My Profile is global — NOT org-suffixed (main-app ShellChromeBridge).
2025
+ { label: "My profile", icon: icon("user"), href: withOrg(abs("/my-profile")) },
2034
2026
  {
2035
- label: "Organization Settings",
2036
- icon: icon("briefcase"),
2027
+ label: "Organization settings",
2028
+ icon: icon("settings"),
2037
2029
  href: withOrg(abs("/organization-settings")),
2038
2030
  children: branches(ORG_SETTINGS_CHILDREN)
2039
2031
  }
@@ -2089,7 +2081,8 @@ function standardPolitogyNav(opts) {
2089
2081
  } : { label: "Dashboard", icon: icon("home"), href: withOrg(PETITION_MODE_ORIGIN) }
2090
2082
  ];
2091
2083
  const mode = opts.mode ?? "relationship";
2092
- const items = mode === "campaign" ? buildCampaign() : mode === "petition" ? buildPetition() : buildRelationship();
2084
+ const built = mode === "campaign" ? buildCampaign() : mode === "petition" ? buildPetition() : buildRelationship();
2085
+ const items = applyHiddenItems(built, opts.hiddenItems);
2093
2086
  if (opts.supportItem) {
2094
2087
  items.push({
2095
2088
  label: "Support",
@@ -2219,6 +2212,7 @@ function NavSidebar({
2219
2212
  const ModeIcon = mode?.icon;
2220
2213
  const orgLogoSrc = resolveOrgLogoUrl(org?.logoUrl);
2221
2214
  const orgGradient = org?.brandColor ? `linear-gradient(to right, ${org.brandColor}, ${org.brandColorSecondary || org.brandColor})` : "linear-gradient(to right, #007BFF, #A54B7A, #E63946)";
2215
+ const orgCardStyle = hexToRgba(org?.brandColor, 0.42) ? { borderColor: hexToRgba(org?.brandColor, 0.42) } : void 0;
2222
2216
  const activeGroupKey = react.useMemo(() => {
2223
2217
  for (let i = 0; i < nav.items.length; i++) {
2224
2218
  const item = nav.items[i];
@@ -2252,7 +2246,7 @@ function NavSidebar({
2252
2246
  "aria-modal": open ? "true" : void 0,
2253
2247
  "aria-label": open ? "Navigation" : void 0,
2254
2248
  className: clsx__default.default(
2255
- "fixed inset-y-0 left-0 w-[240px] flex-col border-r border-[color:var(--line)] bg-[color:var(--sidebar-bg,var(--nav-surface))] lg:flex",
2249
+ "fixed inset-y-0 left-0 w-[var(--sidebar-width,236px)] flex-col border-r border-[color:var(--sidebar-line,var(--line))] bg-[color:var(--sidebar-bg,var(--nav-surface))] lg:flex",
2256
2250
  open ? "flex z-50" : "hidden z-30"
2257
2251
  ),
2258
2252
  style: { fontFamily: 'var(--font-family, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif)' },
@@ -2272,31 +2266,31 @@ function NavSidebar({
2272
2266
  children: /* @__PURE__ */ jsxRuntime.jsx(CloseIcon, {})
2273
2267
  }
2274
2268
  ) : null,
2275
- /* @__PURE__ */ jsxRuntime.jsx(
2276
- Link,
2277
- {
2278
- href: brand.homeHref ?? "/",
2279
- className: "flex h-[64px] flex-shrink-0 items-center gap-2.5 px-6",
2280
- children: brand.logoSrc ? /* @__PURE__ */ jsxRuntime.jsx(
2281
- "img",
2282
- {
2283
- src: brand.logoSrc,
2284
- alt: brand.logoAlt ?? brand.title,
2285
- draggable: false,
2286
- className: "h-10 w-auto object-contain"
2287
- }
2288
- ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2289
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-[color:var(--color-accent)] text-base font-black text-white shadow-sm", children: brand.badgeLabel }),
2290
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col leading-tight", children: [
2291
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-extrabold tracking-[-0.01em] text-[color:var(--color-accent)]", children: brand.title }),
2292
- brand.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
2269
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-shrink-0 flex-col gap-[14px] border-b border-[color:var(--sidebar-line,var(--line))] px-[18px] py-4", children: [
2270
+ /* @__PURE__ */ jsxRuntime.jsx(
2271
+ Link,
2272
+ {
2273
+ href: brand.homeHref ?? "/",
2274
+ className: "flex items-center gap-2.5",
2275
+ children: brand.logoSrc ? /* @__PURE__ */ jsxRuntime.jsx(
2276
+ "img",
2277
+ {
2278
+ src: brand.logoSrc,
2279
+ alt: brand.logoAlt ?? brand.title,
2280
+ draggable: false,
2281
+ className: "h-[var(--sidebar-brand-logo-height,24px)] w-auto object-contain"
2282
+ }
2283
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2284
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-9 w-9 items-center justify-center rounded-xl bg-[color:var(--color-accent)] text-base font-black text-white shadow-sm", children: brand.badgeLabel }),
2285
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex flex-col leading-tight", children: [
2286
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-base font-extrabold tracking-[-0.01em] text-[color:var(--color-accent)]", children: brand.title }),
2287
+ brand.subtitle ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-semibold uppercase tracking-[0.16em] text-[color:var(--muted)]", children: brand.subtitle }) : null
2288
+ ] })
2293
2289
  ] })
2294
- ] })
2295
- }
2296
- ),
2297
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-y-auto px-4 pb-2", children: [
2298
- org ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-3", children: [
2299
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2 flex items-center justify-between px-1", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--sidebar-text-muted,var(--muted))]", children: "Organization" }) }),
2290
+ }
2291
+ ),
2292
+ org ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2293
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-[7px] flex items-center justify-between", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: NAV_EYEBROW_CLASS, children: "Organization" }) }),
2300
2294
  org.loading ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex w-full items-center gap-2.5 rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] px-2.5 py-2 shadow-sm", children: [
2301
2295
  /* @__PURE__ */ jsxRuntime.jsx(
2302
2296
  "span",
@@ -2314,34 +2308,41 @@ function NavSidebar({
2314
2308
  ),
2315
2309
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "sr-only", children: "Loading organization" })
2316
2310
  ] }) : org.organizations && org.organizations.length > 0 && org.onSwitch ? /* @__PURE__ */ jsxRuntime.jsxs("details", { "data-nav-details": true, className: "group relative", children: [
2317
- /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "flex w-full cursor-pointer list-none flex-col overflow-hidden rounded-[5px] border border-[color:var(--line)] bg-[color:var(--panel-elevated)] text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)] [&::-webkit-details-marker]:hidden", children: [
2318
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-[70px] w-full items-center justify-center bg-white px-3", children: [
2319
- orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "max-h-[70px] max-w-full object-contain" }) : org.canUploadLogo ? /* @__PURE__ */ jsxRuntime.jsx(
2320
- "a",
2321
- {
2322
- href: org.uploadLogoHref ?? "#",
2323
- onClick: (e) => e.stopPropagation(),
2324
- className: "text-xs font-semibold text-[color:var(--color-accent)] underline",
2325
- children: "Upload logo"
2326
- }
2327
- ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-neutral-800", children: org.name ?? "Organization" }),
2328
- /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute inset-x-0 bottom-0 h-[3px]", style: { backgroundImage: orgGradient } })
2329
- ] }),
2330
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-full min-w-0 items-center justify-between gap-2 px-2.5 py-2", children: [
2331
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
2332
- /* @__PURE__ */ jsxRuntime.jsx(
2333
- "span",
2334
- {
2335
- title: org.name ?? void 0,
2336
- className: "truncate text-[13px] font-semibold",
2337
- children: org.name ?? "No organizations"
2338
- }
2339
- ),
2340
- org.id && !org.loading ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
2341
- ] }),
2342
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
2343
- ] })
2344
- ] }),
2311
+ /* @__PURE__ */ jsxRuntime.jsxs(
2312
+ "summary",
2313
+ {
2314
+ style: orgCardStyle,
2315
+ className: "flex w-full cursor-pointer list-none flex-col overflow-hidden rounded-[5px] border border-[color:var(--sidebar-card-border,var(--sidebar-line,var(--line)))] bg-[color:var(--sidebar-card-bg,var(--panel-elevated))] text-left text-[color:var(--sidebar-text,var(--foreground))] transition hover:bg-[color:var(--sidebar-row-hover-bg,var(--panel))] [&::-webkit-details-marker]:hidden",
2316
+ children: [
2317
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-[var(--sidebar-org-band-height,48px)] w-full items-center justify-center bg-white px-3 py-[7px]", children: [
2318
+ orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "max-h-full max-w-[132px] object-contain" }) : org.canUploadLogo ? /* @__PURE__ */ jsxRuntime.jsx(
2319
+ "a",
2320
+ {
2321
+ href: org.uploadLogoHref ?? "#",
2322
+ onClick: (e) => e.stopPropagation(),
2323
+ className: "text-xs font-semibold text-[color:var(--color-accent)] underline",
2324
+ children: "Upload logo"
2325
+ }
2326
+ ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-neutral-800", children: org.name ?? "Organization" }),
2327
+ /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute inset-x-0 bottom-0 h-[3px]", style: { backgroundImage: orgGradient } })
2328
+ ] }),
2329
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-full min-w-0 items-center justify-between gap-[9px] px-[10px] py-2", children: [
2330
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
2331
+ /* @__PURE__ */ jsxRuntime.jsx(
2332
+ "span",
2333
+ {
2334
+ title: org.name ?? void 0,
2335
+ className: "truncate text-[12px] font-semibold leading-[1.2]",
2336
+ children: org.name ?? "No organizations"
2337
+ }
2338
+ ),
2339
+ org.id && !org.loading ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
2340
+ ] }),
2341
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 14 })
2342
+ ] })
2343
+ ]
2344
+ }
2345
+ ),
2345
2346
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute left-0 right-0 z-40 mt-1 max-h-[min(60vh,520px)] overflow-y-auto rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: [
2346
2347
  shouldShowOrgFilter(org.organizations.length) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "sticky top-0 z-10 bg-[color:var(--panel-elevated)] px-2 pb-1 pt-1", children: /* @__PURE__ */ jsxRuntime.jsx(
2347
2348
  "input",
@@ -2410,10 +2411,11 @@ function NavSidebar({
2410
2411
  {
2411
2412
  type: "button",
2412
2413
  onClick: org.onClick,
2413
- className: "flex w-full flex-col overflow-hidden rounded-[5px] border border-[color:var(--line)] bg-[color:var(--panel-elevated)] text-left text-[color:var(--foreground)] shadow-sm transition hover:bg-[color:var(--panel)]",
2414
+ style: orgCardStyle,
2415
+ className: "flex w-full flex-col overflow-hidden rounded-[5px] border border-[color:var(--sidebar-card-border,var(--sidebar-line,var(--line)))] bg-[color:var(--sidebar-card-bg,var(--panel-elevated))] text-left text-[color:var(--sidebar-text,var(--foreground))] transition hover:bg-[color:var(--sidebar-row-hover-bg,var(--panel))]",
2414
2416
  children: [
2415
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-[70px] w-full items-center justify-center bg-white px-3", children: [
2416
- orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "max-h-[70px] max-w-full object-contain" }) : org.canUploadLogo ? /* @__PURE__ */ jsxRuntime.jsx(
2417
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-[var(--sidebar-org-band-height,48px)] w-full items-center justify-center bg-white px-3 py-[7px]", children: [
2418
+ orgLogoSrc ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: orgLogoSrc, alt: "", className: "max-h-full max-w-[132px] object-contain" }) : org.canUploadLogo ? /* @__PURE__ */ jsxRuntime.jsx(
2417
2419
  "a",
2418
2420
  {
2419
2421
  href: org.uploadLogoHref ?? "#",
@@ -2424,31 +2426,31 @@ function NavSidebar({
2424
2426
  ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-sm font-semibold text-neutral-800", children: org.name ?? "Organization" }),
2425
2427
  /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, className: "absolute inset-x-0 bottom-0 h-[3px]", style: { backgroundImage: orgGradient } })
2426
2428
  ] }),
2427
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-full min-w-0 items-center justify-between gap-2 px-2.5 py-2", children: [
2429
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex w-full min-w-0 items-center justify-between gap-[9px] px-[10px] py-2", children: [
2428
2430
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
2429
2431
  /* @__PURE__ */ jsxRuntime.jsx(
2430
2432
  "span",
2431
2433
  {
2432
2434
  title: org.name ?? void 0,
2433
- className: "truncate text-[13px] font-semibold",
2435
+ className: "truncate text-[12px] font-semibold leading-[1.2]",
2434
2436
  children: org.name ?? "No organizations"
2435
2437
  }
2436
2438
  ),
2437
2439
  org.id && !org.loading ? /* @__PURE__ */ jsxRuntime.jsx(OrgIdCopyButton, { id: org.id }) : null
2438
2440
  ] }),
2439
- /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, {})
2441
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 14 })
2440
2442
  ] })
2441
2443
  ]
2442
2444
  }
2443
2445
  )
2444
2446
  ] }) : null,
2445
- mode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
2447
+ mode ? /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2446
2448
  /* @__PURE__ */ jsxRuntime.jsx(
2447
2449
  "div",
2448
2450
  {
2449
2451
  role: "radiogroup",
2450
2452
  "aria-label": "Mode",
2451
- className: "flex items-center justify-start gap-0.5 rounded-lg border border-[color:var(--border-inverse,var(--line))] bg-[color:var(--mode-track-bg,var(--panel-elevated))] p-[3px]",
2453
+ className: "flex w-full items-center gap-1 rounded-lg border border-[color:var(--border-inverse,var(--sidebar-line,var(--line)))] bg-[color:var(--mode-track-bg,var(--panel-elevated))] p-[3px]",
2452
2454
  children: (mode.options && mode.options.length > 0 ? mode.options : [{ id: mode.label, label: mode.label, icon: ModeIcon }]).map(
2453
2455
  (opt) => {
2454
2456
  const selected = opt.label === mode.label;
@@ -2469,8 +2471,8 @@ function NavSidebar({
2469
2471
  },
2470
2472
  disabled: !available,
2471
2473
  className: [
2472
- "flex h-9 items-center justify-center rounded px-[9px] py-[6px] transition",
2473
- !available ? "cursor-not-allowed text-[color:var(--sidebar-text-muted,var(--muted))] opacity-35" : selected ? "bg-[color:var(--mode-accent,var(--color-accent))] text-[color:var(--mode-on-accent,white)] shadow-sm" : "text-[color:var(--sidebar-text-muted,var(--muted))] hover:bg-white/5 hover:text-[color:var(--sidebar-text,var(--foreground))]"
2474
+ "flex flex-1 items-center justify-center rounded-[5px] px-[9px] py-[6px] transition",
2475
+ !available ? "cursor-not-allowed text-[color:var(--sidebar-text-muted,var(--muted))] opacity-35" : selected ? "bg-[color:var(--sidebar-mode-selected-bg,var(--mode-accent,var(--color-accent)))] text-[color:var(--mode-on-accent,white)]" : "text-[color:var(--sidebar-text-muted,var(--muted))] hover:bg-white/5 hover:text-[color:var(--sidebar-text,var(--foreground))]"
2474
2476
  ].join(" "),
2475
2477
  children: OptIcon ? /* @__PURE__ */ jsxRuntime.jsx(OptIcon, { size: 15, className: "shrink-0" }) : null
2476
2478
  },
@@ -2480,93 +2482,99 @@ function NavSidebar({
2480
2482
  )
2481
2483
  }
2482
2484
  ),
2483
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-center text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--sidebar-text-muted,var(--muted))]", children: mode.label })
2484
- ] }) : null,
2485
- /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-col gap-1", children: [
2486
- nav.groupLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-2 pb-1 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--sidebar-text-muted,var(--muted))]", children: nav.groupLabel }) : null,
2487
- nav.items.map((item, idx) => {
2488
- if (item.kind === "section") {
2489
- return /* @__PURE__ */ jsxRuntime.jsx(
2490
- NavSectionRow,
2491
- {
2492
- label: item.label,
2493
- first: idx === 0
2494
- },
2495
- `section-${idx}-${item.label}`
2496
- );
2497
- }
2498
- if (nav.flatten && item.children && item.children.length > 0) {
2499
- return item.children.map((child, i) => {
2500
- if (child.kind === "section") {
2501
- return /* @__PURE__ */ jsxRuntime.jsx(
2502
- NavSectionRow,
2503
- {
2504
- label: child.label,
2505
- first: idx === 0 && i === 0
2506
- },
2507
- `flat-section-${idx}-${i}-${child.label}`
2508
- );
2509
- }
2485
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-left text-[11px] leading-[1.2] text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))]", children: modeCaption(mode.label) })
2486
+ ] }) : null
2487
+ ] }),
2488
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col overflow-y-auto px-[10px] py-3", children: /* @__PURE__ */ jsxRuntime.jsxs("nav", { className: "flex flex-col gap-0.5", children: [
2489
+ nav.groupLabel ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: `${NAV_EYEBROW_CLASS} px-2 pb-1.5 pt-1`, children: nav.groupLabel }) : null,
2490
+ nav.items.map((item, idx) => {
2491
+ if (item.kind === "section") {
2492
+ return /* @__PURE__ */ jsxRuntime.jsx(
2493
+ NavSectionRow,
2494
+ {
2495
+ label: item.label,
2496
+ first: idx === 0
2497
+ },
2498
+ `section-${idx}-${item.label}`
2499
+ );
2500
+ }
2501
+ if (nav.flatten && item.children && item.children.length > 0) {
2502
+ return item.children.map((child, i) => {
2503
+ if (child.kind === "section") {
2510
2504
  return /* @__PURE__ */ jsxRuntime.jsx(
2511
- NavRow,
2505
+ NavSectionRow,
2512
2506
  {
2513
- item: child,
2514
- active: child.href ? activeFn(pathname, child.href) : false,
2515
- Link,
2516
- variant: "flat"
2507
+ label: child.label,
2508
+ first: idx === 0 && i === 0
2517
2509
  },
2518
- child.href ?? `flat-${idx}-${i}-${child.label}`
2510
+ `flat-section-${idx}-${i}-${child.label}`
2519
2511
  );
2520
- });
2521
- }
2522
- if (item.children && item.children.length > 0) {
2523
- const key = groupKeyOf(item, idx);
2524
- const selfActive = item.href ? activeFn(pathname, item.href) : false;
2525
- const parentActive = selfActive || hasActiveChild(item, pathname, activeFn);
2512
+ }
2526
2513
  return /* @__PURE__ */ jsxRuntime.jsx(
2527
- NavGroup,
2514
+ NavRow,
2528
2515
  {
2529
- item,
2530
- active: parentActive,
2531
- open: openGroup === key,
2532
- onToggle: () => toggleGroup(key),
2533
- pathname,
2534
- activeFn,
2535
- Link
2516
+ item: child,
2517
+ active: child.href ? activeFn(pathname, child.href) : false,
2518
+ Link,
2519
+ variant: "flat"
2536
2520
  },
2537
- key
2521
+ child.href ?? `flat-${idx}-${i}-${child.label}`
2538
2522
  );
2539
- }
2540
- const active = item.href ? activeFn(pathname, item.href) : false;
2523
+ });
2524
+ }
2525
+ if (item.children && item.children.length > 0) {
2526
+ const key = groupKeyOf(item, idx);
2527
+ const selfActive = item.href ? activeFn(pathname, item.href) : false;
2528
+ const parentActive = selfActive || hasActiveChild(item, pathname, activeFn);
2541
2529
  return /* @__PURE__ */ jsxRuntime.jsx(
2542
- NavRow,
2530
+ NavGroup,
2543
2531
  {
2544
2532
  item,
2545
- active,
2533
+ active: parentActive,
2534
+ open: openGroup === key,
2535
+ onToggle: () => toggleGroup(key),
2536
+ pathname,
2537
+ activeFn,
2546
2538
  Link
2547
2539
  },
2548
- item.href ?? `nav-${idx}-${item.label}`
2540
+ key
2549
2541
  );
2550
- })
2551
- ] })
2552
- ] }),
2553
- footer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-shrink-0 border-t border-[color:var(--line)] px-4 py-3", children: [
2554
- footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap items-center justify-center gap-1.5 text-xs", children: footer.links.map((l, idx) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1.5", children: [
2555
- idx > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[color:var(--muted)]", children: "|" }) : null,
2556
- /* @__PURE__ */ jsxRuntime.jsx(
2557
- "a",
2542
+ }
2543
+ const active = item.href ? activeFn(pathname, item.href) : false;
2544
+ return /* @__PURE__ */ jsxRuntime.jsx(
2545
+ NavRow,
2558
2546
  {
2559
- href: safeHref(l.href),
2560
- className: "font-medium text-[color:var(--foreground)] opacity-80 hover:opacity-100 hover:underline",
2561
- children: l.label
2562
- }
2563
- )
2564
- ] }, l.label)) }) : null,
2547
+ item,
2548
+ active,
2549
+ Link
2550
+ },
2551
+ item.href ?? `nav-${idx}-${item.label}`
2552
+ );
2553
+ })
2554
+ ] }) }),
2555
+ footer ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-shrink-0 border-t border-[color:var(--sidebar-line,var(--line))] px-[14px] py-3", children: [
2556
+ footer.user ? /* @__PURE__ */ jsxRuntime.jsx(
2557
+ NavSidebarUserCard,
2558
+ {
2559
+ user: footer.user,
2560
+ userMenu: footer.userMenu ?? void 0,
2561
+ onSignOut: footer.onSignOut
2562
+ }
2563
+ ) : null,
2564
+ footer.links && footer.links.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex flex-wrap items-center gap-[10px] text-[11px] leading-[1.2] ${footer.user ? "mt-3" : ""}`, children: footer.links.map((l) => /* @__PURE__ */ jsxRuntime.jsx(
2565
+ "a",
2566
+ {
2567
+ href: safeHref(l.href),
2568
+ className: "text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))] hover:text-[color:var(--sidebar-text,var(--foreground))] hover:underline",
2569
+ children: l.label
2570
+ },
2571
+ l.label
2572
+ )) }) : null,
2565
2573
  footer.envLabel && footer.envLabel.trim().toLowerCase() !== "production" ? /* @__PURE__ */ jsxRuntime.jsxs(
2566
2574
  "div",
2567
2575
  {
2568
2576
  "data-shell-meta": true,
2569
- className: "mt-1.5 flex flex-col items-center gap-0.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--sidebar-meta-text,var(--sidebar-text))]",
2577
+ className: "mt-1.5 flex flex-col items-start gap-0.5 font-[family-name:var(--font-plex-mono)] text-[10px] text-[color:var(--sidebar-meta-text,var(--sidebar-text))]",
2570
2578
  children: [
2571
2579
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2572
2580
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2629,6 +2637,27 @@ function HamburgerIcon() {
2629
2637
  }
2630
2638
  );
2631
2639
  }
2640
+ function BookOpenIcon() {
2641
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2642
+ "svg",
2643
+ {
2644
+ width: 14,
2645
+ height: 14,
2646
+ viewBox: "0 0 24 24",
2647
+ fill: "none",
2648
+ stroke: "currentColor",
2649
+ strokeWidth: 2,
2650
+ strokeLinecap: "round",
2651
+ strokeLinejoin: "round",
2652
+ className: "shrink-0",
2653
+ "aria-hidden": "true",
2654
+ children: [
2655
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v14" }),
2656
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z" })
2657
+ ]
2658
+ }
2659
+ );
2660
+ }
2632
2661
  function SearchIcon({ className } = {}) {
2633
2662
  return /* @__PURE__ */ jsxRuntime.jsxs(
2634
2663
  "svg",
@@ -2670,12 +2699,12 @@ function CloseIcon() {
2670
2699
  }
2671
2700
  );
2672
2701
  }
2673
- function ChevronDown({ className } = {}) {
2702
+ function ChevronDown({ className, size = 16 } = {}) {
2674
2703
  return /* @__PURE__ */ jsxRuntime.jsx(
2675
2704
  "svg",
2676
2705
  {
2677
- width: 16,
2678
- height: 16,
2706
+ width: size,
2707
+ height: size,
2679
2708
  viewBox: "0 0 24 24",
2680
2709
  fill: "none",
2681
2710
  stroke: "currentColor",
@@ -2689,6 +2718,28 @@ function ChevronDown({ className } = {}) {
2689
2718
  );
2690
2719
  }
2691
2720
  var NAV_HOVER_GRADIENT = "var(--nav-hover-gradient)";
2721
+ var NAV_EYEBROW_CLASS = "text-[11px] font-bold uppercase leading-[1.2] tracking-[0.09em] text-[color:var(--sidebar-eyebrow,var(--sidebar-text-muted,var(--muted)))]";
2722
+ var NAV_TOP_ICON_SIZE = 17;
2723
+ var NAV_TOP_ROW_CLASS = (active) => `gap-[11px] px-[10px] py-[9px] text-[13px] leading-none ${active ? "font-semibold" : "font-normal"}`;
2724
+ var NAV_TOP_ACTIVE_CLASS = "bg-[color:var(--sidebar-active-bg,var(--color-accent))] text-[color:var(--sidebar-active-text,white)]";
2725
+ var NAV_TOP_INACTIVE_CLASS = "text-[color:var(--sidebar-row-text,var(--sidebar-text,var(--muted)))] hover:bg-[color:var(--sidebar-row-hover-bg,var(--panel-elevated))] hover:text-[color:var(--sidebar-text,var(--foreground))]";
2726
+ function modeCaption(label) {
2727
+ return label.replace(/\s+Mode$/i, " mode");
2728
+ }
2729
+ function NavActiveRail() {
2730
+ return /* @__PURE__ */ jsxRuntime.jsx(
2731
+ "span",
2732
+ {
2733
+ "aria-hidden": "true",
2734
+ className: "pointer-events-none absolute inset-y-0 left-0 w-[2px]",
2735
+ style: { background: "var(--mode-accent, transparent)" }
2736
+ }
2737
+ );
2738
+ }
2739
+ function NavRowBadge({ value }) {
2740
+ if (value === void 0 || value === null || value === "") return null;
2741
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto shrink-0 font-[family-name:var(--font-plex-mono,ui-monospace,monospace)] text-[11px] font-semibold leading-none text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))]", children: value });
2742
+ }
2692
2743
  function NavSectionRow({
2693
2744
  label,
2694
2745
  first,
@@ -2698,9 +2749,9 @@ function NavSectionRow({
2698
2749
  "p",
2699
2750
  {
2700
2751
  className: [
2701
- "text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--sidebar-text-muted,var(--muted))]",
2702
- variant === "sub" ? "px-3 pb-0.5" : "px-2 pb-1",
2703
- first ? "" : "mt-3"
2752
+ NAV_EYEBROW_CLASS,
2753
+ variant === "sub" ? "px-3 pb-0.5" : "px-2 pb-1.5 pt-1",
2754
+ first ? "" : variant === "sub" ? "mt-3" : "mt-[14px]"
2704
2755
  ].join(" "),
2705
2756
  children: label
2706
2757
  }
@@ -2715,39 +2766,32 @@ function NavRow({
2715
2766
  const Icon = item.icon;
2716
2767
  const isSub = variant === "sub";
2717
2768
  const washActive = variant !== "top";
2718
- const activeClasses = washActive ? "bg-[color:var(--mode-accent-surface,var(--panel-elevated))] text-[color:var(--color-accent)] shadow-sm" : "bg-[color:var(--color-accent)] text-white shadow-sm";
2769
+ const activeClasses = washActive ? "bg-[color:var(--mode-accent-surface,var(--panel-elevated))] text-[color:var(--color-accent)] shadow-sm" : NAV_TOP_ACTIVE_CLASS;
2719
2770
  const wrapperClasses = [
2720
- "group relative block overflow-hidden rounded-md transition",
2721
- active ? activeClasses : "text-[color:var(--sidebar-text,var(--muted))] hover:text-white"
2771
+ "group relative block overflow-hidden transition",
2772
+ isSub ? "rounded-md" : "rounded-[5px]",
2773
+ active ? activeClasses : isSub ? "text-[color:var(--sidebar-text,var(--muted))] hover:text-white" : NAV_TOP_INACTIVE_CLASS
2722
2774
  ].join(" ");
2723
- const rowPadding = isSub ? "px-3 py-1.5 text-[13px]" : "px-3 py-2 text-sm";
2724
- const iconSize = isSub ? 16 : 18;
2725
- const iconClass = active && !washActive ? "text-white" : "text-current";
2775
+ const rowPadding = isSub ? "gap-3 px-3 py-1.5 text-[13px] font-semibold" : NAV_TOP_ROW_CLASS(active);
2776
+ const iconSize = isSub ? 16 : NAV_TOP_ICON_SIZE;
2777
+ const iconClass = "text-current";
2726
2778
  const inner = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2727
- !active ? /* @__PURE__ */ jsxRuntime.jsx(
2728
- "span",
2729
- {
2730
- "aria-hidden": "true",
2731
- className: "pointer-events-none absolute inset-y-0 left-0 w-0 transition-[width] duration-300 group-hover:w-full",
2732
- style: { background: NAV_HOVER_GRADIENT }
2733
- }
2734
- ) : (
2735
- // Active-row rail, in the Mode's accent. `--mode-accent` is only set by
2736
- // a declared Mode chrome group, and the inline `transparent` fallback
2737
- // is what every other app gets — so this span is present but invisible
2738
- // there, and being absolutely positioned it moves nothing.
2739
- /* @__PURE__ */ jsxRuntime.jsx(
2779
+ !active ? (
2780
+ // Sub rows keep the left→right gradient sweep; top-level rows take a
2781
+ // flat wash on hover (design rail: rgba(255,255,255,.05)).
2782
+ isSub ? /* @__PURE__ */ jsxRuntime.jsx(
2740
2783
  "span",
2741
2784
  {
2742
2785
  "aria-hidden": "true",
2743
- className: "pointer-events-none absolute inset-y-0 left-0 w-[3px] rounded-l-md",
2744
- style: { background: "var(--mode-accent, transparent)" }
2786
+ className: "pointer-events-none absolute inset-y-0 left-0 w-0 transition-[width] duration-300 group-hover:w-full",
2787
+ style: { background: NAV_HOVER_GRADIENT }
2745
2788
  }
2746
- )
2747
- ),
2748
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `relative z-10 flex items-center gap-3 font-semibold ${rowPadding}`, children: [
2749
- Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: iconSize, className: iconClass }) : null,
2750
- item.label
2789
+ ) : null
2790
+ ) : /* @__PURE__ */ jsxRuntime.jsx(NavActiveRail, {}),
2791
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `relative z-10 flex items-center ${rowPadding}`, children: [
2792
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: iconSize, className: `shrink-0 ${iconClass}` }) : null,
2793
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
2794
+ /* @__PURE__ */ jsxRuntime.jsx(NavRowBadge, { value: item.badge })
2751
2795
  ] })
2752
2796
  ] });
2753
2797
  if (item.disabled) {
@@ -2786,8 +2830,8 @@ function NavGroup({
2786
2830
  const Icon = item.icon;
2787
2831
  const headerClasses = [
2788
2832
  "group relative block w-full overflow-hidden text-left transition",
2789
- open ? "rounded-t-md" : "rounded-md",
2790
- active ? "bg-[color:var(--color-accent)] text-white shadow-sm" : open ? "bg-[color:var(--panel)] text-[color:var(--foreground)]" : "text-[color:var(--sidebar-text,var(--muted))] hover:text-white"
2833
+ open ? "rounded-t-[5px]" : "rounded-[5px]",
2834
+ active ? NAV_TOP_ACTIVE_CLASS : open ? "bg-[color:var(--sidebar-row-hover-bg,var(--panel))] text-[color:var(--sidebar-text,var(--foreground))]" : NAV_TOP_INACTIVE_CLASS
2791
2835
  ].join(" ");
2792
2836
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2793
2837
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2801,17 +2845,11 @@ function NavGroup({
2801
2845
  "aria-expanded": open,
2802
2846
  className: headerClasses,
2803
2847
  children: [
2804
- !active && !open ? /* @__PURE__ */ jsxRuntime.jsx(
2805
- "span",
2806
- {
2807
- "aria-hidden": "true",
2808
- className: "pointer-events-none absolute inset-y-0 left-0 w-0 transition-[width] duration-300 group-hover:w-full",
2809
- style: { background: NAV_HOVER_GRADIENT }
2810
- }
2811
- ) : null,
2812
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative z-10 flex items-center gap-3 px-3 py-2 text-sm font-semibold", children: [
2813
- Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: 18, className: active ? "text-white" : "text-current" }) : null,
2814
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex-1", children: item.label }),
2848
+ active ? /* @__PURE__ */ jsxRuntime.jsx(NavActiveRail, {}) : null,
2849
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `relative z-10 flex items-center ${NAV_TOP_ROW_CLASS(active)}`, children: [
2850
+ Icon ? /* @__PURE__ */ jsxRuntime.jsx(Icon, { size: NAV_TOP_ICON_SIZE, className: "shrink-0 text-current" }) : null,
2851
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
2852
+ /* @__PURE__ */ jsxRuntime.jsx(NavRowBadge, { value: item.badge }),
2815
2853
  /* @__PURE__ */ jsxRuntime.jsx(
2816
2854
  ChevronDown,
2817
2855
  {
@@ -2928,8 +2966,8 @@ function OrgIdCopyButton({ id }) {
2928
2966
  setTimeout(() => setCopied(false), 1500);
2929
2967
  },
2930
2968
  title: "Copy organization id",
2931
- className: "rounded px-1 text-[11px] text-[color:var(--muted)] hover:bg-[color:var(--panel)]",
2932
- children: copied ? "Copied" : `...${id.slice(-8)}`
2969
+ className: "-ml-1 rounded px-1 text-left font-[family-name:var(--font-plex-mono,ui-monospace,monospace)] text-[12px] leading-[1.2] text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))] hover:bg-[color:var(--sidebar-row-hover-bg,var(--panel))]",
2970
+ children: copied ? "Copied" : `\u2026${id.slice(-8)}`
2933
2971
  }
2934
2972
  );
2935
2973
  }
@@ -2946,7 +2984,8 @@ function NavTopBar({
2946
2984
  onOpenNav,
2947
2985
  navOpen,
2948
2986
  search,
2949
- buildNotes
2987
+ buildNotes,
2988
+ accountMenu = "topbar"
2950
2989
  }) {
2951
2990
  const rawName = user?.name?.trim() || "";
2952
2991
  const hasRealName = rawName.length > 0 && !rawName.includes("@");
@@ -2954,7 +2993,7 @@ function NavTopBar({
2954
2993
  const initials = (hasRealName ? rawName : user?.email ?? "P").slice(0, 2).toUpperCase();
2955
2994
  const secondary = hasRealName ? user?.email || [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member" : [user?.tier, user?.role].filter(Boolean).join(" \xB7 ") || "member";
2956
2995
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "sticky top-0 z-20", children: [
2957
- /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex h-[var(--topbar-height,60px)] items-center justify-between gap-4 border-b border-[color:var(--line)] bg-[color:var(--panel)] px-[var(--topbar-padding-x,20px)]", children: [
2996
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { className: "flex h-[var(--topbar-height,60px)] items-center justify-between gap-[14px] border-b border-[color:var(--line)] bg-[color:var(--panel)] px-[var(--topbar-padding-x,20px)]", children: [
2958
2997
  onOpenNav ? /* @__PURE__ */ jsxRuntime.jsx(
2959
2998
  "button",
2960
2999
  {
@@ -2967,8 +3006,8 @@ function NavTopBar({
2967
3006
  children: /* @__PURE__ */ jsxRuntime.jsx(HamburgerIcon, {})
2968
3007
  }
2969
3008
  ) : null,
2970
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "relative flex w-[320px] max-w-full shrink-0 items-center", children: [
2971
- /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "pointer-events-none absolute left-3 h-4 w-4 text-[color:var(--muted)]" }),
3009
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "relative flex h-9 w-[320px] max-w-full shrink-0 items-center", children: [
3010
+ /* @__PURE__ */ jsxRuntime.jsx(SearchIcon, { className: "pointer-events-none absolute left-[10px] h-[15px] w-[15px] text-[color:var(--muted)]" }),
2972
3011
  /* @__PURE__ */ jsxRuntime.jsx(
2973
3012
  "input",
2974
3013
  {
@@ -2980,32 +3019,35 @@ function NavTopBar({
2980
3019
  if (e.key === "Enter") search.onSubmit?.(e.target.value);
2981
3020
  } : void 0,
2982
3021
  readOnly: !search?.onChange,
2983
- className: "w-full rounded-md border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1.5 pl-9 pr-10 text-sm text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-accent)]"
3022
+ className: "h-full w-full rounded-[5px] border border-[color:var(--line)] bg-[color:var(--surface-sunken,var(--panel-elevated))] pl-[33px] pr-10 text-[13px] text-[color:var(--foreground)] placeholder:text-[color:var(--muted)] focus:outline-none focus:ring-2 focus:ring-[color:var(--color-accent)]"
2984
3023
  }
2985
3024
  ),
2986
3025
  /* @__PURE__ */ jsxRuntime.jsx(
2987
3026
  "kbd",
2988
3027
  {
2989
3028
  "aria-hidden": true,
2990
- className: "pointer-events-none absolute right-2.5 rounded border border-[color:var(--line)] px-1 py-0.5 text-[10px] font-medium text-[color:var(--muted)]",
3029
+ className: "pointer-events-none absolute right-[10px] rounded-[3px] border border-[color:var(--line)] px-[5px] py-px font-[family-name:var(--font-plex-mono,ui-monospace,monospace)] text-[12px] leading-[1.2] text-[color:var(--muted)]",
2991
3030
  children: "/"
2992
3031
  }
2993
3032
  )
2994
3033
  ] }),
2995
3034
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative ml-auto flex shrink-0 items-center gap-[var(--topbar-icon-gap,8px)]", children: [
2996
- /* @__PURE__ */ jsxRuntime.jsx(
3035
+ /* @__PURE__ */ jsxRuntime.jsxs(
2997
3036
  "button",
2998
3037
  {
2999
3038
  type: "button",
3000
3039
  onClick: buildNotes?.onOpen,
3001
- className: "shrink-0 rounded-md px-2 py-1.5 text-sm font-medium text-[color:var(--muted)] transition hover:bg-[color:var(--panel-elevated)] hover:text-[color:var(--foreground)]",
3002
- children: buildNotes?.label ?? "Build notes"
3040
+ className: "inline-flex h-8 shrink-0 items-center gap-1.5 rounded-[5px] px-3 text-xs font-semibold text-[color:var(--muted)] transition hover:bg-[color:var(--surface-sunken,var(--panel-elevated))] hover:text-[color:var(--foreground)]",
3041
+ children: [
3042
+ /* @__PURE__ */ jsxRuntime.jsx(BookOpenIcon, {}),
3043
+ buildNotes?.label ?? "Build notes"
3044
+ ]
3003
3045
  }
3004
3046
  ),
3005
3047
  actionsList && actionsList.length > 0 ? actionsList.map((action) => /* @__PURE__ */ jsxRuntime.jsx(NavTopBarActionButton, { action }, action.key)) : null,
3006
3048
  actions,
3007
3049
  themeToggle,
3008
- /* @__PURE__ */ jsxRuntime.jsxs("details", { "data-nav-details": true, className: "group/acct relative", children: [
3050
+ accountMenu === "topbar" ? /* @__PURE__ */ jsxRuntime.jsxs("details", { "data-nav-details": true, className: "group/acct relative", children: [
3009
3051
  /* @__PURE__ */ jsxRuntime.jsxs("summary", { className: "group/tbtip relative flex cursor-pointer list-none items-center gap-2.5 rounded-full py-1 pl-1 pr-2 transition hover:bg-[color:var(--panel-elevated)] [&::-webkit-details-marker]:hidden", children: [
3010
3052
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-[var(--topbar-icon-button-size,32px)] w-[var(--topbar-icon-button-size,32px)] items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-accent)] text-xs font-bold text-white", children: user?.avatarUrl || user?.memojiUrl ? /* @__PURE__ */ jsxRuntime.jsx(
3011
3053
  "img",
@@ -3036,127 +3078,9 @@ function NavTopBar({
3036
3078
  }
3037
3079
  ) })
3038
3080
  ] }),
3039
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-0 z-30 mt-2 flex max-h-[calc(100vh-5rem)] w-72 flex-col overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]", children: [
3040
- /* @__PURE__ */ jsxRuntime.jsx(TopBarPanelArrow, {}),
3041
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain", children: [
3042
- userMenu?.myProfileHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3043
- "a",
3044
- {
3045
- href: safeHref(userMenu.myProfileHref),
3046
- className: "flex w-full items-center gap-2.5 px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3047
- children: [
3048
- /* @__PURE__ */ jsxRuntime.jsx(ProfileIcon, {}),
3049
- "My Profile"
3050
- ]
3051
- }
3052
- ) : null,
3053
- userMenu?.organizationSettingsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3054
- "a",
3055
- {
3056
- href: safeHref(userMenu.organizationSettingsHref),
3057
- className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3058
- children: [
3059
- /* @__PURE__ */ jsxRuntime.jsx(BriefcaseIcon, {}),
3060
- "Organization Settings"
3061
- ]
3062
- }
3063
- ) : null,
3064
- userMenu?.roles && userMenu.roles.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
3065
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Roles & Organizations" }),
3066
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1.5", children: userMenu.roles.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-wrap items-center gap-1.5 text-sm", children: [
3067
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-[color:var(--panel)] px-2 py-0.5 text-xs font-bold uppercase tracking-wide text-[color:var(--foreground)]", children: r.role }),
3068
- r.orgName ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[color:var(--muted)]", children: [
3069
- "in ",
3070
- r.orgName
3071
- ] }) : null
3072
- ] }, `${r.role}-${i}`)) })
3073
- ] }) : null,
3074
- userMenu?.accounts && userMenu.accounts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
3075
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Accounts" }),
3076
- /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: userMenu.accounts.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2.5", children: [
3077
- /* @__PURE__ */ jsxRuntime.jsxs(
3078
- "button",
3079
- {
3080
- type: "button",
3081
- onClick: () => {
3082
- if (!a.active) userMenu.onSwitchAccount?.(a.id);
3083
- },
3084
- className: "flex min-w-0 flex-1 items-center gap-2.5 rounded-lg px-1 py-1 text-left hover:bg-[color:var(--panel)]",
3085
- children: [
3086
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-accent)] text-[11px] font-bold text-white", children: a.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : (a.name ?? a.email ?? "?").slice(0, 2).toUpperCase() }),
3087
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col leading-tight", children: [
3088
- /* @__PURE__ */ jsxRuntime.jsx(
3089
- "span",
3090
- {
3091
- className: [
3092
- "truncate text-sm font-semibold",
3093
- a.active ? "text-[color:var(--color-accent)]" : "text-[color:var(--foreground)]"
3094
- ].join(" "),
3095
- children: a.name ?? a.email ?? a.id.slice(0, 8)
3096
- }
3097
- ),
3098
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-[color:var(--muted)]", children: a.active ? "Active" : a.email ?? "" })
3099
- ] })
3100
- ]
3101
- }
3102
- ),
3103
- !a.active && userMenu.onRemoveAccount ? /* @__PURE__ */ jsxRuntime.jsx(
3104
- "button",
3105
- {
3106
- type: "button",
3107
- "aria-label": `Remove ${a.name ?? a.email ?? "account"}`,
3108
- onClick: () => userMenu.onRemoveAccount?.(a.id),
3109
- className: "shrink-0 rounded p-1 text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3110
- children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
3111
- }
3112
- ) : null
3113
- ] }, a.id)) }),
3114
- userMenu.addAccountHref || userMenu.onAddAccount ? userMenu.addAccountHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3115
- "a",
3116
- {
3117
- href: safeHref(userMenu.addAccountHref),
3118
- className: "mt-1.5 flex items-center gap-2.5 rounded-lg px-1 py-1.5 text-sm font-semibold text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3119
- children: [
3120
- /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
3121
- "Add Account"
3122
- ]
3123
- }
3124
- ) : /* @__PURE__ */ jsxRuntime.jsxs(
3125
- "button",
3126
- {
3127
- type: "button",
3128
- onClick: userMenu.onAddAccount,
3129
- className: "mt-1.5 flex w-full items-center gap-2.5 rounded-lg px-1 py-1.5 text-left text-sm font-semibold text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3130
- children: [
3131
- /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
3132
- "Add Account"
3133
- ]
3134
- }
3135
- ) : null
3136
- ] }) : null,
3137
- userMenu?.theme ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-[color:var(--line)]", children: /* @__PURE__ */ jsxRuntime.jsx(
3138
- NavThemeControl,
3139
- {
3140
- value: userMenu.theme.value,
3141
- onChange: userMenu.theme.onChange
3142
- }
3143
- ) }) : null
3144
- ] }),
3145
- onSignOut ? /* @__PURE__ */ jsxRuntime.jsxs(
3146
- "button",
3147
- {
3148
- type: "button",
3149
- onClick: onSignOut,
3150
- className: "flex w-full shrink-0 items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-accent)]",
3151
- children: [
3152
- /* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
3153
- "Sign out"
3154
- ]
3155
- }
3156
- ) : null
3157
- ] })
3158
- ] }),
3159
- !hasRealName && user ? /* @__PURE__ */ jsxRuntime.jsx(NamePromptBubble, { profileHref: userMenu?.myProfileHref }) : null
3081
+ /* @__PURE__ */ jsxRuntime.jsx(NavAccountMenuPanel, { userMenu, onSignOut, placement: "below" })
3082
+ ] }) : null,
3083
+ accountMenu === "topbar" && !hasRealName && user ? /* @__PURE__ */ jsxRuntime.jsx(NamePromptBubble, { profileHref: userMenu?.myProfileHref }) : null
3160
3084
  ] })
3161
3085
  ] }),
3162
3086
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -3171,7 +3095,191 @@ function NavTopBar({
3171
3095
  )
3172
3096
  ] });
3173
3097
  }
3174
- function NamePromptBubble({ profileHref }) {
3098
+ function NavAccountMenuPanel({
3099
+ userMenu,
3100
+ onSignOut,
3101
+ placement
3102
+ }) {
3103
+ const wrapper = placement === "above" ? "absolute bottom-full left-0 right-0 z-40 mb-2 flex max-h-[calc(100vh-5rem)] flex-col overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 text-[color:var(--foreground)] shadow-[var(--shadow-soft)]" : "absolute right-0 z-30 mt-2 flex max-h-[calc(100vh-5rem)] w-72 flex-col overflow-hidden rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] py-1 shadow-[var(--shadow-soft)]";
3104
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: wrapper, children: [
3105
+ placement === "below" ? /* @__PURE__ */ jsxRuntime.jsx(TopBarPanelArrow, {}) : null,
3106
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain", children: [
3107
+ userMenu?.myProfileHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3108
+ "a",
3109
+ {
3110
+ href: safeHref(userMenu.myProfileHref),
3111
+ className: "flex w-full items-center gap-2.5 px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3112
+ children: [
3113
+ /* @__PURE__ */ jsxRuntime.jsx(ProfileIcon, {}),
3114
+ "My Profile"
3115
+ ]
3116
+ }
3117
+ ) : null,
3118
+ userMenu?.organizationSettingsHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3119
+ "a",
3120
+ {
3121
+ href: safeHref(userMenu.organizationSettingsHref),
3122
+ className: "flex w-full items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3123
+ children: [
3124
+ /* @__PURE__ */ jsxRuntime.jsx(BriefcaseIcon, {}),
3125
+ "Organization Settings"
3126
+ ]
3127
+ }
3128
+ ) : null,
3129
+ userMenu?.roles && userMenu.roles.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
3130
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Roles & Organizations" }),
3131
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1.5", children: userMenu.roles.map((r, i) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex flex-wrap items-center gap-1.5 text-sm", children: [
3132
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-md bg-[color:var(--panel)] px-2 py-0.5 text-xs font-bold uppercase tracking-wide text-[color:var(--foreground)]", children: r.role }),
3133
+ r.orgName ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[color:var(--muted)]", children: [
3134
+ "in ",
3135
+ r.orgName
3136
+ ] }) : null
3137
+ ] }, `${r.role}-${i}`)) })
3138
+ ] }) : null,
3139
+ userMenu?.accounts && userMenu.accounts.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
3140
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Accounts" }),
3141
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-col gap-1", children: userMenu.accounts.map((a) => /* @__PURE__ */ jsxRuntime.jsxs("li", { className: "flex items-center gap-2.5", children: [
3142
+ /* @__PURE__ */ jsxRuntime.jsxs(
3143
+ "button",
3144
+ {
3145
+ type: "button",
3146
+ onClick: () => {
3147
+ if (!a.active) userMenu.onSwitchAccount?.(a.id);
3148
+ },
3149
+ className: "flex min-w-0 flex-1 items-center gap-2.5 rounded-lg px-1 py-1 text-left hover:bg-[color:var(--panel)]",
3150
+ children: [
3151
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-8 w-8 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[color:var(--color-accent)] text-[11px] font-bold text-white", children: a.avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: a.avatarUrl, alt: "", className: "h-full w-full object-cover" }) : (a.name ?? a.email ?? "?").slice(0, 2).toUpperCase() }),
3152
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col leading-tight", children: [
3153
+ /* @__PURE__ */ jsxRuntime.jsx(
3154
+ "span",
3155
+ {
3156
+ className: [
3157
+ "truncate text-sm font-semibold",
3158
+ a.active ? "text-[color:var(--color-accent)]" : "text-[color:var(--foreground)]"
3159
+ ].join(" "),
3160
+ children: a.name ?? a.email ?? a.id.slice(0, 8)
3161
+ }
3162
+ ),
3163
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs text-[color:var(--muted)]", children: a.active ? "Active" : a.email ?? "" })
3164
+ ] })
3165
+ ]
3166
+ }
3167
+ ),
3168
+ !a.active && userMenu.onRemoveAccount ? /* @__PURE__ */ jsxRuntime.jsx(
3169
+ "button",
3170
+ {
3171
+ type: "button",
3172
+ "aria-label": `Remove ${a.name ?? a.email ?? "account"}`,
3173
+ onClick: () => userMenu.onRemoveAccount?.(a.id),
3174
+ className: "shrink-0 rounded p-1 text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3175
+ children: /* @__PURE__ */ jsxRuntime.jsx(TrashIcon, {})
3176
+ }
3177
+ ) : null
3178
+ ] }, a.id)) }),
3179
+ userMenu.addAccountHref || userMenu.onAddAccount ? userMenu.addAccountHref ? /* @__PURE__ */ jsxRuntime.jsxs(
3180
+ "a",
3181
+ {
3182
+ href: safeHref(userMenu.addAccountHref),
3183
+ className: "mt-1.5 flex items-center gap-2.5 rounded-lg px-1 py-1.5 text-sm font-semibold text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3184
+ children: [
3185
+ /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
3186
+ "Add Account"
3187
+ ]
3188
+ }
3189
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
3190
+ "button",
3191
+ {
3192
+ type: "button",
3193
+ onClick: userMenu.onAddAccount,
3194
+ className: "mt-1.5 flex w-full items-center gap-2.5 rounded-lg px-1 py-1.5 text-left text-sm font-semibold text-[color:var(--color-accent)] hover:bg-[color:var(--panel)]",
3195
+ children: [
3196
+ /* @__PURE__ */ jsxRuntime.jsx(AddUserIcon, {}),
3197
+ "Add Account"
3198
+ ]
3199
+ }
3200
+ ) : null
3201
+ ] }) : null,
3202
+ userMenu?.theme ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-[color:var(--line)]", children: /* @__PURE__ */ jsxRuntime.jsx(
3203
+ NavThemeControl,
3204
+ {
3205
+ value: userMenu.theme.value,
3206
+ onChange: userMenu.theme.onChange
3207
+ }
3208
+ ) }) : null,
3209
+ userMenu?.navDisplay ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t border-[color:var(--line)] px-4 py-3", children: [
3210
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-2 text-[11px] font-semibold uppercase tracking-[0.08em] text-[color:var(--muted)]", children: "Navigation display" }),
3211
+ /* @__PURE__ */ jsxRuntime.jsx("div", { role: "radiogroup", "aria-label": "Navigation display", className: "flex flex-col gap-0.5", children: NAV_DISPLAY_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsxs(
3212
+ "button",
3213
+ {
3214
+ type: "button",
3215
+ role: "radio",
3216
+ "aria-checked": userMenu.navDisplay?.value === opt.value,
3217
+ onClick: () => userMenu.navDisplay?.onChange(opt.value),
3218
+ className: [
3219
+ "flex w-full items-center justify-between rounded-md px-2 py-1.5 text-left text-sm font-medium",
3220
+ userMenu.navDisplay?.value === opt.value ? "text-[color:var(--color-accent)]" : "text-[color:var(--foreground)] hover:bg-[color:var(--panel)]"
3221
+ ].join(" "),
3222
+ children: [
3223
+ opt.label,
3224
+ userMenu.navDisplay?.value === opt.value ? /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: "\u2713" }) : null
3225
+ ]
3226
+ },
3227
+ opt.value
3228
+ )) })
3229
+ ] }) : null
3230
+ ] }),
3231
+ onSignOut ? /* @__PURE__ */ jsxRuntime.jsxs(
3232
+ "button",
3233
+ {
3234
+ type: "button",
3235
+ onClick: onSignOut,
3236
+ className: "flex w-full shrink-0 items-center gap-2.5 border-t border-[color:var(--line)] px-4 py-2.5 text-left text-sm font-medium text-[color:var(--foreground)] hover:bg-[color:var(--panel)] hover:text-[color:var(--color-accent)]",
3237
+ children: [
3238
+ /* @__PURE__ */ jsxRuntime.jsx(SignOutIcon, {}),
3239
+ "Sign out"
3240
+ ]
3241
+ }
3242
+ ) : null
3243
+ ] });
3244
+ }
3245
+ function NavSidebarUserCard({
3246
+ user,
3247
+ userMenu,
3248
+ onSignOut
3249
+ }) {
3250
+ const rawName = user.name?.trim() || "";
3251
+ const hasRealName = rawName.length > 0 && !rawName.includes("@");
3252
+ const displayName = hasRealName ? rawName : user.email ?? "Signed in";
3253
+ const initials = (hasRealName ? rawName : user.email ?? "P").slice(0, 2).toUpperCase();
3254
+ const secondary = hasRealName ? user.email || [user.tier, user.role].filter(Boolean).join(" \xB7 ") || "member" : [user.tier, user.role].filter(Boolean).join(" \xB7 ") || "member";
3255
+ return /* @__PURE__ */ jsxRuntime.jsxs("details", { "data-nav-details": true, className: "group/acct relative", children: [
3256
+ /* @__PURE__ */ jsxRuntime.jsxs(
3257
+ "summary",
3258
+ {
3259
+ "aria-label": "My Account",
3260
+ className: "flex w-full cursor-pointer list-none items-center gap-[9px] rounded-[5px] px-1 py-1 text-left transition hover:bg-[color:var(--sidebar-row-hover-bg,var(--panel-elevated))] [&::-webkit-details-marker]:hidden",
3261
+ children: [
3262
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden rounded-full bg-[color:var(--sidebar-avatar-bg,var(--color-accent))] text-[11px] font-bold text-white", children: user.avatarUrl || user.memojiUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: user.avatarUrl || user.memojiUrl, alt: "", className: "h-full w-full object-cover" }) : initials }),
3263
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-1 flex-col", children: [
3264
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[12px] font-semibold leading-[1.2] text-[color:var(--sidebar-text,var(--foreground))]", children: displayName }),
3265
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-[11px] leading-[1.3] text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))]", children: secondary })
3266
+ ] }),
3267
+ /* @__PURE__ */ jsxRuntime.jsx(ChevronDown, { size: 14, className: "shrink-0 rotate-180 text-[color:var(--sidebar-caption,var(--sidebar-text-muted,var(--muted)))] transition group-open/acct:rotate-0" })
3268
+ ]
3269
+ }
3270
+ ),
3271
+ /* @__PURE__ */ jsxRuntime.jsx(NavAccountMenuPanel, { userMenu, onSignOut, placement: "above" }),
3272
+ !hasRealName ? /* @__PURE__ */ jsxRuntime.jsx(NamePromptBubble, { profileHref: userMenu?.myProfileHref, placement: "above" }) : null
3273
+ ] });
3274
+ }
3275
+ function hexToRgba(hex, alpha) {
3276
+ if (!hex) return void 0;
3277
+ const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
3278
+ if (!m) return void 0;
3279
+ const n = parseInt(m[1], 16);
3280
+ return `rgba(${n >> 16 & 255}, ${n >> 8 & 255}, ${n & 255}, ${alpha})`;
3281
+ }
3282
+ function NamePromptBubble({ profileHref, placement = "below" }) {
3175
3283
  const KEY = "politogy_name_prompt_dismissed";
3176
3284
  const [dismissed, setDismissed] = react.useState(true);
3177
3285
  react.useEffect(() => {
@@ -3182,42 +3290,48 @@ function NamePromptBubble({ profileHref }) {
3182
3290
  }
3183
3291
  }, []);
3184
3292
  if (dismissed) return null;
3185
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute right-4 top-full z-30 mt-2 w-64 rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] p-3 shadow-[var(--shadow-soft)]", children: [
3186
- /* @__PURE__ */ jsxRuntime.jsx(
3187
- "span",
3188
- {
3189
- "aria-hidden": true,
3190
- className: "absolute -top-[5px] right-[11px] h-2.5 w-2.5 rotate-45 border-l border-t border-[color:var(--line)] bg-[color:var(--panel-elevated)]"
3191
- }
3192
- ),
3193
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: "Add your name" }),
3194
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs leading-snug text-[color:var(--muted)]", children: "Set your first and last name so teammates see you \u2014 not your email." }),
3195
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
3196
- profileHref ? /* @__PURE__ */ jsxRuntime.jsx(
3197
- "a",
3198
- {
3199
- href: safeHref(profileHref),
3200
- className: "rounded-full bg-[color:var(--color-accent)] px-3 py-1 text-xs font-semibold text-white hover:opacity-90",
3201
- children: "Set name"
3202
- }
3203
- ) : null,
3204
- /* @__PURE__ */ jsxRuntime.jsx(
3205
- "button",
3206
- {
3207
- type: "button",
3208
- onClick: () => {
3209
- setDismissed(true);
3210
- try {
3211
- sessionStorage.setItem(KEY, "1");
3212
- } catch {
3293
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3294
+ "div",
3295
+ {
3296
+ className: placement === "above" ? "absolute bottom-full left-0 z-30 mb-2 w-64 rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] p-3 text-left shadow-[var(--shadow-soft)]" : "absolute right-4 top-full z-30 mt-2 w-64 rounded-xl border border-[color:var(--line)] bg-[color:var(--panel-elevated)] p-3 shadow-[var(--shadow-soft)]",
3297
+ children: [
3298
+ /* @__PURE__ */ jsxRuntime.jsx(
3299
+ "span",
3300
+ {
3301
+ "aria-hidden": true,
3302
+ className: placement === "above" ? "absolute -bottom-[5px] left-[14px] h-2.5 w-2.5 rotate-45 border-b border-r border-[color:var(--line)] bg-[color:var(--panel-elevated)]" : "absolute -top-[5px] right-[11px] h-2.5 w-2.5 rotate-45 border-l border-t border-[color:var(--line)] bg-[color:var(--panel-elevated)]"
3303
+ }
3304
+ ),
3305
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-[color:var(--foreground)]", children: "Add your name" }),
3306
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 text-xs leading-snug text-[color:var(--muted)]", children: "Set your first and last name so teammates see you \u2014 not your email." }),
3307
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
3308
+ profileHref ? /* @__PURE__ */ jsxRuntime.jsx(
3309
+ "a",
3310
+ {
3311
+ href: safeHref(profileHref),
3312
+ className: "rounded-full bg-[color:var(--color-accent)] px-3 py-1 text-xs font-semibold text-white hover:opacity-90",
3313
+ children: "Set name"
3213
3314
  }
3214
- },
3215
- className: "rounded-full border border-[color:var(--line)] px-3 py-1 text-xs font-semibold text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3216
- children: "Dismiss"
3217
- }
3218
- )
3219
- ] })
3220
- ] });
3315
+ ) : null,
3316
+ /* @__PURE__ */ jsxRuntime.jsx(
3317
+ "button",
3318
+ {
3319
+ type: "button",
3320
+ onClick: () => {
3321
+ setDismissed(true);
3322
+ try {
3323
+ sessionStorage.setItem(KEY, "1");
3324
+ } catch {
3325
+ }
3326
+ },
3327
+ className: "rounded-full border border-[color:var(--line)] px-3 py-1 text-xs font-semibold text-[color:var(--foreground)] hover:bg-[color:var(--panel)]",
3328
+ children: "Dismiss"
3329
+ }
3330
+ )
3331
+ ] })
3332
+ ]
3333
+ }
3334
+ );
3221
3335
  }
3222
3336
  function formatFirstLast(name) {
3223
3337
  const parts = name.split(/\s+/).filter(Boolean);
@@ -3278,8 +3392,8 @@ function NavTopBarActionButton({ action }) {
3278
3392
  const BrandGlyph = action.slot ? resolveBrandGlyph(glyphName) : null;
3279
3393
  const Glyph = BrandGlyph ?? Icon;
3280
3394
  const content = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3281
- /* @__PURE__ */ jsxRuntime.jsx(Glyph, { size: 20, className: "text-current" }),
3282
- typeof action.badge === "number" && action.badge > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-1 -right-1 flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--color-accent)] px-1 text-[10px] text-white", children: action.badge }) : null,
3395
+ /* @__PURE__ */ jsxRuntime.jsx(Glyph, { size: TOPBAR_GLYPH_SIZE, className: "text-current" }),
3396
+ typeof action.badge === "number" && action.badge > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute -top-[5px] -right-[5px] flex h-4 min-w-4 items-center justify-center rounded-full bg-[color:var(--chrome-danger,var(--color-accent))] px-1 text-[10px] font-bold leading-4 text-white", children: action.badge }) : null,
3283
3397
  /* @__PURE__ */ jsxRuntime.jsx(TopBarTooltip, { title: action.label, description: action.description })
3284
3398
  ] });
3285
3399
  const isExternal = action.href ? /^https?:\/\//.test(action.href) : false;
@@ -3406,6 +3520,7 @@ function NavAppShell({
3406
3520
  topBar,
3407
3521
  betaBanner,
3408
3522
  contentWrapper = "padded",
3523
+ accountMenuPlacement = "sidebar",
3409
3524
  modeId,
3410
3525
  children
3411
3526
  }) {
@@ -3422,15 +3537,29 @@ function NavAppShell({
3422
3537
  document.body.style.overflow = previous;
3423
3538
  };
3424
3539
  }, [navOpen]);
3540
+ const sidebarFooter = accountMenuPlacement === "sidebar" && topBar.user ? {
3541
+ ...sidebar.footer ?? {},
3542
+ user: sidebar.footer?.user ?? topBar.user,
3543
+ userMenu: sidebar.footer?.userMenu ?? topBar.userMenu,
3544
+ onSignOut: sidebar.footer?.onSignOut ?? topBar.onSignOut
3545
+ } : sidebar.footer;
3425
3546
  return /* @__PURE__ */ jsxRuntime.jsxs(
3426
3547
  "div",
3427
3548
  {
3428
3549
  "data-politogy-mode": modeId,
3429
3550
  className: "min-h-screen bg-[color:var(--background)] text-[color:var(--foreground)]",
3430
3551
  children: [
3431
- /* @__PURE__ */ jsxRuntime.jsx(NavSidebar, { ...sidebar, open: navOpen, onClose: () => setNavOpen(false) }),
3432
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:pl-[240px]", children: [
3433
- /* @__PURE__ */ jsxRuntime.jsx(NavTopBar, { ...topBar, onOpenNav: () => setNavOpen(true), navOpen }),
3552
+ /* @__PURE__ */ jsxRuntime.jsx(NavSidebar, { ...sidebar, footer: sidebarFooter, open: navOpen, onClose: () => setNavOpen(false) }),
3553
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "lg:pl-[var(--sidebar-width,236px)]", children: [
3554
+ /* @__PURE__ */ jsxRuntime.jsx(
3555
+ NavTopBar,
3556
+ {
3557
+ ...topBar,
3558
+ accountMenu: topBar.accountMenu ?? (accountMenuPlacement === "sidebar" ? "sidebar" : "topbar"),
3559
+ onOpenNav: () => setNavOpen(true),
3560
+ navOpen
3561
+ }
3562
+ ),
3434
3563
  betaBanner ? /* @__PURE__ */ jsxRuntime.jsx(NavBetaBanner, { ...betaBanner }) : null,
3435
3564
  topBar.title ? /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "px-6 pt-7 text-xl font-semibold text-[color:var(--foreground)]", children: topBar.title }) : null,
3436
3565
  contentWrapper === "bare" ? /* @__PURE__ */ jsxRuntime.jsx("div", { "data-shell-content": "", className: topBar.title ? "mt-[22px]" : void 0, children }) : (
@@ -4108,7 +4237,12 @@ var MODE_BRAND_LOGOS = {
4108
4237
  var modeBrandLogo = (mode) => mode && MODE_BRAND_LOGOS[mode] || DEFAULT_BRAND_LOGO;
4109
4238
  var POLITOGY_APPS = {
4110
4239
  vrm: { title: "Politogy VRM", origin: "https://app.politogyvrm.com", storagePrefix: "vrm" },
4111
- contacts: { title: "Contacts", origin: "https://contacts.politogyvrm.com", storagePrefix: "contacts" },
4240
+ contacts: {
4241
+ title: "Contacts",
4242
+ origin: "https://contacts.politogyvrm.com",
4243
+ storagePrefix: "contacts",
4244
+ pageTitle: false
4245
+ },
4112
4246
  forms: { title: "Forms", origin: "https://forms.politogyvrm.com", storagePrefix: "forms" },
4113
4247
  surveys: { title: "Surveys", origin: "https://surveys.politogyvrm.com", storagePrefix: "surveys" },
4114
4248
  polls: { title: "Polls", origin: "https://polls.politogyvrm.com", storagePrefix: "polls" },
@@ -4149,6 +4283,9 @@ var POLITOGY_APPS = {
4149
4283
  canvass: { title: "Field Canvassing", origin: "https://canvass.politogyvrm.com", storagePrefix: "canvass", mode: "campaign" },
4150
4284
  signs: { title: "Sign Mapping", origin: "https://signs.politogyvrm.com", storagePrefix: "signs", mode: "campaign" }
4151
4285
  };
4286
+ var DEFAULT_SEARCH_PLACEHOLDER = {
4287
+ contacts: "Search contacts, segments, forms"
4288
+ };
4152
4289
  function PolitogyAppFrame({
4153
4290
  app,
4154
4291
  pathname,
@@ -4307,10 +4444,11 @@ function FrameBody({
4307
4444
  // under it. This is routing data, not chrome configuration.
4308
4445
  contactsItem: data?.contactsItem,
4309
4446
  petitionsItem: data?.petitionsItem,
4447
+ hiddenItems: data?.hiddenNavItems,
4310
4448
  mode: navMode,
4311
4449
  activeOrgId
4312
4450
  }),
4313
- [data?.contactsItem, data?.petitionsItem, navMode, activeOrgId]
4451
+ [data?.contactsItem, data?.petitionsItem, data?.hiddenNavItems, navMode, activeOrgId]
4314
4452
  );
4315
4453
  const navItems = core?.nav ?? derivedNavItems;
4316
4454
  const navIsActive = react.useMemo(() => {
@@ -4359,7 +4497,7 @@ function FrameBody({
4359
4497
  // A flat rail's sub-menu IS the nav, so the 'tabs only' preference
4360
4498
  // (which strips children) would leave one childless parent row —
4361
4499
  // flat apps ignore the stripping.
4362
- items: navDisplay === "tabs" && meta.navShape !== "flat" ? navItems.map(({ children: _sub, ...item }) => item) : navItems,
4500
+ items: (navDisplay === "tabs" || data?.flatNav === true) && meta.navShape !== "flat" ? navItems.map(({ children: _sub, ...item }) => item) : navItems,
4363
4501
  isActive: navIsActive,
4364
4502
  flatten: meta.navShape === "flat"
4365
4503
  },
@@ -4386,7 +4524,7 @@ function FrameBody({
4386
4524
  topBar: {
4387
4525
  // Core's pages publish their own identity; every other app is one
4388
4526
  // product surface and takes its registry title.
4389
- title: core?.page?.title ?? (grants?.includes("title") ? data?.frame?.title : void 0) ?? meta.title,
4527
+ title: core?.page?.title ?? (grants?.includes("title") ? data?.frame?.title : void 0) ?? (meta.pageTitle === false ? void 0 : meta.title),
4390
4528
  breadcrumbs,
4391
4529
  user: {
4392
4530
  name: identity.userName,
@@ -4402,7 +4540,7 @@ function FrameBody({
4402
4540
  // search is the only thing on the left end of the bar. No app wires
4403
4541
  // search yet, so this stays decorative (`NavTopBarProps.search` — "an
4404
4542
  // app that has not wired search yet still gets the correct chrome").
4405
- search: {},
4543
+ search: { placeholder: data?.searchPlaceholder ?? DEFAULT_SEARCH_PLACEHOLDER[app] ?? "Search" },
4406
4544
  // "Build notes" — a quiet text button that starts the right-hand
4407
4545
  // group. The spec names the element and its position but not its
4408
4546
  // destination content; `onOpen` is left unset until that is defined.
@@ -4801,8 +4939,6 @@ function ChromeTopBarActions() {
4801
4939
  const state = useChromeState();
4802
4940
  if (!state) return null;
4803
4941
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4804
- /* @__PURE__ */ jsxRuntime.jsx(FuelAction, { orgId: state.orgId }),
4805
- /* @__PURE__ */ jsxRuntime.jsx(NavDisplayAction, { value: state.navDisplay, onChange: state.setNavDisplay }),
4806
4942
  /* @__PURE__ */ jsxRuntime.jsx(
4807
4943
  TopBarWidgetSlot,
4808
4944
  {
@@ -4811,9 +4947,10 @@ function ChromeTopBarActions() {
4811
4947
  children: /* @__PURE__ */ jsxRuntime.jsx(ChromeDevNotesMenu, {})
4812
4948
  }
4813
4949
  ),
4950
+ /* @__PURE__ */ jsxRuntime.jsx(FuelAction, { orgId: state.orgId }),
4814
4951
  /* @__PURE__ */ jsxRuntime.jsx(SpecOverlayAction, { orgId: state.orgId, orgName: state.orgs.find((o) => o.id === state.orgId)?.name ?? null }),
4815
- /* @__PURE__ */ jsxRuntime.jsx(SupportAction, { orgId: state.orgId }),
4816
- /* @__PURE__ */ jsxRuntime.jsx(NotificationsAction, { orgId: state.orgId })
4952
+ /* @__PURE__ */ jsxRuntime.jsx(NotificationsAction, { orgId: state.orgId }),
4953
+ /* @__PURE__ */ jsxRuntime.jsx(SupportAction, { orgId: state.orgId })
4817
4954
  ] });
4818
4955
  }
4819
4956
  function ChromeDevNotesMenu() {
@@ -4867,7 +5004,8 @@ function useChromeUserMenu({
4867
5004
  roles: (state?.orgs ?? []).filter((o) => o.rawRole || o.role).map((o) => ({ role: o.rawRole ?? o.role, orgName: o.name })),
4868
5005
  accounts: [{ id: userId, name: userName, email, avatarUrl: null, active: true }],
4869
5006
  addAccountHref: `${coreBase}/my-profile#accounts`,
4870
- theme: { value: theme.value, onChange: theme.setValue }
5007
+ theme: { value: theme.value, onChange: theme.setValue },
5008
+ navDisplay: state ? { value: state.navDisplay, onChange: state.setNavDisplay } : void 0
4871
5009
  };
4872
5010
  }
4873
5011
  var MODE_DEFS = [
@@ -4930,10 +5068,19 @@ function useChromeMode(arg = {}) {
4930
5068
  return {
4931
5069
  label: current.label,
4932
5070
  icon: current.icon,
4933
- options: options.length > 1 ? options.map((o) => ({ id: o.id, label: o.label, icon: o.icon })) : void 0,
4934
- onSelect: options.length > 1 ? (id) => {
5071
+ // Left-Navigation §7 / Reference/UI-Platform: always the row of three icon
5072
+ // buttons. A mode the organization does not hold is shown dimmed and not
5073
+ // selectable, never hidden. Before entitlements land only the current mode
5074
+ // reads as available (no layout shift, nothing clickable it shouldn't be).
5075
+ options: MODE_DEFS.map((o) => ({
5076
+ id: o.id,
5077
+ label: o.label,
5078
+ icon: o.icon,
5079
+ available: entitled ? Boolean(entitled[o.id]) || o.id === current.id : o.id === current.id
5080
+ })),
5081
+ onSelect: options.length > 0 ? (id) => {
4935
5082
  const def = MODE_DEFS.find((m) => m.id === id);
4936
- if (!def) return;
5083
+ if (!def || !entitled?.[def.id]) return;
4937
5084
  setSelectedModeId(id);
4938
5085
  try {
4939
5086
  localStorage.setItem(MODE_STORAGE_KEY, id);