// ──────────────────────────────────────────────────────────────────────────
// AI BARTENDER — главный компонент (Half-Empty edition)
// ──────────────────────────────────────────────────────────────────────────

const { INGREDIENTS, GLASSWARE, METHODS, RECIPES, CHARACTERS, IDENTITY_FRAGMENTS } = window.GAME_DATA;
const DATA = window.GAME_DATA;

// ── Дефолты для Tweaks ────────────────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "palette": "storm",
  "font": "display",
  "grunge": 0.35,
  "drinkDifficulty": "normal",
  "currentCustomer": "kira9",
  "music": false,
  "musicVolume": 0.35
}/*EDITMODE-END*/;

// Палитры (тема оформления) — используются и в настройках, и в Tweaks.
const PALETTE_OPTIONS = [
  { value: 'storm', label: 'Шторм', swatch: ['#ff3cb4', '#38e6ff', '#0e0a1a'] },
  { value: 'sodium', label: 'Натрий', swatch: ['#ffa83c', '#ff5a28', '#15100a'] },
  { value: 'toxic', label: 'Токсик', swatch: ['#78ff50', '#ff3cc8', '#0a160d'] },
  { value: 'hardwire', label: 'Хардвайр', swatch: ['#8ab4ff', '#ffffff', '#0a0e14'] },
];

// ── Настройки игрока (тема/музыка) — сохраняются в localStorage ─────────────
const SETTINGS_KEY = 'zarya7:settings';
const SETTINGS_KEYS = ['palette', 'font', 'grunge', 'music', 'musicVolume'];

function loadSettings() {
  try {
    const raw = JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}');
    const out = {};
    for (const k of SETTINGS_KEYS) if (k in raw) out[k] = raw[k];
    return out;
  } catch { return {}; }
}
function saveSettings(t) {
  try {
    const out = {};
    for (const k of SETTINGS_KEYS) out[k] = t[k];
    localStorage.setItem(SETTINGS_KEY, JSON.stringify(out));
  } catch {}
}

// ── Список профилей (только список ников для удобства; игровое состояние
//     теперь хранится на сервере и подтягивается через /api/game/state) ────
const PROFILES_KEY = 'zarya7:profiles';
const LAST_PROFILE_KEY = 'zarya7:lastProfile';

// Длительность смены (день) — 10 минут реального времени.
const SHIFT_SECONDS = 600;

function listProfiles() {
  try { return JSON.parse(localStorage.getItem(PROFILES_KEY) || '[]'); }
  catch { return []; }
}
function saveProfileList(list) {
  localStorage.setItem(PROFILES_KEY, JSON.stringify(list));
}

// Маппинг серверной истории чата в формат сообщений UI.
function mapHistoryMsg(m) {
  if (m.role === 'assistant') return { from: 'them', text: m.content };
  // user: синтетические пометки о подаче/помощи начинаются с '['
  if (m.content && m.content.startsWith('[')) {
    return { from: 'sys', text: m.content.replace(/^\[|\]$/g, '') };
  }
  return { from: 'me', text: m.content };
}

function freshCharStates() {
  const s = {};
  for (const c of CHARACTERS) {
    s[c.id] = {
      fill: 15 + Math.floor(Math.random() * 12), // стартуют наполовину пустыми
      mood: c.mood,
      secretsRevealed: 0,
      drinksServed: 0,
      lastDrinks: [],
      helpDone: [],
    };
  }
  return s;
}

function freshChats() {
  const c = {};
  for (const ch of CHARACTERS) c[ch.id] = [{ from: 'them', text: ch.opening }];
  return c;
}

// ── ОСНОВНОЙ КОМПОНЕНТ ───────────────────────────────────────────────────
function App() {
  const [T, setTweak] = useTweaks({ ...TWEAK_DEFAULTS, ...loadSettings() });
  const [settingsOpen, setSettingsOpen] = useState(false);

  // палитра / шрифт / гранж
  useEffect(() => {
    const root = document.documentElement;
    root.setAttribute('data-palette', T.palette);
    root.setAttribute('data-font', T.font);
    root.style.setProperty('--grunge', String(T.grunge));
  }, [T.palette, T.font, T.grunge]);

  // сохранение настроек игрока в localStorage
  useEffect(() => {
    saveSettings(T);
  }, [T.palette, T.font, T.grunge, T.music, T.musicVolume]);

  // громкость музыки
  useEffect(() => {
    if (window.MusicManager) window.MusicManager.setVolume(T.musicVolume ?? 0.35);
  }, [T.musicVolume]);

  // музыка вкл/выкл
  useEffect(() => {
    if (window.MusicManager) window.MusicManager.setEnabled(!!T.music);
  }, [T.music]);

  // ── профиль / логин ──────────────────────────────────────────────────
  const [profile, setProfile] = useState(null); // { nick }
  const [profiles, setProfiles] = useState(listProfiles());

  // подгрузить/инициализировать состояние
  const [view, setView] = useState('bar');
  const [charStates, setCharStates] = useState(freshCharStates);
  const [chats, setChats] = useState(freshChats);
  const [draft, setDraft] = useState({ ingredients: [], glass: null, method: null });
  const [unlockedRecipes, setUnlockedRecipes] = useState(() => RECIPES.filter(r => r.unlocked).map(r => r.id));
  const [unlockedFragments, setUnlockedFragments] = useState([]);
  const [presentIds, setPresentIds] = useState([]); // кто уже зашёл в бар (управляет ИИ-дирижёр)
  const [isThinking, setIsThinking] = useState(false);
  const [tips, setTips] = useState(0);
  const [shiftTime, setShiftTime] = useState(0);
  const [serveOverlay, setServeOverlay] = useState(null);
  const [dossierActiveId, setDossierActiveId] = useState('kira9');

  // ── ЛОГИН (гидратация с сервера) ───────────────────────────────────────
  const onLogin = async (nick) => {
    let states = freshCharStates();
    let chatsData = freshChats();
    let fragIds = [];
    let tipsVal = 0;
    let present = [];

    try {
      const st = await window.AIService.getState(nick);
      tipsVal = st.tips || 0;
      fragIds = (st.fragments || []).map(f => f.fragmentId);
      present = st.present || [];

      for (const n of (st.npcs || [])) {
        if (states[n.npcId]) {
          states[n.npcId] = {
            fill: n.fill,
            mood: n.mood || states[n.npcId].mood,
            secretsRevealed: n.secretsRevealed || 0,
            drinksServed: n.drinksServed || 0,
            lastDrinks: n.lastDrinks || [],
            helpDone: n.helpDone || [],
          };
        }
      }

      // истории всех клиентов параллельно
      const entries = await Promise.all(CHARACTERS.map(async (c) => {
        try {
          const h = await window.AIService.getHistory(c.id, nick);
          const msgs = (h.messages || []).map(mapHistoryMsg);
          return [c.id, msgs.length ? msgs : [{ from: 'them', text: c.opening }]];
        } catch {
          return [c.id, [{ from: 'them', text: c.opening }]];
        }
      }));
      chatsData = Object.fromEntries(entries);
    } catch (e) {
      console.warn('Не удалось загрузить состояние с сервера, старт с чистого листа', e);
    }

    // подстраховка: если сервер не вернул присутствующих — пусть будет первый из ростера
    if (present.length === 0) present = [CHARACTERS[0].id];

    setCharStates(states);
    setChats(chatsData);
    setUnlockedRecipes(RECIPES.filter(r => r.unlocked).map(r => r.id));
    setUnlockedFragments(fragIds);
    setPresentIds(present);
    setTips(tipsVal);
    setShiftTime(0);
    setProfile({ nick });
    // текущий клиент — первый из присутствующих
    setTweak('currentCustomer', present[0]);

    // обновляем список профилей
    const list = listProfiles();
    if (!list.includes(nick)) {
      const newList = [nick, ...list].slice(0, 8);
      saveProfileList(newList);
      setProfiles(newList);
    }
    localStorage.setItem(LAST_PROFILE_KEY, nick);
  };

  const onLogout = () => {
    setProfile(null);
  };

  // таймер смены
  useEffect(() => {
    if (!profile || view === 'endshift') return;
    const id = setInterval(() => setShiftTime(s => s + 1), 1000);
    return () => clearInterval(id);
  }, [profile, view]);

  // день длится 10 минут реального времени — затем смена закрывается
  useEffect(() => {
    if (!profile || view === 'endshift') return;
    if (shiftTime >= SHIFT_SECONDS) setView('endshift');
  }, [profile, view, shiftTime]);

  // ── текущие данные ───────────────────────────────────────────────────
  // присутствующие клиенты (кого впустил ИИ-дирижёр)
  const presentCharacters = useMemo(
    () => CHARACTERS.filter(c => presentIds.includes(c.id)),
    [presentIds]
  );
  const currentCharacter = useMemo(
    () => CHARACTERS.find(c => c.id === T.currentCustomer && presentIds.includes(c.id))
      || presentCharacters[0]
      || CHARACTERS[0],
    [T.currentCustomer, presentIds, presentCharacters]
  );
  const currentState = charStates[currentCharacter.id];
  const currentChat = chats[currentCharacter.id] || [];

  // какой фрагмент про бармена этот клиент может уронить
  const fragmentForCurrentChar = useMemo(() => {
    return IDENTITY_FRAGMENTS.find(f => f.from === currentCharacter.id);
  }, [currentCharacter]);
  // уже уронил?
  const fragmentAlreadyDropped = fragmentForCurrentChar && unlockedFragments.includes(fragmentForCurrentChar.id);
  // ещё доступен (не уронен)
  const fragmentAvailable = fragmentForCurrentChar && !fragmentAlreadyDropped ? fragmentForCurrentChar : null;

  // ── мутации ──────────────────────────────────────────────────────────
  const patchCharState = (id, patch) => {
    setCharStates(prev => ({
      ...prev,
      [id]: { ...prev[id], ...(typeof patch === 'function' ? patch(prev[id]) : patch) }
    }));
  };
  const pushMsg = (charId, msg) => {
    setChats(prev => ({ ...prev, [charId]: [...(prev[charId] || []), msg] }));
  };

  // применить серверную реакцию: состояние уже посчитано на бэке
  const applyServerReaction = (charId, resp, opts = {}) => {
    const ch = CHARACTERS.find(c => c.id === charId);

    if (resp.state) {
      patchCharState(charId, {
        fill: resp.state.fill,
        mood: resp.state.mood,
        secretsRevealed: resp.state.secretsRevealed,
        drinksServed: resp.state.drinksServed,
        lastDrinks: resp.state.lastDrinks || [],
        helpDone: resp.state.helpDone || [],
      });
    }

    pushMsg(charId, { from: 'them', text: resp.reply, kind: opts.kind });

    if (resp.revealedSecretIndex != null) {
      pushMsg(charId, { from: 'sys', text: `▷ ОТКРЫТ СЕКРЕТ ${resp.revealedSecretIndex + 1}/${ch.secrets.length} · ДОБАВЛЕНО В ДОСЬЕ` });
    }

    if (resp.fragment && !unlockedFragments.includes(resp.fragment.fragmentId)) {
      setUnlockedFragments(prev => [...prev, resp.fragment.fragmentId]);
      pushMsg(charId, { from: 'sys', text: `▷ ЗАЦЕПКА ПРО ТЕБЯ · ОТКРЫТА В «КТО Я»`, kind: 'fragment' });
    }

    if (typeof resp.tips === 'number') {
      setTips(resp.tips);
    }

    // ИИ-дирижёр впустил нового посетителя
    if (resp.arrival && resp.arrival.npcId) {
      admitArrival(resp.arrival);
    }
  };

  // зарегистрировать приход нового клиента (по решению дирижёра)
  const admitArrival = (arrival) => {
    const id = arrival.npcId;
    setPresentIds(prev => (prev.includes(id) ? prev : [...prev, id]));
    const ch = CHARACTERS.find(c => c.id === id);
    setChats(prev => {
      const existing = prev[id] || [];
      const seeded = existing.length ? existing : (ch ? [{ from: 'them', text: ch.opening }] : []);
      return {
        ...prev,
        [id]: [
          { from: 'sys', text: `▷ НОВЫЙ ПОСЕТИТЕЛЬ: ${ch ? ch.name : id}${arrival.reason ? ' — ' + arrival.reason : ''}`, kind: 'arrival' },
          ...seeded,
        ],
      };
    });
  };

  // ИИ-дирижёр: в течение смены посетители подходят сами по времени.
  // Тикаем сразу при входе и затем периодически, пока смена идёт.
  useEffect(() => {
    if (!profile || view === 'endshift') return;
    let cancelled = false;
    const fire = async () => {
      try {
        const res = await window.AIService.tick(profile.nick);
        if (!cancelled && res && res.arrival && res.arrival.npcId) admitArrival(res.arrival);
      } catch (e) {
        console.warn('tick failed', e);
      }
    };
    const id = setInterval(fire, 120000);
    return () => { cancelled = true; clearInterval(id); };
  }, [profile, view]);

  // ── отправка реплики игрока ──────────────────────────────────────────
  const sendPlayerMessage = async (text) => {
    const charId = currentCharacter.id;
    pushMsg(charId, { from: 'me', text });
    setIsThinking(true);
    try {
      const resp = await window.AIService.characterReply({
        npcId: charId,
        playerId: profile.nick,
        message: text,
      });
      applyServerReaction(charId, resp);
    } catch (err) {
      console.error('chat error', err);
      pushMsg(charId, { from: 'them', text: '[ПОМЕХИ В СЕТИ. ПОВТОРИ]' });
    }
    setIsThinking(false);
  };

  // ── подача напитка ───────────────────────────────────────────────────
  const onServe = async (recipe) => {
    const charId = currentCharacter.id;
    const drinkColor = recipe
      ? (DATA.INGREDIENTS.find(i => i.id === recipe.ingredients[0])?.color || '#ff3cb4')
      : (DATA.INGREDIENTS.find(i => i.id === draft.ingredients[0])?.color || '#ff3cb4');
    const drinkName = recipe ? recipe.name : 'САМОДЕЛЬНЫЙ МИКС';

    setServeOverlay({ name: drinkName, color: drinkColor });
    setTimeout(() => setServeOverlay(null), 1300);

    pushMsg(charId, { from: 'sys', text: `▷ ПОДАНО: ${drinkName}` });

    if (recipe && !unlockedRecipes.includes(recipe.id)) {
      setUnlockedRecipes(u => [...u, recipe.id]);
    }

    setIsThinking(true);
    const customMix = recipe ? null : {
      ingredients: draft.ingredients.map(id => DATA.INGREDIENTS.find(x => x.id === id)?.name).filter(Boolean),
      glass: DATA.GLASSWARE.find(g => g.id === draft.glass)?.name,
      method: DATA.METHODS.find(m => m.id === draft.method)?.name,
    };
    try {
      const resp = await window.AIService.drinkReaction({
        npcId: charId,
        playerId: profile.nick,
        recipeName: recipe ? recipe.name : null,
        recipeDesc: recipe ? recipe.desc : null,
        customMix,
      });
      applyServerReaction(charId, resp);
    } catch (err) {
      console.error('drink error', err);
    }
    setIsThinking(false);

    setDraft({ ingredients: [], glass: null, method: null });
  };

  // ── помощь персонажу ─────────────────────────────────────────────────
  const onHelp = async (act) => {
    const charId = currentCharacter.id;
    const cur = charStates[charId];
    if ((cur.helpDone || []).includes(act.id)) return;

    pushMsg(charId, { from: 'sys', text: `▷ ПОМОЩЬ: ${act.label}` });
    pushMsg(charId, { from: 'sys', text: act.done, kind: 'help' });

    setIsThinking(true);
    try {
      const resp = await window.AIService.helpReaction({
        npcId: charId,
        playerId: profile.nick,
        helpId: act.id,
        helpLabel: act.label,
        helpDone: act.done,
      });
      applyServerReaction(charId, resp);
    } catch (err) {
      console.error('help error', err);
    }
    setIsThinking(false);
  };

  // ── переключение клиента ─────────────────────────────────────────────
  const switchCustomer = (id) => {
    if (!presentIds.includes(id)) return; // нельзя переключиться на ещё не вошедшего
    setTweak('currentCustomer', id);
    if (view !== 'bar') setView('bar');
  };

  // ── конец / рестарт ──────────────────────────────────────────────────
  const endShift = () => setView('endshift');
  // Новая смена: сервер сбрасывает зал (и историю), дирижёр заново впускает
  // посетителей; затем заново гидратируемся.
  const restartShift = async () => {
    if (!profile) { setView('bar'); return; }
    try {
      await window.AIService.newShift(profile.nick);
    } catch (e) {
      console.error('newShift failed', e);
    }
    await onLogin(profile.nick);
    setView('bar');
  };

  // итоги
  const totalHelps = Object.values(charStates).reduce((sum, s) => sum + (s.helpDone?.length || 0), 0);
  const shiftStats = useMemo(() => {
    const log = [];
    for (const c of CHARACTERS) {
      const s = charStates[c.id];
      if (s.drinksServed > 0 || (s.helpDone || []).length > 0) {
        log.push(`${c.name}: налито ${s.drinksServed}, заполнен ${Math.round(s.fill)}%, помощи ${(s.helpDone || []).length}/${c.helpActions.length}`);
      }
    }
    if (log.length === 0) log.push('Ты не налил никому. Странная смена.');
    return {
      tips, helps: totalHelps,
      fragments: unlockedFragments.length, totalFragments: IDENTITY_FRAGMENTS.length,
      log,
    };
  }, [charStates, tips, totalHelps, unlockedFragments]);

  // обратный отсчёт смены (день = 10 минут)
  const clock = (() => {
    const left = Math.max(0, SHIFT_SECONDS - shiftTime);
    const mm = Math.floor(left / 60);
    const ss = left % 60;
    return `${String(mm).padStart(2, '0')}:${String(ss).padStart(2, '0')}`;
  })();

  // ── РЕНДЕР ───────────────────────────────────────────────────────────
  if (!profile) {
    return (
      <div className="app login-mode">
        <div className="scene-bg"></div>
        <div className="scanlines"></div>
        <div className="grain"></div>
        <LoginScreen
          onLogin={onLogin}
          lastProfile={localStorage.getItem(LAST_PROFILE_KEY)}
          profiles={profiles}
        />
      </div>
    );
  }

  return (
    <div className="app">
      <div className="scene-bg"></div>
      <div className="scanlines"></div>
      <div className="grain"></div>

      {/* верх */}
      <div className="topbar">
        <div className="brand">
          <span className="dot"></span>
          NULL//POINTER
          <small>// NEON-Q · BAR // {profile.nick.toUpperCase()}</small>
        </div>
        <div className="tabs">
          {[
            ['bar', 'Бар'],
            ['identity', 'Кто я'],
            ['dossier', 'Досье'],
            ['recipes', 'Рецепты'],
            ['endshift', 'Итоги'],
          ].map(([k, l]) => (
            <button
              key={k}
              className={`tab ${view === k ? 'active' : ''}`}
              onClick={() => k === 'endshift' ? endShift() : setView(k)}
            >
              {l}
              {k === 'identity' && unlockedFragments.length > 0 && (
                <span className="tab-badge">{unlockedFragments.length}</span>
              )}
            </button>
          ))}
        </div>
        <div className="shift-meta">
          <div className="item"><span className="lbl">ДО ЗАКРЫТИЯ</span><span className="val">{clock}</span></div>
          <div className="item"><span className="lbl">ЧАЕВЫЕ</span><span className="val good">¢{tips}</span></div>
          <div className="item"><span className="lbl">ФРАГ-ТЫ</span><span className="val">{unlockedFragments.length}/{IDENTITY_FRAGMENTS.length}</span></div>
          <button className="logout" onClick={() => setSettingsOpen(true)} title="Настройки">⚙</button>
          <button className="logout" onClick={onLogout} title="Сменить профиль">⎋</button>
        </div>
      </div>

      {/* центр */}
      <div style={{ position: 'relative', minHeight: 0, overflow: 'hidden' }}>
        {view === 'bar' && (
          <div className="bar-screen">
            <div className="scene-col">
              <div className="panel">
                <div className="panel-h"><strong>КЛИЕНТ // {currentCharacter.name.slice(0, 14)}</strong><span>ID 0x{currentCharacter.id.toUpperCase()}</span></div>
                <ClientCard character={currentCharacter} state={currentState} fragmentDropped={fragmentAlreadyDropped} />
              </div>
              <Portrait character={currentCharacter} />
              <CraftPanel data={DATA} draft={draft} setDraft={setDraft} onServe={onServe} difficulty={T.drinkDifficulty} />
            </div>
            <div className="chat-col">
              <div className="panel">
                <div className="panel-h"><strong>ЗАЛ</strong><span>{presentCharacters.length} / {CHARACTERS.length} ПОСЕТИТЕЛЕЙ</span></div>
                <div className="panel-body scroll-pretty" style={{ padding: 8 }}>
                  <div className="client-strip" style={{ flexDirection: 'column' }}>
                    {presentCharacters.map(c => {
                      const st = charStates[c.id];
                      const hasFrag = IDENTITY_FRAGMENTS.find(f => f.from === c.id && unlockedFragments.includes(f.id));
                      return (
                        <button
                          key={c.id}
                          className={`chit ${c.id === currentCharacter.id ? 'active' : ''}`}
                          onClick={() => switchCustomer(c.id)}
                        >
                          <span>{c.name}</span>
                          <span className="chit-meta">{Math.round(st.fill)}%{hasFrag ? ' ◆' : ''}</span>
                        </button>
                      );
                    })}
                  </div>
                </div>
              </div>
              <ChatPanel
                character={currentCharacter}
                messages={currentChat}
                onSend={sendPlayerMessage}
                isThinking={isThinking}
                playerName={profile.nick}
              />
              <HelpPanel
                character={currentCharacter}
                state={currentState}
                charStates={charStates}
                onHelp={onHelp}
              />
            </div>
          </div>
        )}

        {view === 'identity' && (
          <IdentityScreen
            fragments={IDENTITY_FRAGMENTS}
            unlockedIds={unlockedFragments}
            playerName={profile.nick}
            characters={CHARACTERS}
          />
        )}

        {view === 'dossier' && (
          <DossierScreen
            characters={presentCharacters.length ? presentCharacters : [CHARACTERS[0]]}
            charStates={charStates}
            activeId={dossierActiveId}
            setActiveId={setDossierActiveId}
          />
        )}

        {view === 'recipes' && (
          <RecipesScreen data={DATA} unlocked={unlockedRecipes} />
        )}

        {view === 'endshift' && (
          <EndShiftScreen shiftStats={shiftStats} onRestart={restartShift} playerName={profile.nick} />
        )}

        {serveOverlay && (
          <div className="serve-overlay">
            <div>
              <div className="serve-glass" style={{
                '--drink-color': serveOverlay.color,
                '--fill-top': '30%',
              }}></div>
              <div className="serve-text">
                ПОДАЁТСЯ
                <strong>{serveOverlay.name}</strong>
              </div>
            </div>
          </div>
        )}
      </div>

      {/* низ */}
      <div className="statusbar">
        <span className="live">UPLINK · AI BARTENDER v0.8 · {profile.nick}</span>
        <span className="tip">КЛИЕНТ: <strong>{currentCharacter.name}</strong></span>
        <span className="tip">ЗАПОЛНЕНОСТЬ: <strong>{Math.round(currentState.fill)}%</strong></span>
        <span className="spacer"></span>
        <span className="tip">правило: слушай → налей нужное → помоги → услышь о себе</span>
      </div>

      {/* Настройки игрока */}
      {settingsOpen && (
        <div className="login-overlay" onClick={() => setSettingsOpen(false)}>
          <div className="login-card" onClick={e => e.stopPropagation()} style={{ maxWidth: 460, textAlign: 'left' }}>
            <div className="login-sub">НАСТРОЙКИ · NULL//POINTER</div>
            <h1 style={{ marginBottom: 14 }}>ПУЛЬТ</h1>

            <div style={{ marginBottom: 16 }}>
              <div className="micro-label" style={{ marginBottom: 6 }}>ТЕМА</div>
              <PaletteSwatches
                label="палитра"
                value={T.palette}
                onChange={v => setTweak('palette', v)}
                options={PALETTE_OPTIONS}
              />
              <div style={{ display: 'flex', gap: 8, marginTop: 10 }}>
                {[['display', 'Дисплей'], ['mono', 'Моно']].map(([v, l]) => (
                  <button
                    key={v}
                    className={`btn ${T.font === v ? 'primary' : ''}`}
                    onClick={() => setTweak('font', v)}
                    style={{ flex: 1 }}
                  >{l}</button>
                ))}
              </div>
            </div>

            <div style={{ marginBottom: 18 }}>
              <div className="micro-label" style={{ marginBottom: 6 }}>ЗВУК</div>
              <button
                className={`btn ${T.music ? 'primary' : ''}`}
                onClick={() => setTweak('music', !T.music)}
                style={{ width: '100%' }}
              >
                {T.music ? '♪ МУЗЫКА: ВКЛ' : '♪ МУЗЫКА: ВЫКЛ'}
              </button>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 10, opacity: T.music ? 1 : 0.45 }}>
                <span className="micro-label" style={{ whiteSpace: 'nowrap' }}>ГРОМКОСТЬ</span>
                <input
                  type="range"
                  min={0} max={100} step={1}
                  value={Math.round((T.musicVolume ?? 0.35) * 100)}
                  onChange={e => setTweak('musicVolume', Number(e.target.value) / 100)}
                  style={{ flex: 1 }}
                />
                <span style={{ fontFamily: 'var(--font-mono)', fontSize: 12, minWidth: 38, textAlign: 'right' }}>
                  {Math.round((T.musicVolume ?? 0.35) * 100)}%
                </span>
              </div>
            </div>

            <button className="btn primary" onClick={() => setSettingsOpen(false)} style={{ width: '100%' }}>ГОТОВО ▸</button>
          </div>
        </div>
      )}

      {/* Tweaks */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Палитра">
          <PaletteSwatches
            label="палитра"
            value={T.palette}
            onChange={v => setTweak('palette', v)}
            options={[
              { value: 'storm', swatch: ['#ff3cb4', '#38e6ff', '#0e0a1a'] },
              { value: 'sodium', swatch: ['#ffa83c', '#ff5a28', '#15100a'] },
              { value: 'toxic', swatch: ['#78ff50', '#ff3cc8', '#0a160d'] },
              { value: 'hardwire', swatch: ['#8ab4ff', '#ffffff', '#0a0e14'] },
            ]}
          />
        </TweakSection>

        <TweakSection label="Внешний вид">
          <TweakRadio
            label="шрифт"
            value={T.font}
            options={[
              { value: 'display', label: 'дисплей' },
              { value: 'mono', label: 'моно' },
            ]}
            onChange={v => setTweak('font', v)}
          />
          <TweakSlider
            label="гранж / шум"
            value={T.grunge}
            min={0} max={1} step={0.05}
            onChange={v => setTweak('grunge', v)}
          />
        </TweakSection>

        <TweakSection label="Игровая логика">
          <TweakRadio
            label="сложность напитков"
            value={T.drinkDifficulty}
            options={[
              { value: 'easy', label: 'мягкая' },
              { value: 'normal', label: 'точно' },
            ]}
            onChange={v => setTweak('drinkDifficulty', v)}
          />
          <TweakSelect
            label="клиент за стойкой"
            value={T.currentCustomer}
            options={presentCharacters.map(c => ({ value: c.id, label: c.name }))}
            onChange={v => switchCustomer(v)}
          />
          <TweakButton label="сбросить смену" onClick={restartShift}>СБРОС ▸</TweakButton>
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

function clamp(n, lo, hi) { return Math.max(lo, Math.min(hi, n)); }

function PaletteSwatches({ label, value, onChange, options }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, padding: '6px 0' }}>
      <div style={{ fontFamily: 'var(--font-mono)', fontSize: 11, letterSpacing: '0.1em', color: 'var(--ink-dim)', textTransform: 'uppercase' }}>{label}</div>
      <div style={{ display: 'flex', gap: 6 }}>
        {options.map((o, i) => {
          const swatch = o.swatch;
          const v = o.value;
          const active = v === value;
          return (
            <button
              key={i}
              onClick={() => onChange(v)}
              title={v}
              style={{
                width: 44, height: 26, position: 'relative',
                border: `1px solid ${active ? 'var(--accent-1)' : 'var(--panel-edge)'}`,
                background: '#000', padding: 0, cursor: 'pointer',
                boxShadow: active ? '0 0 10px var(--accent-1)' : 'none',
              }}>
              <div style={{ display: 'flex', height: '100%' }}>
                {swatch.map((c, j) => <div key={j} style={{ flex: 1, background: c }}></div>)}
              </div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

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