// Dashboard widgets — Apple-style cards with large radii and soft shadows.
const {
  Mail, Facebook, ChevronRight, Flame, Trophy, Clock, BookOpen, Headphones,
  PenLine, MessageCircle, ArrowUpRight, Pencil, RotateCcw, AlertCircle,
  TrendingUp, Calendar
} = LucideReact;

/* ---------- shared primitives ---------- */
const Card = ({ children, className = "", style = {} }) => (
  <div
    className={`bg-white rounded-[28px] ${className}`}
    style={{ boxShadow: "0 1px 2px rgba(15,23,42,0.04), 0 8px 24px -12px rgba(15,23,42,0.08), inset 0 0 0 1px rgba(15,23,42,0.04)", ...style }}
  >
    {children}
  </div>
);

const SectionHeader = ({ title, action, sub }) => (
  <div className="flex items-end justify-between mb-3">
    <div>
      <h3 className="text-[19px] text-[#0B1220]" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", fontWeight: 500, letterSpacing: "-0.01em" }}>{title}</h3>
      {sub && <p className="text-[13px] text-[#6B7588] mt-0.5">{sub}</p>}
    </div>
    {action}
  </div>
);

/* ---------- Profile card ---------- */
const ProfileCard = ({ avatarId, onChangeAvatar, name, email, provider, joined, level, streak = 0, totalSessions = 0, accuracyRate = 0 }) => {
  const ProviderIcon = provider === "google" ? Mail : Facebook;
  const providerLabel = provider === "google" ? "Gmail" : "Facebook";
  const providerColor = provider === "google" ? "#EA4335" : "#1877F2";

  return (
    <Card className="p-7 relative overflow-hidden">
      {/* soft pastel blob accent */}
      <div className="absolute -top-20 -right-20 w-64 h-64 rounded-full opacity-50" style={{ background: "radial-gradient(circle, #EAF2FF 0%, transparent 70%)" }} />
      <div className="relative flex items-start gap-5">
        <button onClick={onChangeAvatar} className="relative shrink-0 group" aria-label="Change avatar">
          <AvatarTile id={avatarId} size={96} ring ringClass="ring-[3px] ring-white" />
          <div className="absolute -bottom-1 -right-1 w-9 h-9 rounded-full bg-[#0A6CFF] flex items-center justify-center transition group-hover:scale-110" style={{ boxShadow: "0 0 0 3px white" }}>
            <Pencil size={14} strokeWidth={2.2} className="text-white" />
          </div>
        </button>

        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-2 flex-wrap">
            <h2 className="text-[26px] text-[#0B1220] truncate" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", fontWeight: 500, letterSpacing: "-0.015em" }}>
              {name}
            </h2>
            <span className="px-2.5 h-6 rounded-full bg-[#EAF2FF] text-[#0857d6] text-[11px] font-semibold uppercase tracking-wider flex items-center">
              {level}
            </span>
          </div>
          <div className="text-[13.5px] text-[#2B3445] mt-1">{email}</div>

          <div className="mt-4 flex items-center gap-2 flex-wrap">
            {/* Social login badge */}
            <div className="inline-flex items-center gap-2 h-8 px-3 rounded-full bg-[#F6F8FB] border border-[#E6EAF1]">
              <div className="w-5 h-5 rounded-full flex items-center justify-center" style={{ background: providerColor + "1A" }}>
                <ProviderIcon size={11} strokeWidth={2.4} style={{ color: providerColor }} />
              </div>
              <span className="text-[12px] font-medium text-[#0B1220]">Signed in with {providerLabel}</span>
            </div>
            <div className="inline-flex items-center gap-1.5 h-8 px-3 rounded-full bg-[#F6F8FB] border border-[#E6EAF1]">
              <Calendar size={12} strokeWidth={2.2} className="text-[#6B7588]" />
              <span className="text-[12px] font-medium text-[#2B3445]">Joined {joined}</span>
            </div>
          </div>
        </div>
      </div>

      {/* Stats strip */}
      <div className="relative mt-6 grid grid-cols-3 gap-3">
        <StatPill icon={<Flame size={16} className="text-[#E8804A]" strokeWidth={2.2} />} label="Day streak" value={`${streak}`} />
        <StatPill icon={<Trophy size={16} className="text-[#C9A05C]" strokeWidth={2.2} />} label="Tests taken" value={`${totalSessions}`} />
        <StatPill icon={<TrendingUp size={16} className="text-[#5C9E72]" strokeWidth={2.2} />} label="Avg. accuracy" value={totalSessions > 0 ? `${accuracyRate}%` : "—"} />
      </div>
    </Card>
  );
};

const StatPill = ({ icon, label, value }) => (
  <div className="rounded-[18px] bg-[#F6F8FB] px-3 py-3 flex items-center gap-3">
    <div className="w-9 h-9 rounded-full bg-white flex items-center justify-center shrink-0">{icon}</div>
    <div className="min-w-0">
      <div className="text-[11px] text-[#6B7588] uppercase tracking-wider truncate">{label}</div>
      <div className="text-[18px] text-[#0B1220] font-semibold leading-tight" style={{ fontFamily: "Plus Jakarta Sans, sans-serif" }}>{value}</div>
    </div>
  </div>
);

/* ---------- Progress card: line chart + ring ---------- */
const ProgressCard = ({ data, target = 100, current, scoreMin = 0, scoreMax = 100 }) => {
  if (!data || data.length < 2) return null;
  const W = 360, H = 130, P = 14;
  const min = scoreMin, max = scoreMax;
  const xs = data.map((_, i) => P + (i * (W - P * 2)) / (data.length - 1));
  const ys = data.map((d) => H - P - ((d.score - min) / (max - min)) * (H - P * 2));
  const linePath = xs.map((x, i) => `${i === 0 ? "M" : "L"} ${x} ${ys[i]}`).join(" ");
  const areaPath = `${linePath} L ${xs[xs.length - 1]} ${H} L ${xs[0]} ${H} Z`;

  // Ring math
  const ringSize = 116, stroke = 10, r = (ringSize - stroke) / 2, c = 2 * Math.PI * r;
  const pct = Math.min(1, current / target);

  return (
    <Card className="p-6">
      <SectionHeader title="Learning progress" sub="Last 8 mock tests" />

      <div className="flex items-center gap-6 mt-2">
        {/* Ring */}
        <div className="relative shrink-0" style={{ width: ringSize, height: ringSize }}>
          <svg width={ringSize} height={ringSize} className="-rotate-90">
            <circle cx={ringSize / 2} cy={ringSize / 2} r={r} fill="none" stroke="#F6F8FB" strokeWidth={stroke} />
            <circle
              cx={ringSize / 2} cy={ringSize / 2} r={r}
              fill="none" stroke="#0A6CFF" strokeWidth={stroke} strokeLinecap="round"
              strokeDasharray={c} strokeDashoffset={c * (1 - pct)}
            />
          </svg>
          <div className="absolute inset-0 flex flex-col items-center justify-center">
            <div className="text-[10px] uppercase tracking-wider text-[#6B7588]">Score</div>
            <div className="text-[28px] text-[#0B1220] leading-none mt-0.5" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", fontWeight: 500 }}>{current}%</div>
            <div className="text-[10.5px] text-[#6B7588] mt-1">target {target}%</div>
          </div>
        </div>

        {/* Line chart */}
        <div className="flex-1 min-w-0">
          <div className="flex items-baseline gap-2 mb-1">
            <span className="text-[12px] text-[#6B7588]">Trend</span>
            <span className="inline-flex items-center gap-0.5 text-[12px] text-[#5C9E72] font-semibold">
              <TrendingUp size={12} strokeWidth={2.4} /> +0.8
            </span>
            <span className="text-[12px] text-[#6B7588]">vs. last month</span>
          </div>
          <svg viewBox={`0 0 ${W} ${H}`} className="w-full h-[130px]">
            <defs>
              <linearGradient id="areaGrad" x1="0" x2="0" y1="0" y2="1">
                <stop offset="0%" stopColor="#0A6CFF" stopOpacity="0.10" />
                <stop offset="100%" stopColor="#0A6CFF" stopOpacity="0" />
              </linearGradient>
            </defs>
            {/* gridlines */}
            {[0.25, 0.5, 0.75].map((t) => (
              <line key={t} x1={P} x2={W - P} y1={P + t * (H - P * 2)} y2={P + t * (H - P * 2)} stroke="#F6F8FB" strokeDasharray="3 4" />
            ))}
            <path d={areaPath} fill="url(#areaGrad)" />
            <path d={linePath} fill="none" stroke="#0A6CFF" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
            {xs.map((x, i) => (
              <g key={i}>
                <circle cx={x} cy={ys[i]} r={i === xs.length - 1 ? 4.5 : 3} fill="white" stroke="#0A6CFF" strokeWidth="2" />
              </g>
            ))}
            {/* last value pill */}
            <g transform={`translate(${xs[xs.length - 1] - 22}, ${ys[ys.length - 1] - 26})`}>
              <rect width="44" height="20" rx="10" fill="#0A6CFF" />
              <text x="22" y="14" textAnchor="middle" fontSize="11" fontWeight="600" fill="white" fontFamily="Plus Jakarta Sans, sans-serif">
                {data[data.length - 1].score}
              </text>
            </g>
          </svg>
          <div className="flex justify-between mt-1 text-[10.5px] text-[#8A93A6] px-[14px]">
            {data.map((d, i) => <span key={i}>{d.label}</span>)}
          </div>
        </div>
      </div>
    </Card>
  );
};

/* ---------- Course / set cards ---------- */
const SKILL_META = {
  reading:   { icon: BookOpen,      bg: "#EAF2FF", fg: "#0857d6", label: "Reading" },
  listening: { icon: Headphones,    bg: "#E0E7FF", fg: "#4338CA", label: "Listening" },
  writing:   { icon: PenLine,       bg: "#EDE9FE", fg: "#7C3AED", label: "Writing" },
  speaking:  { icon: MessageCircle, bg: "#D1FAE5", fg: "#065F46", label: "Speaking" },
};

const CourseCard = ({ course, onOpen, onBuy }) => {
  const meta = SKILL_META[course.skill];
  const Icon = meta.icon;
  const hasAccess = course.hasAccess;
  const isFree    = !course.price || course.price === 0;
  const banners   = Array.isArray(course.bannerUrls) ? course.bannerUrls : [];
  const [bannerIdx, setBannerIdx] = (typeof React !== "undefined" ? React.useState(0) : [0, () => {}]);

  // Auto-rotate banner every 4s
  if (typeof React !== "undefined") {
    React.useEffect(() => {
      if (banners.length <= 1) return;
      const t = setInterval(() => setBannerIdx(i => (i + 1) % banners.length), 4000);
      return () => clearInterval(t);
    }, [banners.length]);
  }

  // Click target: open the simulator if user already has access; otherwise
  // route to the buy handler. We render a non-button wrapper so the card can
  // contain an inner Buy button without nested-button warnings.
  const onClick = () => (hasAccess ? onOpen(course) : (onBuy && onBuy(course)));

  return (
    <div
      onClick={onClick}
      className="group text-left relative overflow-hidden rounded-[24px] bg-white transition hover:-translate-y-0.5 cursor-pointer"
      style={{ boxShadow: "0 1px 2px rgba(15,23,42,0.04), 0 8px 24px -12px rgba(15,23,42,0.08), inset 0 0 0 1px rgba(15,23,42,0.04)" }}
    >
      {banners.length > 0 && (
        <div className="relative" style={{ aspectRatio: "2/1", overflow: "hidden", background: "#F6F8FB" }}>
          {banners.map((u, i) => (
            <img key={u} src={u} alt="" loading="lazy"
              style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover", opacity: i === bannerIdx ? 1 : 0, transition: "opacity 600ms ease" }} />
          ))}
          {banners.length > 1 && (
            <div style={{ position: "absolute", bottom: 8, left: 0, right: 0, display: "flex", justifyContent: "center", gap: 4 }}>
              {banners.map((_, i) => (
                <span key={i} style={{ width: 6, height: 6, borderRadius: "50%", background: i === bannerIdx ? "white" : "rgba(255,255,255,0.5)" }} />
              ))}
            </div>
          )}
        </div>
      )}

      <div className="p-5">
      <div className="flex items-start justify-between">
        <div className="w-12 h-12 rounded-2xl flex items-center justify-center" style={{ background: meta.bg }}>
          <Icon size={20} strokeWidth={2} style={{ color: meta.fg }} />
        </div>
        {hasAccess && (
          <div className="w-8 h-8 rounded-full bg-[#F6F8FB] flex items-center justify-center transition group-hover:bg-[#0A6CFF] group-hover:text-white">
            <ChevronRight size={15} strokeWidth={2.2} />
          </div>
        )}
      </div>

      <div className="mt-4">
        <div className="text-[10.5px] uppercase tracking-[0.14em] text-[#6B7588] font-medium">{meta.label} · {course.level}</div>
        <h4 className="mt-1 text-[16px] text-[#0B1220] leading-tight font-semibold" style={{ letterSpacing: "-0.01em" }}>
          {course.title}
        </h4>
        <p className="text-[12.5px] text-[#6B7588] mt-1">
          {course.roundsCount && course.roundsCount > 1
            ? <>ทำได้ <strong className="text-[#0B1220]">{course.roundsCount} ชุด</strong> · ชุดละ {course.questionsPerRound} ข้อ · ใช้ได้ {course.accessDays || 30} วัน</>
            : <>{course.lessons} ข้อ · ใช้ได้ {course.accessDays || 30} วัน</>}
        </p>
      </div>

      {hasAccess ? (
        // Owned: show progress + days-remaining + rounds-left
        <div className="mt-4">
          <div className="flex justify-between items-baseline mb-1.5 text-[11px] text-[#2B3445]">
            <span>
              เหลืออีก {course.daysRemaining} วัน
              {(course.roundsLeft != null) && (
                <span style={{ marginLeft: 6 }}>· ทำได้อีก <strong style={{ color: course.roundsLeft === 0 ? "#DC2626" : "#0B1220" }}>{course.roundsLeft}/{course.roundsTotal}</strong> รอบ</span>
              )}
            </span>
            <span className="text-[12px] font-semibold text-[#0B1220]" style={{ fontFamily: "Plus Jakarta Sans, sans-serif" }}>{course.progress}%</span>
          </div>
          <div className="h-1.5 rounded-full bg-[#F6F8FB] overflow-hidden">
            <div className="h-full rounded-full transition-all" style={{ width: `${course.progress}%`, background: "#0A6CFF" }} />
          </div>
          {course.roundsLeft === 0 && (
            <div className="mt-2 text-[11px] text-[#DC2626] font-semibold flex items-center gap-1.5">
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><rect x="4" y="11" width="16" height="10" rx="2"/><path d="M8 11V7a4 4 0 1 1 8 0v4"/></svg>
              ใช้ครบทุกรอบแล้ว — ดูเฉลยย้อนหลังได้แต่ทำใหม่ไม่ได้
            </div>
          )}
        </div>
      ) : (
        // Not owned: show price + Buy / Activate button
        <div className="mt-4">
          {course.seasonLabel && (
            <div className="mb-2" style={{ display: "inline-flex", alignItems: "center", gap: 6, padding: "3px 10px", borderRadius: 99, background: "#FEF3C7", color: "#92400E", fontSize: 11, fontWeight: 700 }}>
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M12 2s4 4 4 8a4 4 0 0 1-8 0c0-1 .5-2 1-3 .5 1 1 1 1 1s-1-3 2-6Z"/></svg>
              {course.seasonLabel} · ลด {course.seasonDiscountPercent}%
            </div>
          )}
          <div className="flex items-end justify-between gap-3">
            <div>
              {isFree ? (
                <div className="text-[20px] font-bold text-[#0B1220]" style={{ fontFamily: "Plus Jakarta Sans, sans-serif" }}>ฟรี</div>
              ) : (
                <div>
                  {course.originalPrice && course.originalPrice > course.price && (
                    <div className="text-[12px] text-[#8A93A6]" style={{ textDecoration: "line-through" }}>฿{Number(course.originalPrice).toLocaleString()}</div>
                  )}
                  <div className="text-[22px] font-bold text-[#0A6CFF]" style={{ fontFamily: "Plus Jakarta Sans, sans-serif" }}>฿{Number(course.price).toLocaleString()}</div>
                </div>
              )}
            </div>
            <button
              type="button"
              onClick={(e) => { e.stopPropagation(); onBuy && onBuy(course); }}
              className="px-4 h-9 rounded-full font-semibold text-[13px] transition"
              style={{ background: isFree ? "#0B1220" : "#0A6CFF", color: "white" }}
            >
              {isFree ? "เริ่มใช้งาน" : "ซื้อชุดนี้"}
            </button>
          </div>
        </div>
      )}
      </div>
    </div>
  );
};

/* ---------- Recent mistakes ---------- */
const MistakeRow = ({ m, onReview }) => {
  const meta = SKILL_META[m.skill];
  const Icon = meta.icon;
  return (
    <button
      onClick={() => onReview(m)}
      className="w-full text-left flex items-center gap-3 p-3 rounded-2xl hover:bg-[#F6F8FB] transition group"
    >
      <div className="w-10 h-10 rounded-xl flex items-center justify-center shrink-0" style={{ background: meta.bg }}>
        <Icon size={16} strokeWidth={2} style={{ color: meta.fg }} />
      </div>
      <div className="flex-1 min-w-0">
        <div className="flex items-center gap-2">
          <span className="text-[10.5px] uppercase tracking-wider text-[#6B7588] font-medium">{meta.label}</span>
          <span className="text-[10.5px] text-[#8A93A6]">·</span>
          <span className="text-[10.5px] text-[#8A93A6]">{m.when}</span>
        </div>
        <p className="text-[13.5px] text-[#0B1220] mt-0.5 truncate" style={{ letterSpacing: "-0.005em" }}>
          {m.question}
        </p>
        <div className="flex items-center gap-2 mt-1.5">
          <span className="inline-flex items-center gap-1 text-[11px] text-[#C8553D]">
            <span className="w-1.5 h-1.5 rounded-full bg-[#C8553D]" />
            Your answer: <span className="font-medium">{m.your}</span>
          </span>
          <span className="text-[11px] text-[#8A93A6]">·</span>
          <span className="inline-flex items-center gap-1 text-[11px] text-[#5C9E72]">
            <span className="w-1.5 h-1.5 rounded-full bg-[#5C9E72]" />
            Correct: <span className="font-medium">{m.correct}</span>
          </span>
        </div>
      </div>
      <div className="shrink-0 w-9 h-9 rounded-full bg-[#F6F8FB] flex items-center justify-center transition group-hover:bg-[#0A6CFF] group-hover:text-white">
        <RotateCcw size={14} strokeWidth={2.2} />
      </div>
    </button>
  );
};

const RecentMistakesCard = ({ mistakes, onReview }) => (
  <Card className="p-6">
    <SectionHeader
      title="Recent mistakes"
      sub="Review and master what slipped"
      action={
        <button className="text-[12.5px] text-[#2B3445] hover:text-[#0B1220] flex items-center gap-1 transition">
          See all <ArrowUpRight size={13} strokeWidth={2.2} />
        </button>
      }
    />
    <div className="flex flex-col gap-1">
      {mistakes.map((m) => <MistakeRow key={m.id} m={m} onReview={onReview} />)}
    </div>
  </Card>
);

/* ── Analytics 1: Error Pattern ── */
const ErrorPatternCard = ({ data }) => (
  <Card className="p-6">
    <SectionHeader title="Error pattern" sub="Accuracy by question type" />
    <div className="flex flex-col gap-3.5 mt-3">
      {data.map((item) => {
        const pct = Math.round((item.correct / item.total) * 100);
        const color = pct >= 75 ? "#5C9E72" : pct >= 60 ? "#C9A05C" : "#C8553D";
        return (
          <div key={item.type}>
            <div className="flex items-center justify-between mb-1.5">
              <span className="text-[13px] text-[#0B1220] font-medium">{item.type}</span>
              <span className="text-[13.5px] font-semibold" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", color }}>{pct}%</span>
            </div>
            <div className="h-2 rounded-full bg-[#F6F8FB] overflow-hidden">
              <div className="h-full rounded-full" style={{ width: `${pct}%`, background: color }} />
            </div>
          </div>
        );
      })}
    </div>
    <div className="mt-4 flex items-center gap-4 text-[11px] text-[#8A93A6]">
      <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-[#5C9E72] shrink-0" /> ≥75% strong</span>
      <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-[#C9A05C] shrink-0" /> 60–74% ok</span>
      <span className="flex items-center gap-1.5"><span className="w-2.5 h-2.5 rounded-full bg-[#C8553D] shrink-0" /> &lt;60% weak</span>
    </div>
  </Card>
);

/* ── Analytics 2: Speed vs Accuracy ── */
const SpeedAccuracyCard = ({ data }) => {
  const W = 300, H = 210, PAD = 34;
  const accMin = 40, accMax = 100, timeMin = 20, timeMax = 130;
  const toX = (a) => PAD + ((a - accMin) / (accMax - accMin)) * (W - PAD * 2);
  const toY = (t) => PAD + ((t - timeMin) / (timeMax - timeMin)) * (H - PAD * 2);
  const midX = toX(70), midY = toY(75);
  const skillColor = { reading: "#0857d6", listening: "#4338CA", writing: "#7C3AED", speaking: "#065F46" };
  return (
    <Card className="p-6">
      <SectionHeader title="Speed vs. accuracy" sub="Know what to fix and how" />
      <svg viewBox={`0 0 ${W} ${H}`} className="w-full mt-1">
        <rect x={PAD} y={PAD} width={midX - PAD} height={midY - PAD} fill="#FEF9C3" opacity="0.7" rx="5" />
        <rect x={midX} y={PAD} width={W - PAD - midX} height={midY - PAD} fill="#D1FAE5" opacity="0.7" rx="5" />
        <rect x={PAD} y={midY} width={midX - PAD} height={H - PAD - midY} fill="#FEE2E2" opacity="0.7" rx="5" />
        <rect x={midX} y={midY} width={W - PAD - midX} height={H - PAD - midY} fill="#EAF2FF" opacity="0.6" rx="5" />
        <line x1={midX} y1={PAD} x2={midX} y2={H - PAD} stroke="#cfd6e3" strokeWidth="1" />
        <line x1={PAD} y1={midY} x2={W - PAD} y2={midY} stroke="#cfd6e3" strokeWidth="1" />
        <text x={PAD + (midX - PAD) / 2} y={PAD + 13} textAnchor="middle" fontSize="7.5" fill="#B8860B" fontWeight="700" fontFamily="Plus Jakarta Sans,sans-serif">Careless</text>
        <text x={midX + (W - PAD - midX) / 2} y={PAD + 13} textAnchor="middle" fontSize="7.5" fill="#3F6A4A" fontWeight="700" fontFamily="Plus Jakarta Sans,sans-serif">Strong</text>
        <text x={PAD + (midX - PAD) / 2} y={H - PAD - 5} textAnchor="middle" fontSize="7.5" fill="#C8553D" fontWeight="700" fontFamily="Plus Jakarta Sans,sans-serif">Weak area</text>
        <text x={midX + (W - PAD - midX) / 2} y={H - PAD - 5} textAnchor="middle" fontSize="7.5" fill="#3D5A75" fontWeight="700" fontFamily="Plus Jakarta Sans,sans-serif">Need speed</text>
        <text x={W / 2} y={H - 3} textAnchor="middle" fontSize="7" fill="#8A93A6" fontFamily="Plus Jakarta Sans,sans-serif">← Low accuracy · High accuracy →</text>
        {data.map((d, i) => {
          const cx = toX(d.accuracy), cy = toY(d.avgTime);
          const col = skillColor[d.skill] || "#0A6CFF";
          return (
            <g key={i}>
              <circle cx={cx} cy={cy} r={7} fill={col} opacity="0.9" />
              <circle cx={cx} cy={cy} r={7} fill="none" stroke="white" strokeWidth="1.5" />
              <text x={cx} y={cy - 10} textAnchor="middle" fontSize="7.5" fill="#0B1220" fontWeight="600" fontFamily="Plus Jakarta Sans,sans-serif">{d.label}</text>
            </g>
          );
        })}
      </svg>
      <div className="flex flex-wrap gap-x-4 gap-y-1 mt-1 text-[11px] text-[#6B7588]">
        {Object.entries(skillColor).map(([s, c]) => (
          <span key={s} className="flex items-center gap-1.5 capitalize">
            <span className="w-2 h-2 rounded-full shrink-0" style={{ background: c }} />{s}
          </span>
        ))}
      </div>
    </Card>
  );
};

/* ── Analytics 3: Forgetting Curve ── */
const ForgettingCurveCard = ({ data }) => {
  const urgencyMeta = {
    high:   { label: "Review now",  color: "#C8553D", bg: "#FDECEA" },
    medium: { label: "Review soon", color: "#C9A05C", bg: "#FEF3DC" },
    low:    { label: "Stable",      color: "#5C9E72", bg: "#DDEDD4" },
  };
  return (
    <Card className="p-6">
      <SectionHeader title="Forgetting curve" sub="Topics you knew — but may be losing" />
      <div className="flex flex-col gap-2 mt-2">
        {data.map((item, i) => {
          const u = urgencyMeta[item.urgency];
          const sm = SKILL_META[item.skill];
          const Icon = sm.icon;
          return (
            <div key={i} className="flex items-center gap-3 p-3 rounded-2xl bg-[#F6F8FB]">
              <div className="w-9 h-9 rounded-xl flex items-center justify-center shrink-0" style={{ background: sm.bg }}>
                <Icon size={15} strokeWidth={2} style={{ color: sm.fg }} />
              </div>
              <div className="flex-1 min-w-0">
                <div className="text-[13px] font-medium text-[#0B1220] truncate">{item.topic}</div>
                <div className="text-[11px] text-[#6B7588] mt-0.5">Last correct: {item.lastCorrect}</div>
              </div>
              <span className="shrink-0 px-2.5 h-6 rounded-full text-[11px] font-semibold flex items-center" style={{ background: u.bg, color: u.color }}>{u.label}</span>
            </div>
          );
        })}
      </div>
    </Card>
  );
};

/* ── Analytics 4: Consistency ── */
const ConsistencyCard = ({ scores, scoreMin = 0, scoreMax = 100 }) => {
  if (!scores || scores.length < 2) return null;
  const min = Math.min(...scores);
  const max = Math.max(...scores);
  const avg = scores.reduce((a, b) => a + b, 0) / scores.length;
  const std = Math.sqrt(scores.reduce((a, b) => a + Math.pow(b - avg, 2), 0) / scores.length);
  const absMin = scoreMin, absMax = scoreMax;
  const toP = (v) => ((v - absMin) / (absMax - absMin)) * 100;
  const label = std < 0.4 ? "Very consistent" : std < 0.7 ? "Moderate" : "Variable";
  const color = std < 0.4 ? "#5C9E72" : std < 0.7 ? "#C9A05C" : "#C8553D";
  return (
    <Card className="p-6">
      <SectionHeader title="Consistency" sub="Score stability across all tests" />
      <div className="mt-3 flex items-baseline gap-3">
        <div className="text-[50px] leading-none" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", fontWeight: 500, color }}>{std.toFixed(2)}</div>
        <div>
          <div className="text-[11px] uppercase tracking-wider text-[#6B7588]">Std. Dev.</div>
          <div className="text-[13px] font-semibold mt-0.5" style={{ color }}>{label}</div>
        </div>
      </div>
      <div className="mt-5">
        <div className="flex justify-between text-[11px] text-[#6B7588] mb-2">
          <span>Min <strong className="text-[#0B1220]">{min}</strong></span>
          <span>Avg <strong className="text-[#0B1220]">{avg.toFixed(1)}</strong></span>
          <span>Max <strong className="text-[#0B1220]">{max}</strong></span>
        </div>
        <div className="relative h-3 rounded-full bg-[#F6F8FB]">
          <div className="absolute h-full rounded-full" style={{ left: `${toP(min)}%`, width: `${toP(max) - toP(min)}%`, background: color + "33" }} />
          <div className="absolute w-3.5 h-3.5 rounded-full border-2 border-white shadow -top-0.5 -translate-x-1/2" style={{ left: `${toP(avg)}%`, background: color }} />
        </div>
        <div className="flex justify-between text-[10px] text-[#8A93A6] mt-1"><span>{absMin}</span><span>{Math.round((absMin + absMax) / 2)}</span><span>{absMax}</span></div>
      </div>
      <div className="mt-5">
        <div className="text-[11px] text-[#6B7588] mb-2">All test scores</div>
        <div className="flex items-end gap-1 h-12">
          {scores.map((s, i) => {
            const h = Math.max(10, ((s - absMin) / (absMax - absMin)) * 100);
            return <div key={i} className="flex-1 rounded-t" style={{ height: `${h}%`, background: i === scores.length - 1 ? "#0A6CFF" : "#cfd6e3" }} />;
          })}
        </div>
      </div>
    </Card>
  );
};

/* ── Analytics 5: Predicted Score ── */
const PredictedScoreCard = ({ data, target, current, scoreMin = 0, scoreMax = 100 }) => {
  if (!data || data.length < 3) return null;
  const W = 560, H = 140, P = 20;
  const sMin = scoreMin, sMax = scoreMax;
  const n = data.length;
  const sumX = n * (n - 1) / 2;
  const sumY = data.reduce((a, d) => a + d.score, 0);
  const sumXY = data.reduce((a, d, i) => a + i * d.score, 0);
  const sumX2 = data.reduce((a, _, i) => a + i * i, 0);
  const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
  const periodsLeft = slope > 0 ? Math.min(8, Math.ceil((target - current) / slope)) : 6;
  const predicted = Array.from({ length: periodsLeft }, (_, i) => ({
    score: Math.min(target, current + slope * (i + 1)),
  }));
  const totalLen = n + periodsLeft;
  const toX = (i) => P + (i / (totalLen - 1)) * (W - P * 2);
  const toY = (s) => H - P - ((s - sMin) / (sMax - sMin)) * (H - P * 2);
  const hXs = data.map((_, i) => toX(i)), hYs = data.map(d => toY(d.score));
  const pXs = predicted.map((_, i) => toX(n + i)), pYs = predicted.map(d => toY(d.score));
  const histPath = hXs.map((x, i) => `${i === 0 ? "M" : "L"} ${x} ${hYs[i]}`).join(" ");
  const predPath = pXs.length ? `M ${hXs[n - 1]} ${hYs[n - 1]} ` + pXs.map((x, i) => `L ${x} ${pYs[i]}`).join(" ") : "";
  const targetY = toY(target);
  const eta = periodsLeft === 1 ? "~1 week" : periodsLeft <= 4 ? `~${periodsLeft} weeks` : `~${Math.ceil(periodsLeft / 4)} months`;
  return (
    <Card className="p-6">
      <div className="flex items-start justify-between mb-3">
        <div>
          <h3 className="text-[19px] text-[#0B1220]" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", fontWeight: 500, letterSpacing: "-0.01em" }}>Predicted score</h3>
          <p className="text-[13px] text-[#6B7588] mt-0.5">At current pace → reach {target} in <strong style={{ color: "#5C9E72" }}>{eta}</strong></p>
        </div>
        <div className="text-right shrink-0 ml-4">
          <div className="text-[11px] uppercase tracking-wider text-[#6B7588]">ETA to target</div>
          <div className="text-[30px] leading-none mt-1 font-medium" style={{ fontFamily: "Plus Jakarta Sans, sans-serif", color: "#5C9E72" }}>{eta}</div>
        </div>
      </div>
      <svg viewBox={`0 0 ${W} ${H}`} className="w-full">
        <line x1={P} y1={targetY} x2={W - P} y2={targetY} stroke="#5C9E72" strokeWidth="1.5" strokeDasharray="5 4" opacity="0.7" />
        <text x={W - P - 4} y={targetY - 5} textAnchor="end" fontSize="9" fill="#5C9E72" fontWeight="700" fontFamily="Plus Jakarta Sans,sans-serif">Target {target}</text>
        {predPath && <path d={`${predPath} L ${pXs[pXs.length - 1]} ${H - P} L ${hXs[n - 1]} ${H - P} Z`} fill="#5C9E72" opacity="0.06" />}
        <path d={`${histPath} L ${hXs[n - 1]} ${H - P} L ${hXs[0]} ${H - P} Z`} fill="#0A6CFF" opacity="0.04" />
        <path d={histPath} fill="none" stroke="#0A6CFF" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
        {predPath && <path d={predPath} fill="none" stroke="#5C9E72" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" strokeDasharray="6 4" />}
        {hXs.map((x, i) => <circle key={i} cx={x} cy={hYs[i]} r={i === n - 1 ? 5 : 3.5} fill="white" stroke="#0A6CFF" strokeWidth="2" />)}
        {pXs.map((x, i) => <circle key={i} cx={x} cy={pYs[i]} r={3} fill="white" stroke="#5C9E72" strokeWidth="1.5" />)}
        {data.filter((_, i) => i % 2 === 0).map((d, j) => (
          <text key={j} x={hXs[j * 2]} y={H - 4} textAnchor="middle" fontSize="8.5" fill="#8A93A6" fontFamily="Plus Jakarta Sans,sans-serif">{d.label}</text>
        ))}
      </svg>
      <div className="flex items-center gap-5 mt-2 text-[11.5px] text-[#6B7588]">
        <span className="flex items-center gap-1.5"><span className="inline-block w-5" style={{ borderTop: "2.5px solid #0A6CFF" }} /> Actual</span>
        <span className="flex items-center gap-1.5"><span className="inline-block w-5" style={{ borderTop: "2px dashed #5C9E72" }} /> Projected</span>
      </div>
    </Card>
  );
};

window.ProfileCard = ProfileCard;
window.ProgressCard = ProgressCard;
window.CourseCard = CourseCard;
window.RecentMistakesCard = RecentMistakesCard;
window.Card = Card;
window.SectionHeader = SectionHeader;
window.ErrorPatternCard = ErrorPatternCard;
window.SpeedAccuracyCard = SpeedAccuracyCard;
window.ForgettingCurveCard = ForgettingCurveCard;
window.ConsistencyCard = ConsistencyCard;
window.PredictedScoreCard = PredictedScoreCard;
