// Shared UI primitives + tokens for the Unblock Research ecosystem
// Aesthetic: scientific instrument × terminal. Off-white paper, near-black ink,
// acidic chartreuse accent. Inter Tight + JetBrains Mono + Instrument Serif.

const UB = {
  paper: '#F2F0E8',
  paperDeep: '#E9E6DC',
  ink: '#0E0E10',
  ink2: '#26252A',
  ink3: '#5B5A60',
  ink4: '#8A8990',
  rule: '#1A1A1C',
  rule2: 'rgba(14,14,16,0.12)',
  rule3: 'rgba(14,14,16,0.06)',
  accent: 'oklch(0.86 0.19 118)', // acidic chartreuse
  accentDim: 'oklch(0.86 0.19 118 / 0.18)',
  ok: 'oklch(0.72 0.13 155)',
  warn: 'oklch(0.78 0.16 70)',
  bad: 'oklch(0.62 0.20 25)',
  info: 'oklch(0.66 0.13 240)',
  serif: '"Instrument Serif", "Times New Roman", serif',
  sans: '"Inter Tight", "Inter", system-ui, sans-serif',
  mono: '"JetBrains Mono", "SF Mono", ui-monospace, monospace'
};

// ---- Layout: a uniform 1280×800 dapp frame ----------------------------------
// Auto-fits content: if children render taller than 800px, the inner stack is
// scaled down (origin top-left) so nothing spills out of the frame. Reads tweaks
// from window.__UB_TWEAKS for global overrides (density, font scale, dark, accent).
function Dapp({ children, dark: darkProp = false, frameless = false }) {
  const innerRef = React.useRef(null);
  const [scale, setScale] = React.useState(1);
  const [tw, setTw] = React.useState(() => window.__UB_TWEAKS || {});
  React.useEffect(() => {
    const onTw = () => {
      const t = window.__UB_TWEAKS || {};
      if (t.accent && t.accent !== UB.accent) {
        UB.accent = t.accent;
        // recompute accentDim
        UB.accentDim = t.accent + '2E'; // ~18% alpha hex suffix
      }
      setTw({ ...t });
    };
    window.addEventListener('__ub_tweaks', onTw);
    onTw();
    return () => window.removeEventListener('__ub_tweaks', onTw);
  }, []);
  React.useLayoutEffect(() => {
    const el = innerRef.current;
    if (!el) return;
    const measure = () => {
      if (tw.fitOverflow === false) { setScale(1); return; }
      const sh = el.scrollHeight, sw = el.scrollWidth;
      const sy = sh > 800 ? 800 / sh : 1;
      const sx = sw > 1280 ? 1280 / sw : 1;
      const next = Math.min(sx, sy, 1);
      setScale(next < 0.999 ? next : 1);
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(el);
    return () => ro.disconnect();
  }, [children, tw.fontScale, tw.density, tw.fitOverflow, tw.compactNav]);

  const dark = tw.dark != null ? tw.dark : darkProp;
  const fontScale = tw.fontScale || 1;
  return (
    <div style={{
      width: 1280, height: 800,
      background: dark ? UB.ink : UB.paper,
      color: dark ? '#EAE9E2' : UB.ink,
      fontFamily: UB.sans,
      fontSize: 13 * fontScale, lineHeight: 1.45,
      position: 'relative',
      letterSpacing: '-0.005em',
      WebkitFontSmoothing: 'antialiased',
      overflow: 'hidden'
    }}>
      <div ref={innerRef} style={{
        width: 1280,
        minHeight: 800,
        display: 'flex', flexDirection: 'column',
        transform: scale === 1 ? 'none' : `scale(${scale})`,
        transformOrigin: 'top left',
        // When scaled, widen so the visual fills the frame (height auto-fills via minHeight).
        ...(scale !== 1 ? { width: 1280 / scale, minHeight: 800 / scale } : null)
      }}>
        {children}
      </div>
    </div>);

}

// Top chrome: tiny system bar + dapp title bar
function ChromeBar({ dapp, code, status = 'ONLINE', user = 'malcolm.harriot.eth', balance = '12,840 UBR' }) {
  return (
    <div style={{
      borderBottom: `1px solid ${UB.rule}`,
      background: UB.ink, color: UB.paper,
      display: 'flex', alignItems: 'stretch', height: 38, fontFamily: UB.mono, fontSize: 11,
      flexShrink: 0
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '0 14px', borderRight: '1px solid rgba(255,255,255,.08)' }}>
        <UBLogo size={14} mono />
        <span style={{ letterSpacing: '0.12em', fontWeight: 600 }}>UNBLOCK</span>
        <span style={{ color: 'rgba(255,255,255,.4)' }}>/</span>
        <span style={{ color: UB.accent, fontWeight: 600 }}>{dapp}</span>
        <span style={{ color: 'rgba(255,255,255,.3)', marginLeft: 4 }}>{code}</span>
      </div>
      <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 24, color: 'rgba(255,255,255,.55)' }}>
        <span>UB-CHAIN · BLOCK 8,419,206</span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
          <Pulse /> {status}
        </span>
        <span>GAS 0.00021 UBR</span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '0 14px', borderLeft: '1px solid rgba(255,255,255,.08)' }}>
        <span style={{ color: UB.accent }}>◆ {balance}</span>
        <span style={{ color: 'rgba(255,255,255,.5)' }}>{user}</span>
      </div>
    </div>);

}

function Pulse() {
  return <span style={{ width: 6, height: 6, background: UB.accent, borderRadius: 1, boxShadow: `0 0 0 2px ${UB.accentDim}` }} />;
}

// Two-column layout used by most dapps: thin icon rail + expandable context rail + workspace.
// Mendeley-style: clicking an icon swaps which context rail is shown; clicking the active
// icon collapses the context rail (icon-only mode).
const __DEFAULT_DAPP_SECTIONS = [
  { id: 'workspace', icon: 'home',    label: 'Workspace' },
  { id: 'identity',  icon: 'user',    label: 'Identity' },
  { id: 'lab',       icon: 'flask',   label: 'Lab & Pipeline' },
  { id: 'markets',   icon: 'cube',    label: 'Markets' },
  { id: 'infra',     icon: 'cpu',     label: 'Infrastructure' },
  { id: 'admin',     icon: 'shield',  label: 'Admin' },
];

function IconRail({ sections, active, onPick, onAdd, dark = false }) {
  const railBtn = (children, opts = {}) =>
    <button {...opts} style={{
      appearance: 'none', border: 0, background: 'transparent',
      width: '100%', height: 38,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      cursor: 'pointer', position: 'relative',
      ...(opts.style || {})
    }}>{children}</button>;
  return (
    <div style={{
      width: 52, flexShrink: 0,
      borderRight: `1px solid ${dark ? 'rgba(255,255,255,.06)' : UB.rule2}`,
      background: dark ? '#0B0B0C' : UB.ink,
      display: 'flex', flexDirection: 'column',
      padding: '10px 0',
      gap: 2
    }}>
      {sections.map((s) => {
        const isActive = s.id === active;
        return railBtn(
          <>
            {isActive && <span style={{
              position: 'absolute', left: 0, top: 6, bottom: 6, width: 2,
              background: UB.accent
            }} />}
            <span style={{ display: 'inline-flex' }}>{Icon[s.icon] || Icon.cube}</span>
          </>,
          {
            key: s.id,
            onClick: () => onPick(s.id),
            title: s.label,
            style: { color: isActive ? UB.accent : 'rgba(255,255,255,.55)', transition: 'color .12s' }
          }
        );
      })}
      {/* divider + add */}
      <div style={{ height: 1, background: 'rgba(255,255,255,.08)', margin: '8px 12px' }} />
      {railBtn(
        <span style={{
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          width: 22, height: 22, border: '1px dashed rgba(255,255,255,.35)',
          color: 'rgba(255,255,255,.7)'
        }}>{Icon.plus}</span>,
        { title: 'Add dapp · open marketplace', onClick: () => onAdd && onAdd() }
      )}
      <div style={{ flex: 1 }} />
      {railBtn(Icon.bell, { title: 'Notifications', style: { color: 'rgba(255,255,255,.4)' } })}
      {railBtn(Icon.cog, { title: 'Settings', style: { color: 'rgba(255,255,255,.4)' } })}
    </div>
  );
}

function Layout({ rail, sections, defaultSection, sectionRails, children, dark = false }) {
  const sx = sections || __DEFAULT_DAPP_SECTIONS;
  const initial = defaultSection || sx[0]?.id;
  const [activeSection, setActiveSection] = React.useState(initial);
  const [expanded, setExpanded] = React.useState(true);
  const [marketOpen, setMarketOpen] = React.useState(false);
  const tw = (typeof window !== 'undefined' && window.__UB_TWEAKS) || {};

  // Resolve which secondary rail content to show.
  // - If `sectionRails` provided, use sectionRails[activeSection]; clicking unmapped
  //   icons collapses the rail.
  // - Else (legacy `rail`), the same content is shown regardless of which icon is
  //   active — the icon highlight just shifts. Clicking the active icon collapses.
  const secondary = sectionRails ? sectionRails[activeSection] : rail;

  const pick = (id) => {
    if (id === activeSection) {
      setExpanded((e) => !e);
    } else {
      setActiveSection(id);
      setExpanded(true);
    }
  };
  const showRail = expanded && secondary;
  const compact = !!tw.compactNav;
  const railW = compact ? 168 : 200;

  return (
    <div style={{ flex: 1, display: 'flex', minHeight: 0, background: dark ? UB.ink : UB.paper, position: 'relative' }}>
      <IconRail sections={sx} active={activeSection} onPick={pick} dark={dark} onAdd={() => setMarketOpen(true)} />
      {showRail && (
        <div style={{
          width: railW, flexShrink: 0,
          borderRight: `1px solid ${dark ? 'rgba(255,255,255,.08)' : UB.rule2}`,
          background: dark ? '#161617' : UB.paperDeep,
          padding: '14px 0',
          fontSize: 12,
          overflow: 'hidden'
        }}>
          {secondary}
        </div>
      )}
      <div style={{ flex: 1, minWidth: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
        {children}
      </div>
      {marketOpen && <DappMarketplace onClose={() => setMarketOpen(false)} />}
    </div>);

}

// Sidebar nav item
function NavGroup({ label, children }) {
  return (
    <div style={{ marginBottom: 14 }}>
      <div style={{
        padding: '0 16px 6px',
        fontFamily: UB.mono, fontSize: 9, letterSpacing: '0.18em',
        color: UB.ink4, textTransform: 'uppercase'
      }}>{label}</div>
      {children}
    </div>);

}

function NavItem({ icon, label, badge, active, dark = false }) {
  const baseColor = dark ? '#A8A7A0' : UB.ink2;
  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '7px 16px',
      color: active ? dark ? UB.accent : UB.ink : baseColor,
      background: active ? dark ? 'rgba(255,255,255,.04)' : 'rgba(14,14,16,.05)' : 'transparent',
      borderLeft: active ? `2px solid ${UB.accent}` : '2px solid transparent',
      cursor: 'pointer',
      fontWeight: active ? 600 : 450
    }}>
      <span style={{ width: 14, display: 'inline-flex', justifyContent: 'center', opacity: active ? 1 : 0.7 }}>{icon}</span>
      <span style={{ flex: 1 }}>{label}</span>
      {badge != null &&
      <span style={{
        fontFamily: UB.mono, fontSize: 9,
        padding: '2px 5px',
        background: active ? UB.accent : dark ? 'rgba(255,255,255,.08)' : 'rgba(0,0,0,.07)',
        color: active ? UB.ink : dark ? '#999' : UB.ink3,
        letterSpacing: '0.05em'
      }}>{badge}</span>
      }
    </div>);

}

// Page header inside the workspace
function PageHead({ eyebrow, title, sub, actions }) {
  return (
    <div style={{
      padding: '20px 28px 18px',
      borderBottom: `1px solid ${UB.rule2}`,
      display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', gap: 24,
      flexShrink: 0
    }}>
      <div>
        {eyebrow &&
        <div style={{
          fontFamily: UB.mono, fontSize: 10, letterSpacing: '0.2em',
          color: UB.ink3, textTransform: 'uppercase', marginBottom: 6
        }}>{eyebrow}</div>
        }
        <h1 style={{
          margin: 0, fontFamily: UB.serif, fontWeight: 400,
          fontSize: 32, lineHeight: 1.05, letterSpacing: '-0.02em'
        }}>{title}</h1>
        {sub && <div style={{ marginTop: 8, color: UB.ink3, maxWidth: 640 }}>{sub}</div>}
      </div>
      {actions && <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>{actions}</div>}
    </div>);

}

// Buttons
function Btn({ children, primary, ghost, small, icon, onClick, style, dark = false }) {
  const h = small ? 26 : 32;
  return (
    <button onClick={onClick} style={{
        height: h, padding: small ? '0 10px' : '0 14px',
        fontFamily: UB.sans, fontSize: small ? 11 : 12, fontWeight: 500,
        border: primary ? 'none' : `1px solid ${dark ? 'rgba(255,255,255,.18)' : UB.rule}`,
        background: primary ? UB.accent : ghost ? 'transparent' : dark ? 'rgba(255,255,255,.06)' : '#fff',
        color: primary ? UB.ink : dark ? '#EAE9E2' : UB.ink,
        cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', gap: 6,
        letterSpacing: '0.005em',
        whiteSpace: 'nowrap',
        flexShrink: 0,
        ...style
      }}>
      {icon && <span style={{ display: 'inline-flex' }}>{icon}</span>}
      {children}
    </button>);

}

// Pill / tag
function Tag({ children, tone = 'default', mono = true, style }) {
  const tones = {
    default: { bg: 'rgba(0,0,0,.05)', fg: UB.ink2 },
    accent: { bg: UB.accentDim, fg: UB.ink },
    ok: { bg: 'oklch(0.72 0.13 155 / 0.15)', fg: 'oklch(0.42 0.13 155)' },
    warn: { bg: 'oklch(0.78 0.16 70 / 0.18)', fg: 'oklch(0.45 0.16 70)' },
    bad: { bg: 'oklch(0.62 0.20 25 / 0.15)', fg: 'oklch(0.45 0.18 25)' },
    info: { bg: 'oklch(0.66 0.13 240 / 0.15)', fg: 'oklch(0.40 0.13 240)' },
    ink: { bg: UB.ink, fg: UB.paper }
  };
  const t = tones[tone] || tones.default;
  return (
    <span style={{
      fontFamily: mono ? UB.mono : UB.sans,
      fontSize: 10, letterSpacing: mono ? '0.06em' : '0',
      padding: '2px 7px',
      background: t.bg, color: t.fg,
      textTransform: mono ? 'uppercase' : 'none',
      whiteSpace: 'nowrap',
      ...style
    }}>{children}</span>);

}

// Card / panel
function Card({ title, code, action, children, style, dark = false, pad = true }) {
  return (
    <div style={{
      border: `1px solid ${dark ? 'rgba(255,255,255,.10)' : UB.rule2}`,
      background: dark ? '#161617' : '#fff',
      display: 'flex', flexDirection: 'column',
      ...style
    }}>
      {(title || action) &&
      <div style={{
        padding: '10px 14px',
        borderBottom: `1px solid ${dark ? 'rgba(255,255,255,.06)' : UB.rule2}`,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10
      }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 8 }}>
            <span style={{ fontWeight: 600, fontSize: 12, letterSpacing: '-0.005em' }}>{title}</span>
            {code && <span style={{ fontFamily: UB.mono, fontSize: 10, color: UB.ink4 }}>{code}</span>}
          </div>
          {action}
        </div>
      }
      <div style={{ padding: pad ? 14 : 0, flex: 1, minHeight: 0 }}>
        {children}
      </div>
    </div>);

}

// Stat block (large mono number)
function Stat({ label, value, delta, mono = true, sub, large }) {
  return (
    <div style={{ minWidth: 0 }}>
      <div style={{
        fontFamily: UB.mono, fontSize: 9, letterSpacing: '0.18em',
        color: UB.ink3, textTransform: 'uppercase', marginBottom: 4
      }}>{label}</div>
      <div style={{
          fontFamily: mono ? UB.mono : UB.serif,
          fontWeight: mono ? 500 : 400,
          fontSize: large ? 28 : 17, letterSpacing: '-0.02em',
          lineHeight: 1, color: UB.ink,
          whiteSpace: 'nowrap',
          overflow: 'hidden',
          textOverflow: 'ellipsis'
        }}>{value}</div>
      {(delta || sub) &&
      <div style={{ marginTop: 4, display: 'flex', alignItems: 'center', gap: 6, fontSize: 11, color: UB.ink3 }}>
          {delta && <span style={{ color: delta.startsWith('-') ? UB.bad : UB.ok, fontFamily: UB.mono }}>{delta}</span>}
          {sub && <span>{sub}</span>}
        </div>
      }
    </div>);

}

// Mono row used for ledger-like data
function Row({ cells, head, dark = false }) {
  return (
    <div style={{
      display: 'grid',
      gridTemplateColumns: cells.map((c) => c.w || '1fr').join(' '),
      gap: 12, alignItems: 'center',
      padding: '8px 14px',
      borderBottom: `1px solid ${dark ? 'rgba(255,255,255,.05)' : UB.rule3}`,
      fontFamily: UB.mono, fontSize: 11,
      color: head ? UB.ink3 : dark ? '#D9D8D2' : UB.ink2,
      textTransform: head ? 'uppercase' : 'none',
      letterSpacing: head ? '0.12em' : '0',
      fontSize: head ? 9 : 11
    }}>
      {cells.map((c, i) => <div key={i} style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: c.wrap ? 'normal' : 'nowrap', ...c.style }}>{c.v}</div>)}
    </div>);

}

// Decorative bracket "registry corner"
function Corners({ children, style }) {
  const C = (pos) =>
  <span style={{
    position: 'absolute', width: 10, height: 10,
    borderColor: UB.ink, borderStyle: 'solid', borderWidth: 0,
    ...pos
  }} />;

  return (
    <div style={{ position: 'relative', padding: 14, ...style }}>
      {C({ top: 0, left: 0, borderTopWidth: 1, borderLeftWidth: 1 })}
      {C({ top: 0, right: 0, borderTopWidth: 1, borderRightWidth: 1 })}
      {C({ bottom: 0, left: 0, borderBottomWidth: 1, borderLeftWidth: 1 })}
      {C({ bottom: 0, right: 0, borderBottomWidth: 1, borderRightWidth: 1 })}
      {children}
    </div>);

}

// Logo: a simple ⊞ "unblock" mark — three connected nodes inside a square
function UBLogo({ size = 18, mono = false, color }) {
  const c = color || (mono ? 'currentColor' : UB.ink);
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="1.5">
      <rect x="2.5" y="2.5" width="19" height="19" />
      <circle cx="7.5" cy="7.5" r="1.6" fill={c} stroke="none" />
      <circle cx="16.5" cy="7.5" r="1.6" fill={c} stroke="none" />
      <circle cx="12" cy="16.5" r="1.6" fill={c} stroke="none" />
      <path d="M7.5 7.5 L16.5 7.5 M7.5 7.5 L12 16.5 M16.5 7.5 L12 16.5" />
    </svg>);

}

// Tiny inline SVG icons (16×16). Stroke-based, currentColor.
const Icon = {
  home: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 7L8 2l6 5v7H2z" /></svg>,
  user: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="8" cy="6" r="2.5" /><path d="M3 14c0-3 2.2-5 5-5s5 2 5 5" /></svg>,
  book: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 2h7a3 3 0 0 1 3 3v9H6a3 3 0 0 1-3-3z" /><path d="M3 11h10" /></svg>,
  doc: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M3 2h7l3 3v9H3z" /><path d="M5.5 7h5M5.5 9.5h5M5.5 12h3" /></svg>,
  flask: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M6 2v4L3 13a1.5 1.5 0 0 0 1.4 2h7.2A1.5 1.5 0 0 0 13 13l-3-7V2" /><path d="M5 2h6" /></svg>,
  cube: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M8 2 2 5v6l6 3 6-3V5z" /><path d="M2 5l6 3 6-3M8 8v6" /></svg>,
  cpu: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><rect x="3" y="3" width="10" height="10" /><rect x="6" y="6" width="4" height="4" /><path d="M6 1v2M10 1v2M6 13v2M10 13v2M1 6h2M1 10h2M13 6h2M13 10h2" /></svg>,
  storage: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><ellipse cx="8" cy="4" rx="5" ry="2" /><path d="M3 4v4c0 1.1 2.2 2 5 2s5-.9 5-2V4M3 8v4c0 1.1 2.2 2 5 2s5-.9 5-2V8" /></svg>,
  bank: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 6L8 2l6 4M3 6h10v7H3zM5.5 6v7M10.5 6v7M2 14h12" /></svg>,
  rocket: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M11 2c2 0 3 1 3 3 0 4-3 7-7 9L4 12c2-4 5-7 7-7zM11 5a1 1 0 1 1-2 0 1 1 0 0 1 2 0zM5 11l-2 1 1-3" /></svg>,
  chain: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><rect x="2" y="6" width="5" height="4" /><rect x="9" y="6" width="5" height="4" /><path d="M7 8h2" /></svg>,
  store: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 6h12l-1-3H3zM3 6v8h10V6M6 14V9h4v5" /></svg>,
  search: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="7" cy="7" r="4.5" /><path d="M10.5 10.5L14 14" /></svg>,
  bell: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M4 11V7a4 4 0 0 1 8 0v4l1 1H3zM6.5 13a1.5 1.5 0 0 0 3 0" /></svg>,
  plus: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M6 1v10M1 6h10" /></svg>,
  arrow: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M2 6h8M7 3l3 3-3 3" /></svg>,
  check: <svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M2 6.5L5 9l5-6" /></svg>,
  branch: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="4" cy="3" r="1.5" /><circle cx="4" cy="13" r="1.5" /><circle cx="12" cy="6" r="1.5" /><path d="M4 4.5v7M4 10c0-3 8-1 8-3" /></svg>,
  shield: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><path d="M8 1l5 2v5c0 3-2 5-5 7-3-2-5-4-5-7V3z" /></svg>,
  cog: <svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.4"><circle cx="8" cy="8" r="2.4" /><path d="M8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.5 3.5l1.4 1.4M11.1 11.1l1.4 1.4M3.5 12.5l1.4-1.4M11.1 4.9l1.4-1.4" /></svg>
};

// Small bar chart (sparkline-style)
function MiniBars({ values, max, h = 36, color }) {
  const m = max || Math.max(...values);
  return (
    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 2, height: h }}>
      {values.map((v, i) =>
      <div key={i} style={{
        flex: 1,
        height: `${v / m * 100}%`,
        background: color || UB.ink2,
        minHeight: 1
      }} />
      )}
    </div>);

}

// Spark line
function Sparkline({ values, w = 120, h = 32, color }) {
  const max = Math.max(...values),min = Math.min(...values);
  const dx = w / (values.length - 1);
  const d = values.map((v, i) => `${i === 0 ? 'M' : 'L'} ${i * dx} ${h - (v - min) / (max - min || 1) * (h - 4) - 2}`).join(' ');
  return (
    <svg width={w} height={h}>
      <path d={d} fill="none" stroke={color || UB.ink} strokeWidth="1.2" />
    </svg>);

}

// Striped placeholder (for imagery)
function StripePlaceholder({ label = 'PLACEHOLDER', height = 80, style }) {
  return (
    <div style={{
      height, display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: `repeating-linear-gradient(135deg, ${UB.paperDeep}, ${UB.paperDeep} 4px, transparent 4px, transparent 10px)`,
      border: `1px dashed ${UB.rule2}`,
      fontFamily: UB.mono, fontSize: 9, letterSpacing: '0.18em', color: UB.ink3,
      ...style
    }}>{label}</div>);

}

Object.assign(window, {
  UB, Dapp, ChromeBar, Layout, IconRail, NavGroup, NavItem, PageHead, Btn, Tag, Card,
  Stat, Row, Corners, UBLogo, Icon, MiniBars, Sparkline, StripePlaceholder, Pulse
});