// ============================================================
// vibn-ai-templates/components.jsx
// ------------------------------------------------------------
// The core component set. Every visual property is wired to a
// CSS variable from tokens.css — flipping `class="theme-glass"`
// (or any other theme class) reskins the whole library.
//
// Components export to `window` for use in script-tag HTML
// projects. In a real codebase, swap the bottom-of-file
// assignment for `export { … }`.
//
// Components included:
//   Button, IconButton, Field, Input, Textarea, Select,
//   Checkbox, Radio, Switch, Card, Badge, Tag, Avatar,
//   AvatarStack, Tabs, Table, Modal, Banner, Divider,
//   FieldGroup, KBD, Spinner.
// ============================================================

// ─── Helpers ─────────────────────────────────────────────────
const cx = (...names) => names.filter(Boolean).join(" ");
const noop = () => {};

// ─── Button ──────────────────────────────────────────────────
// variant: primary (default), secondary, ghost, destructive
// size: sm | md (default) | lg
// leadingIcon / trailingIcon: <Icon name="…"/>
// loading: disables and shows a spinner
const Button = ({
  children, variant = "primary", size = "md", full = false,
  leadingIcon, trailingIcon, loading, disabled, onClick = noop, style, type = "button",
  ...rest
}) => {
  const sizing = {
    sm: { padY: 6,  padX: 12, font: "var(--text-sm)", iconSize: 13 },
    md: { padY: 9,  padX: 16, font: "var(--text-md)", iconSize: 15 },
    lg: { padY: 12, padX: 22, font: "var(--text-lg)", iconSize: 16 },
  }[size];

  const variants = {
    primary: {
      background: "var(--button-bg)",
      color: "var(--button-fg)",
      border: "1px solid var(--button-border)",
    },
    secondary: {
      background: "var(--button-secondary-bg)",
      color: "var(--button-secondary-fg)",
      border: "1px solid var(--button-secondary-border)",
    },
    ghost: {
      background: "transparent",
      color: "var(--button-ghost-fg)",
      border: "1px solid transparent",
    },
    destructive: {
      background: "var(--danger)",
      color: "#ffffff",
      border: "1px solid var(--danger)",
    },
  }[variant];

  return (
    <button
      type={type}
      disabled={disabled || loading}
      onClick={onClick}
      style={{
        ...variants,
        padding: `${sizing.padY}px ${sizing.padX}px`,
        borderRadius: "var(--button-radius)",
        fontFamily: "var(--font-sans)",
        fontSize: sizing.font,
        fontWeight: "var(--weight-medium)",
        lineHeight: 1.2,
        cursor: disabled || loading ? "not-allowed" : "pointer",
        opacity: disabled ? 0.5 : 1,
        display: "inline-flex",
        alignItems: "center",
        justifyContent: "center",
        gap: 8,
        width: full ? "100%" : "auto",
        whiteSpace: "nowrap",
        transition: "background var(--duration) var(--ease), transform var(--duration-fast) var(--ease)",
        ...style,
      }}
      {...rest}
    >
      {loading ? <Spinner size={sizing.iconSize}/> : leadingIcon}
      <span>{children}</span>
      {!loading && trailingIcon}
    </button>
  );
};

// ─── IconButton ──────────────────────────────────────────────
const IconButton = ({ icon, name, size = "md", variant = "ghost", onClick = noop, label, style }) => {
  const dims = { sm: 28, md: 32, lg: 38 }[size];
  const iconSize = { sm: 14, md: 16, lg: 18 }[size];
  const variants = {
    ghost:     { background: "transparent", color: "var(--text-2)", border: "1px solid transparent" },
    secondary: { background: "var(--button-secondary-bg)", color: "var(--button-secondary-fg)",
                 border: "1px solid var(--button-secondary-border)" },
  }[variant];
  return (
    <button
      aria-label={label}
      onClick={onClick}
      style={{
        ...variants,
        width: dims, height: dims, borderRadius: "var(--radius-sm)",
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        cursor: "pointer", padding: 0,
        ...style,
      }}
    >
      {icon ?? <Icon name={name} size={iconSize} />}
    </button>
  );
};

// ─── Spinner ─────────────────────────────────────────────────
const Spinner = ({ size = 14, stroke = 2 }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true">
    <circle cx="12" cy="12" r="9" stroke="currentColor" strokeOpacity="0.2" strokeWidth={stroke} />
    <path d="M12 3a9 9 0 0 1 9 9" stroke="currentColor" strokeWidth={stroke} strokeLinecap="round">
      <animateTransform attributeName="transform" type="rotate"
        from="0 12 12" to="360 12 12" dur="0.9s" repeatCount="indefinite"/>
    </path>
  </svg>
);

// ─── Field (wraps a labelled input with hint / error) ────────
const Field = ({ label, hint, error, optional, htmlFor, children, style }) => (
  <div style={{ marginBottom: "var(--space-4)", ...style }}>
    {label && (
      <label htmlFor={htmlFor} style={{
        display: "flex", justifyContent: "space-between", alignItems: "baseline",
        fontSize: "var(--text-sm)", fontWeight: "var(--weight-medium)",
        color: "var(--text)", marginBottom: 6,
      }}>
        <span>{label}</span>
        {optional && <span style={{ color: "var(--text-3)", fontWeight: 400 }}>optional</span>}
      </label>
    )}
    {children}
    {(hint || error) && (
      <div style={{
        fontSize: "var(--text-xs)", marginTop: 5,
        color: error ? "var(--danger)" : "var(--text-3)",
      }}>{error || hint}</div>
    )}
  </div>
);

// ─── Input ───────────────────────────────────────────────────
// Bare input (use inside <Field>). leadingIcon / trailingIcon
// add an inner ornament. invalid red-rings the border.
const Input = ({
  value, placeholder, type = "text", leadingIcon, trailingIcon,
  invalid, disabled, autofocus, onChange = noop, id, style, ...rest
}) => (
  <div style={{
    display: "flex", alignItems: "center", gap: 8,
    padding: "10px 12px",
    borderRadius: "var(--field-radius)",
    background: "var(--field-bg)",
    border: `1px solid ${invalid ? "var(--danger)" : "var(--field-border)"}`,
    boxShadow: autofocus ? "var(--shadow-focus)" : "var(--shadow-sm)",
    fontSize: "var(--text-md)",
    color: "var(--text)",
    backdropFilter: "blur(var(--surface-blur))",
    WebkitBackdropFilter: "blur(var(--surface-blur))",
    opacity: disabled ? 0.5 : 1,
    transition: "border-color var(--duration), box-shadow var(--duration)",
    ...style,
  }}>
    {leadingIcon && <span style={{ color: "var(--text-3)", display: "flex" }}>{leadingIcon}</span>}
    <input
      id={id}
      type={type}
      value={value}
      placeholder={placeholder}
      disabled={disabled}
      onChange={(e) => onChange(e.target.value, e)}
      style={{
        flex: 1, minWidth: 0, border: "none", outline: "none", background: "transparent",
        fontFamily: "inherit", fontSize: "inherit", color: "inherit",
        padding: 0,
      }}
      {...rest}
    />
    {trailingIcon && <span style={{ color: "var(--text-3)", display: "flex" }}>{trailingIcon}</span>}
  </div>
);

// ─── Textarea ────────────────────────────────────────────────
const Textarea = ({ value, placeholder, rows = 4, onChange = noop, invalid, id, style, ...rest }) => (
  <textarea
    id={id}
    value={value}
    placeholder={placeholder}
    rows={rows}
    onChange={(e) => onChange(e.target.value, e)}
    style={{
      width: "100%", display: "block", padding: "10px 12px",
      borderRadius: "var(--field-radius)",
      background: "var(--field-bg)",
      border: `1px solid ${invalid ? "var(--danger)" : "var(--field-border)"}`,
      fontSize: "var(--text-md)", color: "var(--text)",
      fontFamily: "var(--font-sans)", resize: "vertical",
      outline: "none", boxShadow: "var(--shadow-sm)",
      backdropFilter: "blur(var(--surface-blur))",
      WebkitBackdropFilter: "blur(var(--surface-blur))",
      ...style,
    }}
    {...rest}
  />
);

// ─── Select (presentation only — clicks the menu open visually) ─
const Select = ({ value, placeholder, options = [], leadingIcon, style, ...rest }) => (
  <div style={{
    display: "flex", alignItems: "center", gap: 8,
    padding: "10px 12px", borderRadius: "var(--field-radius)",
    background: "var(--field-bg)", border: "1px solid var(--field-border)",
    fontSize: "var(--text-md)", color: value ? "var(--text)" : "var(--text-3)",
    cursor: "pointer", boxShadow: "var(--shadow-sm)",
    backdropFilter: "blur(var(--surface-blur))",
    WebkitBackdropFilter: "blur(var(--surface-blur))",
    ...style,
  }} {...rest}>
    {leadingIcon && <span style={{ color: "var(--text-3)", display: "flex" }}>{leadingIcon}</span>}
    <span style={{ flex: 1 }}>{value || placeholder}</span>
    <Icon name="chevDown" size={14} style={{ color: "var(--text-3)" }} />
  </div>
);

// ─── Checkbox ────────────────────────────────────────────────
const Checkbox = ({ checked, indeterminate, disabled, label, hint, onChange = noop, style }) => (
  <label style={{
    display: "flex", alignItems: "flex-start", gap: 10, cursor: disabled ? "not-allowed" : "pointer",
    opacity: disabled ? 0.5 : 1, ...style,
  }}>
    <span
      role="checkbox"
      aria-checked={indeterminate ? "mixed" : !!checked}
      onClick={() => !disabled && onChange(!checked)}
      style={{
        width: 16, height: 16, borderRadius: 4, marginTop: 1, flexShrink: 0,
        border: `1px solid ${checked || indeterminate ? "var(--accent)" : "var(--border-strong)"}`,
        background: checked || indeterminate ? "var(--accent)" : "var(--surface)",
        color: "var(--text-on-accent)",
        display: "inline-flex", alignItems: "center", justifyContent: "center",
        transition: "background var(--duration), border-color var(--duration)",
      }}
    >
      {checked && !indeterminate && <Icon name="checkOnly" size={11} stroke={2.6}/>}
      {indeterminate && <div style={{ width: 8, height: 2, background: "currentColor", borderRadius: 1 }}/>}
    </span>
    {(label || hint) && (
      <span style={{ minWidth: 0 }}>
        {label && <span style={{ fontSize: "var(--text-md)", color: "var(--text)" }}>{label}</span>}
        {hint && <div style={{ fontSize: "var(--text-xs)", color: "var(--text-3)", marginTop: 2 }}>{hint}</div>}
      </span>
    )}
  </label>
);

// ─── Radio ───────────────────────────────────────────────────
const Radio = ({ checked, disabled, label, hint, onChange = noop, style }) => (
  <label style={{
    display: "flex", alignItems: "flex-start", gap: 10, cursor: disabled ? "not-allowed" : "pointer",
    opacity: disabled ? 0.5 : 1, ...style,
  }}>
    <span
      role="radio"
      aria-checked={!!checked}
      onClick={() => !disabled && onChange(true)}
      style={{
        width: 16, height: 16, borderRadius: "50%", marginTop: 1, flexShrink: 0,
        border: `1px solid ${checked ? "var(--accent)" : "var(--border-strong)"}`,
        background: "var(--surface)",
        position: "relative",
      }}
    >
      {checked && <span style={{
        position: "absolute", top: 3, left: 3, right: 3, bottom: 3,
        background: "var(--accent)", borderRadius: "50%",
      }}/>}
    </span>
    {(label || hint) && (
      <span style={{ minWidth: 0 }}>
        {label && <span style={{ fontSize: "var(--text-md)", color: "var(--text)" }}>{label}</span>}
        {hint && <div style={{ fontSize: "var(--text-xs)", color: "var(--text-3)", marginTop: 2 }}>{hint}</div>}
      </span>
    )}
  </label>
);

// ─── Switch ──────────────────────────────────────────────────
const Switch = ({ checked, disabled, onChange = noop, label, hint, style }) => (
  <label style={{
    display: "flex", alignItems: "center", gap: 12,
    cursor: disabled ? "not-allowed" : "pointer", opacity: disabled ? 0.5 : 1, ...style,
  }}>
    <span
      role="switch"
      aria-checked={!!checked}
      onClick={() => !disabled && onChange(!checked)}
      style={{
        width: 34, height: 20, borderRadius: 999,
        background: checked ? "var(--accent)" : "var(--surface-alt)",
        border: `1px solid ${checked ? "var(--accent)" : "var(--border-strong)"}`,
        position: "relative", flexShrink: 0,
        transition: "background var(--duration), border-color var(--duration)",
      }}
    >
      <span style={{
        position: "absolute", top: 1, left: checked ? 15 : 1,
        width: 16, height: 16, borderRadius: "50%",
        background: checked ? "var(--text-on-accent)" : "var(--surface)",
        boxShadow: "0 1px 3px rgba(0,0,0,0.2)",
        transition: "left var(--duration) var(--ease)",
      }}/>
    </span>
    {(label || hint) && (
      <span style={{ flex: 1, minWidth: 0 }}>
        {label && <div style={{ fontSize: "var(--text-md)", color: "var(--text)" }}>{label}</div>}
        {hint && <div style={{ fontSize: "var(--text-xs)", color: "var(--text-3)", marginTop: 2 }}>{hint}</div>}
      </span>
    )}
  </label>
);

// ─── Card / Surface ──────────────────────────────────────────
// Card paints a `surface` background with border + shadow.
// Use `variant="raised"` for shadow-lg, "flat" for no shadow.
const Card = ({ children, variant = "default", padding = 20, style, ...rest }) => {
  const shadows = {
    default: "var(--shadow-sm)",
    raised:  "var(--shadow)",
    floating:"var(--shadow-lg)",
    flat:    "none",
  };
  return (
    <div style={{
      background: "var(--surface)",
      border: "1px solid var(--border)",
      borderRadius: "var(--card-radius)",
      padding,
      boxShadow: shadows[variant] || shadows.default,
      backdropFilter: "blur(var(--surface-blur))",
      WebkitBackdropFilter: "blur(var(--surface-blur))",
      color: "var(--text)",
      ...style,
    }} {...rest}>{children}</div>
  );
};

const CardHeader = ({ title, subtitle, action, style }) => (
  <div style={{
    display: "flex", justifyContent: "space-between", alignItems: "flex-start",
    marginBottom: "var(--space-4)", gap: 16, ...style,
  }}>
    <div style={{ minWidth: 0 }}>
      {title && <div style={{
        fontSize: "var(--text-lg)", fontWeight: "var(--weight-semibold)",
        color: "var(--text)", letterSpacing: "-0.01em",
      }}>{title}</div>}
      {subtitle && <div style={{
        fontSize: "var(--text-sm)", color: "var(--text-2)", marginTop: 2,
      }}>{subtitle}</div>}
    </div>
    {action}
  </div>
);

// ─── Badge / Tag ─────────────────────────────────────────────
// tone: neutral | accent | success | warn | danger | info
const Badge = ({ children, tone = "neutral", dot, leadingIcon, style }) => {
  const palette = {
    neutral: { bg: "var(--surface-alt)", fg: "var(--text-2)", dotColor: "var(--text-3)" },
    accent:  { bg: "var(--accent-soft)", fg: "var(--accent)",  dotColor: "var(--accent)" },
    success: { bg: "var(--success-soft)", fg: "var(--success)", dotColor: "var(--success)" },
    warn:    { bg: "var(--warn-soft)",    fg: "var(--warn)",    dotColor: "var(--warn)" },
    danger:  { bg: "var(--danger-soft)",  fg: "var(--danger)",  dotColor: "var(--danger)" },
    info:    { bg: "var(--accent-soft)",  fg: "var(--accent)",  dotColor: "var(--accent)" },
  }[tone] || {};
  return (
    <span style={{
      display: "inline-flex", alignItems: "center", gap: 6,
      padding: "2px 8px", borderRadius: "var(--radius-pill)",
      background: palette.bg, color: palette.fg,
      fontSize: "var(--text-xs)", fontWeight: "var(--weight-medium)",
      whiteSpace: "nowrap", lineHeight: 1.4,
      ...style,
    }}>
      {dot && <span style={{ width: 6, height: 6, borderRadius: "50%", background: palette.dotColor }}/>}
      {leadingIcon}
      {children}
    </span>
  );
};
const Tag = Badge;        // alias

// ─── Avatar ──────────────────────────────────────────────────
const avatarPalette = ["#d4b8a8", "#e8a87c", "#c8e8a8", "#a8c8e8", "#c8a8e8", "#e8c8a8", "#a8e8c8", "#e8a8c8"];
const hashName = (s = "") => {
  let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) | 0;
  return avatarPalette[Math.abs(h) % avatarPalette.length];
};
const Avatar = ({ name = "?", src, size = 32, status, color, ring, style }) => {
  const initials = name.split(/\s+/).filter(Boolean).map(w => w[0]).slice(0, 2).join("").toUpperCase();
  return (
    <span style={{
      position: "relative", display: "inline-flex", flexShrink: 0,
      width: size, height: size, borderRadius: "50%",
      background: color || hashName(name), color: "#3a2820",
      alignItems: "center", justifyContent: "center",
      fontSize: Math.round(size * 0.4), fontWeight: 600,
      boxShadow: ring ? `0 0 0 2px var(--surface), 0 0 0 ${2 + ring}px var(--accent)` : "none",
      overflow: "hidden", ...style,
    }}>
      {src
        ? <img src={src} alt={name} style={{ width: "100%", height: "100%", objectFit: "cover" }}/>
        : initials}
      {status && <span style={{
        position: "absolute", bottom: 0, right: 0,
        width: Math.max(8, size * 0.28), height: Math.max(8, size * 0.28),
        borderRadius: "50%", border: "2px solid var(--surface)",
        background: status === "online" ? "var(--success)" :
                    status === "busy" ? "var(--danger)" : "var(--text-3)",
      }}/>}
    </span>
  );
};

const AvatarStack = ({ items = [], size = 28, max = 4 }) => {
  const shown = items.slice(0, max);
  const remaining = items.length - shown.length;
  return (
    <div style={{ display: "inline-flex" }}>
      {shown.map((p, i) => (
        <Avatar key={i} name={p.name} src={p.src} color={p.color} size={size}
          style={{ marginLeft: i ? -size * 0.32 : 0, boxShadow: "0 0 0 2px var(--surface)" }}/>
      ))}
      {remaining > 0 && (
        <span style={{
          width: size, height: size, borderRadius: "50%",
          background: "var(--surface-alt)", color: "var(--text-2)",
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          fontSize: Math.round(size * 0.4), fontWeight: 600,
          marginLeft: -size * 0.32, boxShadow: "0 0 0 2px var(--surface)",
        }}>+{remaining}</span>
      )}
    </div>
  );
};

// ─── Tabs ────────────────────────────────────────────────────
// Controlled: pass `active` (label of active tab) + `onChange`.
const Tabs = ({ items = [], active, onChange = noop, style, variant = "underline" }) => {
  return (
    <div style={{
      display: "flex", gap: 4, borderBottom: variant === "underline" ? "1px solid var(--border)" : "none",
      ...style,
    }}>
      {items.map(t => {
        const isActive = t.label === active || t.id === active;
        if (variant === "pill") {
          return (
            <button key={t.id || t.label}
              onClick={() => onChange(t.id || t.label)}
              style={{
                padding: "6px 12px", borderRadius: "var(--radius-pill)",
                fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)",
                background: isActive ? "var(--accent)" : "transparent",
                color: isActive ? "var(--text-on-accent)" : "var(--text-2)",
                border: "none", cursor: "pointer", fontWeight: 500,
                display: "inline-flex", alignItems: "center", gap: 6,
              }}>
              {t.icon}{t.label}
              {t.count != null && (
                <span style={{
                  fontSize: 10, padding: "1px 6px", borderRadius: 999,
                  background: isActive ? "rgba(255,255,255,0.18)" : "var(--surface-alt)",
                  color: "inherit",
                }}>{t.count}</span>
              )}
            </button>
          );
        }
        return (
          <button key={t.id || t.label}
            onClick={() => onChange(t.id || t.label)}
            style={{
              padding: "10px 4px", margin: "0 12px 0 0",
              fontFamily: "var(--font-sans)", fontSize: "var(--text-md)",
              fontWeight: "var(--weight-medium)",
              background: "transparent", border: "none", cursor: "pointer",
              color: isActive ? "var(--text)" : "var(--text-2)",
              borderBottom: isActive ? "2px solid var(--accent)" : "2px solid transparent",
              position: "relative", top: 1,
              display: "inline-flex", alignItems: "center", gap: 6, whiteSpace: "nowrap",
            }}>
            {t.icon}{t.label}
            {t.count != null && (
              <span style={{
                fontSize: 10, padding: "1px 6px", borderRadius: 999,
                background: isActive ? "var(--accent-soft)" : "var(--surface-alt)",
                color: isActive ? "var(--accent)" : "var(--text-3)",
              }}>{t.count}</span>
            )}
          </button>
        );
      })}
    </div>
  );
};

// ─── Table ───────────────────────────────────────────────────
// columns: [{ key, label, width?, align?, render? }]
// rows:    [{ id, [key]: value, … }]
const Table = ({ columns = [], rows = [], selectable, selected = [], onSelectionChange = noop, density = "comfortable" }) => {
  const padY = density === "compact" ? 8 : 12;
  const allChecked = rows.length > 0 && selected.length === rows.length;
  const someChecked = selected.length > 0 && !allChecked;
  const toggleAll = () => onSelectionChange(allChecked ? [] : rows.map(r => r.id));
  const toggleOne = (id) => onSelectionChange(
    selected.includes(id) ? selected.filter(x => x !== id) : [...selected, id]
  );

  const headerCell = {
    padding: `10px 12px`, fontSize: "var(--text-xs)",
    color: "var(--text-3)", fontWeight: "var(--weight-medium)",
    textTransform: "uppercase", letterSpacing: "0.04em", textAlign: "left",
    borderBottom: "1px solid var(--border)", background: "var(--surface)",
  };
  const bodyCell = {
    padding: `${padY}px 12px`, fontSize: "var(--text-md)",
    color: "var(--text)", borderBottom: "1px solid var(--divider)", verticalAlign: "middle",
  };

  return (
    <div style={{
      background: "var(--surface)", border: "1px solid var(--border)",
      borderRadius: "var(--card-radius)", overflow: "hidden",
      backdropFilter: "blur(var(--surface-blur))",
      WebkitBackdropFilter: "blur(var(--surface-blur))",
    }}>
      <table style={{ width: "100%", borderCollapse: "collapse", fontFamily: "var(--font-sans)" }}>
        <thead>
          <tr>
            {selectable && (
              <th style={{ ...headerCell, width: 36, paddingRight: 0 }}>
                <Checkbox checked={allChecked} indeterminate={someChecked} onChange={toggleAll}/>
              </th>
            )}
            {columns.map(c => (
              <th key={c.key} style={{ ...headerCell, width: c.width, textAlign: c.align || "left" }}>
                {c.label}
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((r, i) => (
            <tr key={r.id ?? i}>
              {selectable && (
                <td style={{ ...bodyCell, paddingRight: 0 }}>
                  <Checkbox checked={selected.includes(r.id)} onChange={() => toggleOne(r.id)}/>
                </td>
              )}
              {columns.map(c => (
                <td key={c.key} style={{ ...bodyCell, textAlign: c.align || "left" }}>
                  {c.render ? c.render(r) : r[c.key]}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

// ─── Modal (presentational — wrap your own state) ────────────
const Modal = ({ open, onClose = noop, title, description, footer, children, width = 480 }) => {
  if (!open) return null;
  return (
    <div
      onClick={onClose}
      style={{
        position: "fixed", inset: 0, zIndex: 100,
        background: "rgba(0,0,0,0.5)",
        display: "flex", alignItems: "center", justifyContent: "center", padding: 24,
      }}
    >
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width, maxWidth: "100%", maxHeight: "85vh", overflow: "auto",
          background: "var(--surface)", color: "var(--text)",
          border: "1px solid var(--border)", borderRadius: "var(--modal-radius)",
          boxShadow: "var(--shadow-modal)",
          backdropFilter: "blur(var(--surface-blur))",
          WebkitBackdropFilter: "blur(var(--surface-blur))",
        }}
      >
        <div style={{
          padding: "var(--space-6)", display: "flex",
          justifyContent: "space-between", alignItems: "flex-start", gap: 16,
        }}>
          <div style={{ minWidth: 0 }}>
            {title && <h2 style={{
              margin: 0, fontSize: "var(--text-xl)", fontWeight: "var(--weight-semibold)",
              letterSpacing: "-0.01em", fontFamily: "var(--font-display)",
            }}>{title}</h2>}
            {description && <p style={{
              margin: "6px 0 0", fontSize: "var(--text-md)", color: "var(--text-2)",
            }}>{description}</p>}
          </div>
          <IconButton name="x" size="sm" onClick={onClose} label="Close"/>
        </div>
        {children && <div style={{ padding: "0 var(--space-6) var(--space-6)" }}>{children}</div>}
        {footer && (
          <div style={{
            padding: "var(--space-4) var(--space-6)",
            borderTop: "1px solid var(--divider)",
            display: "flex", justifyContent: "flex-end", gap: 8,
            background: "var(--surface-2)",
          }}>{footer}</div>
        )}
      </div>
    </div>
  );
};

// ─── Banner / Alert ──────────────────────────────────────────
const Banner = ({ tone = "info", title, children, action, onDismiss, icon, style }) => {
  const palette = {
    info:    { bg: "var(--accent-soft)",  fg: "var(--accent)",  iconName: "info" },
    success: { bg: "var(--success-soft)", fg: "var(--success)", iconName: "checkOnly" },
    warn:    { bg: "var(--warn-soft)",    fg: "var(--warn)",    iconName: "alert" },
    danger:  { bg: "var(--danger-soft)",  fg: "var(--danger)",  iconName: "alert" },
  }[tone];
  return (
    <div style={{
      display: "flex", gap: 12, padding: "12px 16px",
      borderRadius: "var(--radius)",
      background: palette.bg, color: "var(--text)",
      border: `1px solid ${palette.fg}33`,
      alignItems: "flex-start",
      ...style,
    }}>
      <span style={{ color: palette.fg, display: "flex", marginTop: 1 }}>
        {icon ?? <Icon name={palette.iconName} size={16} stroke={2}/>}
      </span>
      <div style={{ flex: 1, minWidth: 0 }}>
        {title && <div style={{
          fontSize: "var(--text-md)", fontWeight: "var(--weight-semibold)",
          color: "var(--text)",
        }}>{title}</div>}
        {children && <div style={{
          fontSize: "var(--text-sm)", color: "var(--text-2)", marginTop: title ? 2 : 0,
        }}>{children}</div>}
      </div>
      {action}
      {onDismiss && <IconButton name="x" size="sm" onClick={onDismiss} label="Dismiss"/>}
    </div>
  );
};

// ─── Divider ─────────────────────────────────────────────────
const Divider = ({ label, vertical, style }) => {
  if (vertical) return <span style={{ width: 1, alignSelf: "stretch", background: "var(--border)", ...style }}/>;
  if (!label) return <hr style={{ border: "none", borderTop: "1px solid var(--border)", margin: "var(--space-4) 0", ...style }}/>;
  return (
    <div style={{
      display: "flex", alignItems: "center", gap: 12,
      fontSize: "var(--text-xs)", color: "var(--text-3)",
      letterSpacing: "0.08em", textTransform: "uppercase",
      margin: "var(--space-4) 0", ...style,
    }}>
      <div style={{ flex: 1, height: 1, background: "var(--border)" }}/>
      <span>{label}</span>
      <div style={{ flex: 1, height: 1, background: "var(--border)" }}/>
    </div>
  );
};

// ─── FieldGroup — horizontal segmented control ───────────────
const FieldGroup = ({ options = [], value, onChange = noop, style }) => (
  <div style={{
    display: "inline-flex", padding: 3, gap: 2,
    background: "var(--surface-alt)", border: "1px solid var(--border)",
    borderRadius: "var(--radius)", ...style,
  }}>
    {options.map(o => {
      const v = typeof o === "string" ? o : o.value;
      const label = typeof o === "string" ? o : o.label;
      const sel = v === value;
      return (
        <button key={v} onClick={() => onChange(v)} style={{
          padding: "5px 12px", borderRadius: "calc(var(--radius) - 3px)",
          fontFamily: "var(--font-sans)", fontSize: "var(--text-sm)", whiteSpace: "nowrap",
          background: sel ? "var(--surface)" : "transparent",
          color: sel ? "var(--text)" : "var(--text-2)",
          border: "none", cursor: "pointer",
          boxShadow: sel ? "var(--shadow-sm)" : "none",
          fontWeight: sel ? 500 : 400,
        }}>{label}</button>
      );
    })}
  </div>
);

// ─── KBD ─────────────────────────────────────────────────────
const KBD = ({ children, style }) => (
  <kbd style={{
    fontFamily: "var(--font-mono)", fontSize: "var(--text-xs)",
    padding: "1px 6px", borderRadius: 4,
    background: "var(--surface-alt)", color: "var(--text-2)",
    border: "1px solid var(--border)",
    ...style,
  }}>{children}</kbd>
);

// ─── Exports ─────────────────────────────────────────────────
Object.assign(window, {
  Button, IconButton, Spinner,
  Field, Input, Textarea, Select, FieldGroup,
  Checkbox, Radio, Switch,
  Card, CardHeader, Divider,
  Badge, Tag, Avatar, AvatarStack,
  Tabs, Table, Modal, Banner, KBD,
});
