/* ============================================================
 * HECIAN · 共享 chrome（视觉系统统一 P14）
 * 基准:M05 施工经理工作台
 * 共享于:M01 / M03 / M04 / M07(L0 简化变体)
 *
 * 引入(在各 design-refs/<模块>/index.html):
 *   <link rel="stylesheet" href="../../prototypes/20260426-1530-平台原型-完整版/shared/chrome.css">
 *   <script type="text/babel" data-presets="react" src="../../prototypes/20260426-1530-平台原型-完整版/shared/chrome.jsx"></script>
 *
 * 暴露:window.HX_Chrome = { Brand, Sidebar, MeFooter, useMe, Topbar, Icon }
 *
 * 设计系统标准见同目录 CHROME_PATTERN.md
 * ============================================================ */

(function() {
  if (typeof React === 'undefined') {
    console.warn('[HX_Chrome] React 未加载,chrome 不可用');
    return;
  }

  // ============================================================
  // 0. 移动端底座 CSS 注入(2026-07-03 · 全平台手机适配 · 共享抽屉侧栏)
  //   铁律:所有移动规则锁 @media (max-width:768px);
  //   新增节点(抽屉钮 / backdrop)桌面默认 display:none → 桌面端一个像素不变。
  //   不新造颜色:只用既有 token(--accent 主题红 / --shadow-hud;
  //   backdrop 遮罩 = --ink #1a1d1a 加透明度)。
  // ============================================================
  (function () {
    if (typeof document === 'undefined') return;
    if (document.getElementById('hx-chrome-mobile')) return;
    var st = document.createElement('style');
    st.id = 'hx-chrome-mobile';
    st.textContent = [
      '/* HX Chrome 移动抽屉 · 新增节点桌面隐藏(不影响任何既有元素) */',
      '.hx-drawer-toggle, .hx-sidebar-backdrop { display: none; }',
      '@media (max-width: 768px) {',
      '  /* Sidebar → off-canvas 抽屉(inline style 需 !important 覆盖) */',
      '  aside.hx-sidebar { position: fixed !important; top: 0 !important; left: 0 !important; height: 100vh !important; height: 100dvh !important; z-index: 1200 !important; transform: translateX(-100%); transition: transform .25s ease; }',
      '  aside.hx-sidebar.hx-drawer-open { transform: translateX(0); box-shadow: var(--shadow-hud, 0 6px 24px rgba(0,0,0,.18)); }',
      '  /* 半透明 backdrop(点击关闭 · --ink 基色加透明度) */',
      '  .hx-sidebar-backdrop { display: block; position: fixed; inset: 0; background: rgba(26,29,26,.45); z-index: 1190; opacity: 0; pointer-events: none; transition: opacity .25s ease; }',
      '  .hx-sidebar-backdrop.hx-open { opacity: 1; pointer-events: auto; }',
      '  /* 悬浮 ☰ 抽屉开关(44px 触控 · 主题红圆钮 · 左下角 · Sidebar 自带,不依赖 Topbar) */',
      '  .hx-drawer-toggle { display: flex; align-items: center; justify-content: center; position: fixed; left: 16px; bottom: 16px; width: 44px; height: 44px; border-radius: 50%; border: none; background: var(--accent, #8a3a2a); color: #fff; font-size: 20px; line-height: 1; cursor: pointer; z-index: 1300; box-shadow: var(--shadow-hud, 0 6px 24px rgba(0,0,0,.18)); }',
      '  /* Topbar 收窄(仅移动):标题可截断 · 隐藏署名/租户切换/⌘K 装饰 · AI 搜索钮不撑宽 */',
      '  .hx-topbar { padding: 0 14px !important; }',
      '  .hx-topbar .hx-topbar-title { min-width: 0; overflow: hidden; }',
      '  .hx-topbar .hx-topbar-title > div:first-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }',
      '  .hx-topbar .hx-topbar-contributors, .hx-topbar .hx-tenant-switcher, .hx-kbd { display: none !important; }',
      '  .hx-ai-search { min-width: 0 !important; }',
      '}',
    ].join('\n');
    document.head.appendChild(st);
  })();

  // ============================================================
  // 1. Icon · 内嵌 SVG (跟 M05 components.jsx 同源,自包含)
  // ============================================================
  function HX_Icon({ name, size = 14, color = 'currentColor' }) {
    // emoji / 非 ASCII 字符 fallback(M01 sidebar 用 emoji icons 保留设计意图)
    // 约定:lucide kebab-case → SVG;含非 ASCII 或 ⌂ 这种符号 → 直接当 emoji 文字渲染
    const isLucideName = typeof name === 'string' && /^[a-z][a-z0-9-]*$/.test(name);
    if (!isLucideName) {
      return <span style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        width: size, height: size, fontSize: Math.round(size * 0.95), lineHeight: 1,
      }}>{name}</span>;
    }
    const common = {
      width: size, height: size, viewBox: '0 0 24 24', fill: 'none',
      stroke: color, strokeWidth: 1.6,
      strokeLinecap: 'round', strokeLinejoin: 'round',
    };
    switch (name) {
      case 'home':     return <svg {...common}><path d="M3 11l9-7 9 7"/><path d="M5 10v10h14V10"/></svg>;
      case 'check':    return <svg {...common}><path d="M4 12l5 5L20 6"/></svg>;
      case 'warn':     return <svg {...common}><path d="M12 3l10 18H2z"/><path d="M12 10v5M12 18h0"/></svg>;
      case 'bell':     return <svg {...common}><path d="M6 16V10a6 6 0 0112 0v6l1.5 2h-15z"/><path d="M10 20a2 2 0 004 0"/></svg>;
      case 'book':     return <svg {...common}><path d="M4 4h10a4 4 0 014 4v12H8a4 4 0 01-4-4z"/><path d="M4 16h14"/></svg>;
      case 'chart':    return <svg {...common}><path d="M4 20V8M10 20V4M16 20v-8M22 20H2"/></svg>;
      case 'clip':     return <svg {...common}><rect x="5" y="4" width="14" height="18" rx="2"/><path d="M9 4h6v3H9z"/></svg>;
      case 'bulb':     return <svg {...common}><path d="M9 18h6M10 22h4"/><path d="M12 2a6 6 0 00-4 10c1 1 1 2 1 3h6c0-1 0-2 1-3a6 6 0 00-4-10z"/></svg>;
      case 'user':     return <svg {...common}><circle cx="12" cy="8" r="4"/><path d="M4 22c0-5 4-8 8-8s8 3 8 8"/></svg>;
      case 'arrow':    return <svg {...common}><path d="M5 12h14M13 6l6 6-6 6"/></svg>;
      case 'plus':     return <svg {...common}><path d="M12 5v14M5 12h14"/></svg>;
      case 'search':   return <svg {...common}><circle cx="11" cy="11" r="7"/><path d="M20 20l-3-3"/></svg>;
      case 'dot':      return <svg {...common}><circle cx="12" cy="12" r="3" fill={color}/></svg>;
      case 'download': return <svg {...common}><path d="M12 4v12M6 12l6 6 6-6"/><path d="M4 20h16"/></svg>;
      case 'flag':     return <svg {...common}><path d="M5 21V4M5 4h12l-2 4 2 4H5"/></svg>;
      case 'mic':      return <svg {...common}><rect x="9" y="3" width="6" height="12" rx="3"/><path d="M5 11a7 7 0 0014 0M12 18v3"/></svg>;
      case 'flask':    return <svg {...common}><path d="M9 3v6L4 19a2 2 0 002 3h12a2 2 0 002-3l-5-10V3"/></svg>;
      case 'cog':      return <svg {...common}><circle cx="12" cy="12" r="3"/><path d="M19 12a7 7 0 00-.1-1.2l2-1.5-2-3.4-2.3.9a7 7 0 00-2-1.2L14 3h-4l-.5 2.6a7 7 0 00-2 1.2L5.1 5.9l-2 3.4 2 1.5A7 7 0 005 12c0 .4 0 .8.1 1.2l-2 1.5 2 3.4 2.3-.9c.6.5 1.3.9 2 1.2L10 21h4l.5-2.6a7 7 0 002-1.2l2.3.9 2-3.4-2-1.5c.1-.4.1-.8.1-1.2z"/></svg>;
      case 'logout':   return <svg {...common}><path d="M9 3H4v18h5"/><path d="M16 8l4 4-4 4M20 12H10"/></svg>;
      case 'tweaks':   return <svg {...common}><circle cx="6" cy="6" r="2"/><circle cx="18" cy="12" r="2"/><circle cx="6" cy="18" r="2"/><path d="M8 6h12M2 12h14M8 18h12"/></svg>;
      // B 档新增 4 个 lucide SVG · M01 sidebar 用
      case 'compass':  return <svg {...common}><circle cx="12" cy="12" r="10"/><polygon points="16.24 7.76 14.12 14.12 7.76 16.24 9.88 9.88"/></svg>;
      case 'target':   return <svg {...common}><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>;
      case 'building': return <svg {...common}><rect x="4" y="2" width="16" height="20" rx="2"/><path d="M9 22V12h6v10M8 6h.01M16 6h.01M8 10h.01M16 10h.01"/></svg>;
      case 'package':  return <svg {...common}><path d="M21 8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"/><path d="m3.3 7 8.7 5 8.7-5M12 22V12"/></svg>;
      // 2026-05-13 · M07 ② Brief* 周会摘要组件用 · 趋势 + 决策 + 跳转
      case 'up':            return <svg {...common}><polyline points="6 14 12 8 18 14"/></svg>;
      case 'down':          return <svg {...common}><polyline points="6 10 12 16 18 10"/></svg>;
      case 'flat':          return <svg {...common}><line x1="6" y1="12" x2="18" y2="12"/></svg>;
      case 'hand':          return <svg {...common}><path d="M18 11V6a2 2 0 0 0-4 0v5"/><path d="M14 10V4a2 2 0 0 0-4 0v6"/><path d="M10 10.5V6a2 2 0 0 0-4 0v8"/><path d="M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"/></svg>;
      case 'chevron-right': return <svg {...common}><polyline points="9 18 15 12 9 6"/></svg>;
      default:         return <svg {...common}><circle cx="12" cy="12" r="5"/></svg>;
    }
  }

  // ============================================================
  // 2. Brand 头(左侧 sidebar 顶部)
  // 2026-05-18 · OS 化阶段 2 A 项:logo_mark + displayName 读 window.HX_BRAND
  //   fallback 保留(brand.js 未加载或 tenant 切换前的安全值)
  // ============================================================
  function HX_Brand({ moduleNumber, kicker = '' }) {
    var B = window.HX_BRAND || {};
    var logoMark = B.logo_mark || 'H';
    var displayName = (typeof B.displayName === 'function')
      ? B.displayName()
      : 'HECIAN · 数字化 OS';
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{
          width: 28, height: 28, borderRadius: 6, background: 'var(--accent)',
          color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontWeight: 700, fontSize: 14, letterSpacing: '-.02em',
        }}>{logoMark}</div>
        <div style={{ lineHeight: 1.2 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>
            {displayName}
          </div>
          <div className="mono" style={{
            fontSize: 10, color: 'var(--ink-4)', letterSpacing: '.04em',
          }}>
            {kicker} {moduleNumber ? `· 模块${moduleNumber}` : ''}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // 3a. useMe hook · 取当前登录 session 的 ME(B 方案 全替换)
  //   返回的 me 对象含 loading: true (异步未完成时) / false (已完成);
  //   MeFooter 检测 loading=true 时显示骨架,避免短暂显示错的 fallback 姓名;
  //   profile 字段映射:full_name → name, level_code → levelCode, level_label → level
  // ============================================================
  function HX_useMe(fallback) {
    fallback = fallback || { avatar: '·', name: '--', levelCode: '', level: '' };
    const [me, setMe] = React.useState(Object.assign({}, fallback, { loading: true }));
    React.useEffect(function () {
      if (!window.HecianAuth || typeof window.HecianAuth.getProfile !== 'function') {
        setMe(Object.assign({}, fallback, { loading: false }));
        return;
      }
      let cancelled = false;
      window.HecianAuth.getProfile().then(function (p) {
        if (cancelled) return;
        if (!p) {
          // 没 session → 用 fallback 显示(M01/M03 等模块默认 BD/CM 演示人设)
          setMe(Object.assign({}, fallback, { loading: false }));
          return;
        }
        setMe({
          avatar: (p.full_name && p.full_name.charAt(0)) || fallback.avatar,
          name: p.full_name || fallback.name,
          levelCode: p.level_code || fallback.levelCode,
          level: p.level_label || fallback.level,
          loading: false,
        });
      }).catch(function () {
        setMe(Object.assign({}, fallback, { loading: false }));
      });
      return function () { cancelled = true; };
    }, []);
    return me;
  }

  // ============================================================
  // 3. Me Footer(左下用户身份 chip)· loading=true 时显示骨架
  // 2026-05-20 · X4 = A 全平台 role → level_label · 默认隐藏 levelCode(L1/L2/L3)
  //   只显示 level (= level_label · "市场部总监" / "施工经理" 等岗位名)
  //   admin 调试需要 levelCode 时:<MeFooter showLevelCode={true} />
  // ============================================================
  function HX_MeFooter({ avatar = '·', name = '--', levelCode = '', level = '', loading = false, showLevelCode = false }) {
    if (loading) {
      return (
        <div style={{
          padding: 14, borderTop: '1px solid var(--line-2)',
          display: 'flex', alignItems: 'center', gap: 10,
        }}>
          <div style={{
            width: 32, height: 32, borderRadius: 6,
            background: 'var(--chip)',
            opacity: 0.6,
          }}/>
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{
              height: 13, borderRadius: 3,
              background: 'var(--chip)', opacity: 0.6,
              width: '60%', marginBottom: 4,
            }}/>
            <div style={{
              height: 9, borderRadius: 3,
              background: 'var(--chip)', opacity: 0.4,
              width: '40%',
            }}/>
          </div>
        </div>
      );
    }
    return (
      <div style={{
        padding: 14, borderTop: '1px solid var(--line-2)',
        display: 'flex', alignItems: 'center', gap: 10,
      }}>
        <div style={{
          width: 32, height: 32, borderRadius: 6,
          background: 'var(--accent-soft)', color: 'var(--accent-ink)',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          fontWeight: 600, fontSize: 13,
        }}>{avatar}</div>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 13, fontWeight: 600, color: 'var(--ink)' }}>{name}</div>
          <div className="mono" style={{ fontSize: 10, color: 'var(--ink-4)' }}>
            {showLevelCode && levelCode ? <span>{levelCode} · </span> : null}{level}
          </div>
        </div>
      </div>
    );
  }

  // ============================================================
  // 4. Sidebar 框架(232px 宽 · 接受 groups + brand + footer props)
  // ============================================================
  //
  // groups: [{ label, items: [{ id, label, icon, badge?, v11? }] }]
  // route / setRoute: 当前路由 + 切换函数
  // brand: <HX_Brand .../> 节点
  // footer: <HX_MeFooter .../> 节点
  //
  function HX_Sidebar({ groups = [], route, setRoute, brand, footer, collapsible = false }) {
    // A 档(章 20):下钻路由 `{父}?detail={id}` / `{父}#detail-{id}` · sidebar 仍激活父子页
    // 解析 route 取 base(去掉 ?detail / #detail 后缀),让"父子页激活态"保持视觉连续
    const baseRoute = typeof route === 'string'
      ? route.split('?')[0].split('#')[0]
      : route;
    // 二级折叠能力(collapsible · 数据驱动 · 默认 false = 原一级平铺,现有 13 SPA 不传则零影响)
    // babel-standalone 安全:React.useState 不解构(承本文件 line 326 风格)· expanded = {gi:true}
    var curGi = collapsible ? groups.findIndex(function (g) { return (g.items || []).some(function (it) { return it.id === baseRoute; }); }) : -1;
    var expState = React.useState(function () { var s = {}; if (collapsible && curGi >= 0) s[curGi] = true; return s; });
    var expanded = expState[0], setExpanded = expState[1];
    React.useEffect(function () { if (collapsible && curGi >= 0) setExpanded(function (p) { var n = Object.assign({}, p); n[curGi] = true; return n; }); }, [curGi]);
    var toggleGroup = function (gi) { setExpanded(function (p) { var n = Object.assign({}, p); n[gi] = !n[gi]; return n; }); };
    // 移动抽屉状态(2026-07-03 · ≤768px off-canvas):
    //   桌面零影响——backdrop/toggle 默认 display:none,.hx-drawer-open 在桌面无任何规则。
    //   ☰ 做成 Sidebar 自带悬浮钮(非 Topbar):Topbar/Sidebar 是分离挂载的兄弟组件
    //   (M07 甚至各自独立 ReactDOM root),状态留在 Sidebar 内最稳、API 零变化。
    var drawerSt = React.useState(false);
    var drawerOpen = drawerSt[0], setDrawerOpen = drawerSt[1];
    React.useEffect(function () {
      if (!drawerOpen) return;
      function onKey(e) { if (e.key === 'Escape') setDrawerOpen(false); }
      document.addEventListener('keydown', onKey);
      return function () { document.removeEventListener('keydown', onKey); };
    }, [drawerOpen]);
    return (
      <React.Fragment>
      {/* 移动 backdrop · 点击关抽屉(桌面 display:none,不进 flex 布局) */}
      <div
        className={'hx-sidebar-backdrop' + (drawerOpen ? ' hx-open' : '')}
        onClick={function () { setDrawerOpen(false); }}
      />
      <aside className={'hx-sidebar' + (drawerOpen ? ' hx-drawer-open' : '')} style={{
        width: 232, flexShrink: 0, background: 'var(--bg-elev)',
        borderRight: '1px solid var(--line)',
        display: 'flex', flexDirection: 'column',
        height: '100vh', position: 'sticky', top: 0,
      }}>
        {/* Brand 头 */}
        {brand && (
          <div style={{ padding: '18px 18px 14px', borderBottom: '1px solid var(--line-2)' }}>
            {brand}
          </div>
        )}

        {/* 2026-05-20 · jarvi 反馈"问 AI 功能保留一个 · 保留右侧的(Topbar)" → sidebar AI chip 不再 mount
           合并入口在 Topbar HX_AISearchTrigger(搜索+问 AI 合并控件)· 单源 · 不再双入口 */}

        {/* Nav 主体 */}
        <nav style={{ padding: '8px 10px', flex: 1, overflowY: 'auto' }}>
          {groups.map((g, gi) => {
            // alwaysOpen(2026-06-09 · M06 理库引入):该组永远展开、不渲染折叠头(label 为空则连标题都不渲染)
            //   加法 · 向后兼容:无 alwaysOpen 的组行为不变 · 用于"总览"这种单页不想套折叠层级的场景
            const isOpen = !collapsible || g.alwaysOpen || expanded[gi];
            return (
            <div key={gi}>
              {(collapsible && !g.alwaysOpen) ? (
                <button onClick={() => toggleGroup(gi)} style={{
                  width: '100%', textAlign: 'left', cursor: 'pointer', fontFamily: 'inherit',
                  padding: '8px 8px', marginTop: gi === 0 ? 2 : 10,
                  background: curGi === gi ? 'var(--accent-soft)' : 'transparent',
                  border: '1px solid transparent', borderRadius: 6,
                  display: 'flex', alignItems: 'center', gap: 8,
                  fontSize: 13, fontWeight: 700, letterSpacing: '.01em',
                  color: curGi === gi ? 'var(--accent-ink)' : 'var(--ink)',
                }}>
                  {g.icon && <HX_Icon name={g.icon} size={16} color={curGi === gi ? 'var(--accent)' : 'var(--ink-2)'} />}
                  <span style={{ flex: 1 }}>{g.label}</span>
                  <span style={{ fontSize: 9, color: curGi === gi ? 'var(--accent)' : 'var(--ink-4)' }}>{isOpen ? '▾' : '▸'}</span>
                </button>
              ) : (
                g.label ? <div className="mono" style={{
                  fontSize: 10, color: 'var(--ink-4)', letterSpacing: '.08em',
                  padding: gi === 0 ? '8px 10px 6px' : '14px 10px 6px',
                }}>{g.label}</div> : null
              )}
              {isOpen && (
                <div style={collapsible ? { marginLeft: 15, paddingLeft: 7, borderLeft: '1px solid var(--line-2)' } : null}>
                {g.items.map(it => {
                // 自定义控件节点(node · 模块在分组里塞一个控件用 · 2026-06-19 M06 项目切换器入侧栏引入 · 向后兼容:无 node 的 item 走原逻辑)
                if (it.node) {
                  return <div key={it.key || 'node'} style={{ padding: '4px 4px 6px' }}>{it.node}</div>;
                }
                // 二级组内小标题(subhead · 不可点 · collapsible 主题分块用 · 2026-06-03 M06 IA 重排引入 · 向后兼容:无 subhead 的 item 走原逻辑)
                if (it.subhead) {
                  return (
                    <div key={'sub-' + it.subhead} className="mono" style={{
                      fontSize: 9, color: 'var(--ink-4)', letterSpacing: '.06em',
                      padding: '9px 10px 3px', textTransform: 'uppercase',
                    }}>{it.subhead}</div>
                  );
                }
                // G 档:解析 label 尾部 [占位] / [规划中] / [Jet] / [Jarvi] tag,自动渲染为 chip badge
                // 设计语言模板章 19 + 21:占位徽章 + 作者署名徽章
                // 2026-05-12 · 第三方测试 L3 修复 · 新增 [规划中](占位更面向用户的说法,平滑过渡)
                // 2026-05-14 · 作者署名扩展:[Jet] = 外部贡献(蓝)/ [Jarvi] = 平台开发者(砖红);
                //   改用"作者署名"标识子页出处,方便审稿/迭代时追溯。
                const labelMatch = typeof it.label === 'string'
                  ? it.label.match(/^(.*?)\s*\[(占位|规划中|Jet|Jarvi)\]\s*$/)
                  : null;
                const cleanLabel = labelMatch ? labelMatch[1] : it.label;
                const labelTag = labelMatch ? labelMatch[2] : null;
                return (
                  <button
                    key={it.id}
                    onClick={() => { setDrawerOpen(false); setRoute && setRoute(it.id); }}
                    style={{
                      width: '100%', textAlign: 'left', cursor: 'pointer',
                      padding: collapsible ? '7px 10px' : '8px 10px', margin: '1px 0',
                      background: baseRoute === it.id ? 'var(--panel)' : 'transparent',
                      border: baseRoute === it.id ? '1px solid var(--line)' : '1px solid transparent',
                      color: baseRoute === it.id ? 'var(--ink)' : 'var(--ink-2)',
                      borderRadius: 6, fontSize: 13,
                      fontWeight: baseRoute === it.id ? 600 : 500,
                      display: 'flex', alignItems: 'center', gap: 10,
                      fontFamily: 'inherit',
                    }}
                  >
                    <HX_Icon
                      name={it.icon}
                      size={15}
                      color={baseRoute === it.id ? 'var(--accent)' : 'var(--ink-3)'}
                    />
                    <span style={{ flex: 1 }}>{cleanLabel}</span>
                    {(labelTag === '占位' || labelTag === '规划中') && (
                      <span className="mono" style={{
                        fontSize: 9, padding: '1px 4px', borderRadius: 3,
                        background: 'var(--chip)', color: 'var(--ink-3)',
                        letterSpacing: '.04em',
                      }}>{labelTag}</span>
                    )}
                    {labelTag === 'Jet' && (
                      <span className="mono" style={{
                        fontSize: 9, padding: '1px 4px', borderRadius: 3,
                        background: '#e0e7ff', color: '#4338ca',
                        letterSpacing: '.04em',
                      }}>Jet</span>
                    )}
                    {labelTag === 'Jarvi' && (
                      <span className="mono" style={{
                        fontSize: 9, padding: '1px 4px', borderRadius: 3,
                        background: 'var(--accent-soft)', color: 'var(--accent-ink)',
                        letterSpacing: '.04em',
                      }}>Jarvi</span>
                    )}
                    {it.badge != null && (
                      <span style={{
                        fontSize: 10,
                        background: baseRoute === it.id ? 'var(--accent)' : 'var(--chip)',
                        color: baseRoute === it.id ? '#fff' : 'var(--ink-3)',
                        padding: '1px 6px', borderRadius: 10,
                        minWidth: 18, textAlign: 'center',
                      }}>{it.badge}</span>
                    )}
                  </button>
                );
                })}
                </div>
              )}
            </div>
            );
          })}


        </nav>

        {/* Me Footer */}
        {footer}
      </aside>
      {/* 移动悬浮 ☰ / ✕ 抽屉开关(仅 ≤768px 显示 · 44px 触控 · 主题红圆钮 · 左下角) */}
      <button
        className="hx-drawer-toggle"
        aria-label={drawerOpen ? '关闭导航' : '打开导航'}
        onClick={function () { setDrawerOpen(!drawerOpen); }}
      >{drawerOpen ? '✕' : '☰'}</button>
      </React.Fragment>
    );
  }

  // ============================================================
  // 4b. TenantSwitcher · 租户切换器 chip(OS 化阶段 2 E · 2026-05-18 加)
  //   依赖 shared/tenant.js · 未加载则不渲染(SPA 无 tenant.js 时优雅降级)
  //   位置:Topbar 内 · user-menu chip 旁边(左侧)
  // ============================================================
  function HX_TenantSwitcher() {
    var T = window.HX_Tenants;
    if (!T) return null;
    var current = T.getCurrent();
    // 为兼容 babel-standalone,用 useState 解构 + 命名(避免 ES6 array destructuring 边界 case)
    var ts = React.useState(false);
    var isOpen = ts[0];
    var setIsOpen = ts[1];
    var isHost = current.id === 'hecian';

    React.useEffect(function(){
      function onDocClick(e){
        if (!e.target || !e.target.closest || !e.target.closest('.hx-tenant-switcher')) {
          setIsOpen(false);
        }
      }
      if (isOpen) document.addEventListener('click', onDocClick);
      return function(){ document.removeEventListener('click', onDocClick); };
    }, [isOpen]);

    return (
      <div className="hx-tenant-switcher" style={{ position: 'relative' }}>
        <button onClick={function(e){ e.stopPropagation(); setIsOpen(!isOpen); }}
          title={isHost ? '当前演示视角:HECIAN(本店)' : '演示视角:' + current.name_cn + ' · 切换可看 mock 数据变化'}
          style={{
            display: 'inline-flex', alignItems: 'center', gap: 6,
            padding: '6px 10px', borderRadius: 6, cursor: 'pointer',
            border: '1px solid ' + (isHost ? 'var(--line)' : current.accent_color),
            background: isHost ? 'var(--panel)' : current.accent_color + '14',
            color: isHost ? 'var(--ink-2)' : current.accent_color,
            fontSize: 12, fontWeight: 500, fontFamily: 'inherit',
          }}>
          <span style={{
            display: 'inline-block', width: 7, height: 7, borderRadius: '50%',
            background: current.accent_color,
          }}/>
          <span>{current.short_label}</span>
          <span className="mono" style={{ fontSize: 9, opacity: 0.7 }}>▾</span>
        </button>
        {isOpen && (
          <div style={{
            position: 'absolute', top: 'calc(100% + 4px)', right: 0,
            minWidth: 240, background: 'var(--bg-elev)', border: '1px solid var(--line)',
            borderRadius: 6, boxShadow: '0 8px 24px rgba(0,0,0,.12)', zIndex: 1000,
            overflow: 'hidden',
          }}>
            <div className="mono" style={{
              padding: '8px 12px', fontSize: 9, letterSpacing: '.08em', color: 'var(--ink-4)',
              borderBottom: '1px solid var(--line-2)', background: 'var(--bg-elev-2,#fafafa)',
            }}>TENANT · 行业 OS 演示</div>
            {T.list.map(function(t){
              var isCur = t.id === current.id;
              return (
                <button key={t.id}
                  onClick={function(){ T.setCurrent(t.id); }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: 10, width: '100%',
                    padding: '10px 12px', cursor: 'pointer', textAlign: 'left',
                    background: isCur ? 'var(--accent-soft)' : 'transparent',
                    border: 'none', borderBottom: '1px solid var(--line-2)',
                    fontSize: 13, fontFamily: 'inherit',
                  }}>
                  <span style={{
                    width: 24, height: 24, borderRadius: 4, flexShrink: 0,
                    background: t.accent_color, color: '#fff',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontWeight: 700, fontSize: 12,
                  }}>{(t.brand_overrides && t.brand_overrides.logo_mark) || '和'}</span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontWeight: isCur ? 600 : 500, color: 'var(--ink)' }}>{t.name_cn}</div>
                    <div className="mono" style={{ fontSize: 10, color: 'var(--ink-4)', marginTop: 2 }}>
                      {t.id} · {t.tier === 'host' ? '本店' : '演示'} · {t.mock_metrics.projects} 项目 · {t.mock_metrics.employees} 人
                    </div>
                  </div>
                  {isCur && <span style={{ color: 'var(--accent)', fontSize: 14 }}>✓</span>}
                </button>
              );
            })}
            <div style={{
              padding: '8px 12px', fontSize: 10, color: 'var(--ink-4)',
              background: 'var(--bg-elev-2,#fafafa)',
            }}>
              切换 = 演示视角变化(brand + mock 数字)· 真多租户隔离待 IT 工程化
            </div>
          </div>
        )}
      </div>
    );
  }

  // ============================================================
  // 4c. HX_AISearchTrigger · 搜索 + 问 AI 合并控件 · PR 12 A-05(jarvi 2026-05-20 拍 Y)
  //   设计:看起来像搜索框 + ✨ AI 紫色 · 点击 → 弹 ai-chip Panel(真 LLM)
  //   降级: ai-chip.jsx 未加载 → 回退灰色"搜索框占位"(不可点)
  // ============================================================
  function HX_AISearchTrigger({ searchPlaceholder }) {
    var panelState = React.useState(false);
    var panelOpen = panelState[0];
    var setPanelOpen = panelState[1];
    var hasAI = (typeof window !== 'undefined') && window.HX_AIChip && window.HX_AIChip.Panel;

    if (!hasAI) {
      // 降级:ai-chip 未加载 → 灰色搜索框占位(可视化但不可点)
      return (
        <div className="hx-ai-search" style={{
          display: 'flex', alignItems: 'center', gap: 8, padding: '6px 10px',
          border: '1px solid var(--line)', borderRadius: 6, background: 'var(--panel)',
          minWidth: 280, opacity: 0.6,
        }} title="AI 未加载 · 搜索功能待 IT 工程化">
          <HX_Icon name="search" size={14} color="var(--ink-4)"/>
          <span style={{ fontSize: 12, color: 'var(--ink-4)' }}>{searchPlaceholder}</span>
          <span className="mono hx-kbd" style={{ marginLeft: 'auto', fontSize: 10, color: 'var(--ink-4)', background: 'var(--chip)', padding: '1px 5px', borderRadius: 3 }}>⌘K</span>
        </div>
      );
    }

    // 2026-05-20 · jarvi 反馈"搜索框太长了 · 需要短一点" → minWidth 320 → 200 · placeholder 缩短
    return (
      <React.Fragment>
        <button
          className="hx-ai-search"
          onClick={function(){ setPanelOpen(true); }}
          title="问小绚 · 承当前页上下文 · 也可以搜索资源(跟右下小绚悬浮宠物同源)"
          style={{
            display: 'flex', alignItems: 'center', gap: 6, padding: '6px 10px',
            border: '1px solid var(--ai, #5b21b6)', borderRadius: 6,
            background: 'var(--ai-soft, #ede9fe)',
            color: 'var(--ai-ink, #4c1d95)',
            minWidth: 200, cursor: 'pointer',
            fontFamily: 'inherit',
          }}
        >
          <span style={{ fontSize: 14 }}>✨</span>
          <span style={{ fontSize: 12, color: 'var(--ai-ink, #4c1d95)', fontWeight: 500 }}>
            小绚 / 搜索
          </span>
          <span className="mono hx-kbd" style={{
            marginLeft: 'auto', fontSize: 10, color: 'var(--ai-ink, #4c1d95)',
            background: 'rgba(91, 33, 182, 0.12)', padding: '1px 5px', borderRadius: 3,
          }}>⌘K</span>
        </button>
        {panelOpen && React.createElement(window.HX_AIChip.Panel, { onClose: function(){ setPanelOpen(false); } })}
      </React.Fragment>
    );
  }

  // ============================================================
  // 4d. HX_NotificationBell · 通知铃铛(2026-05-22 抽出单源 · 给九宫格 index.html 也复用)
  //   演示版占位 · 正式版由 IT 对接消息推送
  //   props: hasNew (默认 true · 显示橘色 dot)
  // ============================================================
  function HX_NotificationBell({ hasNew = true }) {
    return (
      <button title="通知(演示版暂不支持)" style={{
        width: 32, height: 32, borderRadius: 6,
        border: '1px solid var(--line)', background: 'var(--panel)',
        cursor: 'not-allowed', position: 'relative', opacity: 0.45,
      }}>
        <HX_Icon name="bell" size={15} color="var(--ink-2)"/>
        {hasNew && (
          <span style={{
            position: 'absolute', top: 5, right: 6,
            width: 6, height: 6, borderRadius: '50%',
            background: 'var(--warn)',
          }}/>
        )}
      </button>
    );
  }

  // ============================================================
  // 5. Topbar(56px 高 · 顶部 sticky · 搜索 + right slot + 铃铛)
  // ============================================================
  function HX_Topbar({
    title, sub, right,
    /* 2026-05-14 · 参与者署名 · 数组 [{ name, role, did }] · jarvi 主动告知,无则不显示
     * 用途:页面/模块级"工作量体现",右对齐在搜索框前面;hover chip 看具体 did(做了啥) */
    contributors,
    searchPlaceholder = '搜索…',
    bellHasNew = true,
  }) {
    // A.2 user-menu chip 嵌入式 mount 到 right slot,不再 fixed 浮窗
    const userMenuRef = React.useRef(null);
    React.useEffect(function () {
      if (window.HecianUserMenu && typeof window.HecianUserMenu.setInline === 'function' && userMenuRef.current) {
        window.HecianUserMenu.setInline(userMenuRef.current);
      }
    }, []);

    return (
      <div className="hx-topbar" style={{
        height: 56, display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: '0 28px', borderBottom: '1px solid var(--line)',
        background: 'var(--bg-elev)', position: 'sticky', top: 0, zIndex: 5,
      }}>
        <div className="hx-topbar-title">
          <div style={{ fontSize: 15, fontWeight: 600, color: 'var(--ink)' }}>{title}</div>
          {sub && (
            <div className="mono" style={{ fontSize: 11, color: 'var(--ink-4)', marginTop: 2 }}>
              {sub}
            </div>
          )}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          {/* 参与者署名(2026-05-14)· 右对齐在搜索框前面 · hover chip 看"做了啥"
           * 数据:contributors = [{ name, role, did }] · 不传则不渲染(用户:"没告知的先不写") */}
          {contributors && contributors.length > 0 && (
            <div className="hx-topbar-contributors" style={{ display: 'flex', alignItems: 'center', gap: 6, marginRight: 4 }}>
              <span className="mono" style={{ fontSize: 9, color: 'var(--ink-4)', letterSpacing: '.08em' }}>参与</span>
              {contributors.map(c => (
                <span
                  key={c.name}
                  title={c.role ? c.role + ' · ' + (c.did || '') : c.did}
                  style={{
                    display: 'inline-flex', alignItems: 'center', gap: 4,
                    padding: '2px 8px 2px 3px', borderRadius: 999,
                    background: 'var(--accent-soft)', color: 'var(--accent-ink)',
                    fontSize: 11, fontWeight: 500, cursor: 'help',
                  }}
                >
                  <span style={{
                    width: 16, height: 16, borderRadius: '50%',
                    background: 'var(--accent)', color: '#fff',
                    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: 9, fontWeight: 700, fontFamily: 'var(--font-sans)',
                  }}>{c.name.charAt(0)}</span>
                  {c.name}
                </span>
              ))}
            </div>
          )}
          {/* OS 化阶段 2 E · Tenant 切换器 · shared/tenant.js 未加载时不渲染 */}
          <HX_TenantSwitcher />

          {/* 2026-05-20 · PR 12 A-05 jarvi 拍 Y · 搜索 + 问 AI 合并控件
             jarvi TXT 00:31:14 "搜索框应该和问 AI 结合在一起 · 不需要五个东西"
             实施: 1 个统一输入控件 · 点击触发 AI Panel · 看起来像搜索框但加 ✨ AI 图标
             ai-chip.jsx Topbar 独立 chip 不再 render(避免视觉重复) · openPanel 由本控件触发 */}
          <HX_AISearchTrigger searchPlaceholder={searchPlaceholder} />

          {/* right slot (页面级按钮) */}
          {right}

          {/* 铃铛 · 2026-05-22 抽成 HX_NotificationBell 单源组件(给九宫格 index.html 也复用) */}
          <HX_NotificationBell hasNew={bellHasNew} />

          {/* A.2 user-menu chip mount 点 (嵌入式,不再 fixed 浮窗) */}
          <span ref={userMenuRef} style={{ display: 'inline-flex', alignItems: 'center' }} />
        </div>
      </div>
    );
  }

  // ============================================================
  // 6. 暴露到全局命名空间(避免污染)
  // ============================================================
  // Tweaks(开发期调色板 / 主题切换 / 强调色)+ HX_Seg + ACCENT_MAP + applyAccent
  // 已于 2026-05-09 移除 · 演示版本不暴露 dev 工具 · 强调色固化品牌色砖红
  window.HX_Chrome = {
    Brand:           HX_Brand,
    Sidebar:         HX_Sidebar,
    MeFooter:        HX_MeFooter,
    useMe:           HX_useMe,
    Topbar:          HX_Topbar,
    Icon:            HX_Icon,
    // 2026-05-22 · 单独 export 给九宫格 index.html(非 HX_Topbar 场景)复用
    AISearchTrigger: HX_AISearchTrigger,
    NotificationBell: HX_NotificationBell,
  };

  console.info('[HX_Chrome] loaded · Brand / Sidebar / MeFooter / useMe / Topbar / Icon / AISearchTrigger / NotificationBell');
})();
