/* Shared screen primitives. Page header, card, pill, kbd, segmented control, empty state.
   Used by every v2 screen so they feel like one app. */

/* Page header. Title + subtitle + right slot, used at the top of every screen. */
const PageHeader = ({ eyebrow, title, subtitle, right, jurisdictionPill, children }) => (
  <div style={{ padding: '24px 32px 16px', borderBottom: `1px solid ${THEME.line}` }}>
    <div style={{ display: 'flex', alignItems: 'flex-end', gap: 16 }}>
      <div style={{ flex: 1, minWidth: 0 }}>
        {eyebrow && (
          <div style={{ fontSize: 11, color: THEME.textMute, letterSpacing: 1.4, textTransform: 'uppercase', fontWeight: 600, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 8 }}>
            {eyebrow}
            {jurisdictionPill && <JurisdictionPill jx={jurisdictionPill}/>}
          </div>
        )}
        <div style={{ fontFamily: 'Fraunces', fontSize: 28, color: THEME.text, fontWeight: 500, letterSpacing: -0.5, lineHeight: 1.1 }}>
          {title}
        </div>
        {subtitle && (
          <div style={{ fontSize: 12.5, color: THEME.textDim, marginTop: 6, maxWidth: 600, lineHeight: 1.5 }}>
            {subtitle}
          </div>
        )}
      </div>
      {right && <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>{right}</div>}
    </div>
    {children}
  </div>
);

/* Card. Neutral surface used for stat cells, panel sections, list items.
   Glass surface — backdrop-blur is mandatory across ALL card variants so
   cards always show what's behind them through a frosted lens. The bg
   opacity is intentionally low so the blur is the dominant visual signal. */
const Card = ({ children, padding = '14px 16px', style = {}, hover, onClick, accent, onMouseEnter, onMouseLeave }) => (
  <div onClick={onClick} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} style={{
    padding, borderRadius: 12,
    background: THEME.mode === 'dark' ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.55)',
    backdropFilter: 'blur(28px) saturate(1.6)',
    WebkitBackdropFilter: 'blur(28px) saturate(1.6)',
    border: `1px solid ${THEME.line}`,
    borderLeft: accent ? `3px solid ${accent}` : `1px solid ${THEME.line}`,
    cursor: onClick ? 'pointer' : 'default',
    transition: 'all 0.15s ease',
    ...style,
  }}>{children}</div>
);

/* Stat cell. Used in 4-up stat strips. Card-pattern sibling — same glass surface. */
const Stat = ({ label, value, sub, tone }) => (
  <div style={{
    padding: '14px 16px', borderRadius: 12,
    background: THEME.mode === 'dark' ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.55)',
    backdropFilter: 'blur(28px) saturate(1.6)',
    WebkitBackdropFilter: 'blur(28px) saturate(1.6)',
    border: `1px solid ${THEME.line}`,
  }}>
    <div style={{ fontSize: 10.5, color: THEME.textMute, letterSpacing: 1, textTransform: 'uppercase', fontWeight: 600 }}>{label}</div>
    <div style={{ fontFamily: 'Fraunces', fontSize: 26, color: tone || THEME.text, fontWeight: 500, letterSpacing: -0.5, marginTop: 6, lineHeight: 1 }}>{value}</div>
    {sub && <div style={{ fontSize: 11, color: THEME.textDim, marginTop: 6 }}>{sub}</div>}
  </div>
);

/* Pill. Small status / tag chip. */
const Pill = ({ children, tone = THEME.blue, soft, dense, style = {} }) => (
  <span style={{
    fontSize: dense ? 10 : 10.5, fontWeight: 600, padding: dense ? '1px 6px' : '2px 7px', borderRadius: 5,
    background: soft ? tone + '20' : tone,
    color: soft ? tone : '#D7DEE2',
    border: soft ? `1px solid ${tone}40` : 'none',
    letterSpacing: 0.4,
    textTransform: 'uppercase',
    display: 'inline-flex', alignItems: 'center', gap: 4,
    ...style,
  }}>{children}</span>
);

/* Jurisdiction pill. UT vs NY. */
const JurisdictionPill = ({ jx }) => {
  const isUtah = jx === 'utah' || jx === 'UT';
  return (
    <span style={{
      fontSize: 9.5, fontWeight: 700, padding: '2px 6px', borderRadius: 4, letterSpacing: 0.6,
      background: isUtah ? 'rgba(91,167,115,0.18)' : THEME.blueSoft,
      color: isUtah ? THEME.success : THEME.blue,
      border: `1px solid ${isUtah ? 'rgba(91,167,115,0.3)' : THEME.blue + '30'}`,
    }}>{isUtah ? 'UT' : 'NY'}</span>
  );
};

/* Stage pill. Colored by stage name. */
const StagePill = ({ stage }) => {
  const tone = (STAGE_TONE[stage] && STAGE_TONE[stage]()) || THEME.blue;
  return (
    <span style={{
      fontSize: 10.5, fontWeight: 600, padding: '2px 8px', borderRadius: 5, letterSpacing: 0.3,
      background: tone + '20', color: tone, border: `1px solid ${tone}40`,
    }}>{stage}</span>
  );
};

/* Segmented control. For view toggles, tabs, etc. */
const Segmented = ({ options, value, onChange, dense, full }) => (
  <div style={{
    display: 'flex', gap: 3, padding: 3,
    background: THEME.mode === 'dark' ? 'rgba(255,255,255,0.04)' : 'rgba(255,255,255,0.55)',
    border: `1px solid ${THEME.line}`,
    borderRadius: 9,
    width: full ? '100%' : 'auto',
  }}>
    {options.map(o => {
      const a = value === o.value;
      return (
        <div key={o.value} onClick={() => onChange(o.value)} style={{
          flex: full ? 1 : 'initial',
          padding: dense ? '4px 10px' : '6px 12px',
          borderRadius: 6, cursor: 'pointer',
          background: a ? THEME.blue : 'transparent',
          color: a ? '#D7DEE2' : THEME.textDim,
          fontSize: dense ? 11 : 12, fontWeight: 600, textAlign: 'center',
          whiteSpace: 'nowrap',
        }}>{o.label}</div>
      );
    })}
  </div>
);

/* Tab strip. Horizontal underline tabs. */
const TabStrip = ({ tabs, value, onChange, padded }) => (
  <div style={{
    padding: padded ? '0 32px' : 0,
    borderBottom: `1px solid ${THEME.line}`,
    display: 'flex', gap: 4,
  }}>
    {tabs.map(t => {
      const a = value === t.value;
      return (
        <div key={t.value} onClick={() => onChange(t.value)} style={{
          padding: '11px 14px', cursor: 'pointer', fontSize: 12.5, fontWeight: 600,
          color: a ? THEME.text : THEME.textDim,
          borderBottom: `2px solid ${a ? THEME.blue : 'transparent'}`,
          marginBottom: -1,
          display: 'flex', alignItems: 'center', gap: 6,
        }}>
          {t.label}
          {t.count != null && (
            <span style={{
              fontSize: 10, padding: '1px 5px', borderRadius: 8,
              background: a ? THEME.blue : THEME.mode === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.05)',
              color: a ? '#D7DEE2' : THEME.textMute, fontWeight: 600,
            }}>{t.count}</span>
          )}
        </div>
      );
    })}
  </div>
);

/* Filter chip row */
const FilterChips = ({ chips, value, onChange }) => (
  <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
    {chips.map(c => {
      const a = value === c.value;
      return (
        <div key={c.value} onClick={() => onChange(c.value)} style={{
          padding: '5px 11px', borderRadius: 7, fontSize: 11.5, fontWeight: 500,
          background: a ? THEME.blue : THEME.mode === 'dark' ? 'rgba(255,255,255,0.025)' : 'rgba(255,255,255,0.55)',
          color: a ? '#D7DEE2' : THEME.textDim,
          border: `1px solid ${a ? THEME.blue : THEME.line}`,
          cursor: 'pointer', whiteSpace: 'nowrap',
        }}>{c.label}</div>
      );
    })}
  </div>
);

/* Btn — canonical Phase 2 dedup target 1 (locked 2026-05-08).
   ALL variants pill-shaped (border-radius 999). Real <button> for native
   keyboard + focus + screen reader. Mode-aware colors. Backward-compatible
   with legacy callers using icon/tone/soft props (soft folded into ghost
   per locked spec; tone overrides accent color when kind === 'accent'). */
const Btn = ({ children, kind = 'primary', icon, onClick, tone, style = {}, disabled, ariaLabel, type = 'button' }) => {
  const isLight = THEME.mode === 'light';
  const accent = tone || THEME.blue;

  // Map legacy 'soft' to ghost per locked spec (soft was dropped — ADA marginal).
  const k = kind === 'soft' ? 'ghost' : kind;

  const base = {
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
    gap: 8, padding: '10px 20px',
    borderRadius: 999,
    fontSize: 13, fontWeight: 600, lineHeight: 1.2, letterSpacing: 0.05,
    fontFamily: 'inherit', whiteSpace: 'nowrap', textAlign: 'center',
    cursor: disabled ? 'not-allowed' : 'pointer',
    transition: 'background 0.18s ease, color 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease',
    outline: 'none',
    appearance: 'none', WebkitAppearance: 'none', MozAppearance: 'none',
    userSelect: 'none',
    opacity: disabled ? 0.55 : 1,
  };

  const variants = {
    primary: {
      background: isLight ? '#2A3640' : '#2A3640',
      color: '#F2F4F8',
      border: '1px solid rgba(255,255,255,0.10)',
    },
    secondary: {
      background: 'transparent',
      color: isLight ? '#1A232C' : '#F2F4F8',
      border: `1px solid ${isLight ? 'rgba(20,30,50,0.32)' : 'rgba(242,244,248,0.22)'}`,
      fontWeight: 500, padding: '10px 18px',
    },
    ghost: {
      background: 'transparent',
      color: isLight ? 'rgba(20,30,50,0.72)' : 'rgba(242,244,248,0.78)',
      border: `1px solid ${isLight ? 'rgba(20,30,50,0.18)' : 'rgba(255,255,255,0.10)'}`,
      fontWeight: 500, padding: '10px 18px',
    },
    accent: {
      background: accent,
      color: '#D7DEE2',
      border: `1px solid ${accent}`,
      boxShadow: `0 4px 12px ${accent}40`,
    },
  };
  const variant = variants[k] || variants.primary;
  const merged = { ...base, ...variant, ...(style || {}) };

  return (
    <button
      type={type}
      onClick={disabled ? undefined : onClick}
      disabled={disabled}
      aria-disabled={disabled || undefined}
      aria-label={ariaLabel}
      onMouseEnter={(e) => {
        if (disabled) return;
        if (k === 'primary')   e.currentTarget.style.background = isLight ? '#3A4754' : '#3A4858';
        if (k === 'secondary') e.currentTarget.style.background = isLight ? 'rgba(20,30,50,0.06)' : 'rgba(255,255,255,0.06)';
        if (k === 'ghost')     e.currentTarget.style.background = isLight ? 'rgba(20,30,50,0.04)' : 'rgba(255,255,255,0.04)';
        if (k === 'accent')    e.currentTarget.style.boxShadow = `0 6px 16px ${accent}60`;
      }}
      onMouseLeave={(e) => {
        if (disabled) return;
        if (k === 'primary')   e.currentTarget.style.background = '#2A3640';
        if (k === 'secondary') e.currentTarget.style.background = 'transparent';
        if (k === 'ghost')     e.currentTarget.style.background = 'transparent';
        if (k === 'accent')    e.currentTarget.style.boxShadow = `0 4px 12px ${accent}40`;
      }}
      style={merged}
    >
      {icon && <Icon d={icon} size={14} stroke={k === 'primary' || k === 'accent' ? '#D7DEE2' : (isLight ? '#1A232C' : '#F2F4F8')}/>}
      {children}
    </button>
  );
};

/* Empty state */
const EmptyState = ({ icon, title, sub }) => (
  <div style={{
    padding: '40px 20px', borderRadius: 14,
    border: `1px dashed ${THEME.line}`,
    display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
    color: THEME.textMute,
  }}>
    {icon && <Icon d={icon} size={28} stroke={THEME.textMute}/>}
    <div style={{ fontFamily: 'Fraunces', fontSize: 18, color: THEME.textDim, fontWeight: 500 }}>{title}</div>
    {sub && <div style={{ fontSize: 12, textAlign: 'center', maxWidth: 360, lineHeight: 1.5 }}>{sub}</div>}
  </div>
);

/* Meta row. Small key:value pair, used in headers */
const Meta = ({ label, value, tone }) => (
  <div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 11.5 }}>
    <span style={{ color: THEME.textMute }}>{label}</span>
    <span style={{ color: tone || THEME.text, fontWeight: 600 }}>{value}</span>
  </div>
);

/* Avatar. Initials in gradient circle */
const Avatar = ({ initials, size = 30, tone }) => (
  <div style={{
    width: size, height: size, borderRadius: size / 2, flexShrink: 0,
    background: tone || `linear-gradient(135deg, ${THEME.blue}, ${THEME.accent})`,
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    color: '#D7DEE2', fontWeight: 600, fontSize: size * 0.36,
  }}>{initials}</div>
);

/* Progress bar */
const Progress = ({ pct, tone, height = 6, label }) => (
  <div>
    {label && (
      <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 11, color: THEME.textDim, marginBottom: 4 }}>
        <span>{label}</span>
        <span style={{ color: THEME.text, fontWeight: 600 }}>{pct}%</span>
      </div>
    )}
    <div style={{
      height, borderRadius: height / 2,
      background: THEME.mode === 'dark' ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.05)',
      overflow: 'hidden',
    }}>
      <div style={{
        height: '100%', width: pct + '%', borderRadius: height / 2,
        background: tone || `linear-gradient(90deg, ${THEME.success}, ${THEME.blue})`,
      }}/>
    </div>
  </div>
);

/* Section heading inside a screen. Two locked sizes (2026-07-02, desktop DS):
   eyebrow = 12 / 0.6 (sits above a page title); section = 11.5 / 1.1 (in-page
   group header, the default). Collapses the many ad-hoc letter-spacings into
   one system. Author children in sentence case; CSS uppercases. */
const SectionLabel = ({ children, right, size = 'section', style = {} }) => {
  const S = { eyebrow: { fontSize: 12, letterSpacing: 0.6 }, section: { fontSize: 11.5, letterSpacing: 1.1 } };
  const s = S[size] || S.section;
  return (
    <div style={{ display: 'flex', alignItems: 'center', marginBottom: 10, ...style }}>
      <span style={{ fontSize: s.fontSize, color: THEME.textMute, letterSpacing: s.letterSpacing, textTransform: 'uppercase', fontWeight: 600 }}>{children}</span>
      <div style={{ flex: 1 }}/>
      {right}
    </div>
  );
};

/* Popover. Sibling of Card visually (same border + radius) but with an OPAQUE
   background so content behind it doesn't bleed through. Cards are glass
   meant to layer over content; popovers are modal-ish and need to read on
   their own. Behavior: click-outside dismisses, Esc dismisses, anchors via
   absolute positioning relative to the nearest positioned ancestor.

   Placement values: 'bottom-right' (default), 'bottom-left', 'bottom-stretch'
   (full width inside parent). Width is ignored when placement is 'bottom-stretch'.
*/
const Popover = ({ open, onClose, children, width = 320, placement = 'bottom-right', style = {} }) => {
  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') onClose && onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open, onClose]);

  if (!open) return null;

  let pos = { top: 'calc(100% + 8px)', right: 0 };
  let sizeStyle = { width };
  if (placement === 'bottom-left')    { pos = { top: 'calc(100% + 8px)', left: 0 };  }
  if (placement === 'bottom-stretch') { pos = { top: 'calc(100% + 8px)', left: 0, right: 0 }; sizeStyle = {}; }

  return (
    <React.Fragment>
      {/* Click-outside catcher. Fixed full-viewport so any click outside the
          popover dismisses it. Sits below the popover in z-stack. */}
      <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 9600, background: 'transparent' }}/>
      <div
        role="dialog"
        style={{
          position: 'absolute', ...pos, ...sizeStyle,
          zIndex: 9999,
          padding: '16px 18px', borderRadius: 12,
          /* FULLY opaque background. No alpha — the 0.985 alpha version
             still bled because the chat thread underneath was rendering
             outside the popover's stacking context. Solid hex it is. */
          background: THEME.mode === 'dark' ? '#161C26' : '#FCFAF4',
          border: `1px solid ${THEME.line}`,
          boxShadow: '0 18px 50px rgba(0,0,0,0.50), 0 4px 16px rgba(0,0,0,0.30)',
          color: THEME.text,
          fontSize: 13, lineHeight: 1.55,
          ...style,
        }}>
        {children}
      </div>
    </React.Fragment>
  );
};

window.PageHeader = PageHeader;
window.Card = Card;
window.Popover = Popover;
window.Stat = Stat;
window.Pill = Pill;
window.JurisdictionPill = JurisdictionPill;
window.StagePill = StagePill;
window.Segmented = Segmented;
window.TabStrip = TabStrip;
window.FilterChips = FilterChips;
window.Btn = Btn;
window.EmptyState = EmptyState;
window.Meta = Meta;
window.Avatar = Avatar;
window.Progress = Progress;
window.SectionLabel = SectionLabel;
