// ui.jsx — shared UI primitives for RT-Flow

const { useState, useEffect, useRef, useMemo } = React;

// ─────────── icons (tiny line-based SVG, 18×18 default) ───────────
const I = {
  Search: (p) => <svg width={p.size||18} height={p.size||18} viewBox="0 0 18 18" fill="none"><circle cx="8" cy="8" r="5.25" stroke="currentColor" strokeWidth="1.5"/><path d="M12 12l3.5 3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>,
  Bell: (p) => <svg width={p.size||18} height={p.size||18} viewBox="0 0 18 18" fill="none"><path d="M4.5 13.5h9V8.25a4.5 4.5 0 10-9 0v5.25z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/><path d="M7 15.75a2 2 0 004 0" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>,
  Plus: (p) => <svg width={p.size||18} height={p.size||18} viewBox="0 0 18 18" fill="none"><path d="M9 3.5v11M3.5 9h11" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>,
  Chevron: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><path d="M5 3l4 4-4 4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  ChevronDown: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><path d="M3 5l4 4 4-4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Check: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><path d="M3 7.5l3 3 5-6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  X: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><path d="M3.5 3.5l7 7m0-7l-7 7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  Bolt: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M9 2L3.5 9h3.5L7 14l5.5-7H9l0-5z" fill="currentColor"/></svg>,
  Refresh: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M13 6a5 5 0 10-1 5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/><path d="M13 2.5V6h-3.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Filter: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M2.5 4h11M5 8h6M7 12h2" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  Clock: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><circle cx="7" cy="7" r="5.25" stroke="currentColor" strokeWidth="1.4"/><path d="M7 4.5V7l1.8 1.2" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round"/></svg>,
  Lock: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><rect x="3" y="6.5" width="8" height="5.5" rx="1" stroke="currentColor" strokeWidth="1.4"/><path d="M5 6.5V4.5a2 2 0 014 0v2" stroke="currentColor" strokeWidth="1.4"/></svg>,
  External: (p) => <svg width={p.size||14} height={p.size||14} viewBox="0 0 14 14" fill="none"><path d="M5 3H3v8h8V9M8 3h3v3M11 3l-5 5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/></svg>,
  Doc: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M4 2h5l3 3v9H4V2z" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/><path d="M9 2v3h3" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/></svg>,
  Folder: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M2 4.5a1 1 0 011-1h3l1.5 1.5H13a1 1 0 011 1V12a1 1 0 01-1 1H3a1 1 0 01-1-1V4.5z" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/></svg>,
  Image: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><rect x="2" y="3" width="12" height="10" rx="1" stroke="currentColor" strokeWidth="1.4"/><circle cx="6" cy="7" r="1.2" fill="currentColor"/><path d="M2.5 12L6 9l3 3 2-2 2.5 2.5" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/></svg>,
  Stack: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M2 5l6-3 6 3-6 3-6-3z" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/><path d="M2 8l6 3 6-3M2 11l6 3 6-3" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/></svg>,
  Chart: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M2.5 13.5h11M4.5 11V8M8 11V4M11.5 11V6.5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  Workspace: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><rect x="2.5" y="2.5" width="5" height="5" rx="1" stroke="currentColor" strokeWidth="1.4"/><rect x="8.5" y="2.5" width="5" height="5" rx="1" stroke="currentColor" strokeWidth="1.4"/><rect x="2.5" y="8.5" width="5" height="5" rx="1" stroke="currentColor" strokeWidth="1.4"/><rect x="8.5" y="8.5" width="5" height="5" rx="1" stroke="currentColor" strokeWidth="1.4"/></svg>,
  Tasks: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M3 4h10M3 8h10M3 12h6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/></svg>,
  Pipeline: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><circle cx="4" cy="8" r="2" stroke="currentColor" strokeWidth="1.4"/><circle cx="12" cy="8" r="2" stroke="currentColor" strokeWidth="1.4"/><path d="M6 8h4" stroke="currentColor" strokeWidth="1.4"/></svg>,
  Alert: (p) => <svg width={p.size||16} height={p.size||16} viewBox="0 0 16 16" fill="none"><path d="M8 2.5L14 13H2L8 2.5z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round"/><path d="M8 6.5v3.5M8 11.5v.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"/></svg>,
};

// ─────────── status chip (small) ───────────
function Chip({ kind = 'pending', children, mono = false, dot = false }) {
  const k = STATUS_STYLE[kind] || STATUS_STYLE.pending;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      background: k.bg, color: k.ink,
      fontSize: 11, fontWeight: 600, lineHeight: '14px',
      padding: '3px 7px', borderRadius: 6,
      fontFamily: mono ? "'IBM Plex Mono', monospace" : undefined,
      letterSpacing: 0,
    }}>
      {dot && <span style={{ width: 5, height: 5, borderRadius: 5, background: k.ink }}/>}
      {children}
    </span>
  );
}
const STATUS_STYLE = {
  pending: { bg: 'var(--st-pending-bg)', ink: 'var(--st-pending-ink)' },
  active:  { bg: 'var(--st-active-bg)',  ink: 'var(--st-active-ink)' },
  mine:    { bg: 'var(--st-mine-bg)',    ink: 'var(--st-mine-ink)' },
  done:    { bg: 'var(--st-done-bg)',    ink: 'var(--st-done-ink)' },
  blocked: { bg: 'var(--st-block-bg)',   ink: 'var(--st-block-ink)' },
  urgent:  { bg: '#FBE5E7',              ink: '#9F1117' },
  brand:   { bg: 'var(--brand-soft)',    ink: 'var(--brand)' },
};

// ─────────── Section header (uppercase tiny) ───────────
function SectionLabel({ children, right }) {
  return (
    <div style={{
      display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between',
      padding: '18px 20px 8px',
      color: 'var(--t3)', fontSize: 11, fontWeight: 600,
      letterSpacing: '0.08em', textTransform: 'uppercase',
    }}>
      <span>{children}</span>
      {right && <span style={{ fontWeight: 500, textTransform: 'none', letterSpacing: 0, color: 'var(--t3)' }}>{right}</span>}
    </div>
  );
}

// ─────────── Card surface ───────────
function Card({ children, style, onClick, pad = 16, accent }) {
  return (
    <div onClick={onClick} style={{
      background: 'var(--surface)',
      borderRadius: 14,
      border: '1px solid var(--border)',
      boxShadow: 'var(--sh-card)',
      padding: pad,
      position: 'relative',
      overflow: 'hidden',
      cursor: onClick ? 'pointer' : 'default',
      ...style,
    }}>
      {accent && <div style={{ position:'absolute', left:0, top:0, bottom:0, width:3, background: accent }}/>}
      {children}
    </div>
  );
}

// ─────────── Avatar (initials, brand-tinted) ───────────
function Avatar({ name, size = 28, color }) {
  const init = (name||'?').slice(-2);
  return (
    <div style={{
      width: size, height: size, borderRadius: size,
      background: color || 'var(--brand)',
      color: '#fff', fontSize: size * 0.42, fontWeight: 600,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      letterSpacing: 0.2, fontFamily: "'IBM Plex Sans', system-ui",
    }}>{init}</div>
  );
}

// ─────────── Patient sigil (square, mono code) ───────────
function PatientSigil({ tag, size = 32, color = 'var(--brand)' }) {
  return (
    <div style={{
      width: size, height: size, borderRadius: 8,
      background: 'var(--brand-soft)',
      color: 'var(--brand)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: "'IBM Plex Mono', monospace",
      fontSize: size * 0.38, fontWeight: 600,
      border: '1px solid rgba(0,72,92,0.10)',
    }}>{tag}</div>
  );
}

// ─────────── Mono wait timer ───────────
function WaitTimer({ value, label = '等待' }) {
  return (
    <div style={{ display:'inline-flex', alignItems:'center', gap:6, color:'var(--t2)' }}>
      <I.Clock size={12} />
      <span style={{ fontSize: 12 }}>{label}</span>
      <span className="mono" style={{ fontSize: 12, color: 'var(--t1)', fontWeight: 600 }}>{value}</span>
    </div>
  );
}

// ─────────── Node bead (single circle/line for timeline) ───────────
function NodeBead({ state, label, k, current, small = false }) {
  const size = small ? 10 : 14;
  const colorMap = {
    done:    { fill: 'var(--brand)',           stroke: 'var(--brand)' },
    active:  { fill: '#fff',                   stroke: '#1F5AA8' },
    mine:    { fill: '#fff',                   stroke: '#C0252F' },
    blocked: { fill: '#fff',                   stroke: '#B14B0E' },
    pending: { fill: '#fff',                   stroke: '#C9D1D7' },
    urgent:  { fill: '#fff',                   stroke: '#9F1117' },
  };
  const c = colorMap[state] || colorMap.pending;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 5, minWidth: 22 }}>
      <div style={{
        width: size, height: size, borderRadius: size,
        background: state === 'done' ? c.fill : '#fff',
        border: `2px solid ${c.stroke}`,
        boxShadow: current ? `0 0 0 4px ${c.stroke}22` : 'none',
        position: 'relative',
      }}>
        {state === 'active' && (
          <span style={{
            position:'absolute', inset: 3, borderRadius: 10,
            background: c.stroke,
          }}/>
        )}
        {state === 'mine' && (
          <span style={{
            position:'absolute', inset: 3, borderRadius: 10,
            background: c.stroke,
          }}/>
        )}
        {state === 'blocked' && (
          <span style={{
            position:'absolute', inset: 0, borderRadius: 10,
            border: `2px solid ${c.stroke}`,
            animation: 'rt-pulse 1.6s infinite',
          }}/>
        )}
      </div>
      {label && (
        <span style={{
          fontSize: 9, color: current ? 'var(--t1)' : 'var(--t3)',
          fontWeight: current ? 600 : 500,
          whiteSpace: 'nowrap',
          letterSpacing: -0.05,
        }}>{label}</span>
      )}
    </div>
  );
}

// connecting line between beads
function BeadLine({ state }) {
  const color = state === 'done' ? 'var(--brand)' : '#D8DDE2';
  return (
    <div style={{
      flex: 1, height: 2, background: color, marginTop: 6, minWidth: 0,
      borderRadius: 2,
    }}/>
  );
}

// ─────────── 12-node compact strip ───────────
function NodeStrip({ progress, current, dense = false }) {
  return (
    <div style={{ display: 'flex', alignItems: 'flex-start', gap: 0, padding: '4px 0' }}>
      {NODES.map((n, i) => {
        const p = progress[n.id] || { state: 'pending' };
        return (
          <React.Fragment key={n.id}>
            <NodeBead
              state={p.state}
              label={dense ? '' : n.short}
              k={n.k}
              current={current === n.id}
              small={dense}
            />
            {i < NODES.length - 1 && <BeadLine state={p.state} />}
          </React.Fragment>
        );
      })}
    </div>
  );
}

// ─────────── Task card (the headline list item) ───────────
function TaskCard({ task, onClick }) {
  const p = PATIENTS[task.patient];
  const node = NODES.find(n => n.id === task.node);

  const priInk = {
    urgent: '#9F1117', mine: 'var(--st-mine-ink)',
    active: 'var(--st-active-ink)', done: 'var(--st-done-ink)',
    pending: 'var(--t3)', blocked: 'var(--st-block-ink)',
  }[task.pri] || 'var(--t2)';

  const priChipKind = task.pri === 'urgent' ? 'urgent'
    : task.pri === 'mine' ? 'mine'
    : task.pri === 'active' ? 'active'
    : task.pri === 'blocked' ? 'blocked'
    : task.pri === 'done' ? 'done'
    : 'pending';

  const priLabel = {
    urgent: '紧急 · 等我',
    mine: '等我',
    active: '进行中',
    pending: '待启动',
    done: '已完成',
    blocked: '已驳回',
  }[task.pri] || '—';

  return (
    <Card onClick={onClick} pad={0} style={{
      borderColor: task.pri === 'urgent' ? '#F2C4C7' : task.pri === 'mine' ? '#F0D2D4' : 'var(--border)',
    }} accent={task.pri === 'urgent' ? '#C0252F' : task.pri === 'mine' ? '#C0252F' : null}>
      <div style={{ padding: '14px 14px 12px 16px' }}>
        {/* row 1: patient + chip */}
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', gap: 10 }}>
          <div style={{ display:'flex', alignItems:'center', gap: 10, minWidth: 0, flex: 1 }}>
            <PatientSigil tag={p.photoTag} size={28} />
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{
                fontSize: 15, fontWeight: 600, color: 'var(--t1)', letterSpacing: -0.1,
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>
                {task.title}
              </div>
              <div style={{
                fontSize: 12, color: 'var(--t3)', marginTop: 1,
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>
                <span className="mono">{p.id}</span> · {p.dx.split(' · ')[0]}
              </div>
            </div>
          </div>
          <Chip kind={priChipKind} dot={task.pri==='urgent' || task.pri==='mine'}>{priLabel}</Chip>
        </div>

        {/* row 2: plan/sub */}
        <div style={{
          marginTop: 12, padding: '10px 12px',
          background: 'var(--surface-2)',
          borderRadius: 9,
          border: '1px solid var(--border-soft)',
        }}>
          <div style={{ display:'flex', gap: 14, fontSize: 12, color:'var(--t2)' }}>
            <div style={{ minWidth: 36 }}>计划</div>
            <div style={{ flex: 1, color: 'var(--t1)', fontWeight: 500 }}>{task.sub}</div>
          </div>
          <div style={{ display:'flex', gap: 14, fontSize: 12, color:'var(--t2)', marginTop: 4 }}>
            <div style={{ minWidth: 36 }}>节点</div>
            <div style={{ flex: 1, color: 'var(--t1)', fontWeight: 500 }}>
              <span style={{ color: 'var(--brand)', fontWeight: 600 }}>{node?.k}</span> · {node?.label}
            </div>
          </div>
          {task.meta && (
            <div style={{ display:'flex', gap: 14, fontSize: 12, color:'var(--t2)', marginTop: 4 }}>
              <div style={{ minWidth: 36 }}>上游</div>
              <div style={{ flex: 1, color: 'var(--t2)' }}>{task.meta.join(' · ')}</div>
            </div>
          )}
          {task.sla && (
            <div style={{ display:'flex', gap: 14, fontSize: 12, color:'var(--t2)', marginTop: 4 }}>
              <div style={{ minWidth: 36 }}>SLA</div>
              <div style={{ flex: 1, color: task.pri==='urgent'?'#9F1117':'var(--t1)', fontWeight: 500 }} className={task.pri==='urgent'?'mono':''}>
                {task.sla}
              </div>
            </div>
          )}
        </div>

        {/* row 3: wait timer + tags */}
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginTop: 10 }}>
          {task.waiting
            ? <WaitTimer value={task.waiting} label={task.pri==='urgent'?'剩余':'等待'} />
            : <span style={{ fontSize: 12, color: 'var(--t3)' }}>—</span>
          }
          <div style={{ display:'flex', alignItems:'center', gap: 6, color: 'var(--t3)' }}>
            <span style={{ fontSize: 12 }}>展开</span>
            <I.Chevron size={12} />
          </div>
        </div>
      </div>
    </Card>
  );
}

// ─────────── Role pill (top header) ───────────
function RolePill({ role, onClick }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 8,
      background: '#fff', border: '1px solid var(--border)',
      borderRadius: 999, padding: '5px 10px 5px 5px',
      cursor: 'pointer',
    }}>
      <span style={{
        width: 26, height: 26, borderRadius: 26,
        background: role.color, color: '#fff',
        display:'flex', alignItems:'center', justifyContent:'center',
        fontSize: 13, fontWeight: 700,
      }}>{role.short}</span>
      <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--t1)' }}>{role.workbench}</span>
      <I.ChevronDown size={12} />
    </button>
  );
}

// ─────────── Custom top bar (replaces large iOS nav title) ───────────
function TopBar({ role, onRoleClick, title, subtitle, action, back, onBack }) {
  return (
    <div style={{
      padding: '0 16px 8px',
      background: 'var(--bg)',
      position: 'sticky', top: 0, zIndex: 5,
    }}>
      <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', height: 44 }}>
        {back
          ? <button onClick={onBack} style={{
              width: 36, height: 36, border: 'none', background: 'transparent',
              display:'flex', alignItems:'center', justifyContent:'center', color: 'var(--t1)', cursor:'pointer',
              marginLeft: -8,
            }}>
              <svg width="18" height="18" viewBox="0 0 18 18" fill="none"><path d="M11 4l-5 5 5 5" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"/></svg>
            </button>
          : <RolePill role={role} onClick={onRoleClick} />
        }
        <div style={{ display:'flex', alignItems:'center', gap: 6 }}>
          <IconBtn><I.Search /></IconBtn>
          <IconBtn dot><I.Bell /></IconBtn>
          <Avatar name={role.me} color={role.color} size={28} />
        </div>
      </div>
      {title && (
        <div style={{ paddingTop: 4 }}>
          <h1 style={{
            margin: 0, fontSize: 26, fontWeight: 700,
            letterSpacing: -0.5, color: 'var(--t1)',
          }}>{title}</h1>
          {subtitle && <div style={{ marginTop: 2, color: 'var(--t2)', fontSize: 13 }}>{subtitle}</div>}
        </div>
      )}
    </div>
  );
}

function IconBtn({ children, onClick, dot }) {
  return (
    <button onClick={onClick} style={{
      width: 36, height: 36, borderRadius: 10,
      background: '#fff', border: '1px solid var(--border)',
      display: 'flex', alignItems:'center', justifyContent:'center',
      color: 'var(--t1)', cursor:'pointer', position: 'relative',
    }}>
      {children}
      {dot && <span style={{
        position:'absolute', top: 7, right: 7, width: 7, height: 7,
        background: '#C0252F', borderRadius: 7, border: '1.5px solid #fff',
      }}/>}
    </button>
  );
}

// ─────────── Bottom tab bar ───────────
function BottomTabs({ current, onChange }) {
  const tabs = [
    { id: 'workspace', label: '工作台', icon: I.Workspace },
    { id: 'tasks',     label: '任务',   icon: I.Tasks },
    { id: 'pipeline',  label: '流水线', icon: I.Pipeline },
    { id: 'files',     label: '档案',   icon: I.Folder },
    { id: 'insights',  label: '洞察',   icon: I.Chart },
  ];
  return (
    <div style={{
      position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 30,
      padding: '8px 8px 28px',
      background: 'rgba(255,255,255,0.92)',
      borderTop: '1px solid var(--border)',
      backdropFilter: 'blur(20px) saturate(180%)',
      WebkitBackdropFilter: 'blur(20px) saturate(180%)',
    }}>
      <div style={{ display:'flex', justifyContent:'space-around' }}>
        {tabs.map(t => {
          const active = current === t.id;
          const Icon = t.icon;
          return (
            <button key={t.id} onClick={() => onChange(t.id)} style={{
              border:'none', background:'transparent', padding: '6px 8px',
              display:'flex', flexDirection:'column', alignItems:'center', gap: 3,
              cursor:'pointer',
              color: active ? 'var(--brand)' : 'var(--t3)',
              minWidth: 56,
            }}>
              <Icon size={20} />
              <span style={{ fontSize: 10.5, fontWeight: active ? 600 : 500, letterSpacing: 0.1 }}>{t.label}</span>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ─────────── Hero alert (top of workspace) ───────────
function HeroAlert({ alert, onAction }) {
  if (!alert) return null;
  const urgent = alert.level === 'urgent';
  return (
    <div style={{
      margin: '0 16px 12px',
      background: urgent ? 'linear-gradient(135deg, #C0252F 0%, #9F1117 100%)' : 'linear-gradient(135deg, #D97757 0%, #B14B0E 100%)',
      color: '#fff', borderRadius: 14, padding: 14,
      boxShadow: '0 6px 18px rgba(192,37,47,0.18)',
      position: 'relative', overflow: 'hidden',
    }}>
      <div style={{
        position: 'absolute', top: -30, right: -30, width: 120, height: 120,
        borderRadius: 120, background: 'rgba(255,255,255,0.08)',
      }}/>
      <div style={{ display:'flex', alignItems:'center', gap: 8, fontSize: 11, opacity: 0.85, letterSpacing: '0.06em', textTransform:'uppercase', fontWeight: 600 }}>
        <I.Alert size={14}/>
        <span>{urgent ? '紧急 · 等我' : '需介入'}</span>
        {alert.sla && (
          <span style={{ marginLeft: 'auto', background: 'rgba(0,0,0,0.18)', padding: '2px 7px', borderRadius: 6 }} className="mono">
            剩余 {alert.sla}
          </span>
        )}
      </div>
      <div style={{ fontSize: 17, fontWeight: 700, marginTop: 8, letterSpacing: -0.1 }}>{alert.title}</div>
      <div style={{ fontSize: 13, opacity: 0.9, marginTop: 2 }}>{alert.body}</div>
      <button onClick={onAction} style={{
        marginTop: 12, background: 'rgba(255,255,255,0.95)',
        color: urgent ? '#9F1117' : '#B14B0E',
        border: 'none', borderRadius: 8, padding: '8px 14px',
        fontSize: 13, fontWeight: 600, cursor: 'pointer',
        display: 'flex', alignItems: 'center', gap: 6,
      }}>
        {alert.action}
        <I.Chevron size={12}/>
      </button>
    </div>
  );
}

// ─────────── Misc helpers ───────────
function KV({ label, value, mono }) {
  return (
    <div style={{ display:'flex', gap: 14, fontSize: 12, lineHeight: '18px' }}>
      <div style={{ minWidth: 56, color:'var(--t3)' }}>{label}</div>
      <div style={{ flex: 1, color: 'var(--t1)' }} className={mono?'mono':''}>{value}</div>
    </div>
  );
}

function MiniBar({ value, target, ok = true, width = 80 }) {
  const w = Math.min(100, (value/target)*100);
  const color = ok ? '#18713B' : '#B14B0E';
  return (
    <div style={{ width, height: 6, borderRadius: 3, background: '#EEF2F4', overflow:'hidden' }}>
      <div style={{ width: w+'%', height: '100%', background: color }}/>
    </div>
  );
}

// global anim keyframes
(function injectKf(){
  if (document.getElementById('rt-kf')) return;
  const s = document.createElement('style');
  s.id = 'rt-kf';
  s.textContent = `
    @keyframes rt-pulse { 0% { transform: scale(1); opacity: .8 } 50% { transform: scale(1.2); opacity: 0 } 100% { transform: scale(1); opacity: 0 } }
    @keyframes rt-blink { 0%,100%{opacity:1} 50%{opacity:.35} }
  `;
  document.head.appendChild(s);
})();

Object.assign(window, {
  I, Chip, SectionLabel, Card, Avatar, PatientSigil, WaitTimer,
  NodeBead, BeadLine, NodeStrip, TaskCard, RolePill, TopBar,
  IconBtn, BottomTabs, HeroAlert, KV, MiniBar,
});
