/* Polish desktop shared primitives — Leren pijler.
   Tokens-driven (light + dark). Inherits design language from Plannen-polish. */

const { useState, useEffect, useRef } = React;

/* ─── Lucide icon ──────────────────────────────────────────── */
function PI({ name, size = 16, color = 'currentColor', strokeWidth = 2, style, fill = 'none' }) {
  const icons = window.lucide && window.lucide.icons;
  if (!icons) return null;
  const pascal = name.split('-').map(s => s[0].toUpperCase() + s.slice(1)).join('');
  const data = icons[pascal] || icons[name];
  if (!data) return null;
  const children = Array.isArray(data) ? (data[2] || []) : (data.children || []);
  const html = children.map(c => {
    if (!Array.isArray(c)) return '';
    const [tag, a] = c;
    if (!tag || !a) return '';
    const attrs = Object.entries(a).map(([k, v]) => `${k}="${v}"`).join(' ');
    return `<${tag} ${attrs}/>`;
  }).join('');
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill={fill}
      stroke={color} strokeWidth={strokeWidth} strokeLinecap="round" strokeLinejoin="round"
      style={{ flexShrink: 0, ...(style || {}) }}
      dangerouslySetInnerHTML={{ __html: html }} />
  );
}

/* ─── BrowserShell ───────────────────────────────────────── */
function BrowserShell({ t, children, width = 1240, height = 880, url = 'snapsnel.nl/leren' }) {
  const isDark = t.mode === 'dark';
  return (
    <div style={{
      width, borderRadius: 14, overflow: 'hidden',
      background: t.bg,
      boxShadow: isDark
        ? '0 40px 100px rgba(0,0,0,0.55), 0 0 0 1px rgba(255,255,255,0.06)'
        : '0 24px 60px rgba(15,23,42,0.14), 0 4px 14px rgba(15,23,42,0.08), 0 0 0 1px rgba(15,23,42,0.06)',
    }}>
      <div style={{
        height: 40, background: isDark ? '#1A2235' : '#F1F5F9',
        display: 'flex', alignItems: 'center', padding: '0 16px', gap: 10,
        borderBottom: `1px solid ${isDark ? '#0B1120' : '#E2E8F0'}`,
      }}>
        <div style={{ display: 'flex', gap: 6 }}>
          <span style={{ width: 12, height: 12, borderRadius: 999, background: '#FF5F56' }} />
          <span style={{ width: 12, height: 12, borderRadius: 999, background: '#FFBD2E' }} />
          <span style={{ width: 12, height: 12, borderRadius: 999, background: '#27C93F' }} />
        </div>
        <div style={{
          flex: 1, maxWidth: 420, margin: '0 auto',
          background: isDark ? 'rgba(255,255,255,0.06)' : '#FFFFFF',
          border: isDark ? 'none' : '1px solid #E2E8F0',
          height: 24, borderRadius: 6,
          display: 'flex', alignItems: 'center', padding: '0 10px',
          fontSize: 11, color: isDark ? '#94A3B8' : '#64748B',
          fontWeight: 600, gap: 6,
        }}>🔒 {url}</div>
      </div>
      <div style={{ height, display: 'flex', background: t.bg, overflow: 'hidden' }}>
        {children}
      </div>
    </div>
  );
}

/* ─── Pulse mascot ──────────────────────────────────────── */
function PulseMascot({ size = 36, mood = 'idle', style }) {
  // Map Leren-moods to available SVG assets
  const map = { idle: 'idle', thinking: 'thinking', encouraging: 'happy', curious: 'thinking',
                zen: 'idle', happy: 'happy', celebrating: 'celebrating' };
  const file = map[mood] || 'idle';
  const src = `polish/assets/pulse-${file}.svg`;
  const width = Math.round(size * (200 / 280));
  return (
    <img src={src} alt="" width={width} height={size}
      style={{ display: 'block', flexShrink: 0, ...(style || {}) }} />
  );
}

function Lockup({ t }) {
  const snelColor = t.mode === 'dark' ? '#00B4D8' : '#0096C7';
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
      <PulseMascot size={36} mood="idle" />
      <span style={{
        fontFamily: 'Fredoka One', fontWeight: 400, fontSize: 20,
        lineHeight: 1, letterSpacing: '-0.02em',
      }}>
        <span style={{ color: t.fg }}>Snap</span>
        <span style={{ color: snelColor }}>Snel</span>
      </span>
    </div>
  );
}

/* ─── Sidebar — productie-shell uit DS ─────────────────────
   280px breed. Header user-card (Pulse + naam + level + XP-bar +
   streak/snaps-pills) → nav (7 items: Dashboard / Leren / Oefenen /
   Plannen / Quiz maken / Ranglijst / Winkel) → bottom utilities.
   Identiek over alle pijlers. */
const SIDEBAR_PILLAR_COLORS = {
  leren:   { light: '#0096C7', dark: '#00B4D8' },
  oefenen: { light: '#15803D', dark: '#22C55E' },
  plannen: { light: '#7C3AED', dark: '#9D4EDD' },
};

function Sidebar({
  t, active = 'leren',
  user = { name: 'sam_bakker', level: 7, xp: 73, xpMax: 272, streak: 12, snaps: 1240 },
  streakFrozen = false,
}) {
  const dark = t.mode === 'dark';
  const items = [
    { id: 'dashboard', label: 'Dashboard',  icon: 'layout-grid',     pillar: null },
    { id: 'leren',     label: 'Leren',      icon: 'graduation-cap',  pillar: 'leren' },
    { id: 'oefenen',   label: 'Oefenen',    icon: 'target',          pillar: 'oefenen' },
    { id: 'plannen',   label: 'Plannen',    icon: 'calendar',        pillar: 'plannen' },
    { id: 'maker',     label: 'Quiz maken', icon: 'wand-2',          pillar: null },
    { id: 'ranglijst', label: 'Ranglijst',  icon: 'trophy',          pillar: null },
    { id: 'winkel',    label: 'Winkel',     icon: 'store',           pillar: null },
  ];
  const xpPct = Math.max(0, Math.min(100, (user.xp / user.xpMax) * 100));
  const activeItem = items.find(i => i.id === active);
  const activePillarColor = activeItem && activeItem.pillar
    ? SIDEBAR_PILLAR_COLORS[activeItem.pillar][dark ? 'dark' : 'light']
    : t.primary;
  const activeBg = dark
    ? `color-mix(in srgb, ${activePillarColor} 14%, transparent)`
    : `color-mix(in srgb, ${activePillarColor} 10%, transparent)`;
  const orange = dark ? '#F97316' : '#C2410C';
  const orangeBg = `color-mix(in srgb, ${orange} 12%, transparent)`;
  const orangeBd = streakFrozen ? orange : `color-mix(in srgb, ${orange} 28%, transparent)`;
  const goldBd = `color-mix(in srgb, ${t.gold} 28%, transparent)`;

  return (
    <aside style={{
      width: 280, flexShrink: 0, height: '100%', background: t.sidebar,
      borderRight: `1px solid ${t.border}`, display: 'flex', flexDirection: 'column',
      boxSizing: 'border-box',
    }}>
      {/* Lockup */}
      <div style={{ padding: '18px 18px 14px' }}>
        <Lockup t={t} />
      </div>

      {/* Header user-card */}
      <div style={{ padding: '0 14px 12px' }}>
        <div style={{
          background: t.card, border: `1px solid ${t.border}`, borderRadius: 14,
          padding: 14, display: 'flex', flexDirection: 'column', gap: 10,
        }}>
          {/* Pulse mascot centered */}
          <div style={{ display: 'flex', justifyContent: 'center', marginTop: -2, marginBottom: -2 }}>
            <PulseMascot size={64} mood="idle" />
          </div>
          {/* Username + level pill */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
            <span style={{
              fontFamily: 'Nunito', fontWeight: 800, fontSize: 15, color: t.fg,
              overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', minWidth: 0,
            }}>{user.name}</span>
            <span style={{
              flexShrink: 0,
              fontFamily: 'Nunito', fontWeight: 700, fontSize: 11,
              padding: '3px 10px', borderRadius: 9999,
              background: t.primaryDim, color: t.primary,
            }}>Level {user.level}</span>
          </div>
          {/* XP progress */}
          <div>
            <div style={{
              width: '100%', height: 6, borderRadius: 9999,
              background: dark ? '#1E293B' : '#E2E8F0', overflow: 'hidden',
            }}>
              <div style={{
                width: `${xpPct}%`, height: '100%',
                background: dark ? '#22C55E' : '#15803D', borderRadius: 9999,
              }} />
            </div>
            <div style={{
              fontFamily: 'Nunito', fontWeight: 600, fontSize: 10.5,
              color: t.fgMute, marginTop: 5,
            }}>{user.xp} / {user.xpMax} XP</div>
          </div>
          {/* Streak + Snaps pills */}
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            <div style={{
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
              height: 32, borderRadius: 9999,
              background: orangeBg, color: orange,
              border: streakFrozen ? `1px dashed ${orangeBd}` : `1px solid ${orangeBd}`,
              fontFamily: 'Nunito', fontWeight: 800, fontSize: 12,
            }}>🔥 {user.streak}</div>
            <div style={{
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5,
              height: 32, borderRadius: 9999,
              background: t.goldDim, color: t.gold,
              border: `1px solid ${goldBd}`,
              fontFamily: 'Nunito', fontWeight: 800, fontSize: 12,
            }}>
              <PI name="zap" size={12} fill="currentColor" strokeWidth={0} color={t.gold} />
              {user.snaps.toLocaleString('nl-NL')}
            </div>
          </div>
        </div>
      </div>

      {/* Navigation */}
      <nav style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2, padding: '8px 12px' }}>
        {items.map(it => {
          const isActive = active === it.id;
          const itemColor = it.pillar
            ? SIDEBAR_PILLAR_COLORS[it.pillar][dark ? 'dark' : 'light']
            : t.primary;
          return (
            <div key={it.id} style={{
              display: 'flex', alignItems: 'center', gap: 12,
              padding: '10px 12px', borderRadius: 10,
              background: isActive ? activeBg : 'transparent',
              color: isActive ? itemColor : t.fgDim,
              fontFamily: 'Nunito', fontWeight: isActive ? 800 : 600, fontSize: 13,
            }}>
              <PI name={it.icon} size={17} />
              <span style={{ flex: 1 }}>{it.label}</span>
              {isActive && (
                <span style={{
                  width: 7, height: 7, borderRadius: 9999,
                  background: '#22C55E',
                  boxShadow: '0 0 0 3px color-mix(in srgb, #22C55E 22%, transparent)',
                }} />
              )}
            </div>
          );
        })}
      </nav>

      {/* Bottom utilities */}
      <div style={{
        padding: '12px 12px 16px',
        borderTop: `1px solid ${t.border}`,
        display: 'flex', flexDirection: 'column', gap: 2,
      }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '9px 12px',
          borderRadius: 10, color: t.fgDim,
          fontFamily: 'Nunito', fontWeight: 700, fontSize: 12.5,
        }}>
          <PI name="bell" size={15} />
          <span style={{ flex: 1 }}>Notificaties</span>
          <span style={{
            background: '#EF4444', color: '#fff', fontSize: 9.5, fontWeight: 800,
            borderRadius: 9999, padding: '1px 6px', minWidth: 16, textAlign: 'center',
          }}>3</span>
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '9px 12px',
          borderRadius: 10, color: t.fgDim,
          fontFamily: 'Nunito', fontWeight: 600, fontSize: 12.5,
        }}>
          <span style={{ width: 15, display: 'inline-flex', justifyContent: 'center', fontSize: 13 }}>🌐</span>
          <span style={{ flex: 1 }}>Taal</span>
          <span style={{
            display: 'inline-flex', alignItems: 'center', gap: 4,
            padding: '2px 8px', borderRadius: 9999,
            background: t.cardSunken, border: `1px solid ${t.border}`,
            fontFamily: 'Nunito', fontWeight: 700, fontSize: 11, color: t.fg,
          }}>
            <span>🇳🇱</span>NL<PI name="chevron-down" size={10} />
          </span>
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '9px 12px',
          borderRadius: 10, color: t.fgDim,
          fontFamily: 'Nunito', fontWeight: 600, fontSize: 12.5,
        }}>
          <PI name={dark ? 'moon' : 'sun'} size={15} />
          <span style={{ flex: 1 }}>Donkere modus</span>
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '9px 12px',
          borderRadius: 10, color: t.fgDim,
          fontFamily: 'Nunito', fontWeight: 600, fontSize: 12.5,
        }}>
          <PI name="settings" size={15} />
          <span style={{ flex: 1 }}>Instellingen</span>
        </div>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '9px 12px',
          borderRadius: 10, color: t.fgDim,
          fontFamily: 'Nunito', fontWeight: 600, fontSize: 12.5,
        }}>
          <PI name="log-out" size={15} />
          <span style={{ flex: 1 }}>Uitloggen</span>
        </div>
      </div>
    </aside>
  );
}

/* ─── SidebarStats — Snaps + Streak naast elkaar (cross-pijler) ───
   Eén compacte rij die op elke pijler-pagina hetzelfde toont.
   Geen sub-titel of secondaire info — pure status. */
function SidebarStats({ t, snaps, streak }) {
  const dark = t.mode === 'dark';
  const orange = dark ? '#F97316' : '#C2410C';
  const orangeBg = dark ? 'rgba(249,115,22,0.10)' : 'rgba(234,88,12,0.06)';
  const orangeBd = dark ? 'rgba(249,115,22,0.28)' : 'rgba(234,88,12,0.24)';
  const goldBd = dark ? 'rgba(255,208,0,0.30)' : 'rgba(217,119,6,0.26)';
  const tile = (bg, bd) => ({
    flex: 1, padding: '8px 10px', borderRadius: 10,
    background: bg, border: `1px solid ${bd}`,
    display: 'flex', alignItems: 'center', gap: 7, minWidth: 0,
  });
  return (
    <div style={{ padding: '10px 0', borderTop: `1px solid ${t.border}` }}>
      <div style={{ display: 'flex', gap: 6 }}>
        <div style={tile(t.goldDim, goldBd)} title={`${snaps} Snaps`}>
          <PI name="zap" size={13} fill="currentColor" color={t.gold} strokeWidth={0} />
          <span style={{
            fontFamily: 'Fredoka One', fontWeight: 400, fontSize: 14, color: t.gold,
            lineHeight: 1, marginTop: -1, overflow: 'hidden', textOverflow: 'ellipsis',
          }}>{snaps.toLocaleString('nl-NL')}</span>
        </div>
        <div style={tile(orangeBg, orangeBd)} title={`${streak} dagen streak`}>
          <span style={{ fontSize: 13, lineHeight: 1 }}>🔥</span>
          <span style={{
            fontFamily: 'Fredoka One', fontWeight: 400, fontSize: 14, color: orange,
            lineHeight: 1, marginTop: -1,
          }}>{streak}</span>
        </div>
      </div>
    </div>
  );
}

/* ─── Topbar ──────────────────────────────────────────────
   Afgeslankt (fase 1.5). Bevat alléén titel + subtitle (verplicht)
   en een rechter-zoekveld + bell-icoon. Snaps en Streak zijn naar
   de Sidebar verhuisd zodat het patroon cross-pijler consistent is.
   `rightExtra` blijft beschikbaar voor context-acties (bv. wisselen-knop). */
function Topbar({ t, title, subtitle, rightExtra }) {
  return (
    <header style={{
      padding: '18px 28px', display: 'flex', alignItems: 'center', gap: 16,
      background: t.topbar, borderBottom: `1px solid ${t.border}`, flexShrink: 0,
    }}>
      <div style={{ minWidth: 0, flex: 1 }}>
        <div style={{ fontFamily: 'Fredoka One', fontSize: 24, lineHeight: '28px',
          color: t.fg, letterSpacing: '-0.01em' }}>{title}</div>
        {subtitle && <div style={{ fontSize: 12.5, fontWeight: 600, color: t.fgDim, marginTop: 3 }}>{subtitle}</div>}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexShrink: 0 }}>
        {/* zoek */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6, height: 34, padding: '0 12px',
          borderRadius: 999, background: t.card, border: `1px solid ${t.border}`,
          color: t.fgMute, fontSize: 12, fontWeight: 600, minWidth: 220,
        }}>
          <PI name="search" size={13} />
          <span>Zoek in vakken, hoofdstukken…</span>
        </div>
        {rightExtra}
        <button style={{
          width: 36, height: 36, borderRadius: 10, background: t.card,
          border: `1px solid ${t.border}`, color: t.fgDim,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer', position: 'relative',
        }}>
          <PI name="bell" size={17} />
        </button>
      </div>
    </header>
  );
}

function SnapsPill({ t, count = 240 }) {
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, height: 32, padding: '0 12px',
      borderRadius: 999, background: t.goldDim, color: t.gold,
      border: `1.5px solid ${hexToRgba(t.mode === 'dark' ? '#FFD000' : '#D97706', 0.30)}`,
      fontFamily: 'Fredoka One', fontSize: 15, lineHeight: 1,
    }}>
      <PI name="zap" size={13} fill="currentColor" color={t.gold} strokeWidth={0} />
      <span style={{ marginTop: -1 }}>{count}</span>
    </div>
  );
}

function StreakPill({ t, count = 5 }) {
  const orange = t.mode === 'dark' ? '#F97316' : '#C2410C';
  const orangeBg = t.mode === 'dark' ? 'rgba(249,115,22,0.14)' : 'rgba(234,88,12,0.10)';
  const orangeBd = t.mode === 'dark' ? 'rgba(249,115,22,0.28)' : 'rgba(234,88,12,0.30)';
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 5, height: 28, padding: '0 10px',
      borderRadius: 999, background: orangeBg, color: orange,
      border: `1.5px solid ${orangeBd}`,
    }}>
      <span style={{ fontSize: 12 }}>🔥</span>
      <span style={{ fontFamily: 'Fredoka One', fontWeight: 400, fontSize: 14, lineHeight: 1, marginTop: -1 }}>{count}</span>
    </div>
  );
}

/* ─── MasteryDots — cross-pijler primitive ───────────────── */
const DOT_SIZE = { xs: 5, sm: 7, md: 9, lg: 12 };
const DOT_GAP  = { xs: 3, sm: 4, md: 5, lg: 6 };
const MASTERY_LABELS = ['niet gestart', 'beginnend', 'bezig', 'goed', 'zeker'];

function MasteryDots({ t, level = 0, size = 'sm', showLabel = false }) {
  const dark = t.mode === 'dark';
  const px = DOT_SIZE[size];
  const gap = DOT_GAP[size];
  const primary = dark ? '#00B4D8' : '#0096C7';
  const correct = dark ? '#22C55E' : '#15803D';
  const ringColor = dark ? '#475569' : '#94A3B8';
  const fillColor = level === 4 ? correct : primary;
  const lvl = Math.max(0, Math.min(4, level));
  const dots = [0, 1, 2, 3].map(i => {
    const filled = i < lvl;
    return (
      <span key={i} style={{
        display: 'inline-block', width: px, height: px, borderRadius: 9999, boxSizing: 'border-box',
        background: filled ? fillColor : 'transparent',
        border: filled ? `1.5px solid ${fillColor}` : `1.5px solid ${ringColor}`,
        boxShadow: lvl === 4 && (size === 'md' || size === 'lg')
          ? `0 0 0 2px color-mix(in srgb, ${correct} 18%, transparent)` : 'none',
      }} />
    );
  });
  const labelColor = lvl === 4 ? correct : (dark ? '#94A3B8' : '#64748B');
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: showLabel ? 8 : 0 }}>
      <span style={{ display: 'inline-flex', alignItems: 'center', gap }}>{dots}</span>
      {showLabel && (
        <span style={{
          fontFamily: 'Nunito', fontWeight: 800, fontSize: 10,
          letterSpacing: 0.3, textTransform: 'uppercase', color: labelColor,
        }}>{lvl}/4 · {MASTERY_LABELS[lvl]}</span>
      )}
    </span>
  );
}

/* ─── Handoff primitives ─────────────────────────────────── */
const PILLAR_DEFS = {
  leren:   { label: 'Leren',   colorLight: '#0096C7', colorDark: '#00B4D8', icon: 'book-open' },
  oefenen: { label: 'Oefenen', colorLight: '#15803D', colorDark: '#22C55E', icon: 'layers' },
  plannen: { label: 'Plannen', colorLight: '#7C3AED', colorDark: '#9D4EDD', icon: 'calendar-clock' },
};

function pillarColor(pillar, dark) {
  const def = PILLAR_DEFS[pillar];
  return dark ? def.colorDark : def.colorLight;
}

function HandoffBadge({ t, pillar, direction = 'to', label, icon }) {
  const c = pillarColor(pillar, t.mode === 'dark');
  const arrow = direction === 'to' ? 'arrow-right' : 'arrow-left';
  const text = label || (direction === 'to' ? `Naar ${PILLAR_DEFS[pillar].label}` : `uit ${PILLAR_DEFS[pillar].label}`);
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 5,
      height: 22, padding: '0 9px',
      borderRadius: 9999, border: `1px dashed ${c}`,
      background: 'transparent', color: c,
      fontFamily: 'Nunito', fontWeight: 800, fontSize: 10.5,
      letterSpacing: 0.3, lineHeight: 1, whiteSpace: 'nowrap',
    }}>
      <PI name={icon || arrow} size={10} color={c} />
      <span>{text}</span>
    </span>
  );
}

function HandoffCard({ t, pillar, direction = 'to', title, subtitle, icon, onClick, compact = false, children, mascot, hideArrow = false }) {
  const dark = t.mode === 'dark';
  const c = pillarColor(pillar, dark);
  const def = PILLAR_DEFS[pillar];
  const iconName = icon || def.icon;
  const tileSize = compact ? 32 : 36;
  return (
    <div onClick={onClick} style={{
      position: 'relative', borderRadius: 14, padding: compact ? 12 : 14,
      border: `1.5px dashed ${c}`,
      background: `color-mix(in oklch, ${c} 5%, transparent)`,
      display: 'flex', flexDirection: 'column', gap: children ? 12 : 0,
      cursor: onClick ? 'pointer' : 'default',
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        {mascot ? (
          <div style={{ flexShrink: 0 }}>{mascot}</div>
        ) : (
          <div style={{
            width: tileSize, height: tileSize, borderRadius: 10,
            background: `color-mix(in oklch, ${c} 14%, transparent)`,
            color: c, display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
          }}>
            <PI name={iconName} size={compact ? 16 : 18} color={c} />
          </div>
        )}
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            fontFamily: 'Nunito', fontWeight: 800, fontSize: compact ? 13 : 14,
            color: t.fg, lineHeight: 1.3,
          }}>{title}</div>
          {subtitle && (
            <div style={{
              fontFamily: 'Nunito', fontWeight: 600, fontSize: compact ? 11 : 12,
              color: t.fgDim, lineHeight: 1.4, marginTop: 2,
            }}>{subtitle}</div>
          )}
        </div>
        {!hideArrow && (
          <PI name={direction === 'to' ? 'arrow-right' : 'arrow-left'} size={14} color={c} />
        )}
      </div>
      {children}
    </div>
  );
}

/* ─── SubjectChip ───────────────────────────────────────── */
const SUBJECT_ICON = {
  wiskunde: 'calculator', biologie: 'leaf', scheikunde: 'flask-conical',
  natuurkunde: 'zap', geschiedenis: 'landmark', aardrijkskunde: 'map',
  economie: 'trending-up', informatica: 'terminal',
  engels: 'book', nederlands: 'book-open-text', frans: 'languages', duits: 'languages',
};
function SubjectChip({ t, subject, size = 'md', iconOnly = false, label }) {
  const hex = SUBJECTS[subject] || '#64748B';
  const iconName = SUBJECT_ICON[subject] || 'book-open';
  const color = t.subjectFg(hex);
  const bg = hexToRgba(hex, 0.12);
  const bd = hexToRgba(hex, 0.22);
  if (iconOnly) {
    const sz = size === 'sm' ? 18 : size === 'lg' ? 28 : 22;
    return (
      <span style={{
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        width: sz, height: sz, borderRadius: 6, background: bg, color,
        border: `1px solid ${bd}`, flexShrink: 0,
      }}>
        <PI name={iconName} size={size === 'sm' ? 11 : size === 'lg' ? 16 : 13} color={color} strokeWidth={2.2} />
      </span>
    );
  }
  const z = size === 'sm'
    ? { pad: '3px 9px 3px 7px', fs: 10, ic: 11, gap: 4 }
    : { pad: '4px 10px 4px 8px', fs: 11, ic: 12, gap: 5 };
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: z.gap, padding: z.pad,
      borderRadius: 20, fontSize: z.fs, fontWeight: 800,
      background: bg, color, border: `1px solid ${bd}`,
      fontFamily: 'Nunito', textTransform: 'capitalize', letterSpacing: 0.1, whiteSpace: 'nowrap',
    }}>
      <PI name={iconName} size={z.ic} color={color} strokeWidth={2.2} />
      {label || subject}
    </span>
  );
}

/* ─── Cards / labels ────────────────────────────────────── */
function GradientBorderCard({ t, subject, radius = 14, children, style }) {
  const hex = SUBJECTS[subject] || '#64748B';
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const el = ref.current;
    const id = 'gb-' + Math.random().toString(36).slice(2, 9);
    el.setAttribute('data-gb', id);
    const css = `[data-gb="${id}"]::before{content:"";position:absolute;inset:0;border-radius:inherit;padding:1.5px;pointer-events:none;z-index:1;background:linear-gradient(135deg, ${hex} 0%, transparent 55%);opacity:0.45;-webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask-composite:exclude;}`;
    const styleEl = document.createElement('style');
    styleEl.textContent = css;
    document.head.appendChild(styleEl);
    return () => { styleEl.remove(); };
  }, [hex]);
  return (
    <div ref={ref} style={{
      background: t.card, border: `1px solid ${t.border}`,
      borderRadius: radius, position: 'relative', overflow: 'hidden', isolation: 'isolate',
      boxShadow: t.mode === 'dark' ? '0 2px 12px rgba(0,0,0,0.30)' : '0 1px 3px rgba(15,23,42,0.08)',
      ...(style || {}),
    }}>
      {children}
    </div>
  );
}

function Label({ t, children, color }) {
  return (
    <span style={{
      fontSize: 10, fontWeight: 800, color: color || t.fgMute,
      letterSpacing: 1.2, textTransform: 'uppercase', fontFamily: 'Nunito',
    }}>{children}</span>
  );
}

function SectionLabel({ t, children, right }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
      <div style={{
        fontSize: 10.5, fontWeight: 800, color: t.fgMute,
        letterSpacing: 0.8, textTransform: 'uppercase',
      }}>{children}</div>
      <div style={{ flex: 1, height: 1, background: t.border }} />
      {right}
    </div>
  );
}

function BtnPrimary({ t, children, icon, onClick, style }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 7,
      padding: '9px 14px', borderRadius: 999,
      background: t.primary, border: 'none',
      color: t.mode === 'dark' ? '#0A0F1E' : '#FFFFFF',
      fontFamily: 'Nunito', fontSize: 13, fontWeight: 800, cursor: 'pointer',
      letterSpacing: 0.1, whiteSpace: 'nowrap',
      ...(style || {}),
    }}>
      {icon && <PI name={icon} size={14} strokeWidth={2.4} />}
      <span style={{ whiteSpace: 'nowrap' }}>{children}</span>
    </button>
  );
}

function BtnGhost({ t, children, icon, onClick, style }) {
  return (
    <button onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '8px 12px', borderRadius: 10,
      background: t.card, border: `1px solid ${t.border}`,
      color: t.fgDim, fontSize: 12, fontWeight: 700, cursor: 'pointer',
      fontFamily: 'Nunito', whiteSpace: 'nowrap',
      ...(style || {}),
    }}>
      {icon && <PI name={icon} size={13} />}
      <span style={{ whiteSpace: 'nowrap' }}>{children}</span>
    </button>
  );
}

function ProgressBar({ t, value = 0, color }) {
  const c = color || t.primary;
  return (
    <div style={{
      height: 6, borderRadius: 999, background: t.cardSunken,
      border: `1px solid ${t.border}`, overflow: 'hidden',
    }}>
      <div style={{
        height: '100%', width: `${Math.max(0, Math.min(100, value))}%`,
        background: c, borderRadius: 999,
      }} />
    </div>
  );
}

/* ─── Showcase shell helpers (cross-view) ───────────────── */
function ModeLabel({ label }) {
  const isDark = label.includes('DARK');
  return (
    <div style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: '4px 10px', borderRadius: 999,
      background: isDark ? '#1A2235' : '#FFFFFF',
      color: isDark ? '#F1F5F9' : '#0F172A',
      border: `1px solid ${isDark ? '#334155' : '#CBD5E1'}`,
      fontSize: 10, fontWeight: 800, letterSpacing: 0.8, textTransform: 'uppercase',
      fontFamily: 'Nunito, system-ui, sans-serif',
    }}>
      <span style={{
        width: 8, height: 8, borderRadius: 999,
        background: isDark ? '#0F172A' : '#FEF3C7',
        border: `1px solid ${isDark ? '#475569' : '#FBBF24'}`,
      }} />
      {label}
    </div>
  );
}

function HeroFrame({ label, t, Component, height, note }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 12, alignItems: 'flex-start' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
        <ModeLabel label={label} />
        {note && (
          <span style={{ fontSize: 12, color: '#94A3B8', fontWeight: 600, fontStyle: 'italic' }}>
            {note}
          </span>
        )}
      </div>
      <Component t={t} height={height} />
    </div>
  );
}

function StateFrame({ label, note, t, Component }) {
  return <HeroFrame label={label} note={note} t={t} Component={Component} />;
}

function SectionHeader({ kicker, title, subtitle }) {
  return (
    <header style={{
      maxWidth: 1320, margin: '0 auto 48px',
      paddingBottom: 22, borderBottom: '1px solid #E2E8F0',
    }}>
      <div style={{
        fontSize: 11, fontWeight: 800, color: '#0096C7',
        letterSpacing: 1.2, textTransform: 'uppercase', marginBottom: 12,
      }}>{kicker}</div>
      <h1 style={{
        fontFamily: 'Fredoka One', fontSize: 42, color: '#0F172A',
        letterSpacing: '-0.02em', lineHeight: 1.1, margin: '0 0 14px',
      }}>{title}</h1>
      {subtitle && (
        <p style={{ fontSize: 15, color: '#475569', lineHeight: 1.65, fontWeight: 500, margin: 0, maxWidth: 820 }}>
          {subtitle}
        </p>
      )}
    </header>
  );
}

function Block({ num, title, note, children }) {
  return (
    <section style={{ marginBottom: 80, maxWidth: 1320, margin: '0 auto 80px' }}>
      <header style={{ marginBottom: 22 }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 14, marginBottom: 8 }}>
          <span style={{ fontFamily: 'Fredoka One', fontSize: 14, color: '#94A3B8', letterSpacing: 0.5 }}>{num}</span>
          <h2 style={{ margin: 0, fontSize: 24, fontWeight: 800, color: '#F1F5F9', letterSpacing: '-0.015em' }}>{title}</h2>
        </div>
        {note && (
          <p style={{ margin: 0, fontSize: 13.5, color: '#CBD5E1', lineHeight: 1.6, fontWeight: 500, maxWidth: 900 }}>
            {note}
          </p>
        )}
      </header>
      {children}
    </section>
  );
}

function ShowcasePage({ kicker, title, subtitle, children }) {
  return (
    <div style={{
      minHeight: '100vh', background: '#0B1220',
      fontFamily: 'Nunito, system-ui, sans-serif',
      padding: '56px 40px 120px',
    }}>
      <div style={{
        maxWidth: 1320, margin: '0 auto 48px', padding: '40px 36px',
        background: '#F1F5F9', borderRadius: 18, border: '1px solid #E2E8F0',
      }}>
        <SectionHeader kicker={kicker} title={title} subtitle={subtitle} />
      </div>
      {children}
    </div>
  );
}

/* ─── Expose ────────────────────────────────────────────── */
Object.assign(window, {
  PI, BrowserShell, Lockup, PulseMascot, Sidebar, SidebarStats, Topbar,
  SnapsPill, StreakPill, SubjectChip, GradientBorderCard,
  MasteryDots, MASTERY_LABELS, HandoffBadge, HandoffCard, PILLAR_DEFS, pillarColor,
  Label, SectionLabel, BtnPrimary, BtnGhost, ProgressBar,
  ModeLabel, HeroFrame, StateFrame, SectionHeader, Block, ShowcasePage,
});
