// app.jsx — main composition

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "theme": "dark",
  "style": "tz",
  "glow": "subtle",
  "density": "regular",
  "radius": "medium",
  "accent": "#6C3FE8",
  "gamification": true,
  "wishLayout": "kanban",
  "showMobile": true,
  "showDesktop": true,
  "mascotId": "spectr",
  "applyMascotTheme": true,
  "unlockedIds": ["spectr", "lumen"]
}/*EDITMODE-END*/;

// Initial priorities (link wishes to quadrants)
const INITIAL_PRIORITIES = [
  // Q1 — срочно + важно
  { id: 'p1', wid: 'w1',  quad: 'q1' }, // Бегать
  { id: 'p2', wid: 'w15', quad: 'q1' }, // Ремонт
  { id: 'p3', wid: 'w8',  quad: 'q1' }, // Накопить на отпуск
  // Q2 — не срочно + важно
  { id: 'p4', wid: 'w10', quad: 'q2' }, // Читать книги
  { id: 'p5', wid: 'w11', quad: 'q2' }, // B2
  { id: 'p6', wid: 'w17', quad: 'q2' }, // Медитация
  { id: 'p7', wid: 'w4',  quad: 'q2' }, // Поездка месяц
  { id: 'p8', wid: 'w6',  quad: 'q2' }, // Senior
  // Q3 — срочно + не важно
  { id: 'p9',  wid: 'w13', quad: 'q3' }, // Велосипеды
  { id: 'p10', wid: 'w16', quad: 'q3' }, // Сад на балконе
  // Q4 — не срочно + не важно
  { id: 'p11', wid: 'w14', quad: 'q4' }, // Поход
  { id: 'p12', wid: 'w12', quad: 'q4' }, // Python
];

const ACCENT_PRESETS = [
  '#6C3FE8', // фиолетовый (TZ)
  '#22D3A3', // мятный
  '#F472B6', // розовый
  '#FB923C', // оранжевый
  '#60A5FA', // голубой
];

// ──────────────────────────────────────────────────────────────
// Sidebar (desktop)
// ──────────────────────────────────────────────────────────────
const SCREENS = [
  { id: 'dashboard',  label: 'Дашборд',       icon: Icon.dashboard, desktop: ScrDashboard,    mobile: MobDashboard,  title: 'Дашборд' },
  { id: 'wishes',     label: 'Карта желаний', icon: Icon.map,       desktop: ScrWishes,       mobile: MobWishes,     title: 'Карта желаний' },
  { id: 'wheel',      label: 'Колесо',        icon: Icon.wheel,     desktop: ScrWheel,        mobile: MobWheel,      title: 'Колесо баланса' },
  { id: 'habits',     label: 'Привычки',      icon: Icon.habits,    desktop: ScrHabits,       mobile: MobHabits,     title: 'Трекер привычек' },
  { id: 'matrix',     label: 'Приоритеты',    icon: Icon.matrix,    desktop: ScrMatrix,       mobile: MobMatrix,     title: 'Матрица приоритетов' },
  { id: 'collection', label: 'Коллекция',     icon: Icon.star,      desktop: ScrCollection,   mobile: MobCollection, title: 'Коллекция героев' },
  { id: 'profile',    label: 'Профиль',       icon: Icon.profile,   desktop: ScrProfile,      mobile: MobProfile,    title: 'Профиль и геймификация' },
];

function Sidebar({ active, onChange }) {
  return (
    <aside className="sidebar">
      <div className="sidebar-brand">
        <div className="logo">К</div>
        <div>
          Карта желаний
          <span className="grp">{GROUP.name} · 4 чел</span>
        </div>
      </div>

      <div className="nav-section">Навигация</div>
      {SCREENS.map(scr => (
        <div key={scr.id}
             className={`nav-item ${active === scr.id ? 'active' : ''}`}
             onClick={() => onChange(scr.id)}
             data-screen-label={`${(SCREENS.indexOf(scr) + 1).toString().padStart(2, '0')} ${scr.label}`}>
          {scr.icon()}
          <span>{scr.label}</span>
          {scr.id === 'wishes' && <span className="badge">{WISHES.length}</span>}
        </div>
      ))}

      <div className="nav-section">Команда</div>
      <div className="members">
        {USERS.map(u => (
          <div key={u.id} className="member">
            <Avatar user={u} size="sm" />
            <span className="name">{u.name} {u.id === ME.id && <span className="dim" style={{ fontSize: 11 }}>(вы)</span>}</span>
            <span className="lvl">L{u.level}</span>
          </div>
        ))}
        <div className="member" style={{ color: 'var(--text-tertiary)', cursor: 'pointer' }}>
          <div style={{ width: 20, height: 20, borderRadius: '50%', display: 'grid', placeItems: 'center', border: '1px dashed var(--border)' }}>+</div>
          <span className="name">Пригласить</span>
        </div>
      </div>
    </aside>
  );
}

// ──────────────────────────────────────────────────────────────
// Top bar (desktop)
// ──────────────────────────────────────────────────────────────
function TopBar({ screen }) {
  return (
    <div className="topbar">
      <div>
        <div className="breadcrumb">{GROUP.name} · {screen.title}</div>
        <h1>{screen.title}</h1>
      </div>
      <div className="topbar-spacer"></div>
      <div className="search">
        <Icon.search style={{ width: 14, height: 14 }} />
        <span>Поиск…</span>
        <span className="mono dim" style={{ marginLeft: 'auto', fontSize: 11, border: '1px solid var(--border)', padding: '0 5px', borderRadius: 3 }}>⌘ K</span>
      </div>
      <button className="icon-btn" aria-label="Уведомления">
        <Icon.bell />
        <span className="pip"></span>
      </button>
      <Avatar user={ME} size="md" />
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Collection wrappers (desktop / mobile)
// ──────────────────────────────────────────────────────────────
function ScrCollection({ state, dispatch }) {
  return (
    <CollectionScreen
      unlockedIds={state.unlockedIds}
      activeMascotId={state.mascotId}
      onSelectMascot={(id) => dispatch('setMascot', id)}
      onOpenPack={(pid) => dispatch('openPack', pid)}
    />
  );
}
function MobCollection({ state, dispatch }) {
  return (
    <MobScreen eyebrow="Коллекция" title={`Герои · ${state.unlockedIds.length}/${MASCOTS.length}`}>
      <div className="col" style={{ gap: 10 }}>
        {Object.keys(PACK_META).map(pid => {
          const meta = PACK_META[pid];
          return (
            <div key={pid}
                 onClick={() => dispatch('openPack', pid)}
                 style={{
                   borderRadius: 'var(--r-lg)',
                   padding: '14px 16px',
                   background: `linear-gradient(135deg, ${meta.color1}, ${meta.color2})`,
                   color: '#fff',
                   cursor: 'pointer',
                   boxShadow: `0 0 calc(18px * var(--glow-mult)) ${meta.color2}55`,
                 }}>
              <div className="row" style={{ justifyContent: 'space-between' }}>
                <div>
                  <div className="mono" style={{ fontSize: 10, letterSpacing: '0.1em', textTransform: 'uppercase', opacity: 0.8 }}>{meta.price}</div>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 18, marginTop: 2 }}>{meta.name}</div>
                  <div style={{ fontSize: 12, opacity: 0.85, marginTop: 2 }}>{meta.desc}</div>
                </div>
                <div style={{ fontSize: 32 }}>🎴</div>
              </div>
            </div>
          );
        })}
      </div>
      <div className="ttl-md mt-4 mb-3">Герои</div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 10 }}>
        {MASCOTS.map(m => {
          const locked = !state.unlockedIds.includes(m.id);
          const rar = RARITY_META[m.rarity];
          return (
            <div key={m.id} className="mascot-card"
                 data-active={state.mascotId === m.id ? '1' : '0'}
                 data-locked={locked ? '1' : '0'}
                 style={{ '--m-glow': locked ? rar.glow : m.glow }}
                 onClick={() => { if (!locked) dispatch('setMascot', m.id); }}>
              <span className="rarity-tag">{rar.label}</span>
              <div style={{ display: 'grid', placeItems: 'center', minHeight: 90 }}>
                <Mascot id={m.id} size={78} emotion="idle" aura />
              </div>
              <div style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 13, marginTop: 8 }}>{m.name}</div>
              <div className="dim" style={{ fontSize: 10, marginTop: 2, minHeight: 24 }}>{m.power}</div>
              {locked && (
                <div className="locked-overlay">
                  <div><b>🔒</b><div style={{ fontSize: 10 }}>{m.unlock?.label || 'В паке'}</div></div>
                </div>
              )}
            </div>
          );
        })}
      </div>
    </MobScreen>
  );
}

// ──────────────────────────────────────────────────────────────
// Desktop app (sidebar + topbar + screen body)
// ──────────────────────────────────────────────────────────────
function DesktopApp({ state, dispatch, screenId, onScreenChange, tweaks, dashboardLayout, onLayoutChange, onOpenMascotPicker }) {
  const screen = SCREENS.find(s => s.id === screenId) || SCREENS[0];
  const Body = screen.desktop;
  const extraProps = screenId === 'dashboard'
    ? { dashboardLayout, onLayoutChange, onOpenMascotPicker }
    : { layout: tweaks.wishLayout };
  return (
    <div className="app">
      <Sidebar active={screenId} onChange={onScreenChange} />
      <div className="content">
        <TopBar screen={screen} />
        <PageTransition id={screenId}>
          <Body state={state} dispatch={dispatch} {...extraProps} />
        </PageTransition>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// Mobile app (iOS frame contents)
// ──────────────────────────────────────────────────────────────
function MobileApp({ state, dispatch, screenId, onScreenChange }) {
  const screen = SCREENS.find(s => s.id === screenId) || SCREENS[0];
  const Body = screen.mobile;
  return (
    <div className="app" style={{ flexDirection: 'column', position: 'relative' }}>
      <PageTransition id={'m-' + screenId}>
        <Body state={state} dispatch={dispatch} />
      </PageTransition>
      <MobBottomNav active={screenId} onChange={onScreenChange} />
    </div>
  );
}

// ──────────────────────────────────────────────────────────────
// State reducer
// ──────────────────────────────────────────────────────────────
const INITIAL_STATE = {
  wishes: WISHES,
  habits: HABITS,
  wheel: { current: { ...WHEEL.current }, target: { ...WHEEL.target }, history: WHEEL.history },
  priorities: INITIAL_PRIORITIES,
  heat: HABITS.map((h, i) => buildHeatmap(i * 11 + 3, 0.5 + i * 0.07)),
  mascotId: 'spectr',
  mascotEmotion: 'idle',
  unlockedIds: ['spectr', 'lumen'],
};

function reduce(state, action, payload) {
  switch (action) {
    case 'moveWish': {
      return {
        ...state,
        wishes: state.wishes.map(w => w.id === payload.id
          ? { ...w, zone: payload.zone ?? w.zone, status: payload.status ?? w.status }
          : w),
      };
    }
    case 'toggleHabit': {
      return {
        ...state,
        habits: state.habits.map(h => h.id === payload
          ? { ...h, doneToday: !h.doneToday, streak: !h.doneToday ? h.streak + 1 : Math.max(0, h.streak - 1) }
          : h),
      };
    }
    case 'wheel': {
      return {
        ...state,
        wheel: { ...state.wheel, current: { ...state.wheel.current, [payload.zone]: payload.value } },
      };
    }
    case 'priority': {
      return {
        ...state,
        priorities: state.priorities.map(p => p.id === payload.id ? { ...p, quad: payload.quad } : p),
      };
    }
    case 'setMascotState': {
      return { ...state, mascotId: payload };
    }
    case 'unlock': {
      const ids = Array.isArray(payload) ? payload : [payload];
      const set = new Set(state.unlockedIds);
      ids.forEach(id => set.add(id));
      return { ...state, unlockedIds: [...set] };
    }
    default: return state;
  }
}

// ──────────────────────────────────────────────────────────────
// MAIN APP
// ──────────────────────────────────────────────────────────────
function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [screenId, setScreenId] = React.useState('dashboard');
  const [state, setState] = React.useState(() => ({ ...INITIAL_STATE, mascotId: TWEAK_DEFAULTS.mascotId }));
  const [toasts, setToasts] = React.useState([]);
  const [floats, setFloats] = React.useState([]);
  const [badgeModal, setBadgeModal] = React.useState(null);
  const [levelUp, setLevelUp] = React.useState(null);
  const [onboarding, setOnboarding] = React.useState(false);
  const [overlay, setOverlay] = React.useState(null); // 'empty' | 'loading' | null
  const [mascotPicker, setMascotPicker] = React.useState(false);
  const [dashboardLayout, setDashboardLayout] = React.useState(() => DEFAULT_LAYOUT.map(w => ({ ...w })));
  const [packOpen, setPackOpen] = React.useState(null); // { packId, contentIds }

  const toastId = React.useRef(1);
  const floatId = React.useRef(1);
  const emotionTimer = React.useRef(null);

  // Sync mascot id from tweaks → state
  React.useEffect(() => {
    setState(s => s.mascotId === t.mascotId ? s : { ...s, mascotId: t.mascotId });
  }, [t.mascotId]);
  React.useEffect(() => {
    if (Array.isArray(t.unlockedIds)) {
      setState(s => s.unlockedIds.join() === t.unlockedIds.join() ? s : { ...s, unlockedIds: [...t.unlockedIds] });
    }
  }, [t.unlockedIds]);

  // Pick 5 cards for a given pack (deterministic: just slice MASCOTS by pack tag)
  const rollPack = React.useCallback((packId) => {
    const pool = MASCOTS.filter(m => m.pack === packId).map(m => m.id);
    // Premium / battle: shuffle for surprise
    if (packId === 'premium' || packId === 'battle') {
      const shuffled = [...pool].sort(() => Math.random() - 0.5);
      return shuffled.slice(0, 5);
    }
    return pool.slice(0, 5);
  }, []);

  // Temporarily set mascot emotion, auto-revert after `ms`
  const reactMascot = React.useCallback((emotion, ms = 2400) => {
    setState(s => ({ ...s, mascotEmotion: emotion }));
    clearTimeout(emotionTimer.current);
    emotionTimer.current = setTimeout(() => {
      setState(s => ({ ...s, mascotEmotion: 'idle' }));
    }, ms);
  }, []);

  const dispatch = React.useCallback((action, payload) => {
    if (action === 'showBadge') { setBadgeModal(payload); reactMascot('celebrate', 2800); return; }
    if (action === 'levelUp')   { setLevelUp({ from: ME.level, to: ME.level + 1 }); reactMascot('celebrate', 3200); return; }
    if (action === 'layout')    { setTweak('wishLayout', payload); return; }
    if (action === 'openMascotPicker') { setMascotPicker(true); return; }
    if (action === 'setMascot') { setTweak('mascotId', payload); return; }
    if (action === 'openPack')  { setPackOpen({ packId: payload, contentIds: rollPack(payload) }); return; }
    if (action === 'xpFloat') {
      if (!t.gamification) return;
      const id = floatId.current++;
      setFloats(f => [...f, { id, ...payload }]);
      setTimeout(() => setFloats(f => f.filter(x => x.id !== id)), 1400);
      return;
    }
    setState(s => reduce(s, action, payload));
    if (action === 'toggleHabit' && t.gamification) {
      const h = state.habits.find(x => x.id === payload);
      if (h && !h.doneToday) {
        const id = toastId.current++;
        const z = ZONE_BY_ID[h.zone];
        setToasts(ts => [...ts, { id, icon: z.icon, title: `+1 к стрику · ${h.streak + 1} 🔥`, sub: h.title, xp: 1 }]);
        setTimeout(() => setToasts(ts => ts.filter(t => t.id !== id)), 3600);
        reactMascot(h.zone === 'spirit' ? 'focus' : 'excited', 1800);
      }
    }
    if (action === 'moveWish' && t.gamification && payload.status === 'done') {
      const id = toastId.current++;
      setToasts(ts => [...ts, { id, icon: '🎉', title: 'Желание завершено!', sub: '+50 XP командной XP', xp: 50 }]);
      setTimeout(() => setToasts(ts => ts.filter(t => t.id !== id)), 3600);
      reactMascot('celebrate', 2400);
    }
  }, [t, state.habits, setTweak, reactMascot]);

  // Apply theme & style data attrs on both apps via a wrapping div
  const radiusOverride = {
    sharp:  { '--r-sm': '4px', '--r-md': '4px', '--r-lg': '6px', '--r-xl': '8px', '--r-2xl': '10px' },
    medium: {}, // use defaults
    round:  { '--r-sm': '12px', '--r-md': '16px', '--r-lg': '22px', '--r-xl': '26px', '--r-2xl': '30px' },
  }[t.radius] || {};

  const densityOverride = {
    compact:  { '--pad': '11px', '--gap': '10px' },
    regular:  { '--pad': '16px', '--gap': '16px' },
    spacious: { '--pad': '22px', '--gap': '22px' },
  }[t.density] || {};

  const glowOverride = {
    off:      { '--glow-mult': '0' },
    subtle:   { '--glow-mult': '1' },
    dramatic: { '--glow-mult': '1.8' },
  }[t.glow] || {};

  // Derive accent ramp from picked color OR active mascot theme
  const activeMascot = MASCOT_BY_ID[t.mascotId];
  const useMascotTheme = t.applyMascotTheme !== false && activeMascot;
  const effAccent = useMascotTheme ? activeMascot.theme.accent : t.accent;
  const accentSoft = effAccent + '2E';
  const accentRing = effAccent + '73';

  const stylesOverride = {
    '--accent': effAccent,
    '--accent-2': effAccent,
    '--accent-soft': accentSoft,
    '--accent-ring': accentRing,
    ...densityOverride,
    ...radiusOverride,
    ...glowOverride,
  };
  // Apply mascot's bg tint if enabled
  if (useMascotTheme && t.theme === 'dark') {
    stylesOverride['--bg-app'] = activeMascot.theme.bgA;
    stylesOverride['--bg-app-2'] = activeMascot.theme.bgB;
  }

  // Viewport scaling (fit dual-frame width to window)
  const [scale, setScale] = React.useState(1);
  React.useEffect(() => {
    const targetWidth = (t.showDesktop ? 1200 : 0) + (t.showMobile ? 410 : 0) + (t.showDesktop && t.showMobile ? 36 : 0) + 80;
    const recalc = () => {
      const aw = window.innerWidth;
      const s = Math.min(1, (aw - 24) / targetWidth);
      setScale(Math.max(0.45, s));
    };
    recalc();
    window.addEventListener('resize', recalc);
    return () => window.removeEventListener('resize', recalc);
  }, [t.showDesktop, t.showMobile]);

  return (
    <div className="stage" style={{ '--vp-scale': scale }}>
      {/* Header */}
      <div className="stage-head">
        <div>
          <div className="title">Карта желаний и привычек</div>
          <div className="sub">Прототип · {SCREENS.find(s => s.id === screenId).title}</div>
        </div>
        <div className="crumbs">
          <span className="dot"></span>
          <span>Sokolovy · 4 members</span>
          <span style={{ marginLeft: 16, color: 'rgba(255,255,255,0.3)' }}>v0.1 · {t.style} / {t.theme}</span>
        </div>
      </div>

      {/* Viewports */}
      <div className="viewports" style={{ transform: `scale(${scale})`, transformOrigin: 'top center' }}>
        {t.showDesktop && (
          <div>
            <div className="vp-label">
              <span className="pill">Desktop · 1280 × 800</span>
              <span>Sidebar nav</span>
            </div>
            <ChromeWindow
              width={1200} height={820}
              tabs={[
                { title: 'Карта желаний — Соколовы' },
                { title: 'Linear' },
                { title: 'Figma' },
              ]}
              activeIndex={0}
              url="https://wishmap.local/dashboard">
              <div
                data-theme={t.theme} data-style={t.style}
                style={{ width: '100%', height: '100%', ...stylesOverride, position: 'relative' }}
                data-screen-label={SCREENS.find(s => s.id === screenId).title}
              >
                <DesktopApp
                  state={state}
                  dispatch={dispatch}
                  screenId={screenId}
                  onScreenChange={setScreenId}
                  tweaks={t}
                  dashboardLayout={dashboardLayout}
                  onLayoutChange={setDashboardLayout}
                  onOpenMascotPicker={() => setMascotPicker(true)}
                />
                {/* Toasts inside the desktop frame */}
                {t.gamification && <ToastStack toasts={toasts} />}
                {floats.map(f => <XPFloater key={f.id} {...f} />)}
                {overlay === 'empty' && (
                  <div style={{ position: 'absolute', inset: 0, background: 'var(--bg-app)', zIndex: 80, padding: 40, overflowY: 'auto' }}>
                    <button className="btn ghost mb-3" onClick={() => setOverlay(null)}>← Назад</button>
                    <div style={{ maxWidth: 480, margin: '40px auto' }}>
                      <EmptyState icon="🌱" title="Здесь пока пусто"
                        desc="Добавь первое желание, чтобы команда увидела его в дашборде. Можно начать с любой зоны жизни — потом легко добавить остальные." />
                    </div>
                  </div>
                )}
                {overlay === 'loading' && (
                  <div style={{ position: 'absolute', inset: 0, background: 'var(--bg-app)', zIndex: 80, padding: 40, overflowY: 'auto' }}>
                    <button className="btn ghost mb-3" onClick={() => setOverlay(null)}>← Назад</button>
                    <div style={{ maxWidth: 880, margin: '20px auto' }}><LoadingSkeleton /></div>
                  </div>
                )}
              </div>
            </ChromeWindow>
          </div>
        )}

        {t.showMobile && (
          <div>
            <div className="vp-label">
              <span className="pill">Mobile · iPhone 16 Pro</span>
              <span>Bottom nav</span>
            </div>
            <IOSDevice width={400} height={844} dark={t.theme === 'dark'}>
              <div
                data-theme={t.theme} data-style={t.style}
                style={{ width: '100%', height: '100%', ...stylesOverride, position: 'relative' }}
              >
                <MobileApp state={state} dispatch={dispatch} screenId={screenId} onScreenChange={setScreenId} />
              </div>
            </IOSDevice>
          </div>
        )}
      </div>

      {/* Stage-level modals (over both frames) */}
      {badgeModal && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 5000, pointerEvents: 'none' }}>
          <div style={{ pointerEvents: 'auto', position: 'absolute', inset: 0 }}
               data-theme={t.theme} data-style={t.style}>
            <BadgeModal badge={badgeModal} onClose={() => setBadgeModal(null)} />
          </div>
        </div>
      )}
      {levelUp && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 5000, pointerEvents: 'none' }}>
          <div style={{ pointerEvents: 'auto', position: 'absolute', inset: 0 }}
               data-theme={t.theme} data-style={t.style}>
            <LevelUpFlash from={levelUp.from} to={levelUp.to} onClose={() => setLevelUp(null)} />
          </div>
        </div>
      )}
      {onboarding && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 5000 }}
             data-theme={t.theme} data-style={t.style}>
          <OnboardingDemo onClose={() => setOnboarding(false)} />
        </div>
      )}
      {mascotPicker && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 5000 }}
             data-theme={t.theme} data-style={t.style}>
          <MascotPicker
            value={t.mascotId}
            unlockedIds={state.unlockedIds}
            onChange={(id) => setTweak('mascotId', id)}
            onClose={() => setMascotPicker(false)}
          />
        </div>
      )}
      {packOpen && (
        <div style={{ position: 'fixed', inset: 0, zIndex: 5500 }}
             data-theme={t.theme} data-style={t.style}>
          <PackOpeningModal
            packId={packOpen.packId}
            contentIds={packOpen.contentIds}
            onUnlock={(ids) => {
              const next = [...new Set([...(t.unlockedIds || []), ...ids])];
              setTweak('unlockedIds', next);
            }}
            onClaim={() => setPackOpen(null)}
            onClose={() => setPackOpen(null)}
          />
        </div>
      )}

      {/* Tweaks panel */}
      <TweaksPanel>
        <TweakSection label="Компаньон">
          <TweakSelect label="Маскот" value={t.mascotId}
            options={MASCOTS.filter(m => (t.unlockedIds || []).includes(m.id)).map(m => ({ value: m.id, label: `${m.name} · ${m.type}` }))}
            onChange={(v) => setTweak('mascotId', v)} />
          <TweakToggle label="Тема меняется под героя" value={t.applyMascotTheme !== false}
            onChange={(v) => setTweak('applyMascotTheme', v)} />
          <TweakButton label="🎭 Открыть выбор компаньона"
            onClick={() => setMascotPicker(true)} />
          <TweakButton label="🎉 Реакция: радость" secondary
            onClick={() => reactMascot('celebrate', 2400)} />
          <TweakButton label="🧘 Реакция: фокус" secondary
            onClick={() => reactMascot('focus', 3000)} />
        </TweakSection>

        <TweakSection label="Card-паки">
          <TweakButton label="🎴 Открыть Стартер Пак"
            onClick={() => setPackOpen({ packId: 'starter', contentIds: rollPack('starter') })} />
          <TweakButton label="✨ Открыть Премиум Пак"
            onClick={() => setPackOpen({ packId: 'premium', contentIds: rollPack('premium') })} />
          <TweakButton label="🔥 Открыть Баттл Пак"
            onClick={() => setPackOpen({ packId: 'battle', contentIds: rollPack('battle') })} />
          <TweakButton label="↺ Сбросить разблокировки" secondary
            onClick={() => setTweak('unlockedIds', ['spectr', 'lumen'])} />
        </TweakSection>

        <TweakSection label="Дашборд">
          <TweakButton label="↺ Сбросить раскладку виджетов" secondary
            onClick={() => setDashboardLayout(DEFAULT_LAYOUT.map(w => ({ ...w })))} />
        </TweakSection>

        <TweakSection label="Тема и стиль">
          <TweakRadio label="Тема" value={t.theme}
            options={[{ value: 'dark', label: 'Тёмная' }, { value: 'light', label: 'Светлая' }]}
            onChange={(v) => setTweak('theme', v)} />
          <TweakSelect label="Визуальный стиль" value={t.style}
            options={[
              { value: 'tz',      label: 'По ТЗ (Kanban с glow)' },
              { value: 'minimal', label: 'Минимал' },
              { value: 'brutal',  label: 'Нео-брутал' },
              { value: 'glass',   label: 'Glassmorphism' },
            ]}
            onChange={(v) => setTweak('style', v)} />
        </TweakSection>

        <TweakSection label="Компоновка">
          <TweakRadio label="Plotность" value={t.density}
            options={[
              { value: 'compact',  label: 'Компактно' },
              { value: 'regular',  label: 'Средне' },
              { value: 'spacious', label: 'Просторно' },
            ]}
            onChange={(v) => setTweak('density', v)} />
          <TweakRadio label="Радиусы" value={t.radius}
            options={[
              { value: 'sharp',  label: 'Острые' },
              { value: 'medium', label: 'Средние' },
              { value: 'round',  label: 'Округлые' },
            ]}
            onChange={(v) => setTweak('radius', v)} />
          <TweakSelect label="Вид «Карты желаний»" value={t.wishLayout}
            options={[
              { value: 'kanban', label: 'Канбан по 8 зонам' },
              { value: 'status', label: 'По статусу (3 колонки)' },
              { value: 'grid',   label: 'Сеткой карточек' },
              { value: 'list',   label: 'Списком' },
            ]}
            onChange={(v) => setTweak('wishLayout', v)} />
        </TweakSection>

        <TweakSection label="Цвет и свечение">
          <TweakColor label="Акцент" value={t.accent}
            options={ACCENT_PRESETS}
            onChange={(v) => setTweak('accent', v)} />
          <TweakRadio label="Glow" value={t.glow}
            options={[
              { value: 'off',      label: 'Off' },
              { value: 'subtle',   label: 'Subtle' },
              { value: 'dramatic', label: 'Drama' },
            ]}
            onChange={(v) => setTweak('glow', v)} />
        </TweakSection>

        <TweakSection label="Геймификация">
          <TweakToggle label="Включена (XP, бейджи, тосты)" value={t.gamification}
            onChange={(v) => setTweak('gamification', v)} />
          <TweakButton label="🏆 Показать получение бейджа"
            onClick={() => setBadgeModal(BADGES[10])} />
          <TweakButton label="✨ Показать level up"
            onClick={() => setLevelUp({ from: ME.level, to: ME.level + 1 })} />
          <TweakButton label="👋 Запустить онбординг" secondary
            onClick={() => setOnboarding(true)} />
        </TweakSection>

        <TweakSection label="Состояния">
          <TweakButton label="Empty state" secondary onClick={() => setOverlay('empty')} />
          <TweakButton label="Loading skeleton" secondary onClick={() => setOverlay('loading')} />
        </TweakSection>

        <TweakSection label="Viewports">
          <TweakToggle label="Desktop" value={t.showDesktop}
            onChange={(v) => setTweak('showDesktop', v)} />
          <TweakToggle label="Mobile" value={t.showMobile}
            onChange={(v) => setTweak('showMobile', v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
