// Biertje — Shared UI Components
const { useState, useEffect } = React;

// ── Mobile breakpoint hook ────────────────────────────────────────
function useIsMobile(breakpoint = 640) {
  const [isMobile, setIsMobile] = useState(window.innerWidth < breakpoint);
  useEffect(() => {
    const handler = () => setIsMobile(window.innerWidth < breakpoint);
    window.addEventListener('resize', handler);
    return () => window.removeEventListener('resize', handler);
  }, []);
  return isMobile;
}

// ── Theme ─────────────────────────────────────────────────────────
const T = {
  amber: '#F5A623', amberDark: '#C7831A', amberLight: '#FEF0C7',
  cream: '#FFF8E7', foam: '#FFFBF0', chalk: '#F5F0E8',
  brown: '#3D2B1F', brownMid: '#6B4F3A', brownLight: '#8C6B52',
  brownBorder: '#E8DDD0',
  berry: '#E83E5A', berryBg: '#FFE4E9',
  sky: '#2BBCDB', skyBg: '#E0F7FA',
  leaf: '#4CAF50', leafBg: '#E8F5E9',
  grape: '#7B5EA7', grapeBg: '#EDE7F6',
  shadow: '0 4px 16px rgba(61,43,31,0.10)',
  shadowLg: '0 8px 32px rgba(61,43,31,0.18)',
  shadowAmber: '0 0 24px rgba(245,166,35,0.40)',
};

// ── Helpers ───────────────────────────────────────────────────────
function getAvatarColor(name) {
  const colors = [T.amberLight, '#EDE7F6', '#E0F7FA', '#FFE4E9', '#F5F0E8', '#E8F5E9'];
  let hash = 0;
  for (let i = 0; i < (name || '').length; i++) {
    hash = ((hash << 5) - hash) + name.charCodeAt(i);
    hash |= 0;
  }
  return colors[Math.abs(hash) % colors.length];
}

function timeAgo(dateStr) {
  if (!dateStr) return '';
  const diff = Date.now() - new Date(dateStr).getTime();
  const mins  = Math.floor(diff / 60000);
  const hours = Math.floor(mins  / 60);
  const days  = Math.floor(hours / 24);
  if (mins  < 1)  return 'just now';
  if (mins  < 60) return `${mins}m ago`;
  if (hours < 24) return `${hours}h ago`;
  if (days  === 1) return 'Yesterday';
  if (days  < 7)  return new Date(dateStr).toLocaleDateString('nl-NL', { weekday: 'short' });
  return new Date(dateStr).toLocaleDateString('nl-NL', { day: 'numeric', month: 'short' });
}

function generateInviteCode() {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  let code = 'BRTJE-';
  for (let i = 0; i < 4; i++) code += chars[Math.floor(Math.random() * chars.length)];
  return code;
}

function computeAchievementProgress(achievement, logs) {
  const total = achievement.total || 1;
  const type  = achievement.trigger_type;

  const filteredLogs = achievement.trigger_category
    ? logs.filter(l => l.drink_category === achievement.trigger_category)
    : logs;

  // ── Data-driven: trigger_type based ──────────────────────────────
  if (type) {
    switch (type) {

      case 'total_count':
        return { progress: Math.min(filteredLogs.length, total), total };

      case 'streak': {
        // Max ever consecutive-day streak
        const dateSet = new Set(filteredLogs.map(l => l.logged_at.slice(0, 10)));
        const sorted  = [...dateSet].sort();
        let maxS = sorted.length ? 1 : 0, cur = sorted.length ? 1 : 0;
        for (let i = 1; i < sorted.length; i++) {
          cur = (new Date(sorted[i]) - new Date(sorted[i-1])) / 86400000 === 1 ? cur + 1 : 1;
          if (cur > maxS) maxS = cur;
        }
        return { progress: Math.min(maxS, total), total };
      }

      case 'unique_locations': {
        const locs = new Set(filteredLogs.filter(l => l.location).map(l => l.location.trim().toLowerCase()));
        return { progress: Math.min(locs.size, total), total };
      }

      case 'unique_beer_names': {
        const names = new Set(filteredLogs.filter(l => l.beer_name).map(l => l.beer_name.trim().toLowerCase()));
        return { progress: Math.min(names.size, total), total };
      }

      case 'unique_beer_types': {
        const types = new Set(filteredLogs.filter(l => l.beer_type).map(l => l.beer_type.trim().toLowerCase()));
        return { progress: Math.min(types.size, total), total };
      }

      case 'timing_friday_pm': {
        const cnt = filteredLogs.filter(l => { const d = new Date(l.logged_at); return d.getDay() === 5 && d.getHours() >= 16 && d.getHours() < 19; }).length;
        return { progress: Math.min(cnt, total), total };
      }

      case 'timing_morning': {
        const cnt = filteredLogs.filter(l => new Date(l.logged_at).getHours() < 10).length;
        return { progress: Math.min(cnt, total), total };
      }

      case 'timing_night': {
        const cnt = filteredLogs.filter(l => new Date(l.logged_at).getHours() < 5).length;
        return { progress: Math.min(cnt, total), total };
      }

      case 'timing_saturday': {
        const sats = new Set(filteredLogs.filter(l => new Date(l.logged_at).getDay() === 6).map(l => l.logged_at.slice(0, 10)));
        return { progress: Math.min(sats.size, total), total };
      }

      case 'timing_weekday': {
        const done = filteredLogs.some(l => { const d = new Date(l.logged_at).getDay(); return d >= 1 && d <= 5; });
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'timing_month_first': {
        const done = filteredLogs.some(l => new Date(l.logged_at).getDate() === 1);
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'timing_jan1': {
        const done = filteredLogs.some(l => { const d = new Date(l.logged_at); return d.getMonth() === 0 && d.getDate() === 1; });
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'timing_apr27': {
        const done = filteredLogs.some(l => { const d = new Date(l.logged_at); return d.getMonth() === 3 && d.getDate() === 27; });
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'both_weekend_days': {
        const wkds = {};
        filteredLogs.forEach(l => {
          const d = new Date(l.logged_at), day = d.getDay();
          if (day === 0 || day === 6) {
            const jan1 = new Date(d.getFullYear(), 0, 1);
            const wk = `${d.getFullYear()}-${Math.floor((d - jan1) / 604800000)}`;
            if (!wkds[wk]) wkds[wk] = new Set();
            wkds[wk].add(day);
          }
        });
        const done = Object.values(wkds).some(s => s.has(0) && s.has(6));
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'with_note': {
        const cnt = filteredLogs.filter(l => l.comment && l.comment.trim().length > 0).length;
        return { progress: Math.min(cnt, total), total };
      }

      case 'with_rating_5': {
        const done = filteredLogs.some(l => l.rating === 5);
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'same_beer_repeat': {
        const filter = achievement.trigger_filter;
        if (filter) {
          const cnt = filteredLogs.filter(l => l.beer_name && l.beer_name.trim().toLowerCase() === filter.trim().toLowerCase()).length;
          return { progress: Math.min(cnt, total), total };
        }
        const counts = {};
        filteredLogs.filter(l => l.beer_name).forEach(l => { const n = l.beer_name.trim().toLowerCase(); counts[n] = (counts[n] || 0) + 1; });
        const maxR = Object.values(counts).length ? Math.max(...Object.values(counts)) : 0;
        return { progress: Math.min(maxR, total), total };
      }

      case 'unique_seasons': {
        const getSeason = m => (m < 2 || m === 11) ? 0 : m < 5 ? 1 : m < 8 ? 2 : 3;
        const seasons = new Set(filteredLogs.map(l => getSeason(new Date(l.logged_at).getMonth())));
        return { progress: Math.min(seasons.size, total), total };
      }

      case 'locations_same_day': {
        const byDay = {};
        filteredLogs.filter(l => l.location).forEach(l => {
          const day = l.logged_at.slice(0, 10);
          if (!byDay[day]) byDay[day] = new Set();
          byDay[day].add(l.location.trim().toLowerCase());
        });
        const maxLocs = Object.values(byDay).length ? Math.max(...Object.values(byDay).map(s => s.size)) : 0;
        return { progress: Math.min(maxLocs, total), total };
      }

      case 'monthly_all_days': {
        const byMonth = {};
        filteredLogs.forEach(l => {
          const d = new Date(l.logged_at);
          const key = `${d.getFullYear()}-${d.getMonth()}`;
          if (!byMonth[key]) byMonth[key] = { days: new Set(), dim: new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate() };
          byMonth[key].days.add(d.getDate());
        });
        const done = Object.values(byMonth).some(m => m.days.size >= m.dim);
        return { progress: done ? 1 : 0, total: 1 };
      }

      case 'weekly_streaks': {
        const weekKey = d => { const j = new Date(d.getFullYear(), 0, 1); return `${d.getFullYear()}-${Math.floor((d - j) / 604800000)}`; };
        const weeks = new Set(filteredLogs.map(l => weekKey(new Date(l.logged_at))));
        const sorted = [...weeks].sort();
        let maxW = sorted.length ? 1 : 0, curW = sorted.length ? 1 : 0;
        for (let i = 1; i < sorted.length; i++) {
          const [ay, aw] = sorted[i-1].split('-').map(Number);
          const [by, bw] = sorted[i].split('-').map(Number);
          const consec = (by === ay && bw === aw + 1) || (by === ay + 1 && aw >= 51 && bw <= 1);
          curW = consec ? curW + 1 : 1;
          if (curW > maxW) maxW = curW;
        }
        return { progress: Math.min(maxW, total), total };
      }

      case 'limited':
      case 'manual':
        return { progress: 0, total: 1 };

      default: break;
    }
  }

  // ── Legacy: name-based (existing English-named badges) ────────────
  switch (achievement.name) {
    case 'First Sip':       return { progress: Math.min(filteredLogs.length, 1),   total: 1  };
    case 'Double Digits':   return { progress: Math.min(filteredLogs.length, 10),  total: 10 };
    case 'Century Club':    return { progress: Math.min(filteredLogs.length, 100), total: 100 };
    case 'On a Roll': {
      const days = new Set(filteredLogs.map(l => l.logged_at.slice(0, 10))).size;
      return { progress: Math.min(days, 7), total: 7 };
    }
    case 'Weekend Warrior': {
      const weeks = {};
      filteredLogs.forEach(l => {
        const d = new Date(l.logged_at), day = d.getDay();
        if (day === 0 || day === 6) {
          const wk = `${d.getFullYear()}-${Math.floor(d.getDate() / 7)}`;
          if (!weeks[wk]) weeks[wk] = new Set();
          weeks[wk].add(day);
        }
      });
      return { progress: Object.values(weeks).some(s => s.has(0) && s.has(6)) ? 1 : 0, total: 1 };
    }
    case 'Night Owl': {
      return { progress: filteredLogs.some(l => new Date(l.logged_at).getHours() < 5) ? 1 : 0, total: 1 };
    }
    case 'World Traveler': {
      const locs = new Set(filteredLogs.filter(l => l.location && !/home|thuis/i.test(l.location)).map(l => l.location.trim().toLowerCase()));
      return { progress: Math.min(locs.size, 5), total: 5 };
    }
    case 'Nomad': {
      const locs = new Set(filteredLogs.filter(l => l.location).map(l => l.location.trim().toLowerCase()));
      return { progress: Math.min(locs.size, 10), total: 10 };
    }
    case 'Homebody': {
      const cnt = filteredLogs.filter(l => l.location && /home|thuis/i.test(l.location)).length;
      return { progress: Math.min(cnt, 20), total: 20 };
    }
    case 'Party Starter':
      return { progress: filteredLogs.some(l => new Date(l.logged_at).getDay() === 5) ? 1 : 0, total: 1 };
    default:
      return { progress: 0, total: 1 };
  }
}

// ── Loading ───────────────────────────────────────────────────────
function Loading({ fullPage = false }) {
  const inner = (
    <div style={{ textAlign: 'center', padding: 40 }}>
      <div style={{ fontSize: 44, marginBottom: 12, display: 'inline-block', animation: 'spin 1s linear infinite' }}>🍺</div>
      <div style={{ fontFamily: "'Fredoka', sans-serif", fontSize: 18, color: T.brownMid }}>Laden...</div>
      <style>{`@keyframes spin { 0%{transform:rotate(0deg)} 100%{transform:rotate(360deg)} }`}</style>
    </div>
  );
  if (!fullPage) return inner;
  return (
    <div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', background: T.cream }}>
      {inner}
    </div>
  );
}

// ── Avatar ────────────────────────────────────────────────────────
function Avatar({ name = '?', size = 40, color, textColor = T.brown, src }) {
  const bg = color || getAvatarColor(name);
  const initial = (name || '?').charAt(0).toUpperCase();
  if (src) {
    return (
      <div style={{
        width: size, height: size, borderRadius: '50%', overflow: 'hidden',
        flexShrink: 0, userSelect: 'none', background: bg,
      }}>
        <img src={src} alt={name}
          style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }}
        />
      </div>
    );
  }
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', background: bg,
      color: textColor, display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: "'Fredoka', sans-serif", fontWeight: 700, fontSize: size * 0.42,
      flexShrink: 0, userSelect: 'none',
    }}>{initial}</div>
  );
}

// ── Badge ─────────────────────────────────────────────────────────
function Badge({ children, variant = 'amber', size = 'md' }) {
  const variants = {
    amber:   { bg: T.amberLight, color: '#9A5F0C' },
    berry:   { bg: T.berryBg,    color: '#C42D47' },
    leaf:    { bg: T.leafBg,     color: '#2E7D32' },
    sky:     { bg: T.skyBg,      color: '#127590' },
    grape:   { bg: T.grapeBg,    color: '#3F2766' },
    dark:    { bg: T.brown,      color: T.foam },
    outline: { bg: 'transparent', color: T.brownMid, border: `2px solid ${T.brownBorder}` },
    locked:  { bg: T.chalk,      color: '#B89880' },
  };
  const sizes = {
    sm: { fontSize: 11, padding: '2px 8px' },
    md: { fontSize: 12, padding: '4px 12px' },
    lg: { fontSize: 14, padding: '6px 16px' },
  };
  const v = variants[variant] || variants.amber;
  const s = sizes[size] || sizes.md;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 4,
      background: v.bg, color: v.color, border: v.border || 'none',
      borderRadius: 9999, fontWeight: 700, fontFamily: "'Nunito', sans-serif", ...s,
    }}>{children}</span>
  );
}

// ── Button ────────────────────────────────────────────────────────
function Button({ children, variant = 'primary', size = 'md', onClick, style: sx = {}, disabled = false, fullWidth = false }) {
  const [pressed, setPressed] = useState(false);
  const [hovered, setHovered] = useState(false);
  const variants = {
    primary:      { bg: T.amber,       color: T.brown,    border: 'none',                             shadow: '0 4px 14px rgba(245,166,35,0.45)' },
    secondary:    { bg: T.foam,        color: T.brown,    border: `2px solid ${T.brownBorder}`,       shadow: T.shadow },
    dark:         { bg: T.brown,       color: T.foam,     border: 'none',                             shadow: '0 4px 14px rgba(61,43,31,0.30)' },
    ghost:        { bg: 'transparent', color: T.brownMid, border: `2px solid rgba(245,166,35,0.40)`,  shadow: 'none' },
    'ghost-light':{ bg: 'transparent', color: T.foam,     border: `2px solid rgba(255,255,255,0.40)`, shadow: 'none' },
    danger:       { bg: T.berry,       color: '#fff',     border: 'none',                             shadow: '0 4px 14px rgba(232,62,90,0.35)' },
    leaf:         { bg: T.leaf,        color: '#fff',     border: 'none',                             shadow: '0 4px 14px rgba(76,175,80,0.30)' },
  };
  const sizes = {
    xs: { fontSize: 12, padding: '5px 12px',  radius: 8  },
    sm: { fontSize: 13, padding: '7px 16px',  radius: 10 },
    md: { fontSize: 15, padding: '11px 24px', radius: 12 },
    lg: { fontSize: 17, padding: '15px 32px', radius: 13 },
    xl: { fontSize: 19, padding: '18px 40px', radius: 14 },
  };
  const v = variants[variant] || variants.primary;
  const s = sizes[size] || sizes.md;
  return (
    <button
      onClick={onClick} disabled={disabled}
      onMouseDown={() => setPressed(true)} onMouseUp={() => setPressed(false)}
      onMouseEnter={() => setHovered(true)} onMouseLeave={() => { setHovered(false); setPressed(false); }}
      style={{
        background: disabled ? T.brownBorder : v.bg, color: disabled ? '#B89880' : v.color,
        border: v.border, borderRadius: s.radius, padding: s.padding, fontSize: s.fontSize,
        fontFamily: "'Nunito', sans-serif", fontWeight: 700,
        cursor: disabled ? 'not-allowed' : 'pointer',
        boxShadow: disabled ? 'none' : v.shadow,
        transform: `scale(${pressed ? 0.97 : hovered ? 1.02 : 1})`,
        transition: 'all 150ms cubic-bezier(0.4,0,0.2,1)',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
        outline: 'none', width: fullWidth ? '100%' : 'auto', ...sx,
      }}
    >{children}</button>
  );
}

// ── Card ──────────────────────────────────────────────────────────
function Card({ children, style: sx = {}, dark = false, hover = false }) {
  const [hovered, setHovered] = useState(false);
  return (
    <div
      onMouseEnter={() => hover && setHovered(true)}
      onMouseLeave={() => hover && setHovered(false)}
      style={{
        background: dark ? T.brown : T.foam, borderRadius: 16,
        border: `1.5px solid ${dark ? 'rgba(245,166,35,0.15)' : 'rgba(245,166,35,0.13)'}`,
        boxShadow: hovered ? T.shadowLg : T.shadow,
        transform: hovered ? 'translateY(-2px)' : 'none',
        transition: 'box-shadow 200ms ease, transform 200ms ease',
        overflow: 'hidden', ...sx,
      }}
    >{children}</div>
  );
}

// ── StatCard ──────────────────────────────────────────────────────
function StatCard({ label, value, sub, dark = false, amber = false, berry = false }) {
  const bg       = dark ? T.brown : amber ? T.amber : berry ? T.berry : T.foam;
  const valColor = dark ? T.amber : amber ? T.brown : berry ? '#fff' : T.amber;
  const lblColor = dark ? 'rgba(255,251,240,0.55)' : amber ? 'rgba(61,43,31,0.60)' : berry ? 'rgba(255,255,255,0.75)' : T.brownLight;
  const subColor = dark ? 'rgba(255,251,240,0.40)' : amber ? T.brownMid : berry ? 'rgba(255,255,255,0.65)' : '#B89880';
  return (
    <div style={{ background: bg, borderRadius: 16, border: `1.5px solid rgba(245,166,35,0.12)`, boxShadow: T.shadow, padding: '16px 20px', flex: 1, minWidth: 110 }}>
      <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: lblColor, marginBottom: 6, fontFamily: "'Nunito', sans-serif" }}>{label}</div>
      <div style={{ fontFamily: "'Fredoka', sans-serif", fontSize: 36, fontWeight: 700, color: valColor, lineHeight: 1 }}>{value}</div>
      {sub && <div style={{ fontSize: 12, color: subColor, marginTop: 5, fontFamily: "'Nunito', sans-serif" }}>{sub}</div>}
    </div>
  );
}

// ── ProgressBar ───────────────────────────────────────────────────
function ProgressBar({ value = 0, max = 100, color = T.amber, height = 8 }) {
  const pct = Math.min(100, Math.max(0, (value / max) * 100));
  return (
    <div style={{ background: T.brownBorder, borderRadius: 9999, height, overflow: 'hidden' }}>
      <div style={{ height: '100%', width: `${pct}%`, background: color, borderRadius: 9999, transition: 'width 600ms cubic-bezier(0.34,1.56,0.64,1)' }} />
    </div>
  );
}

// ── Input ─────────────────────────────────────────────────────────
function Input({ label, placeholder, value, onChange, type = 'text', icon, hint }) {
  const [focused, setFocused] = useState(false);
  return (
    <div style={{ marginBottom: 16 }}>
      {label && <div style={{ fontSize: 12, fontWeight: 700, letterSpacing: '0.06em', textTransform: 'uppercase', color: T.brownLight, marginBottom: 6, fontFamily: "'Nunito', sans-serif" }}>{label}</div>}
      <div style={{ position: 'relative' }}>
        {icon && <div style={{ position: 'absolute', left: 12, top: '50%', transform: 'translateY(-50%)', fontSize: 16, pointerEvents: 'none' }}>{icon}</div>}
        <input
          type={type} placeholder={placeholder} value={value}
          onChange={e => onChange && onChange(e.target.value)}
          onFocus={() => setFocused(true)} onBlur={() => setFocused(false)}
          style={{
            width: '100%', padding: icon ? '11px 14px 11px 40px' : '11px 14px',
            fontFamily: "'Nunito', sans-serif", fontSize: 14, color: T.brown,
            background: T.cream, border: `2px solid ${focused ? T.amber : T.brownBorder}`,
            borderRadius: 10, outline: 'none', transition: 'border-color 150ms ease',
          }}
        />
      </div>
      {hint && <div style={{ fontSize: 11, color: '#B89880', marginTop: 4, fontFamily: "'Nunito', sans-serif" }}>{hint}</div>}
    </div>
  );
}

// ── StarRating ────────────────────────────────────────────────────
function StarRating({ value = 0, onChange, size = 28, readonly = false }) {
  const [hover, setHover] = useState(null);
  const display = hover !== null ? hover : value;
  return (
    <div style={{ display: 'flex', gap: 2 }}>
      {[1,2,3,4,5].map(i => (
        <span key={i}
          onClick={() => !readonly && onChange && onChange(i)}
          onMouseEnter={() => !readonly && setHover(i)}
          onMouseLeave={() => !readonly && setHover(null)}
          style={{ fontSize: size, cursor: readonly ? 'default' : 'pointer', transition: 'transform 120ms ease', display: 'inline-block', transform: hover === i && !readonly ? 'scale(1.3)' : 'scale(1)', lineHeight: 1 }}
        >{display >= i ? '⭐' : '☆'}</span>
      ))}
    </div>
  );
}

// ── GroupBarChart ─────────────────────────────────────────────────
function GroupBarChart({ members, period, onPeriodChange, onMemberClick }) {
  const [animated, setAnimated] = useState(false);
  useEffect(() => {
    setAnimated(false);
    const t = setTimeout(() => setAnimated(true), 150);
    return () => clearTimeout(t);
  }, [period, members]);

  if (!members || members.length === 0) {
    return <div style={{ textAlign: 'center', padding: '32px 0', color: T.brownLight, fontFamily: "'Nunito', sans-serif", fontSize: 14 }}>Nog geen drankjes gelogd — log de eerste!</div>;
  }

  const sorted  = [...members].sort((a, b) => b.count - a.count);
  const maxVal  = Math.max(...sorted.map(m => m.count), 1);
  const medals  = ['🥇','🥈','🥉'];

  return (
    <div>
      {period && (
        <div style={{ display: 'flex', gap: 6, marginBottom: 18 }}>
          {['Dag','Week','Maand','Alles'].map(p => (
            <button key={p} onClick={() => onPeriodChange && onPeriodChange(p)} style={{
              padding: '5px 14px', borderRadius: 9999, border: `2px solid ${period === p ? T.amber : T.brownBorder}`,
              background: period === p ? T.amber : 'transparent',
              color: period === p ? T.brown : T.brownMid,
              fontFamily: "'Nunito', sans-serif", fontWeight: 700, fontSize: 12,
              cursor: 'pointer', transition: 'all 150ms ease',
            }}>{p}</button>
          ))}
        </div>
      )}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
        {sorted.map((m, i) => (
          <div key={m.user_id || m.name} style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{ width: 28, textAlign: 'center', fontSize: i < 3 ? 18 : 13, fontFamily: "'Fredoka', sans-serif", fontWeight: 700, color: i < 3 ? 'inherit' : '#B89880', flexShrink: 0 }}>
              {i < 3 ? medals[i] : i + 1}
            </div>
            <div onClick={() => !m.isMe && onMemberClick && onMemberClick(m.user_id)} style={{ cursor: !m.isMe && onMemberClick ? 'pointer' : 'default' }}>
              <Avatar name={m.name} size={32} />
            </div>
            <div
              onClick={() => !m.isMe && onMemberClick && onMemberClick(m.user_id)}
              style={{ width: 70, fontSize: 12, fontWeight: 700, color: m.isMe ? T.amberDark : T.brownMid, fontFamily: "'Nunito', sans-serif", flexShrink: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', cursor: !m.isMe && onMemberClick ? 'pointer' : 'default', textDecoration: !m.isMe && onMemberClick ? 'underline dotted' : 'none' }}
            >
              {m.name}{m.isMe && ' 👈'}
            </div>
            <div style={{ flex: 1, background: T.brownBorder, borderRadius: 9999, height: 16, overflow: 'hidden' }}>
              <div style={{
                height: '100%', borderRadius: 9999,
                width: animated ? `${(m.count / maxVal) * 100}%` : '0%',
                background: m.isMe ? `linear-gradient(90deg, ${T.amber}, #FCC24A)` : '#C4AD97',
                boxShadow: m.isMe ? '0 0 10px rgba(245,166,35,0.55)' : 'none',
                transition: `width ${600 + i * 110}ms cubic-bezier(0.34,1.56,0.64,1)`,
              }} />
            </div>
            <div style={{ width: 34, textAlign: 'right', fontFamily: "'Fredoka', sans-serif", fontSize: 20, fontWeight: 700, color: m.isMe ? T.amber : T.brownMid, flexShrink: 0 }}>{m.count}</div>
          </div>
        ))}
      </div>
    </div>
  );
}

// ── AchievementCard ───────────────────────────────────────────────
function AchievementCard({ icon, name, desc, tier, progress, total, unlocked }) {
  const [hovered, setHovered] = useState(false);
  const tiers = {
    gold:     { bar: T.amber,   bg: T.amberLight, color: '#9A5F0C', top: `linear-gradient(90deg,${T.amber},#FCC24A)` },
    silver:   { bar: '#9E9E9E', bg: '#F5F5F5',    color: '#616161', top: 'linear-gradient(90deg,#9E9E9E,#E0E0E0)' },
    platinum: { bar: T.grape,   bg: T.grapeBg,    color: '#3F2766', top: `linear-gradient(90deg,${T.grape},#9575CD)` },
    bronze:   { bar: '#CD7F32', bg: '#FFF3E0',    color: '#8B5000', top: 'linear-gradient(90deg,#CD7F32,#E8A44A)' },
    diamond:  { bar: '#0D47A1', bg: '#E3F2FD',    color: '#0D47A1', top: 'linear-gradient(90deg,#1565C0,#42A5F5,#1565C0)' },
    locked:   { bar: '#D4C0A8', bg: T.brownBorder,color: '#B89880', top: T.brownBorder },
  };
  const t = tiers[tier] || tiers.locked;
  return (
    <div
      onMouseEnter={() => setHovered(true)} onMouseLeave={() => setHovered(false)}
      style={{
        background: T.foam, borderRadius: 16, overflow: 'hidden', padding: '20px 18px 16px',
        border: `1.5px solid rgba(245,166,35,0.13)`,
        boxShadow: hovered ? T.shadowLg : T.shadow,
        transform: hovered ? 'translateY(-3px) scale(1.01)' : 'none',
        transition: 'all 200ms ease', position: 'relative',
        opacity: unlocked === false ? 0.58 : 1,
      }}
    >
      <div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 4, background: t.top }} />
      <div style={{ fontSize: 38, marginBottom: 8 }}>{icon}</div>
      <span style={{ display: 'inline-block', fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', background: t.bg, color: t.color, borderRadius: 9999, padding: '2px 10px', marginBottom: 8, fontFamily: "'Nunito', sans-serif" }}>{tier}</span>
      <div style={{ fontFamily: "'Fredoka', sans-serif", fontSize: 18, fontWeight: 700, color: T.brown, marginBottom: 4 }}>{name}</div>
      <div style={{ fontSize: 12, color: T.brownLight, lineHeight: 1.5, marginBottom: 12, fontFamily: "'Nunito', sans-serif" }}>{desc}</div>
      <ProgressBar value={progress} max={total} color={t.bar} />
      <div style={{ fontSize: 11, color: '#B89880', marginTop: 5, fontFamily: "'Nunito', sans-serif" }}>{progress} / {total}</div>
    </div>
  );
}

// ── NavBar ────────────────────────────────────────────────────────
function NavBar({ screen, onNav, onLogBeer, profile, group, groups = [], isOwner, onSwitchGroup, onSignOut }) {
  const [showGroupPicker, setShowGroupPicker] = useState(false);
  const isMobile = useIsMobile();

  const mobileLinks = [
    { id: 'Dashboard',    label: 'Home',    emoji: '🏠' },
    { id: 'Feed',         label: 'Feed',    emoji: '📱' },
    { id: 'Achievements', label: 'Badges',  emoji: '🏆' },
    { id: 'Groups',       label: 'Groepen', emoji: '👥' },
  ];
  const desktopLinks = [
    { id: 'Dashboard',    label: 'Home',    emoji: '🏠' },
    { id: 'Feed',         label: 'Feed',    emoji: '📱' },
    { id: 'Achievements', label: 'Badges',  emoji: '🏆' },
    { id: 'Groups',       label: 'Groepen', emoji: '👥' },
  ];

  const AvatarMenu = ({ size }) => (
    <div
      onClick={() => onNav('Profile')}
      title="Mijn profiel"
      style={{ cursor: 'pointer', borderRadius: '50%', flexShrink: 0 }}
      onMouseEnter={e => e.currentTarget.style.opacity = '0.82'}
      onMouseLeave={e => e.currentTarget.style.opacity = '1'}
    >
      <Avatar name={profile ? profile.display_name : '?'} size={size} src={profile?.avatar_url} />
    </div>
  );

  if (isMobile) {
    const groupLabel = group ? `${group.emoji} ${group.name}` : '🥂 Drankmaatje';
    return (
      <>
        {/* Mobile top bar */}
        <nav style={{
          position: 'sticky', top: 0, zIndex: 100, flexShrink: 0,
          background: T.foam, borderBottom: `1.5px solid rgba(245,166,35,0.20)`,
          height: 54, display: 'flex', alignItems: 'center',
          padding: '0 14px', boxShadow: '0 2px 10px rgba(61,43,31,0.07)',
        }}>
          <div
            onClick={() => onNav('Groups')}
            style={{
              fontFamily: "'Fredoka', sans-serif", fontSize: 18, fontWeight: 700,
              color: T.brown, flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              cursor: 'pointer', userSelect: 'none',
            }}
          >
            {groupLabel}
            {groups.length > 1 && <span style={{ fontSize: 11, color: T.brownLight, marginLeft: 4 }}>▼</span>}
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', flexShrink: 0 }}>
            <Button variant="dark" size="sm" onClick={onLogBeer}
              style={{ fontSize: 12, padding: '7px 13px', borderRadius: 9, minWidth: 0 }}>
              + Log
            </Button>
            <AvatarMenu size={32} />
          </div>
        </nav>

        {/* Mobile bottom nav */}
        <div style={{
          position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 100,
          background: T.foam, borderTop: `1.5px solid rgba(245,166,35,0.18)`,
          display: 'flex', height: 62, alignItems: 'center',
          boxShadow: '0 -2px 10px rgba(61,43,31,0.07)',
          paddingBottom: 'env(safe-area-inset-bottom)',
        }}>
          {/* Left 2 tabs */}
          {mobileLinks.slice(0, 2).map(l => {
            const active = screen === l.id;
            return (
              <button key={l.id} onClick={() => onNav(l.id)} style={{
                flex: 1, display: 'flex', flexDirection: 'column',
                alignItems: 'center', justifyContent: 'center', gap: 2,
                background: active ? T.amberLight : 'transparent',
                border: 'none', cursor: 'pointer', height: '100%',
                borderTop: `2px solid ${active ? T.amber : 'transparent'}`,
                transition: 'all 150ms ease', padding: '4px 0',
              }}>
                <span style={{ fontSize: 19, lineHeight: 1 }}>{l.emoji}</span>
                <span style={{ fontFamily: "'Nunito', sans-serif", fontWeight: 700, fontSize: 10, color: active ? T.amberDark : T.brownLight }}>
                  {l.label}
                </span>
              </button>
            );
          })}

          {/* Central FAB */}
          <div style={{ flex: 1, display: 'flex', justifyContent: 'center', alignItems: 'center', position: 'relative' }}>
            <button
              onClick={onLogBeer}
              style={{
                width: 54, height: 54,
                borderRadius: '50%',
                background: T.amber,
                border: `3px solid ${T.foam}`,
                boxShadow: `0 -3px 16px rgba(245,166,35,0.55), 0 4px 12px rgba(61,43,31,0.22)`,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                cursor: 'pointer', fontSize: 26,
                position: 'relative', bottom: 12,
                transition: 'transform 130ms ease, box-shadow 130ms ease',
              }}
              onMouseEnter={e => { e.currentTarget.style.transform = 'scale(1.1)'; }}
              onMouseLeave={e => { e.currentTarget.style.transform = 'scale(1)'; }}
            >🍺</button>
          </div>

          {/* Right 2 tabs */}
          {mobileLinks.slice(2).map(l => {
            const active = screen === l.id;
            return (
              <button key={l.id} onClick={() => onNav(l.id)} style={{
                flex: 1, display: 'flex', flexDirection: 'column',
                alignItems: 'center', justifyContent: 'center', gap: 2,
                background: active ? T.amberLight : 'transparent',
                border: 'none', cursor: 'pointer', height: '100%',
                borderTop: `2px solid ${active ? T.amber : 'transparent'}`,
                transition: 'all 150ms ease', padding: '4px 0',
              }}>
                <span style={{ fontSize: 19, lineHeight: 1 }}>{l.emoji}</span>
                <span style={{ fontFamily: "'Nunito', sans-serif", fontWeight: 700, fontSize: 10, color: active ? T.amberDark : T.brownLight }}>
                  {l.label}
                </span>
              </button>
            );
          })}
        </div>
      </>
    );
  }

  // ── Desktop nav ──────────────────────────────────────────────────
  return (
    <nav style={{
      display: 'flex', alignItems: 'center', position: 'sticky', top: 0, zIndex: 100,
      background: T.foam, borderBottom: `1.5px solid rgba(245,166,35,0.20)`,
      padding: '0 28px', height: 62, flexShrink: 0, boxShadow: '0 2px 10px rgba(61,43,31,0.07)',
    }}>
      <div style={{ fontFamily: "'Fredoka', sans-serif", fontSize: 26, fontWeight: 700, color: T.brown, marginRight: 32, flexShrink: 0 }}>
        🥂 Drankmaatje
      </div>
      <div style={{ display: 'flex', gap: 2, flex: 1 }}>
        {desktopLinks.map(l => (
          <button key={l.id} onClick={() => onNav(l.id)} style={{
            fontFamily: "'Nunito', sans-serif", fontSize: 14, fontWeight: 600,
            color: screen === l.id ? T.brown : T.brownLight,
            background: screen === l.id ? T.amberLight : 'transparent',
            border: 'none', borderRadius: 9999, padding: '7px 18px', cursor: 'pointer',
            transition: 'all 150ms ease',
          }}>
            {l.emoji} {l.label}
          </button>
        ))}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        {/* Group switcher dropdown */}
        {group && (
          <div style={{ position: 'relative' }}>
            <button
              onClick={() => setShowGroupPicker(p => !p)}
              style={{
                display: 'flex', alignItems: 'center', gap: 7,
                background: showGroupPicker ? T.amberLight : 'transparent',
                border: `1.5px solid ${showGroupPicker ? T.amber : T.brownBorder}`,
                borderRadius: 9999, padding: '6px 12px 6px 10px', cursor: 'pointer',
                fontFamily: "'Nunito', sans-serif", fontWeight: 700, fontSize: 13, color: T.brown,
                transition: 'all 150ms ease',
              }}
            >
              <span>{group.emoji} {group.name}</span>
              <span style={{ fontSize: 9, color: T.brownLight }}>{showGroupPicker ? '▲' : '▼'}</span>
            </button>
            {showGroupPicker && (
              <div style={{
                position: 'absolute', top: 44, right: 0,
                background: T.foam, borderRadius: 14, boxShadow: T.shadowLg,
                border: `1.5px solid ${T.brownBorder}`, minWidth: 230, zIndex: 300, overflow: 'hidden',
              }}>
                <div style={{ padding: '10px 14px 6px', fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: T.brownLight, fontFamily: "'Nunito', sans-serif" }}>
                  Jouw Groepen
                </div>
                {groups.map(g => {
                  const isActive = g.id === group.id;
                  return (
                    <button key={g.id}
                      onClick={() => { if (!isActive && onSwitchGroup) onSwitchGroup(g); setShowGroupPicker(false); }}
                      style={{
                        width: '100%', display: 'flex', alignItems: 'center', gap: 10,
                        padding: '10px 14px', background: isActive ? T.amberLight : 'transparent',
                        border: 'none', cursor: isActive ? 'default' : 'pointer',
                        textAlign: 'left', transition: 'background 150ms ease',
                      }}
                      onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = T.chalk; }}
                      onMouseLeave={e => { e.currentTarget.style.background = isActive ? T.amberLight : 'transparent'; }}
                    >
                      <span style={{ fontSize: 22, width: 30, textAlign: 'center', flexShrink: 0 }}>{g.emoji}</span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontFamily: "'Nunito', sans-serif", fontWeight: 700, fontSize: 13, color: T.brown, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{g.name}</div>
                        {isActive && <div style={{ fontSize: 10, color: T.amberDark, fontFamily: "'Nunito', sans-serif", fontWeight: 700 }}>✓ Actief</div>}
                      </div>
                    </button>
                  );
                })}
                <div style={{ borderTop: `1px solid ${T.brownBorder}`, padding: '4px 0' }}>
                  <button
                    onClick={() => { setShowGroupPicker(false); onNav('Groups'); }}
                    style={{
                      width: '100%', padding: '9px 14px', background: 'transparent', border: 'none',
                      textAlign: 'left', fontFamily: "'Nunito', sans-serif", fontSize: 13, color: T.brownMid,
                      fontWeight: 700, cursor: 'pointer',
                    }}
                    onMouseEnter={e => e.currentTarget.style.background = T.chalk}
                    onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                  >👥 Groepen beheren →</button>
                </div>
              </div>
            )}
          </div>
        )}
        <Button variant="dark" size="sm" onClick={onLogBeer}>+ Log Drankje</Button>
        <AvatarMenu size={36} />
      </div>
    </nav>
  );
}

// ── UnlockToast ───────────────────────────────────────────────────
function UnlockToast({ show, name, icon, onClose, message = 'Achievement Unlocked!' }) {
  const isMobile = useIsMobile();
  return (
    <div style={{
      position: 'fixed', bottom: isMobile ? 74 : 32, left: '50%',
      transform: `translateX(-50%) translateY(${show ? '0' : '80px'}) scale(${show ? 1 : 0.9})`,
      opacity: show ? 1 : 0,
      transition: 'all 420ms cubic-bezier(0.34,1.56,0.64,1)',
      pointerEvents: show ? 'all' : 'none',
      background: T.brown, borderRadius: 20, padding: '22px 36px',
      boxShadow: `${T.shadowLg}, ${T.shadowAmber}`,
      border: '2px solid rgba(245,166,35,0.35)',
      textAlign: 'center', minWidth: 280, zIndex: 1000,
    }}>
      <div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: T.amber, marginBottom: 10, fontFamily: "'Nunito', sans-serif" }}>{message}</div>
      <div style={{ fontSize: 52, marginBottom: 10 }}>{icon}</div>
      <div style={{ fontFamily: "'Fredoka', sans-serif", fontSize: 22, fontWeight: 700, color: T.foam, marginBottom: 16 }}>{name}</div>
      <button onClick={onClose} style={{ background: T.amber, color: T.brown, border: 'none', borderRadius: 9999, padding: '9px 28px', fontFamily: "'Nunito', sans-serif", fontWeight: 800, fontSize: 14, cursor: 'pointer' }}>Proost! 🍺</button>
    </div>
  );
}

Object.assign(window, {
  T, getAvatarColor, timeAgo, generateInviteCode, computeAchievementProgress,
  useIsMobile,
  Loading, Avatar, Badge, Button, Card, StatCard, ProgressBar, Input, StarRating,
  GroupBarChart, AchievementCard, NavBar, UnlockToast,
});
