// shared.jsx — design tokens, primitives, mini chart, sentiment bar, sparklines

// ───────────────────────── Tokens (themed) ─────────────────────────
function useTheme(dark) {
  const t = dark ? {
    name: 'dark',
    bg: '#0a0a0a',
    bgSubtle: '#0f0f10',
    surface: '#161617',
    surfaceHi: '#1d1d1f',
    border: 'rgba(255,255,255,0.08)',
    borderStrong: 'rgba(255,255,255,0.14)',
    text: '#fafafa',
    textMuted: 'rgba(255,255,255,0.62)',
    textDim: 'rgba(255,255,255,0.42)',
    up: '#00D26A',
    upSoft: 'rgba(0,210,106,0.14)',
    down: '#FF453A',
    downSoft: 'rgba(255,69,58,0.14)',
    accent: '#3B82F6',
    accentSoft: 'rgba(59,130,246,0.16)',
    chip: 'rgba(255,255,255,0.06)',
  } : {
    name: 'light',
    bg: '#F7F7F5',
    bgSubtle: '#F1F1EE',
    surface: '#FFFFFF',
    surfaceHi: '#FFFFFF',
    border: 'rgba(0,0,0,0.06)',
    borderStrong: 'rgba(0,0,0,0.12)',
    text: '#0a0a0a',
    textMuted: 'rgba(0,0,0,0.62)',
    textDim: 'rgba(0,0,0,0.42)',
    up: '#00B85C',
    upSoft: 'rgba(0,184,92,0.10)',
    down: '#E5342A',
    downSoft: 'rgba(229,52,42,0.08)',
    accent: '#2563EB',
    accentSoft: 'rgba(37,99,235,0.10)',
    chip: 'rgba(0,0,0,0.04)',
  };
  return t;
}

// Standard font stacks — Geist for everything
const SW_FONT = "'Geist', ui-sans-serif, system-ui, -apple-system, sans-serif";
const SW_MONO = "'Geist Mono', ui-monospace, 'SF Mono', Menlo, monospace";

// ───────────────────────── Number formatting ─────────────────────────
function fmtPrice(v, currency = '€') {
  return currency + v.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}
function fmtPct(v) {
  const sign = v > 0 ? '+' : '';
  return sign + v.toFixed(2) + '%';
}
function fmtSigned(v, currency = '€') {
  const sign = v > 0 ? '+' : v < 0 ? '−' : '';
  return sign + currency + Math.abs(v).toFixed(2);
}

// ───────────────────────── Mini Line Chart ─────────────────────────
// `markers` overlays prediction dots on the line: [{ i: <dataIndex>, dir: 'buy'|'sell' }]
// `bgColor` controls the stroke ring around markers so they pop off the line.
function MiniChart({ data, w = 320, h = 80, color = '#00D26A', stroke = 2,
    fill = true, fillOpacity = 0.15, glow = false,
    markers = [], markerSize = 5, bgColor, upColor = '#00D26A', downColor = '#FF453A' }) {
  if (!data || data.length < 2) return null;
  const min = Math.min(...data);
  const max = Math.max(...data);
  const range = max - min || 1;
  const pad = 2;
  const innerW = w - pad * 2;
  const innerH = h - pad * 2;
  const xFor = (i) => pad + (i / (data.length - 1)) * innerW;
  const yFor = (v) => pad + innerH - ((v - min) / range) * innerH;
  const points = data.map((v, i) => [xFor(i), yFor(v)]);
  const pathD = points.map((p, i) => (i === 0 ? `M${p[0]},${p[1]}` : `L${p[0]},${p[1]}`)).join(' ');
  const areaD = `${pathD} L${points[points.length - 1][0]},${h - pad} L${points[0][0]},${h - pad} Z`;
  const gradId = 'mc-grad-' + Math.floor(Math.random() * 1e9);
  return (
    <svg width={w} height={h} style={{ display: 'block', overflow: 'visible' }}>
      <defs>
        <linearGradient id={gradId} x1="0" y1="0" x2="0" y2="1">
          <stop offset="0%" stopColor={color} stopOpacity={fillOpacity} />
          <stop offset="100%" stopColor={color} stopOpacity="0" />
        </linearGradient>
      </defs>
      {fill && <path d={areaD} fill={`url(#${gradId})`} />}
      <path d={pathD} stroke={color} strokeWidth={stroke} fill="none" strokeLinecap="round" strokeLinejoin="round"
        style={glow ? { filter: `drop-shadow(0 0 6px ${color}55)` } : undefined} />
      {/* prediction markers */}
      {markers.map((m, idx) => {
        if (m.i < 0 || m.i >= data.length) return null;
        const x = xFor(m.i), y = yFor(data[m.i]);
        const c = m.dir === 'sell' ? downColor : upColor;
        const r = m.size || markerSize;
        return (
          <g key={idx}>
            {/* glow halo for emphasis */}
            <circle cx={x} cy={y} r={r + 3} fill={c} opacity="0.18" />
            {/* main dot */}
            <circle cx={x} cy={y} r={r} fill={c} stroke={bgColor || '#fff'} strokeWidth={1.6} />
          </g>
        );
      })}
    </svg>
  );
}

// Predefined predictions for AMZN demo — green-heavy (97% bullish narrative)
const PREDICTIONS_AMZN = [
  { i: 4, dir: 'buy' }, { i: 10, dir: 'buy' }, { i: 14, dir: 'sell' },
  { i: 18, dir: 'buy' }, { i: 24, dir: 'buy' }, { i: 28, dir: 'buy' },
  { i: 33, dir: 'sell' }, { i: 37, dir: 'buy' }, { i: 42, dir: 'buy' },
  { i: 46, dir: 'buy' }, { i: 50, dir: 'buy' },
];

// ───────────────────────── Sparkline (tiny, for cards) ─────────────────────────
function Spark({ data, w = 60, h = 22, color = '#00D26A' }) {
  return <MiniChart data={data} w={w} h={h} color={color} stroke={1.5} fill={false} />;
}

// ───────────────────────── Upside Potential ─────────────────────────
// Compact visualization replacing absolute "TP €253.48" labels.
// A short horizontal bar whose fill width scales with absolute upside (0..maxPct),
// plus a mono percentage label colored by direction. Tiny enough to fit anywhere.
function UpsidePotential({ t, current, target, maxAbsPct = 25, barWidth = 32, size = 'sm' }) {
  const pct = ((target - current) / current) * 100;
  const up = pct >= 0;
  const c = up ? t.up : t.down;
  const fillW = Math.min(100, (Math.abs(pct) / maxAbsPct) * 100);
  const sizes = {
    sm: { fs: 11, gap: 5, barH: 3 },
    md: { fs: 12.5, gap: 6, barH: 4 },
  };
  const sz = sizes[size] || sizes.sm;
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: sz.gap }}>
      <div style={{
        width: barWidth, height: sz.barH, background: t.chip,
        borderRadius: sz.barH / 2, overflow: 'hidden',
      }}>
        <div style={{ width: fillW + '%', height: '100%', background: c, borderRadius: sz.barH / 2 }} />
      </div>
      <span style={{
        fontFamily: SW_MONO, fontSize: sz.fs, fontWeight: 700,
        color: c, letterSpacing: '-0.01em',
      }}>
        {up ? '+' : ''}{pct.toFixed(1)}%
      </span>
    </div>
  );
}
function SentimentBar({ buy, sell, t, height = 8, showLabels = false, animated = true }) {
  const total = buy + sell;
  const buyPct = total ? (buy / total) * 100 : 50;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8, width: '100%' }}>
      {showLabels && (
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
          fontFamily: SW_FONT, fontSize: 11, letterSpacing: '0.02em' }}>
          <span style={{ color: t.up, fontWeight: 600 }}>
            <span style={{ fontFamily: SW_MONO, fontSize: 13 }}>{buy}</span>
            <span style={{ marginLeft: 4, opacity: 0.75 }}>BUY</span>
          </span>
          <span style={{ color: t.textDim, fontWeight: 500 }}>{Math.round(buyPct)}% bullish</span>
          <span style={{ color: t.down, fontWeight: 600 }}>
            <span style={{ marginLeft: 4, opacity: 0.75 }}>SELL</span>
            <span style={{ fontFamily: SW_MONO, fontSize: 13, marginLeft: 4 }}>{sell}</span>
          </span>
        </div>
      )}
      <div style={{
        height, background: t.downSoft, borderRadius: height / 2, overflow: 'hidden',
        position: 'relative', display: 'flex',
      }}>
        <div style={{
          width: `${buyPct}%`, background: t.up, height: '100%',
          borderTopRightRadius: 0, borderBottomRightRadius: 0,
          transition: animated ? 'width .5s cubic-bezier(.2,.8,.3,1)' : 'none',
        }} />
        <div style={{
          flex: 1, background: t.down, height: '100%',
        }} />
      </div>
    </div>
  );
}

// ───────────────────────── Avatar (initials, gradient bg) ─────────────────────────
function Avatar({ name, size = 28, color }) {
  const palette = ['#F97316', '#3B82F6', '#A855F7', '#EC4899', '#14B8A6', '#EAB308', '#F43F5E', '#6366F1'];
  const seed = (name || '?').charCodeAt(0) + (name?.charCodeAt(1) || 0);
  const bg = color || palette[seed % palette.length];
  const initials = (name || '?').slice(0, 2).toUpperCase();
  return (
    <div style={{
      width: size, height: size, borderRadius: size / 2, background: bg,
      color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: SW_FONT, fontWeight: 600, fontSize: size * 0.4, flexShrink: 0,
      letterSpacing: '-0.01em',
    }}>{initials}</div>
  );
}

// ───────────────────────── Ticker logo bubble (square, brand-ish) ─────────────────────────
function Ticker({ symbol, size = 36, bg, fg = '#fff' }) {
  const palette = {
    AMZN: '#FF9900', GOOGL: '#4285F4', AAPL: '#000', MSFT: '#00A4EF', NVDA: '#76B900',
    GPRE: '#1E40AF', GRAL: '#7C3AED', GILD: '#0F766E', PFI: '#475569', WHR: '#DC2626',
    SCCO: '#B45309', FCEL: '#059669', TSEM: '#1E3A8A', BIO: '#BE185D', NOV: '#9333EA',
    REGN: '#0EA5E9',
  };
  const c = bg || palette[symbol] || '#525252';
  return (
    <div style={{
      width: size, height: size, borderRadius: Math.max(8, size * 0.22), background: c,
      color: fg, display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: SW_FONT, fontWeight: 700, fontSize: size * 0.34, letterSpacing: '-0.02em',
      flexShrink: 0,
    }}>{symbol.slice(0, 1)}</div>
  );
}

// ───────────────────────── Icons (lightweight SVG, stroke-based) ─────────────────────────
const Icon = {
  search: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="11" cy="11" r="7" /><path d="m21 21-4.3-4.3" />
    </svg>
  ),
  bell: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
    </svg>
  ),
  home: (c = 'currentColor', s = 22) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m3 9 9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z" />
    </svg>
  ),
  trending: (c = 'currentColor', s = 22) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="22 7 13.5 15.5 8.5 10.5 2 17" /><polyline points="16 7 22 7 22 13" />
    </svg>
  ),
  users: (c = 'currentColor', s = 22) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" /><circle cx="9" cy="7" r="4" />
      <path d="M22 21v-2a4 4 0 0 0-3-3.87" /><path d="M16 3.13a4 4 0 0 1 0 7.75" />
    </svg>
  ),
  news: (c = 'currentColor', s = 22) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="4" width="18" height="16" rx="2" /><line x1="7" y1="9" x2="17" y2="9" />
      <line x1="7" y1="13" x2="17" y2="13" /><line x1="7" y1="17" x2="12" y2="17" />
    </svg>
  ),
  briefcase: (c = 'currentColor', s = 22) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="2" y="7" width="20" height="14" rx="2" /><path d="M16 7V5a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2v2" />
    </svg>
  ),
  more: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={c}>
      <circle cx="5" cy="12" r="2" /><circle cx="12" cy="12" r="2" /><circle cx="19" cy="12" r="2" />
    </svg>
  ),
  back: (c = 'currentColor', s = 20) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m15 18-6-6 6-6" />
    </svg>
  ),
  share: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M4 12v7a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-7" /><polyline points="16 6 12 2 8 6" /><line x1="12" y1="2" x2="12" y2="15" />
    </svg>
  ),
  bookmark: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m19 21-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z" />
    </svg>
  ),
  arrowUp: (c = 'currentColor', s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <path d="m6 9 6-6 6 6" /><path d="M12 3v18" />
    </svg>
  ),
  arrowDown: (c = 'currentColor', s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <path d="m18 15-6 6-6-6" /><path d="M12 3v18" />
    </svg>
  ),
  check: (c = 'currentColor', s = 16) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round">
      <polyline points="20 6 9 17 4 12" />
    </svg>
  ),
  chevR: (c = 'currentColor', s = 16) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m9 18 6-6-6-6" />
    </svg>
  ),
  message: (c = 'currentColor', s = 16) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
    </svg>
  ),
  heart: (c = 'currentColor', s = 16, filled = false) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={filled ? c : 'none'} stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z" />
    </svg>
  ),
  plus: (c = 'currentColor', s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
      <path d="M12 5v14M5 12h14" />
    </svg>
  ),
  flame: (c = 'currentColor', s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={c}>
      <path d="M12 2c1 4 4 5 4 9a4 4 0 1 1-8 0c0-2 1-3 2-4-1 3 1 4 2 4 0-2-2-4 0-9z" />
    </svg>
  ),
  star: (c = 'currentColor', s = 14, filled = false) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={filled ? c : 'none'} stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
    </svg>
  ),
  award: (c = 'currentColor', s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="8" r="6" /><path d="M8.21 13.89 7 23l5-3 5 3-1.21-9.12" />
    </svg>
  ),
};

// ───────────────────────── Sharewise logo (original brand, 1:1) ─────────────────────────
function SwLogo({ size = 22, dark, compact = false }) {
  // compact = use the round icon-only mark instead of the full wordmark.
  // useful in tight top-app-bars where profile/avatar lives on the right.
  const src = compact ? 'assets/sharewise-icon.png'
    : ((typeof window !== 'undefined' && window.__resources && window.__resources.swLogo) || 'assets/sharewise-logo.png');
  // On dark backgrounds, lift the logo slightly so the saturated brand colors
  // pop against the near-black. Subtle — not an inversion, just a brightness boost.
  const darkFilter = dark ? 'brightness(1.15) saturate(1.1)' : 'none';
  return (
    <img src={src} alt="sharewise"
      style={{ height: size, width: 'auto', display: 'block', filter: darkFilter }} />
  );
}

// ───────────────────────── Top App Bar (mobile) ─────────────────────────
// Slim utility bar at the very top of top-level screens: compact icon-only logo on
// the left, search + bell + avatar on the right. Same hierarchy as the desktop
// top-nav (logo · nav · search · bell · avatar).
function TopAppBar({ t, hasNotifications = false, showSearch = true, showBell = true,
    showAvatar = true, userInitial = 'St', userColor }) {
  return (
    <div style={{
      padding: '52px 14px 4px',
      display: 'flex', alignItems: 'center', gap: 10,
    }}>
      <SwLogo size={26} dark={t.name === 'dark'} compact={true} />
      <div style={{ flex: 1 }} />
      {showSearch && (
        <div style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          {Icon.search(t.text, 20)}
        </div>
      )}
      {showBell && (
        <div style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center', position: 'relative' }}>
          {Icon.bell(t.text, 20)}
          {hasNotifications && (
            <div style={{ position: 'absolute', top: 7, right: 7, width: 8, height: 8,
              borderRadius: 4, background: t.accent, border: `1.5px solid ${t.bg}` }} />
          )}
        </div>
      )}
      {showAvatar && (
        <div style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <Avatar name={userInitial} size={30} color={userColor || t.accent} />
        </div>
      )}
    </div>
  );
}

// ───────────────────────── Bottom tab bar (mobile) ─────────────────────────
function BottomTabs({ t, active = 'home', userInitial = 'St', userColor }) {
  const tabs = [
    { id: 'home', label: 'Home', icon: Icon.home },
    { id: 'markets', label: 'Markets', icon: Icon.trending },
    { id: 'community', label: 'Community', icon: Icon.users },
    { id: 'news', label: 'News', icon: Icon.news },
    { id: 'profile', label: 'Profil', kind: 'avatar' },
  ];
  return (
    <div style={{
      position: 'absolute', bottom: 0, left: 0, right: 0, paddingBottom: 26, paddingTop: 8,
      background: t.name === 'dark'
        ? 'linear-gradient(180deg, rgba(10,10,10,0) 0%, rgba(10,10,10,0.92) 30%, rgba(10,10,10,1) 100%)'
        : 'linear-gradient(180deg, rgba(247,247,245,0) 0%, rgba(247,247,245,0.92) 30%, rgba(247,247,245,1) 100%)',
      backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
      borderTop: `0.5px solid ${t.border}`,
      display: 'flex', justifyContent: 'space-around', alignItems: 'center',
    }}>
      {tabs.map(tab => {
        const on = tab.id === active;
        const c = on ? t.text : t.textDim;
        return (
          <div key={tab.id} style={{
            display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 4,
            color: c, padding: '4px 8px',
          }}>
            {tab.kind === 'avatar' ? (
              <div style={{
                position: 'relative',
                padding: on ? 1.5 : 0,
                borderRadius: 999,
                background: on ? t.text : 'transparent',
              }}>
                <Avatar name={userInitial} size={22} color={userColor || t.accent} />
              </div>
            ) : tab.icon(c, 22)}
            <span style={{ fontFamily: SW_FONT, fontSize: 10, fontWeight: on ? 600 : 500, letterSpacing: '0.01em' }}>
              {tab.label}
            </span>
          </div>
        );
      })}
    </div>
  );
}

// ───────────────────────── Card (themed) ─────────────────────────
function Card({ t, children, style, pad = 16, radius = 16 }) {
  return (
    <div style={{
      background: t.surface, borderRadius: radius, padding: pad,
      border: `0.5px solid ${t.border}`,
      ...style,
    }}>{children}</div>
  );
}

// ───────────────────────── Chip / Pill ─────────────────────────
function Chip({ t, children, color, bg, style, size = 'sm' }) {
  const sizes = {
    xs: { fs: 10, pad: '3px 7px', radius: 6, fw: 600 },
    sm: { fs: 11, pad: '4px 9px', radius: 7, fw: 600 },
    md: { fs: 12, pad: '5px 11px', radius: 8, fw: 600 },
  };
  const sz = sizes[size];
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      fontFamily: SW_FONT, fontSize: sz.fs, fontWeight: sz.fw, letterSpacing: '0.01em',
      padding: sz.pad, borderRadius: sz.radius,
      color: color || t.text, background: bg || t.chip,
      whiteSpace: 'nowrap',
      ...style,
    }}>{children}</span>
  );
}

// ───────────────────────── Sample data ─────────────────────────
const SAMPLE = {
  // AMZN price history — synthetic, looks like a real chart
  amzn: [165,168,164,170,175,172,178,176,180,178,176,172,168,164,160,158,162,158,154,156,162,168,170,174,178,182,180,178,184,188,192,195,198,196,200,204,208,212,210,206,202,200,198,202,206,212,218,222,220,226,227,225,227],
  amznDown: [228,227,224,226,222,219,222,224,222,219,217,219,221,224,227,229,231,228,225,227],
  // Sentiment numbers
  buy: 214, sell: 6, hold: 12,
};

// ───────────────────────── Action icons (filled, brand-grade) ─────────────────────────
const ActIcon = {
  trade: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M3 17l6-6 4 4 8-8" /><path d="M17 7h4v4" />
    </svg>
  ),
  predict: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="12" cy="12" r="3" /><path d="M12 2v3M12 19v3M5 12H2M22 12h-3M19.07 4.93l-2.12 2.12M7.05 16.95l-2.12 2.12M19.07 19.07l-2.12-2.12M7.05 7.05L4.93 4.93" />
    </svg>
  ),
  watch: (c, s = 18, filled = false) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={filled ? c : 'none'} stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="m12 17.27 5.18 3.12-1.36-5.86L20.5 9.7l-5.94-.51L12 3.75 9.44 9.19l-5.94.51 4.68 4.83-1.36 5.86z" />
    </svg>
  ),
  paper: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="4" width="18" height="16" rx="2.5" /><line x1="3" y1="10" x2="21" y2="10" /><circle cx="8" cy="15" r="1.5" fill={c} />
    </svg>
  ),
  alert: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" /><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" />
      <circle cx="18" cy="6" r="3" fill="#FF453A" stroke="none" />
    </svg>
  ),
  share2: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <circle cx="18" cy="5" r="3" /><circle cx="6" cy="12" r="3" /><circle cx="18" cy="19" r="3" />
      <line x1="8.6" y1="13.5" x2="15.4" y2="17.5" /><line x1="15.4" y1="6.5" x2="8.6" y2="10.5" />
    </svg>
  ),
  more: (c, s = 18) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill={c}>
      <circle cx="5" cy="12" r="2" /><circle cx="12" cy="12" r="2" /><circle cx="19" cy="12" r="2" />
    </svg>
  ),
  external: (c, s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" />
      <polyline points="15 3 21 3 21 9" /><line x1="10" y1="14" x2="21" y2="3" />
    </svg>
  ),
  reply: (c, s = 14) => (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke={c} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
    </svg>
  ),
};

// ───────────────────────── Bottom action bar (Stock Detail) ─────────────────────────
// One primary Trade button + secondary Predict + icon shelf (Watch, More).
// Floats above the tab bar; opens sheets when tapped.
function StockActionBar({ t, watched = false, side = null /* 'buy'|'sell'|null */, layout = 'standard' }) {
  // layout: 'standard' = trade primary + predict ghost + icons
  //         'duo' = trade + predict equal weight
  const tradeBtn = (
    <div style={{
      flex: 1, padding: '14px 0', borderRadius: 14,
      background: t.up, color: '#fff', fontWeight: 700, fontSize: 14, letterSpacing: '0.01em',
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
      boxShadow: `0 8px 22px ${t.up}55`,
    }}>
      {ActIcon.trade('#fff', 16)}
      <span>Trade</span>
      <span style={{ fontFamily: SW_MONO, opacity: 0.7, fontSize: 12, fontWeight: 600 }}>via Flatex ›</span>
    </div>
  );
  const predictBtn = (
    <div style={{
      flex: layout === 'duo' ? 1 : '0 0 auto',
      padding: layout === 'duo' ? '14px 0' : '14px 18px', borderRadius: 14,
      background: t.surface, color: t.text,
      border: `0.5px solid ${t.borderStrong}`,
      fontWeight: 700, fontSize: 14, letterSpacing: '0.01em',
      display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 7,
    }}>
      {ActIcon.predict(t.text, 15)}
      <span>Predict</span>
    </div>
  );
  const iconBtn = (icon, filled) => (
    <div style={{
      width: 48, height: 48, borderRadius: 14,
      background: filled ? t.upSoft : t.surface,
      border: `0.5px solid ${filled ? t.up : t.borderStrong}`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>{icon}</div>
  );
  return (
    <div style={{
      position: 'absolute', bottom: 80, left: 12, right: 12,
      display: 'flex', gap: 8, alignItems: 'center',
    }}>
      {tradeBtn}
      {predictBtn}
      {layout !== 'duo' && iconBtn(ActIcon.watch(watched ? t.up : t.text, 18, watched), watched)}
      {layout !== 'duo' && iconBtn(ActIcon.more(t.text, 18))}
    </div>
  );
}

// ───────────────────────── Merged Post Card (prediction + discussion) ─────────────────────────
// A post is just a post. It may have a prediction attached (Buy/Sell + TP + confidence) or not.
// Anyone can reply, regardless of whether they have a prediction themselves.
function PostCard({ t, post }) {
  const p = post;
  const hasPred = !!p.prediction;
  const up = hasPred && p.prediction.dir === 'Buy';
  const dn = hasPred && p.prediction.dir === 'Sell';
  const predColor = up ? t.up : dn ? t.down : t.textMuted;
  const delta = hasPred ? ((p.prediction.tp - 227.35) / 227.35 * 100).toFixed(1) : null;
  return (
    <div style={{
      padding: 14, borderRadius: 16, background: t.surface,
      border: `0.5px solid ${t.border}`, position: 'relative',
      // Left edge tint when there's a prediction
      borderLeft: hasPred ? `2px solid ${predColor}` : `0.5px solid ${t.border}`,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
        <Avatar name={p.name} size={36} />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
            <span style={{ fontSize: 13.5, fontWeight: 600, letterSpacing: '-0.005em' }}>{p.name}</span>
            {p.badge && <Chip t={t} size="xs" color={t.accent} bg={t.accentSoft}>{p.badge}</Chip>}
            {p.replyTo && (
              <span style={{ fontSize: 11, color: t.textDim }}>
                replied to <span style={{ color: t.accent, fontWeight: 600 }}>@{p.replyTo}</span>
              </span>
            )}
          </div>
          <div style={{ fontSize: 11, color: t.textDim, fontFamily: SW_MONO, marginTop: 1, display: 'flex', gap: 8 }}>
            <span>{p.acc} accuracy</span>
            <span>·</span>
            <span>{p.when}</span>
          </div>
        </div>
      </div>

      {/* Prediction attached? show as inline chip-row, not a card-within-card */}
      {hasPred && (
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 8, marginBottom: 10,
          padding: '6px 10px 6px 8px', borderRadius: 999,
          background: up ? t.upSoft : t.downSoft,
        }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 3,
            fontFamily: SW_FONT, fontWeight: 700, fontSize: 11, color: predColor, letterSpacing: '0.02em' }}>
            {up ? Icon.arrowUp(predColor, 10) : Icon.arrowDown(predColor, 10)}
            {p.prediction.dir.toUpperCase()}
          </span>
          <span style={{ fontFamily: SW_MONO, fontSize: 11.5, color: t.text, fontWeight: 600 }}>€{p.prediction.tp}</span>
          <span style={{ fontFamily: SW_MONO, fontSize: 11, color: predColor, fontWeight: 600 }}>
            {delta > 0 ? '+' : ''}{delta}%
          </span>
          {p.prediction.conf && (
            <span style={{ fontFamily: SW_MONO, fontSize: 11, color: t.textDim }}>· conf {p.prediction.conf}/10</span>
          )}
        </div>
      )}

      <div style={{ fontSize: 13, color: t.text, lineHeight: 1.5, letterSpacing: '-0.005em', marginBottom: 12, textWrap: 'pretty' }}>
        {p.body}
      </div>

      <div style={{ display: 'flex', alignItems: 'center', gap: 16, color: t.textDim, fontSize: 12, fontWeight: 500 }}>
        <span style={{ display: 'flex', alignItems: 'center', gap: 5 }}>
          {Icon.heart(t.textDim, 14)}
          <span style={{ fontFamily: SW_MONO }}>{p.likes}</span>
        </span>
        <span style={{ display: 'flex', alignItems: 'center', gap: 5, color: p.replies > 5 ? t.text : t.textDim, fontWeight: 600 }}>
          {ActIcon.reply(p.replies > 5 ? t.text : t.textDim, 14)}
          <span style={{ fontFamily: SW_MONO }}>{p.replies}</span>
        </span>
        <span style={{ marginLeft: 'auto', color: t.accent, fontWeight: 600, display: 'flex', alignItems: 'center', gap: 4 }}>
          {p.replies > 0 ? `View ${p.replies} ${p.replies === 1 ? 'reply' : 'replies'}` : 'Reply'}
          {Icon.chevR(t.accent, 13)}
        </span>
      </div>
    </div>
  );
}

// ───────────────────────── Sample posts (predictions + discussions merged) ─────────────────────────
const POSTS = [
  {
    name: 'PapaSmurf', acc: '78%', when: '2h', badge: 'TOP 1%',
    prediction: { dir: 'Buy', tp: 268, conf: 9 },
    body: 'AWS revenue growth re-accelerating + ad business compounding. Q2 setup looks like a blowout. Holding through earnings.',
    likes: 47, replies: 12,
  },
  {
    name: 'Lisa_K', acc: '54%', when: '4h', replyTo: 'PapaSmurf',
    // Discussion post — no prediction. Reply to the prediction above.
    body: 'How are you thinking about the FTC overhang? Feels like a real risk before EOY ruling.',
    likes: 8, replies: 3,
  },
  {
    name: 'fenjal', acc: '71%', when: '6h',
    prediction: { dir: 'Buy', tp: 255, conf: 7 },
    body: 'Cost discipline is real. Margin expansion in NA retail is the underrated story here.',
    likes: 23, replies: 4,
  },
  {
    name: 'Investorin_DE', acc: '49%', when: '12h',
    body: 'Frage: macht jemand hier covered calls auf AMZN? IV ist gerade attraktiv vor den Earnings.',
    likes: 11, replies: 7,
  },
  {
    name: 'Bankster', acc: '64%', when: '1d',
    prediction: { dir: 'Sell', tp: 195, conf: 6 },
    body: 'Multiple is stretched vs historical average. Wait for pullback.',
    likes: 11, replies: 18,
  },
];

Object.assign(window, {
  useTheme, SW_FONT, SW_MONO, fmtPrice, fmtPct, fmtSigned,
  MiniChart, Spark, SentimentBar, UpsidePotential, Avatar, Ticker, Icon, SwLogo, ActIcon,
  BottomTabs, TopAppBar, Card, Chip, SAMPLE, PREDICTIONS_AMZN,
  StockActionBar, PostCard, POSTS,
});
