/* ops2-app.jsx — AppShell ОБИТЕЛЬ: sidebar, topbar, period, Cmd+K, роутер */
(function () {
  const { Button, Badge, IconTile, ThemeSwitcher, Card } = window.DesignSystem_9e0a09;
  const { OpsModal, CardHead } = window;
  const O = window.OPS;

  /* ---------- Cmd+K ---------- */
  function CmdK({ open, onClose, go, openProject, openClient }) {
    const [q, setQ] = React.useState('');
    const inputRef = React.useRef();
    React.useEffect(() => {if (open) {setQ('');setTimeout(() => inputRef.current && inputRef.current.focus(), 50);}}, [open]);
    if (!open) return null;
    const ql = q.toLowerCase();
    const res = [];
    if (ql) {
      O.CLIENTS.filter((c) => c.name.toLowerCase().includes(ql) || c.city.toLowerCase().includes(ql)).slice(0, 3).forEach((c) => res.push({ cat: 'Клиенты', icon: 'contact', name: c.name, sub: O.STAGE[c.stage].l, act: () => {onClose();go('clients');setTimeout(() => openClient(c.id), 30);} }));
      O.PROJECTS.filter((p) => p.client.toLowerCase().includes(ql) || p.code.toLowerCase().includes(ql) || p.address.toLowerCase().includes(ql)).slice(0, 3).forEach((p) => res.push({ cat: 'Проекты', icon: 'folder-kanban', name: p.code + ' · ' + p.client, sub: p.phase, act: () => {onClose();openProject(p.id);} }));
      O.TEAM.filter((t) => t.name.toLowerCase().includes(ql) || t.trade.toLowerCase().includes(ql)).slice(0, 4).forEach((t) => res.push({ cat: 'Люди', icon: 'user', name: t.name, sub: t.role, act: () => {onClose();go('team');} }));
    }
    let lastCat = '';
    return (
      <div className="cmdk-ov open" onClick={(e) => e.target === e.currentTarget && onClose()}>
        <div className="cmdk-modal">
          <div className="cmdk-input-row"><Ic n="search" s={18} c="var(--text-tertiary)" /><input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)} placeholder="Клиенты, проекты, узлы каталога, люди…" /><kbd className="cmdk-esc">esc</kbd></div>
          <div className="cmdk-results">
            {!ql && <div className="cmdk-hint">Ищите по клиентам, проектам, узлам каталога и людям.</div>}
            {ql && res.length === 0 && <div className="cmdk-hint">Ничего не найдено по «{q}»</div>}
            {res.map((r, i) => {
              const head = r.cat !== lastCat ? <div className="cmdk-cat" key={'c' + i}>{r.cat}</div> : null;
              lastCat = r.cat;
              return <React.Fragment key={i}>{head}<button className="cmdk-res" onClick={r.act}><Ic n={r.icon} s={15} c="var(--text-tertiary)" />{r.name}<span className="r-sub">{r.sub}</span></button></React.Fragment>;
            })}
          </div>
          <div className="cmdk-foot"><span>↑↓ навигация</span><span>↵ открыть</span><span>esc закрыть</span></div>
        </div>
      </div>);

  }

  /* ---------- Platform menu — быстрый переход в разделы платформы (Super Admin) ---------- */
  const PLATFORM_LINKS = [
    { href: 'lk/super-dash.html', icon: 'gauge', label: 'Super Admin', sub: 'Пульт платформы' },
    { href: 'lk/super-feed.html', icon: 'activity', label: 'Лента', sub: 'События по объектам' },
    { href: 'lk/super-leads.html', icon: 'user-plus', label: 'Лиды', sub: 'Входящие заявки' },
    { href: 'lk/super-users.html', icon: 'users', label: 'Пользователи', sub: 'Роли и доступ' },
    { href: 'lk/super-finance.html', icon: 'wallet', label: 'Финансы', sub: 'Оплаты и дебиторка' },
    { href: 'lk/super-escalations.html', icon: 'flag-triangle-right', label: 'Эскалации', sub: 'Всё, что требует SU' },
    { href: 'lk/super-documents.html', icon: 'file-text', label: 'Документы', sub: 'Договоры и акты' },
    { href: 'lk/super-cms.html', icon: 'layout-panel-top', label: 'CMS', sub: 'Каталог и контент' },
    { href: 'lk/super-audit.html', icon: 'shield', label: 'Аудит', sub: 'Действия и логи' },
    { href: 'lk/super-profile.html', icon: 'user-cog', label: 'Профиль', sub: 'Настройки Super Admin' },
  ];
  function PlatformMenu() {
    const [open, setOpen] = React.useState(false);
    const ref = React.useRef();
    React.useEffect(() => {
      if (!open) return;
      const off = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
      const kb = (e) => { if (e.key === 'Escape') setOpen(false); };
      document.addEventListener('mousedown', off);
      document.addEventListener('keydown', kb);
      return () => { document.removeEventListener('mousedown', off); document.removeEventListener('keydown', kb); };
    }, [open]);
    return (
      <div className="platform-menu-wrap" ref={ref}>
        <button type="button" className="platform-menu-btn" aria-expanded={open ? 'true' : 'false'} aria-haspopup="menu" onClick={() => setOpen((v) => !v)}>
          <Ic n="layout-grid" s={15} sw={2.2} />
          <span>Платформа</span>
          <Ic n={open ? 'chevron-up' : 'chevron-down'} s={13} sw={2.2} />
        </button>
        {open && (
          <div className="platform-menu" role="menu">
            <div className="platform-menu-l">Super Admin</div>
            {PLATFORM_LINKS.map((l) => (
              <a key={l.href} href={l.href} className="platform-menu-item" role="menuitem">
                <Ic n={l.icon} s={16} sw={2.1} c="var(--text-secondary)" />
                <span>
                  {l.label}
                  <span className="platform-menu-item-sub">{l.sub}</span>
                </span>
                <Ic n="arrow-up-right" s={13} sw={2.2} style={{ marginLeft: 'auto' }} />
              </a>
            ))}
            <div className="platform-menu-sep" />
            <a href="Личные кабинеты.html" className="platform-menu-item" role="menuitem">
              <Ic n="grid-2x2" s={16} sw={2.1} c="var(--text-secondary)" />
              <span>Все ЛК <span className="platform-menu-item-sub">Менеджер · Прораб · Дизайнер · Бухгалтер</span></span>
              <Ic n="arrow-up-right" s={13} sw={2.2} style={{ marginLeft: 'auto' }} />
            </a>
          </div>
        )}
      </div>
    );
  }

  /* ---------- Notify ЛК — заметное уведомление в личный кабинет ---------- */
  function NotifyLkLayer() {
    const [target, setTarget] = React.useState(null);
    const [msg, setMsg] = React.useState('');
    const [pin, setPin] = React.useState(true);
    const [sent, setSent] = React.useState(false);
    React.useEffect(() => {
      const open = (e) => {setTarget(e.detail);setMsg(e.detail && e.detail.context ? e.detail.context : '');setPin(true);setSent(false);};
      window.addEventListener('ops:notify-lk', open);
      return () => window.removeEventListener('ops:notify-lk', open);
    }, []);
    React.useEffect(() => {
      if (!target) return;
      const k = (e) => {if (e.key === 'Escape') setTarget(null);};
      document.addEventListener('keydown', k);
      return () => document.removeEventListener('keydown', k);
    }, [target]);
    if (!target) return null;
    const send = () => {setSent(true);setTimeout(() => setTarget(null), 1400);};
    return (
      <div className="nlk-ov" onClick={(e) => e.target === e.currentTarget && setTarget(null)}>
        <div className="nlk-modal">
          {sent ?
          <div className="nlk-success">
              <span className="nlk-success-ic"><Ic n="check" s={26} sw={2.6} c="#fff" /></span>
              <div className="nlk-success-t">Уведомление отправлено</div>
              <div className="nlk-success-s">{target.name} увидит его при входе в ЛК</div>
            </div> :

          <React.Fragment>
              <div className="nlk-head">
                <span className="nlk-ava" style={{ background: target.avaC || 'var(--ink)' }}>{target.ava || '?'}</span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="nlk-title">Уведомление в ЛК</div>
                  <div className="nlk-sub">{target.name}{target.object ? ' · ' + target.object : ''}</div>
                </div>
                <button className="pd-close" onClick={() => setTarget(null)}><Ic n="x" s={16} /></button>
              </div>
              <div className="nlk-body">
                <div className="nlk-label">Текст уведомления</div>
                <textarea className="nlk-text" rows={3} value={msg} onChange={(e) => setMsg(e.target.value)} placeholder="Что должен увидеть сотрудник…" />
                <label className="nlk-pin" onClick={() => setPin((v) => !v)}>
                  <span className={'nlk-check' + (pin ? ' on' : '')}>{pin && <Ic n="check" s={12} sw={3} c="#fff" />}</span>
                  Показать большим баннером поверх ЛК — чтобы точно обратили внимание
                </label>
                <div className="nlk-preview-l">Превью в ЛК сотрудника</div>
                <div className={'nlk-preview' + (pin ? ' big' : '')}>
                  <span className="nlk-preview-ic"><Ic n="bell-ring" s={pin ? 20 : 16} sw={2.2} /></span>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div className="nlk-preview-from">От Super Admin · сейчас</div>
                    <div className="nlk-preview-msg">{msg || 'Текст уведомления появится здесь'}</div>
                  </div>
                </div>
              </div>
              <div className="nlk-foot">
                <Button variant="ghost" size="sm" onClick={() => setTarget(null)}>Отмена</Button>
                <Button variant="primary" size="sm" iconLeft={<Ic n="send" s={14} />} onClick={send} disabled={!msg.trim()}>Отправить в ЛК</Button>
              </div>
            </React.Fragment>
          }
        </div>
      </div>);
  }

  /* ---------- Zone hero band (Обзор only) ---------- */
  const HERO_DAYS = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс'];
  const HERO_BARS = [42, 58, 35, 72, 88, 24, 18];

  function ZoneHeroBand({ period, prevLabel, go }) {
    const f = O.FUNNEL[period];
    const upcoming = O.PROJECTS.filter((p) => p.stageDueDays != null);
    const dueSoon = upcoming.filter((p) => p.stageDueDays <= 2).length;
    const critical = O.EXCEPTIONS.filter((x) => x.severity === 'high').length;
    const winPct = Math.round((f.contracts / Math.max(f.leads, 1)) * 100);
    const scrollTo = (id) => {
      const el = document.getElementById(id);
      if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    return (
      <div className="zone-hero">
        <button type="button" className="hero-card hero-card--blue" onClick={() => go('analytics')}>
          <div className="hero-card-top">
            <span className="hero-card-label">Воронка</span>
            <Ic n="chevron-right" s={16} c="currentColor" style={{ opacity: 0.45, flexShrink: 0 }} />
          </div>
          <div className="hero-card-value">{f.contracts}</div>
          <div className="hero-card-sub">договоров · win {winPct}% · {f.winrateDelta >= 0 ? '+' : ''}{f.winrateDelta}% к {prevLabel}</div>
          <div className="hero-card-progress"><div className="hero-card-progress-fill" style={{ width: winPct + '%' }} /></div>
        </button>

        <button type="button" className="hero-card hero-card--peach" onClick={() => scrollTo('ov-pipeline')}>
          <div className="hero-card-top">
            <span className="hero-card-label">Скоро сдача этапа</span>
            <Ic n="chevron-right" s={16} c="currentColor" style={{ opacity: 0.45, flexShrink: 0 }} />
          </div>
          <div className="hero-card-value">{upcoming.length}</div>
          <div className={'hero-card-sub' + (dueSoon ? ' danger' : '')}>{dueSoon ? dueSoon + ' критичных (≤2 дн)' : 'все в комфортном окне'}</div>
          <div className="hero-card-progress"><div className="hero-card-progress-fill" style={{ width: Math.min(100, upcoming.length * 12) + '%' }} /></div>
        </button>

        <button type="button" className="hero-card hero-card--lavender" onClick={() => scrollTo('ov-exceptions')}>
          <div className="hero-card-top">
            <span className="hero-card-label">Требует внимания</span>
            <Ic n="chevron-right" s={16} c="currentColor" style={{ opacity: 0.45, flexShrink: 0 }} />
          </div>
          <div className="hero-card-value">{critical}</div>
          <div className="hero-card-sub danger">{O.EXCEPTIONS.length} всего · {critical} критичных</div>
          <div className="hero-card-progress"><div className="hero-card-progress-fill" style={{ width: Math.min(100, critical * 25) + '%' }} /></div>
        </button>

        <div className="hero-card hero-card--chart" aria-label="Активность за неделю">
          <div className="hero-chart-label">Активность за неделю</div>
          <div className="hero-chart-bars">
            {HERO_BARS.map((h, i) =>
            <div key={i} className={'hero-chart-bar' + (i === 4 ? ' active' : '')} style={{ height: h + '%' }} title={HERO_DAYS[i]} />
            )}
          </div>
        </div>
      </div>);
  }

  /* HUB-навигация: якорные секции #zone-*; боковой рельс скрыт — только плавающее меню (как ЛК hub-floatnav) */
  const HUB_ZONES = ['overview', 'workspace', 'feed', 'clients', 'team', 'catalog', 'site', 'analytics'];

  function scrollToZone(id) {
    const el = document.getElementById('zone-' + id);
    if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    try { history.replaceState(null, '', '#' + id); } catch (e) {}
  }

  /* ---------- плавающее меню-пилюля снизу (единственная навигация по разделам) ---------- */
  function FloatNav({ initialActive }) {
    const hubZones = HUB_ZONES.map((id) => O.ZONES.find((z) => z.id === id)).filter(Boolean);
    return (
      <nav className="lk-floatnav" aria-label="Разделы">
        {hubZones.map((z) =>
          <a key={z.id} href={'#' + z.id} data-zone={z.id} className={'lk-floatnav-item' + (z.id === initialActive ? ' active' : '')}
            onClick={(e) => { e.preventDefault(); scrollToZone(z.id); }}>
            {z.label}{z.badge ? <span className="lk-floatnav-badge">{z.badge}</span> : null}
          </a>
        )}
      </nav>
    );
  }

  /* Зона раздаёт виджетам контекст перемещения. Сетка внутри (.ov-wgt-grid,
     .ov-zone) уже grid, поэтому CSS order работает без изменения вёрстки. */
  function OpsZone({ id, children }) {
    const arrange = window.useArrangeZone ? window.useArrangeZone(id) : null;
    const Ctx = window.LKArrangeCtx;
    const body = (
      <section id={'zone-' + id} data-zone-anchor={id} className="ops-sec">
        {children}
      </section>
    );
    return Ctx ? <Ctx.Provider value={arrange}>{body}</Ctx.Provider> : body;
  }

  function App() {
    const initialZone = React.useMemo(() => {
      const hash = (location.hash || '').replace('#', '');
      if (HUB_ZONES.includes(hash)) return hash;
      const q = new URLSearchParams(location.search).get('zone');
      return HUB_ZONES.includes(q) ? q : 'overview';
    }, []);
    const [period, setPeriod] = React.useState(() => localStorage.getItem('ops_period') || 'week');
    const crumbRef = React.useRef(null);
    const [clientsSubTab, setClientsSubTab] = React.useState(() => {const t = new URLSearchParams(location.search).get('tab');return t === 'projects' || t === 'clients' ? t : 'kartoteka';});
    const [drawerProject, setDrawerProject] = React.useState(null);
    const [cmdkOpen, setCmdkOpen] = React.useState(false);
    const [clientCardName, setClientCardName] = React.useState(null);
    const [catalogOpen, setCatalogOpen] = React.useState(false);
    const initialTheme = React.useMemo(() => {
      try { return localStorage.getItem('remontpro_theme') === 'dark' ? 'dark' : 'light'; } catch (e) { return 'light'; }
    }, []);
    const persistTheme = (mode) => {
      try { localStorage.setItem('remontpro_theme', mode); } catch (e) {}
      document.documentElement.setAttribute('data-theme', mode);
      document.documentElement.classList.toggle('dark', mode === 'dark');
    };

    React.useEffect(() => {localStorage.setItem('ops_period', period);}, [period]);

    React.useEffect(() => {
      const h = (e) => {
        if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {e.preventDefault();setCmdkOpen(true);}
        if (e.key === 'Escape') {setCmdkOpen(false);setDrawerProject(null);}
      };
      document.addEventListener('keydown', h);
      return () => document.removeEventListener('keydown', h);
    }, []);

    /* scroll-spy без ре-рендера зон: активный пункт подсвечивается прямой правкой DOM
       (setState на каждый scroll перерисовывал бы все тяжёлые зоны и ломал lucide-иконки). */
    React.useEffect(() => {
      const content = document.querySelector('.content');
      if (!content) return undefined;
      let raf = 0;
      const labelOf = (id) => { const z = O.ZONES.find((x) => x.id === id); return z ? z.label : ''; };
      const apply = (cur) => {
        document.querySelectorAll('[data-zone]').forEach((el) => el.classList.toggle('active', el.getAttribute('data-zone') === cur));
        if (crumbRef.current) crumbRef.current.textContent = labelOf(cur);
        try { history.replaceState(null, '', '#' + cur); } catch (e) {}
      };
      const compute = () => {
        raf = 0;
        const secs = Array.from(document.querySelectorAll('[data-zone-anchor]')).filter((s) => s.getClientRects().length > 0);
        if (!secs.length) return;
        let cur;
        if (content.scrollTop + content.clientHeight >= content.scrollHeight - 4) {
          cur = secs[secs.length - 1].getAttribute('data-zone-anchor');
        } else {
          const refY = content.getBoundingClientRect().top + 64;
          cur = secs[0].getAttribute('data-zone-anchor');
          let bestTop = -Infinity;
          for (const s of secs) {
            const top = s.getBoundingClientRect().top;
            if (top - refY <= 1 && top > bestTop) { bestTop = top; cur = s.getAttribute('data-zone-anchor'); }
          }
        }
        apply(cur);
      };
      const onScroll = () => { if (!raf) raf = requestAnimationFrame(compute); };
      compute();
      content.addEventListener('scroll', onScroll, { passive: true });
      window.addEventListener('resize', onScroll);
      const timers = [300, 900, 1600].map((ms) => setTimeout(compute, ms));
      if (initialZone && initialZone !== 'overview') {
        setTimeout(() => scrollToZone(initialZone), 120);
      }
      return () => { content.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); timers.forEach(clearTimeout); if (raf) cancelAnimationFrame(raf); };
    }, []);

    const prevLabel = O.PERIODS.find((p) => p.id === period).prev;
    const go = (z) => scrollToZone(z);
    const openProject = (id) => setDrawerProject(id);
    const initialObj = O.ZONES.find((z) => z.id === initialZone);

    return (
      <React.Fragment>
        <div className="ops-bg" aria-hidden="true"><span className="b1" /><span className="b2" /><span className="b3" /></div>
        <div className="app-shell hub hub-floatnav">
          <div className="main">
            <header className="topbar">
              <a href="lk/super-dash.html" className="topbar-brand" title="Super Admin · платформа">
                <span className="brand-mark"><Ic n="command" s={16} c="#fff" /></span>
                <span className="topbar-brand-name">Операционный центр</span>
              </a>
              <div className="crumbs">
                <span className="sep">/</span>
                <span className="cur" ref={crumbRef}>{initialObj ? initialObj.label : 'Обзор'}</span>
              </div>
              <div style={{ flex: 1 }} />
              <div className="header-utils">
                <div className="period-seg">
                  {O.PERIODS.map((p) => <button key={p.id} type="button" className={period === p.id ? 'on' : ''} onClick={() => setPeriod(p.id)}>{p.label}</button>)}
                </div>
                <ThemeSwitcher modes={['light', 'dark']} display="icons" defaultMode={initialTheme} persist={false} onChange={persistTheme} />
                <button type="button" className="cmdk-btn" onClick={() => setCmdkOpen(true)}><Ic n="search" s={15} /><span>Поиск</span><span className="kbd"><kbd>⌘</kbd><kbd>K</kbd></span></button>
                <PlatformMenu />
                <div className="topbar-user"><span className="topbar-ava">СА</span><span className="topbar-user-name">Super Admin</span><Ic n="chevron-down" s={14} sw={2.2} c="var(--text-tertiary)" /></div>
              </div>
            </header>

            <div className="main-canvas">
              <div className="content">
                <div className="content-inner">
                  <section id="zone-overview" data-zone-anchor="overview" className="ops-sec">
                    <div className="zone-hero-wrap" style={{ marginBottom: 10 }}>
                      <ZoneHeroBand period={period} prevLabel={prevLabel} go={go} />
                    </div>
                  </section>

                  {window.OpsField ? (
                    <window.OpsField
                      preset="ops-center"
                      storeKey="ops_center_v4"
                      barMode="hub"
                      ctx={{
                        period,
                        prevLabel,
                        go,
                        openProject,
                        openClient: () => { setClientsSubTab('clients'); scrollToZone('clients'); },
                        clientsSubTab,
                        setClientsSubTab,
                        onClientCardOpen: setClientCardName,
                        onCatalogOpen: () => setCatalogOpen(true),
                        head: (zone) => <PageHead zone={zone} />,
                      }}
                    />
                  ) : null}
                </div>
              </div>
            </div>
          </div>

          <FloatNav initialActive={initialZone} />
        </div>

        {drawerProject && <ProjectDrawer id={drawerProject} onClose={() => setDrawerProject(null)} />}
        <CmdK open={cmdkOpen} onClose={() => setCmdkOpen(false)} go={(z) => scrollToZone(z, go)} openProject={openProject} openClient={(id) => {/* clients zone manages its own selection via cmdk: navigate */}} />
        <NotifyLkLayer />
        {window.LKArrangePanel ? <window.LKArrangePanel /> : null}
        {catalogOpen && (
          <OpsModal title="Конструктор проектов" subtitle="Широкий режим для работы со сметной базой" onClose={() => setCatalogOpen(false)} wide>
            <div className="ops-catalog-modal-frame">
              <iframe src="Конструктор%20проектов.html?v=20260804-strip" title="Конструктор проектов" loading="lazy" />
            </div>
          </OpsModal>
        )}
      </React.Fragment>);

  }

  /* ---------- page head per zone ---------- */
  function PageHead({ zone }) {
    const heads = {
      overview: ['Command center · вся организация', 'Обзор', 'Операционный пульт: воронка продаж, ближайшие сдачи этапов, сигналы «готовить счёт», новые и завершающиеся проекты, проблемные точки. Деньги и сквозная аналитика — в разделе «Аналитика».'],
      workspace: ['Настраиваемое рабочее поле', 'Поле', 'Виджеты под ваш сценарий: перетаскивайте карточки за ручку, меняйте размер S/M/L, регулируйте плотность (Ctrl + / − / 0), переключайте композиции. Любая карточка разворачивается в поп-ап с максимумом данных. Раскладка сохраняется автоматически.'],
      feed: ['Сквозная хронология · вся организация', 'Лента', 'Единый поток событий проектов: CRM, сметы, финансы, работы, материалы, документы, оркестрация и система. Фильтр по категориям, drill-down в проект, отдельная полоса «Требует разбора» для остановленных цепочек.'],
      clients: ['Org-wide · клиент + его проекты', 'Клиенты', 'Информативный реестр клиентов, карточки заказов и аналитическая картотека для таргетинга. Не дублирует CRM-воронку менеджера.'],
      team: ['Реестр · ' + O.TEAM.length + ' человек', 'Команда', 'Все люди организации по ролям. Drill-down открывает нативный ЛК сотрудника с баннером «Просмотр от имени Super».'],
      analytics: ['Org-wide реестры · сквозные агрегаты', 'Аналитика', 'Воронка, экономика (оплаты, дебиторка, структура затрат), заметки, качество, люди и часы по всей организации.'],
      catalog: ['Иерархия работ · сметная база', 'Каталог', 'Конструктор проектов: категории, узлы и позиции сметной базы. Открывается встроенным модулем; полноэкранный режим — по ссылке в конструкторе.'],
      site: ['CMS · публичный сайт', 'Сайт', 'Контент-блоки лендинга, публичный калькулятор → пул лидов, SEO-метаданные и метрики трафика. Прототип shell — production CMS в KSH-226.']
    };
    const h = heads[zone];
    if (!h) return null;
    return (
      <div className="ph">
        <div className="ph-eyebrow">{h[0]}</div>
        <h1 className="ph-h1">{h[1]}</h1>
        <p className="ph-lede">{h[2]}</p>
      </div>);

  }

  /* ---------- mount with bundle-ready guard ---------- */
  const REQUIRED = ['Button', 'Card', 'Badge', 'IconTile', 'Input', 'StatWidget', 'AvatarStack'];
  function nsReady() {const ns = window.DesignSystem_9e0a09 || {};return REQUIRED.every((k) => typeof ns[k] === 'function');}
  const root = ReactDOM.createRoot(document.getElementById('root'));
  let tries = 0;
  const rootEl = document.getElementById('root');
  function mount() {
    if (nsReady() && window.OpsWorkspace && window.Overview && window.ClientsZone && window.TeamZone && window.AnalyticsZone && window.FeedZone && window.SiteZone) { root.render(<App />); return; }
    if (tries++ < 80) { setTimeout(mount, 100); return; }
    rootEl.innerHTML = '<div style="padding:32px;font:14px Manrope,system-ui;color:#1a1714;max-width:560px"><strong>Не удалось загрузить Операционный центр</strong><p style="opacity:.75;margin-top:8px">Откройте через <code>http://localhost:4173</code> (не file://). Проверьте консоль и что <code>ds-shim.js</code> подключён после bundle.</p></div>';
  }
  mount();
})();