// ────────────────────────────────────────────────────────────
// Nukus 89 — UI primitives + screens
// Depends on: T, BAR, KITCHEN, EVENTS, FLOOR (window globals from data.jsx)
// ────────────────────────────────────────────────────────────

const { useState, useMemo, useEffect, useRef } = React;

// ─── Primitives ────────────────────────────────────────────
function Hr({ ink, dashed = true, style = {} }) {
  return <div style={{
    height: 1, width: '100%',
    backgroundImage: dashed
      ? `repeating-linear-gradient(90deg, ${ink} 0 4px, transparent 4px 8px)`
      : 'none',
    background: dashed ? undefined : ink,
    opacity: 0.45,
    ...style,
  }} />;
}

function Btn({ children, onClick, kind = 'ghost', theme, full = false, disabled = false, style = {} }) {
  const { ink, bg } = theme;
  const isPrimary = kind === 'primary';
  return (
    <button
      onClick={disabled ? undefined : onClick}
      style={{
        appearance: 'none', cursor: disabled ? 'default' : 'pointer',
        border: `1.5px solid ${ink}`,
        background: isPrimary ? ink : 'transparent',
        color: isPrimary ? bg : ink,
        fontFamily: 'var(--mono)',
        fontSize: 13, fontWeight: 600, letterSpacing: '0.06em',
        padding: '14px 18px', textTransform: 'uppercase',
        width: full ? '100%' : 'auto',
        opacity: disabled ? 0.35 : 1,
        borderRadius: 0, lineHeight: 1,
        ...style,
      }}>
      {children}
    </button>
  );
}

function Chip({ children, active = false, theme, onClick, style = {} }) {
  const { ink, bg } = theme;
  return (
    <button onClick={onClick} style={{
      appearance:'none', cursor:'pointer', borderRadius:0,
      border:`1px solid ${ink}`,
      background: active ? ink : 'transparent',
      color: active ? bg : ink,
      fontFamily:'var(--mono)', fontSize:11, fontWeight:600,
      letterSpacing:'0.08em', padding:'8px 12px',
      textTransform:'uppercase', whiteSpace:'nowrap',
      ...style,
    }}>{children}</button>
  );
}

function CornerTag({ children, theme, style = {} }) {
  return (
    <div style={{
      fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
      letterSpacing:'0.12em', color: theme.mute,
      textTransform:'uppercase', ...style,
    }}>{children}</div>
  );
}

function SectionLabel({ children, theme, right = null }) {
  return (
    <div style={{
      display:'flex', alignItems:'baseline', justifyContent:'space-between',
      padding:'14px 16px 6px',
    }}>
      <div style={{
        fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
        letterSpacing:'0.18em', color: theme.mute, textTransform:'uppercase',
      }}>{children}</div>
      {right}
    </div>
  );
}

// ─── Top header (used on inner screens) ────────────────────
function ScreenHeader({ title, onBack, theme, t, right = null }) {
  const { ink } = theme;
  return (
    <div style={{
      display:'flex', alignItems:'center', justifyContent:'space-between',
      padding:'8px 12px 12px', gap:8,
    }}>
      <button onClick={onBack} style={{
        appearance:'none', border:'none', background:'transparent',
        cursor:'pointer', padding:'6px 10px 6px 4px',
        fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
        letterSpacing:'0.1em', color: ink,
      }}>
        ← {t('back')}
      </button>
      <div style={{
        fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
        letterSpacing:'0.16em', color: ink,
      }}>{title}</div>
      <div style={{ minWidth: 70, textAlign:'right' }}>{right}</div>
    </div>
  );
}

// ─── HOME SCREEN ──────────────────────────────────────────
function HomeScreen({ go, theme, t, lang, setLang }) {
  const { ink, bg, mute } = theme;
  // График: ежедневно с 17:00; ночи пт/сб — до 04:00, остальные — до 01:00 (Ташкент, UTC+5)
  const _now = new Date(Date.now() + 5 * 3600e3);
  const _h = _now.getUTCHours();
  const _closeFor = (dow) => (dow === 5 || dow === 6) ? 4 : 1; // час закрытия ночи, начавшейся в день dow
  const _close = _h >= 17 ? _closeFor(_now.getUTCDay()) : _closeFor((_now.getUTCDay() + 6) % 7);
  const isOpen = _h >= 17 || _h < _close;
  const tiles = [
    { k:'book',    label: t('book'),    sub:'01' },
    { k:'kitchen', label: t('kitchen'), sub:'02' },
    { k:'bar',     label: t('bar'),     sub:'03' },
    { k:'events',  label: t('events'),  sub:'04' },
  ];
  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      {/* header bar with lang + status */}
      <div style={{
        display:'flex', alignItems:'center', justifyContent:'space-between',
        padding:'6px 14px 10px',
      }}>
        <div style={{ display:'flex', gap:4 }}>
          {['ru','en','uz'].map(l => (
            <button key={l} onClick={() => setLang(l)} style={{
              appearance:'none', border:'none', background:'transparent', cursor:'pointer',
              fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
              letterSpacing:'0.12em', padding:'4px 6px',
              color: lang===l ? ink : mute,
              textDecoration: lang===l ? 'underline' : 'none',
              textUnderlineOffset: 3,
            }}>{l.toUpperCase()}</button>
          ))}
        </div>
        <div style={{
          display:'flex', alignItems:'center', gap:6,
          fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
          letterSpacing:'0.14em', color: isOpen ? ink : mute,
        }}>
          <span style={{
            width:7, height:7, borderRadius:'50%',
            background: isOpen ? ink : 'transparent',
            border: `1.5px solid ${isOpen ? ink : mute}`,
            animation: isOpen ? 'pulse 2s ease-in-out infinite' : 'none',
          }} />
          {isOpen ? `${t('open_till')} 0${_close}:00` : `${t('closed_till')} 17:00`}
        </div>
      </div>

      <Hr ink={ink} />

      {/* hero block */}
      <div style={{ padding:'28px 18px 22px' }}>
        <div style={{
          display:'flex', alignItems:'flex-start', justifyContent:'space-between', gap:14,
        }}>
          <div>
            <div style={{
              fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
              letterSpacing:'0.22em', color: mute, marginBottom:10,
            }}>{t('welcome')}</div>
            <div style={{
              fontFamily:'var(--display)', fontSize:'clamp(38px, 12vw, 54px)', fontWeight:900,
              lineHeight:0.88, letterSpacing:'-0.03em', color: ink,
            }}>NUKUS89</div>
            <div style={{ marginTop:8 }}>
              <span style={{
                fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                letterSpacing:'0.16em', color: mute,
              }}>{t('sub_speakeasy')}</span>
            </div>
          </div>
          {/* logo mark */}
          <div style={{
            width:54, height:54, flexShrink:0,
            background: theme.id === 'brand' ? 'transparent' : bg,
            border: theme.id === 'brand' ? 'none' : `1px solid ${ink}`,
            display:'flex', alignItems:'center', justifyContent:'center',
            overflow:'hidden',
          }}>
            <img src={(typeof window !== 'undefined' && window.__resources && window.__resources.logo) || "assets/nukus-logo.jpg"} alt="" style={{
              width: theme.id === 'brand' ? 54 : 36,
              height: theme.id === 'brand' ? 54 : 36,
              objectFit:'contain',
              mixBlendMode: theme.id === 'brand' ? 'multiply' : 'normal',
              filter: theme.invertLogo ? 'invert(1)' : 'none',
            }} />
          </div>
        </div>
        <div style={{
          marginTop:14,
          fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
          letterSpacing:'0.12em', color: mute, lineHeight:1.55,
        }}>
          {t('address')}<br/>
          {t('address_note')} ⋅ 41.28°N / 69.27°E
        </div>
        {/* соцсети */}
        <div style={{ marginTop:10, display:'flex', gap:16 }}>
          <a href="https://www.instagram.com/89.nukus/" target="_blank" rel="noreferrer" style={{
            fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
            letterSpacing:'0.14em', color: ink, textDecoration:'underline', textUnderlineOffset:3,
          }}>{t('instagram')} ↗</a>
          <a href="https://t.me/Nukus_89" target="_blank" rel="noreferrer" style={{
            fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
            letterSpacing:'0.14em', color: ink, textDecoration:'underline', textUnderlineOffset:3,
          }}>{t('telegram')} ↗</a>
        </div>
      </div>

      <Hr ink={ink} />

      {/* big tile menu */}
      <div style={{ flex:1, display:'grid', gridTemplateColumns:'1fr 1fr', borderTop:`0px solid ${ink}` }}>
        {tiles.map((tile, i) => {
          const isRight = i % 2 === 1;
          const isBottom = i >= 2;
          return (
            <button key={tile.k} onClick={() => go(tile.k)} style={{
              appearance:'none', cursor:'pointer',
              background: 'transparent',
              border:'none',
              borderRight: !isRight ? `1px solid ${ink}` : 'none',
              borderBottom: !isBottom ? `1px solid ${ink}` : 'none',
              padding:'18px 16px 16px',
              display:'flex', flexDirection:'column',
              justifyContent:'space-between', alignItems:'flex-end',
              minHeight: 110, color: ink, textAlign:'right',
              transition:'background 0.15s',
            }}
            onMouseEnter={(e)=> e.currentTarget.style.background = theme.hover}
            onMouseLeave={(e)=> e.currentTarget.style.background = 'transparent'}>
              <div style={{
                fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                letterSpacing:'0.2em', color: theme.mute,
              }}>{tile.sub}</div>
              <div style={{
                fontFamily:'var(--display)', fontSize:30, fontWeight:900,
                letterSpacing:'-0.02em', lineHeight:0.95,
              }}>{tile.label}</div>
            </button>
          );
        })}
      </div>

      <Hr ink={ink} />

      {/* NUKUS CLUB — временно скрыт (LOYALTY_ENABLED в data.jsx) */}
      {LOYALTY_ENABLED && (
        <React.Fragment>
          <button onClick={() => go('loyalty')} style={{
            appearance:'none', cursor:'pointer', background: ink,
            border:'none', padding:'14px 16px',
            display:'flex', alignItems:'center', justifyContent:'space-between',
            color: bg,
          }}>
            <div style={{
              fontFamily:'var(--display)', fontSize:16, fontWeight:900,
              letterSpacing:'0.02em',
            }}>NUKUS CLUB ★</div>
            <div style={{
              fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
              letterSpacing:'0.14em',
            }}>{t('loyalty_card')} ↗</div>
          </button>
          <Hr ink={ink} />
        </React.Fragment>
      )}

      {/* bottom bar with my-bookings shortcut */}
      <button onClick={() => go('my')} style={{
        appearance:'none', cursor:'pointer', background:'transparent',
        border:'none', padding:'12px 16px',
        display:'flex', alignItems:'center', justifyContent:'space-between',
        color: ink,
      }}>
        <div style={{
          fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
          letterSpacing:'0.16em',
        }}>→ {t('my')} / {t('your_booking')}</div>
        <div style={{
          fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
        }}>↗</div>
      </button>
    </div>
  );
}

// ─── BOOKING FLOW ─────────────────────────────────────────
function BookingFlow({ go, theme, t, lang, onConfirm }) {
  const { ink, bg, mute } = theme;
  const [step, setStep] = useState(0); // 0 date, 1 time, 2 guests, 3 table, 4 success
  const [date, setDate] = useState(null);
  const [time, setTime] = useState(null);
  const [guests, setGuests] = useState(2);
  const [table, setTable] = useState(null);
  const [comment, setComment] = useState('');
  const [code, setCode] = useState(null);
  const [saving, setSaving] = useState(false);
  const [saveErr, setSaveErr] = useState(false);
  const [busyRows, setBusyRows] = useState([]); // брони на выбранную дату (занятость столов)

  const allTimes = ['18:00','18:30','19:00','19:30','20:00','20:30','21:00','21:30','22:00','22:30','23:00','23:30'];

  // Реальная занятость столов с сервера
  useEffect(() => {
    if (!date) return;
    const ds = `${date.y}-${String(date.m+1).padStart(2,'0')}-${String(date.d).padStart(2,'0')}`;
    fetch('/api/availability?date=' + ds)
      .then(r => r.json())
      .then(rows => setBusyRows(Array.isArray(rows) ? rows : []))
      .catch(() => setBusyRows([]));
  }, [date]);

  // ponytail: стол занят, если бронь в пределах ±2ч от выбранного времени
  const toMin = (s) => { const [h, m] = String(s).split(':').map(Number); return (h || 0) * 60 + (m || 0); };
  const takenTables = useMemo(() => {
    if (!time) return [];
    return busyRows.filter(b => Math.abs(toMin(b.time_str) - toMin(time)) < 120).map(b => b.table_id);
  }, [busyRows, time]);

  // Хелпер: доступные слоты для конкретной даты (с буфером +60 мин)
  function getAvailableTimesForDate(d) {
    // Пт/сб брони только до 21:00 — дальше живая посадка
    let ts = allTimes;
    if (d.dow === 5 || d.dow === 6) ts = ts.filter(tm => tm <= '21:00');
    const now = new Date();
    const isToday = d.d === now.getDate() && d.m === now.getMonth() && d.y === now.getFullYear();
    if (!isToday) return ts;
    const currentMinutes = now.getHours() * 60 + now.getMinutes() + 60;
    return ts.filter(t => {
      const [h, m] = t.split(':').map(Number);
      return h * 60 + m > currentMinutes;
    });
  }

  // Генерируем дни: если на сегодня нет слотов — начинаем с завтра
  const days = useMemo(() => {
    const now = new Date();
    const todayObj = { d: now.getDate(), m: now.getMonth(), y: now.getFullYear(), dow: now.getDay() };
    const startFromTomorrow = getAvailableTimesForDate(todayObj).length === 0;

    const base = new Date(now);
    if (startFromTomorrow) base.setDate(base.getDate() + 1);
    base.setHours(0, 0, 0, 0);

    const out = [];
    for (let i = 0; i < 30; i++) {
      const d = new Date(base);
      d.setDate(d.getDate() + i);
      out.push({ d: d.getDate(), m: d.getMonth(), y: d.getFullYear(), dow: d.getDay() });
    }
    return out;
  }, []);

  // Доступные слоты для выбранной даты
  const times = useMemo(() => {
    if (!date) return allTimes;
    return getAvailableTimesForDate(date);
  }, [date]);

  const back = () => step === 0 ? go('home') : setStep(step - 1);

  const finalize = async () => {
    const c = 'NK-' + Math.floor(Math.random()*9000+1000);
    setSaving(true); setSaveErr(false);
    const ok = await onConfirm({ date, time, guests, table, code: c, comment: comment.trim() });
    setSaving(false);
    if (!ok) { setSaveErr(true); return; }
    setCode(c);
    setStep(4);
  };

  const StepIndicator = () => (
    <div style={{ display:'flex', gap:4, padding:'0 16px 14px' }}>
      {[0,1,2,3].map(i => (
        <div key={i} style={{
          flex:1, height:3,
          background: i <= step ? ink : 'transparent',
          border:`1px solid ${ink}`,
        }} />
      ))}
    </div>
  );

  const stepLabels = [
    t('select_date'), t('select_time'), t('guests'), t('select_table'),
  ];

  if (step === 4) {
    return <BookingSuccess date={date} time={time} guests={guests} table={table} code={code}
      theme={theme} t={t} lang={lang} go={go} />;
  }

  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      <ScreenHeader title={`${stepLabels[step]} · ${step+1}/4`} onBack={back} theme={theme} t={t} />
      <Hr ink={ink} />
      <div style={{ padding:'14px 0 4px' }}>
        <StepIndicator />
      </div>

      <div style={{ flex:1, overflow:'auto' }}>
        {step === 0 && (
          <DateStep days={days} value={date} onPick={(d)=>{ setDate(d); setStep(1); }}
            theme={theme} t={t} lang={lang} />
        )}
        {step === 1 && (
          <TimeStep times={times} unavailable={[]} value={time}
            note={date && (date.dow === 5 || date.dow === 6) ? t('fri_sat_late_note') : null}
            onPick={(x)=>{ setTime(x); setStep(2); }} theme={theme} t={t} />
        )}
        {step === 2 && (
          <GuestStep value={guests} onChange={setGuests} theme={theme} t={t}
            onNext={() => setStep(3)} />
        )}
        {step === 3 && (
          <TableStep guests={guests} value={table} onPick={setTable} takenIds={takenTables}
            theme={theme} t={t} lang={lang} />
        )}
      </div>

      {step === 3 && (
        <div style={{ padding:14, borderTop:`1px solid ${ink}`, display:'flex', flexDirection:'column', gap:10 }}>
          <div>
            <div style={{
              fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
              letterSpacing:'0.14em', color: mute, marginBottom:6,
            }}>{t('comment_label')}</div>
            <textarea maxLength={300} rows={2} value={comment}
              onChange={(e) => setComment(e.target.value)}
              placeholder={t('comment_placeholder')}
              style={{
                fontFamily:'var(--mono)', fontSize:12, border:`1px solid ${ink}`,
                borderRadius:0, background:'transparent', color:ink,
                padding:'8px 10px', resize:'none', width:'100%', boxSizing:'border-box',
              }} />
          </div>
          <div style={{
            fontFamily:'var(--mono)', fontSize:9, fontWeight:600,
            letterSpacing:'0.08em', color: mute, lineHeight:1.7,
            border:`1px dashed ${ink}55`, padding:'8px 10px',
          }}>
            ⚠ {t('noshow_warning')}
          </div>
          {saveErr && (
            <div style={{
              fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
              letterSpacing:'0.08em', color:'#c62828',
            }}>⚠ Ошибка. Попробуйте ещё раз.</div>
          )}
          <Btn kind="primary" full theme={theme} disabled={!table || saving} onClick={finalize}>
            {saving ? '…' : t('confirm') + ' →'}
          </Btn>
        </div>
      )}
    </div>
  );
}

function DateStep({ days, value, onPick, theme, t, lang }) {
  const { ink, mute } = theme;
  // group by month
  const monthGroups = [];
  let curMon = null;
  days.forEach(d => {
    if (d.m !== curMon) { monthGroups.push({ m: d.m, y: d.y, days: [] }); curMon = d.m; }
    monthGroups[monthGroups.length-1].days.push(d);
  });

  return (
    <div style={{ padding:'8px 14px 24px' }}>
      {monthGroups.map(g => (
        <div key={g.m} style={{ marginBottom:14 }}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
            letterSpacing:'0.18em', color: mute, padding:'8px 2px 10px',
          }}>
            {T[lang].months[g.m]} {g.y}
          </div>
          <div style={{
            display:'grid', gridTemplateColumns:'repeat(4, 1fr)', gap:6,
          }}>
            {g.days.map((d, i) => {
              const isSel = value && value.d === d.d && value.m === d.m;
              const now = new Date();
              const isToday = d.d === now.getDate() && d.m === now.getMonth() && d.y === now.getFullYear();
              return (
                <button key={`${d.m}-${d.d}`} onClick={() => onPick(d)} style={{
                  appearance:'none', cursor:'pointer', borderRadius:0,
                  border:`1px solid ${ink}`,
                  background: isSel ? ink : 'transparent',
                  color: isSel ? theme.bg : ink,
                  padding:'10px 6px',
                  display:'flex', flexDirection:'column', alignItems:'flex-start',
                  fontFamily:'var(--mono)',
                }}>
                  <span style={{ fontSize:9, fontWeight:700, letterSpacing:'0.1em', opacity:0.7 }}>
                    {T[lang].weekdays[d.dow]}
                  </span>
                  <span style={{ fontSize:22, fontWeight:800, marginTop:2 }}>
                    {String(d.d).padStart(2,'0')}
                  </span>
                  {isToday && (
                    <span style={{ fontSize:8, fontWeight:700, letterSpacing:'0.1em', marginTop:2, opacity:0.7 }}>
                      {t('today')}
                    </span>
                  )}
                </button>
              );
            })}
          </div>
        </div>
      ))}
    </div>
  );
}

function TimeStep({ times, unavailable, value, onPick, theme, t, note }) {
  const { ink, mute } = theme;
  return (
    <div style={{ padding:'14px 14px 24px' }}>
      <div style={{
        display:'grid', gridTemplateColumns:'repeat(3, 1fr)', gap:6,
      }}>
        {times.map(tm => {
          const isOff = unavailable.includes(tm);
          const isSel = value === tm;
          return (
            <button key={tm} disabled={isOff} onClick={() => onPick(tm)} style={{
              appearance:'none', cursor: isOff ? 'default' : 'pointer', borderRadius:0,
              border:`1px solid ${ink}`,
              background: isSel ? ink : 'transparent',
              color: isSel ? theme.bg : ink,
              padding:'16px 6px',
              fontFamily:'var(--mono)', fontSize:18, fontWeight:700,
              letterSpacing:'0.02em',
              opacity: isOff ? 0.25 : 1,
              textDecoration: isOff ? 'line-through' : 'none',
            }}>{tm}</button>
          );
        })}
      </div>
      {note && (
        <div style={{
          marginTop:8, fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
          letterSpacing:'0.12em', color: mute, lineHeight:1.6,
        }}>{note}</div>
      )}
    </div>
  );
}

function GuestStep({ value, onChange, theme, t, onNext }) {
  const { ink, bg, mute } = theme;
  return (
    <div style={{ padding:'28px 14px 24px', display:'flex', flexDirection:'column', gap:24, alignItems:'center' }}>
      <div style={{
        fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
        letterSpacing:'0.2em', color: mute,
      }}>{t('guests')} · MAX 12</div>

      <div style={{
        display:'flex', alignItems:'center', gap:0,
        border:`1.5px solid ${ink}`,
      }}>
        <button onClick={() => onChange(Math.max(1, value-1))} style={{
          appearance:'none', cursor:'pointer', borderRadius:0,
          background:'transparent', border:'none', color: ink,
          width:60, height:90, fontSize:32, fontFamily:'var(--mono)', fontWeight:700,
          borderRight:`1px solid ${ink}`,
        }}>−</button>
        <div style={{
          width:120, height:90, display:'flex', alignItems:'center', justifyContent:'center',
          fontFamily:'var(--display)', fontSize:56, fontWeight:900, color: ink,
          fontVariantNumeric:'tabular-nums', letterSpacing:'-0.04em',
        }}>{String(value).padStart(2,'0')}</div>
        <button onClick={() => onChange(Math.min(12, value+1))} style={{
          appearance:'none', cursor:'pointer', borderRadius:0,
          background:'transparent', border:'none', color: ink,
          width:60, height:90, fontSize:32, fontFamily:'var(--mono)', fontWeight:700,
          borderLeft:`1px solid ${ink}`,
        }}>+</button>
      </div>

      <div style={{
        fontFamily:'var(--mono)', fontSize:11, fontWeight:600,
        letterSpacing:'0.1em', color: mute, textAlign:'center', lineHeight:1.6,
      }}>
        {value} {t('persons')}
      </div>

      <div style={{ width:'100%', padding:'0 6px' }}>
        <Btn kind="primary" full theme={theme} onClick={onNext}>
          {t('next')} →
        </Btn>
      </div>
    </div>
  );
}

function TableStep({ guests, value, onPick, takenIds = [], theme, t, lang }) {
  const { ink, bg, mute } = theme;
  const zoneNames = {
    bar: t('bar_zone'), window: t('window_zone'),
    main: t('main_hall'), vip: t('vip'),
  };
  return (
    <div style={{ padding:'10px 14px 18px' }}>
      <div style={{
        fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
        letterSpacing:'0.12em', color: mute, marginBottom:8,
      }}>
        PLAN · {guests} {t('persons')}
      </div>

      {/* SVG floor plan */}
      <div style={{ border:`1.5px solid ${ink}`, position:'relative', background: bg }}>
        <svg viewBox="0 0 400 300" width="100%" style={{ display:'block' }}>
          {/* штриховка занятых */}
          <defs>
            <pattern id="hatch" width="6" height="6" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
              <line x1="0" y1="0" x2="0" y2="6" stroke={ink} strokeWidth="1.2" />
            </pattern>
          </defs>

          {/* grid hint — внутри стен */}
          <g stroke={ink} strokeWidth="0.4" opacity="0.12">
            {[75,150,225].map(y => <line key={'h'+y} x1="8" x2="392" y1={y} y2={y} />)}
            {[100,200,300].map(x => <line key={'v'+x} y1="8" y2="292" x1={x} x2={x} />)}
          </g>

          {/* Декор зала (некликабельный): реальное расположение — бар вдоль правой стены, DJ слева сверху */}
          <g style={{ pointerEvents:'none' }}>
            {/* стены по периметру, проём входа снизу по центру */}
            <path d="M 240 292 H 392 V 8 H 8 V 292 H 160" fill="none" stroke={ink} strokeWidth="1.5" />
            <text x="200" y="295" fill={ink} fontFamily="var(--mono)" fontSize="9" fontWeight="700" letterSpacing="2" textAnchor="middle">↓ {t('entry')}</text>
            {/* барная стойка вдоль нижней стены справа от входа, заворот на правую стену */}
            <rect x="240" y="282" width="152" height="10" fill={ink} opacity="0.08" />
            <line x1="240" y1="282" x2="392" y2="282" stroke={ink} strokeWidth="1" />
            <rect x="384" y="245" width="8" height="37" fill={ink} opacity="0.08" />
            <line x1="384" y1="245" x2="384" y2="282" stroke={ink} strokeWidth="1" />
            <text x="330" y="290" fill={ink} fontFamily="var(--mono)" fontSize="8" fontWeight="700" letterSpacing="3" textAnchor="middle">{t('bar')}</text>
            {/* DJ-будка у левой стены, простенок между столами 15 и 17 */}
            <rect x="10" y="95" width="26" height="85" fill="none" stroke={ink} strokeWidth="1" />
            <text x="23" y="137" fill={ink} fontFamily="var(--mono)" fontSize="9" fontWeight="700" letterSpacing="1" textAnchor="middle" dominantBaseline="middle">{t('dj')}</text>
            {/* диван вдоль верхней стены справа */}
            <rect x="228" y="12" width="146" height="8" fill={ink} opacity="0.25" />
            {/* колонны: между 16–21 и 23–25 */}
            <rect x="79" y="188" width="8" height="30" fill={ink} opacity="0.3" />
            <rect x="212" y="188" width="8" height="30" fill={ink} opacity="0.3" />
          </g>

          {/* столы — круглые, ★ = VIP */}
          {FLOOR.map(tb => {
            const isTaken = takenIds.includes(tb.id);
            const tooSmall = tb.seats < guests;
            const isSel = value === tb.id;
            const fill = isTaken ? 'url(#hatch)' : (isSel ? ink : 'transparent');
            const labelColor = isSel ? bg : ink;
            const op = (isTaken || tooSmall) ? 0.5 : 1;
            return (
              <g key={tb.id}
                 style={{ cursor: (isTaken || tooSmall) ? 'default' : 'pointer', opacity: op }}
                 onClick={() => { if (!isTaken && !tooSmall) onPick(tb.id); }}>
                {/* стулья: seats штук равномерно по кругу, полшага смещения — чтобы соседние столы не смыкались */}
                {Array.from({ length: tb.seats }, (_, i) => (
                  <rect key={i} x={tb.x - 1.5} y={tb.y - tb.r - 3.5} width="3" height="2"
                    fill={ink} opacity="0.45"
                    transform={`rotate(${(360 / tb.seats) * (i + 0.5)} ${tb.x} ${tb.y})`} />
                ))}
                <circle cx={tb.x} cy={tb.y} r={tb.r}
                  fill={fill} stroke={ink} strokeWidth={isSel ? 2.5 : 1.5} />
                <text x={tb.x} y={tb.y - 2} fill={labelColor}
                  fontFamily="var(--mono)" fontSize={tb.r >= 13 ? 10 : 9} fontWeight="800"
                  textAnchor="middle" dominantBaseline="middle">{(tb.zone === 'vip' ? '★' : '') + tb.id}</text>
                <text x={tb.x} y={tb.y + 8} fill={labelColor}
                  fontFamily="var(--mono)" fontSize="6" fontWeight="700"
                  textAnchor="middle" dominantBaseline="middle" opacity="0.7">{tb.seats}p</text>
              </g>
            );
          })}
        </svg>
      </div>

      {/* legend */}
      <div style={{
        marginTop:10, display:'flex', gap:14, flexWrap:'wrap',
        fontFamily:'var(--mono)', fontSize:9, fontWeight:700, letterSpacing:'0.12em',
        color: mute,
      }}>
        <span style={{ display:'flex', alignItems:'center', gap:5 }}>
          <span style={{ width:10, height:10, border:`1px solid ${ink}`, display:'inline-block' }} />
          {t('free')}
        </span>
        <span style={{ display:'flex', alignItems:'center', gap:5 }}>
          <span style={{ width:10, height:10, border:`1px solid ${ink}`, background: ink, display:'inline-block' }} />
          {t('selected')}
        </span>
        <span style={{ display:'flex', alignItems:'center', gap:5 }}>
          <span style={{
            width:10, height:10, border:`1px solid ${ink}`, display:'inline-block',
            backgroundImage:`repeating-linear-gradient(45deg, ${ink} 0 1.5px, transparent 1.5px 4px)`,
          }} />
          {t('taken')}
        </span>
      </div>

      {/* selected summary */}
      {value && (() => {
        const tb = FLOOR.find(x => x.id === value);
        return (
          <div style={{
            marginTop:14, border:`1.5px solid ${ink}`, padding:'10px 12px',
            display:'flex', justifyContent:'space-between', alignItems:'center',
          }}>
            <div>
              <div style={{
                fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                letterSpacing:'0.16em', color: mute,
              }}>{t('selected')}</div>
              <div style={{
                fontFamily:'var(--display)', fontSize:22, fontWeight:900,
                letterSpacing:'-0.02em', color: ink, marginTop:2,
              }}>{t('table')} {tb.id}</div>
            </div>
            <div style={{ textAlign:'right' }}>
              <div style={{
                fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                letterSpacing:'0.1em', color: ink,
              }}>{zoneNames[tb.zone]}</div>
              <div style={{
                fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
                letterSpacing:'0.1em', color: mute, marginTop:2,
              }}>{tb.seats} {t('persons')}</div>
            </div>
          </div>
        );
      })()}
    </div>
  );
}

function BookingSuccess({ date, time, guests, table, code, theme, t, lang, go }) {
  const { ink, bg, mute } = theme;
  const dStr = date ? `${String(date.d).padStart(2,'0')} ${T[lang].months[date.m]}` : '—';
  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      <ScreenHeader title={t('booking_pending_title')} onBack={() => go('home')} theme={theme} t={t} />
      <Hr ink={ink} />

      <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'0 22px' }}>
        <div style={{
          fontFamily:'var(--display)', fontSize:64, fontWeight:900,
          color: ink, lineHeight:1,
        }}>✓</div>
        <div style={{
          fontFamily:'var(--mono)', fontSize:13, fontWeight:700,
          letterSpacing:'0.18em', color: ink, marginTop:16, textAlign:'center',
        }}>{t('booking_pending_title')}</div>
        <div style={{
          fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
          letterSpacing:'0.1em', color: mute, marginTop:6, textAlign:'center',
        }}>{t('booking_code')} · {code}</div>
        <div style={{
          fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
          letterSpacing:'0.1em', color: mute, marginTop:6, textAlign:'center',
        }}>{t('booking_pending_note')}</div>

        <div style={{
          marginTop:28, width:'100%', border:`1px solid ${ink}`, padding:'12px 14px',
          display:'flex', flexDirection:'column', gap:6,
        }}>
          <RowKV k={t('booking_for')} v={`${dStr} · ${time}`} theme={theme} />
          <RowKV k={t('table')} v={`${table} · ${guests} ${t('persons')}`} theme={theme} />
        </div>

        <div style={{
          marginTop:14, width:'100%',
          fontFamily:'var(--mono)', fontSize:9, fontWeight:600,
          letterSpacing:'0.08em', color: mute, lineHeight:1.7,
          border:`1px dashed ${ink}55`, padding:'8px 10px',
        }}>
          ⚠ {t('noshow_warning')}
        </div>
      </div>

      <div style={{ padding:14, borderTop:`1px solid ${ink}` }}>
        <Btn kind="primary" full theme={theme} onClick={() => go('home')}>{t('home')}</Btn>
      </div>
    </div>
  );
}

function RowKV({ k, v, theme }) {
  const { ink, mute } = theme;
  return (
    <div style={{ display:'flex', justifyContent:'space-between', alignItems:'baseline' }}>
      <span style={{
        fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
        letterSpacing:'0.16em', color: mute, textTransform:'uppercase',
      }}>{k}</span>
      <span style={{
        fontFamily:'var(--mono)', fontSize:12, fontWeight:700, color: ink,
      }}>{v}</span>
    </div>
  );
}

// ─── KITCHEN / BAR MENUS ──────────────────────────────────
function MenuScreen({ kind, theme, t, lang, go }) {
  const { ink, bg, mute } = theme;
  const isBar = kind === 'bar';
  const cats = isBar
    ? [['cocktails','cocktails'], ['spirits','spirits'], ['wine','wine'], ['beer','beer'], ['nonalc','nonalc']]
    : [['breakfast','breakfast'], ['starters','starters'], ['salads','salads'], ['soups','soups'], ['mains','mains'], ['sides','sides'], ['desserts','desserts'], ['sauces','sauces']];
  const [cat, setCat] = useState(cats[0][0]);
  const [open, setOpen] = useState(null);
  const [ageOk, setAgeOk] = useState(!isBar); // 21+ гейт для бара
  const [stopped, setStopped] = useState([]); // стоп-лист: id позиций «нет в наличии»
  const data = isBar ? BAR : KITCHEN;
  const items = data[cat] || [];

  useEffect(() => {
    fetch('/api/stoplist')
      .then(r => r.json())
      .then(ids => { if (Array.isArray(ids)) setStopped(ids); })
      .catch(() => {});
  }, []);

  if (isBar && !ageOk) {
    return (
      <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
        <ScreenHeader title={t('bar')} onBack={() => go('home')} theme={theme} t={t} />
        <Hr ink={ink} />
        <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center', justifyContent:'center', padding:'0 22px', gap:16 }}>
          <div style={{ fontFamily:'var(--display)', fontSize:64, fontWeight:900, color:ink }}>21+</div>
          <div style={{ fontFamily:'var(--mono)', fontSize:11, fontWeight:700, letterSpacing:'0.14em', color:mute, textAlign:'center', lineHeight:1.6 }}>
            {t('age_warning')}
          </div>
        </div>
        <div style={{ padding:14, borderTop:`1px solid ${ink}`, display:'flex', gap:10 }}>
          <Btn full theme={theme} onClick={() => go('home')}>{t('back')}</Btn>
          <Btn kind="primary" full theme={theme} onClick={() => setAgeOk(true)}>{t('age_confirm')}</Btn>
        </div>
      </div>
    );
  }

  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      <ScreenHeader title={isBar ? t('bar') : t('kitchen')} onBack={() => go('home')} theme={theme} t={t} />
      <Hr ink={ink} />

      {/* sticky cat strip */}
      <div style={{
        display:'flex', gap:0, overflowX:'auto', padding:'10px 12px',
        borderBottom:`1px solid ${ink}`,
      }}>
        {cats.map(([k,labelKey]) => (
          <button key={k} onClick={() => setCat(k)} style={{
            appearance:'none', cursor:'pointer', borderRadius:0, flexShrink:0,
            border: 'none', borderBottom: cat===k ? `2px solid ${ink}` : '2px solid transparent',
            background: 'transparent', color: cat===k ? ink : mute,
            padding:'8px 10px', marginRight:4,
            fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
            letterSpacing:'0.14em',
          }}>{t(labelKey)}</button>
        ))}
      </div>

      <div style={{ flex:1, overflow:'auto' }}>
        {items.map((it) => {
          const isStopped = stopped.includes(it.id);
          return (
            <button key={it.id} onClick={() => setOpen(it)} style={{
              appearance:'none', cursor:'pointer', border:'none', background:'transparent',
              width:'100%', textAlign:'left', color:ink, minWidth:0,
              padding:'12px 16px', borderBottom:`1px solid ${ink}22`,
              opacity: isStopped ? 0.45 : 1, display:'block',
            }}>
              <div style={{ display:'flex', alignItems:'center', gap:8 }}>
                <div style={{
                  fontFamily:'var(--display)', fontSize:15, fontWeight:800,
                  letterSpacing:'-0.01em', color:ink, lineHeight:1.1, minWidth:0,
                }}>{it.n[lang]}</div>
                {isStopped && (
                  <span style={{
                    fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                    letterSpacing:'0.12em', color:bg, background:ink,
                    padding:'2px 6px', flexShrink:0,
                  }}>{t('stopped')}</span>
                )}
              </div>
              <div style={{
                fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
                color:mute, marginTop:4, letterSpacing:'0.04em',
                overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap',
              }}>{it.d[lang]}</div>
              <div style={{
                fontFamily:'var(--display)', fontSize:15, fontWeight:900,
                color:ink, marginTop:4, fontVariantNumeric:'tabular-nums',
              }}>{fmtPrice(it.p, lang)}</div>
            </button>
          );
        })}
        <div style={{
          padding:'18px 16px 28px',
          fontFamily:'var(--mono)', fontSize:9, fontWeight:600,
          letterSpacing:'0.1em', color: mute,
        }}>
          ─── END OF {t(cat).toUpperCase()} ───
        </div>
      </div>

      {open && <ItemSheet it={open} isBar={isBar} isStopped={stopped.includes(open.id)}
        theme={theme} t={t} lang={lang} onClose={() => setOpen(null)} />}
    </div>
  );
}

function ItemSheet({ it, isBar, isStopped = false, theme, t, lang, onClose }) {
  const { ink, bg, mute } = theme;
  return (
    <div onClick={onClose} style={{
      position:'absolute', inset:0, background:'rgba(0,0,0,0.55)',
      display:'flex', alignItems:'flex-end', zIndex:30,
    }}>
      <div onClick={(e)=>e.stopPropagation()} style={{
        width:'100%', background: bg, borderTop:`2px solid ${ink}`,
        padding:'14px 16px 22px',
        animation: 'slideUp 0.22s ease-out',
      }}>
        <div style={{
          display:'flex', justifyContent:'space-between', alignItems:'flex-start', gap:14,
        }}>
          <div style={{ minWidth:0, flex:1 }}>
            <div style={{ display:'flex', alignItems:'center', gap:8 }}>
              <CornerTag theme={theme}>{isBar ? t('bar') : t('kitchen')}</CornerTag>
              {isStopped && (
                <span style={{
                  fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                  letterSpacing:'0.12em', color:bg, background:ink, padding:'2px 6px',
                }}>{t('stopped')}</span>
              )}
            </div>
            <div style={{
              fontFamily:'var(--display)', fontSize:24, fontWeight:900,
              letterSpacing:'-0.02em', color: ink, lineHeight:1.05, marginTop:6,
              opacity: isStopped ? 0.45 : 1,
            }}>{it.n[lang]}</div>
          </div>
          <button onClick={onClose} style={{
            appearance:'none', border:`1px solid ${ink}`, background:'transparent',
            cursor:'pointer', color: ink, width:32, height:32, borderRadius:0,
            fontFamily:'var(--mono)', fontSize:14, fontWeight:700,
          }}>×</button>
        </div>

        <div style={{ marginTop:18, display:'flex', flexDirection:'column', gap:0 }}>
          <RowKV k={t('composition')} v={it.d[lang]} theme={theme} />
          {isBar && <div style={{ height:8 }} />}
          {isBar && <RowKV k={t('vol')} v={`${it.abv}%`} theme={theme} />}
          {isBar && <div style={{ height:8 }} />}
          {isBar && <RowKV k={t('weight')} v={`${it.ml} ML`} theme={theme} />}
          {!isBar && it.g > 0 && <div style={{ height:8 }} />}
          {!isBar && it.g > 0 && <RowKV k={t('weight')} v={`${it.g} G`} theme={theme} />}
        </div>

        <div style={{
          marginTop:18, display:'flex', justifyContent:'space-between', alignItems:'center',
          borderTop:`1.5px solid ${ink}`, paddingTop:14,
        }}>
          <div style={{
            fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
            letterSpacing:'0.18em', color: mute,
          }}>{t('total')}</div>
          <div style={{
            fontFamily:'var(--display)', fontSize:30, fontWeight:900,
            color: ink, letterSpacing:'-0.02em', fontVariantNumeric:'tabular-nums',
          }}>{fmtPrice(it.p, lang)}</div>
        </div>
      </div>
    </div>
  );
}

// ─── EVENTS ──────────────────────────────────────────────
function EventsScreen({ theme, t, lang, go }) {
  const { ink, bg, mute } = theme;
  const [open, setOpen] = useState(null);
  const [events, setEvents] = useState([]);
  const [signups, setSignups] = useState([]); // event_id, на которые записан

  const chatId = (typeof window !== 'undefined' && window.Telegram && window.Telegram.WebApp
    && window.Telegram.WebApp.initDataUnsafe && window.Telegram.WebApp.initDataUnsafe.user)
    ? window.Telegram.WebApp.initDataUnsafe.user.id : 0;

  useEffect(() => {
    fetch('/api/events')
      .then(r => r.json())
      .then(data => { if (Array.isArray(data)) setEvents(data); })
      .catch(() => {});
    if (chatId) {
      nkFetch('/api/signups/' + chatId)
        .then(r => r.json())
        .then(ids => { if (Array.isArray(ids)) setSignups(ids); })
        .catch(() => {});
    }
  }, []);

  const signup = (ev) => {
    if (!chatId || signups.includes(ev.id)) return;
    setSignups(prev => [...prev, ev.id]); // оптимистично
    nkFetch(`/api/events/${ev.id}/signup`, { method: 'POST' })
      .then(r => { if (!r.ok) throw 0; }) // 401/404 — откат, fetch сам их не реджектит
      .catch(() => setSignups(prev => prev.filter(id => id !== ev.id)));
  };

  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      <ScreenHeader title={t('events')} onBack={() => go('home')} theme={theme} t={t} />
      <Hr ink={ink} />
      <div style={{ padding:'14px 16px 6px' }}>
        <div style={{
          fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
          letterSpacing:'0.2em', color: mute,
        }}>{t('upcoming')} · {events.length}</div>
      </div>

      <div style={{ flex:1, overflow:'auto', padding:'4px 0 20px' }}>
        {events.map(ev => {
          const priceLabel = ev.free ? t('free_entry') :
            ev.donate ? t('by_donation') :
            fmtPrice(ev.price, lang);
          return (
            <button key={ev.id || ev.t} onClick={() => setOpen(ev)} style={{
              appearance:'none', cursor:'pointer', border:'none', background:'transparent',
              width:'100%', textAlign:'left', color: ink,
              display:'grid', gridTemplateColumns:'72px 1fr',
              gap:14, padding:'14px 16px', borderTop:`1px solid ${ink}22`,
            }}>
              {/* poster (если есть) либо date stack */}
              {ev.image ? (
                <div style={{ border:`1.5px solid ${ink}`, overflow:'hidden' }}>
                  <img src={ev.image} alt="" style={{
                    width:'100%', height:'100%', objectFit:'cover', display:'block',
                  }} />
                </div>
              ) : (
                <div style={{
                  border:`1.5px solid ${ink}`, padding:'6px 4px',
                  display:'flex', flexDirection:'column', alignItems:'center',
                  justifyContent:'center', gap:2,
                }}>
                  <div style={{
                    fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                    letterSpacing:'0.14em', color: mute,
                  }}>{T[lang].weekdays[ev.dow]}</div>
                  <div style={{
                    fontFamily:'var(--display)', fontSize:26, fontWeight:900,
                    letterSpacing:'-0.04em', lineHeight:1, color: ink,
                  }}>{String(ev.day).padStart(2,'0')}</div>
                  <div style={{
                    fontFamily:'var(--mono)', fontSize:8, fontWeight:700,
                    letterSpacing:'0.16em', color: mute,
                  }}>{T[lang].months[ev.mon]}</div>
                </div>
              )}
              <div style={{ minWidth:0, display:'flex', flexDirection:'column', gap:5 }}>
                <div style={{
                  display:'flex', alignItems:'center', gap:8,
                }}>
                  <span style={{
                    fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                    letterSpacing:'0.14em', color: bg, background: ink, padding:'2px 5px',
                  }}>{ev.tag}</span>
                  <span style={{
                    fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                    letterSpacing:'0.1em', color: mute,
                  }}>{ev.t}</span>
                </div>
                <div style={{
                  fontFamily:'var(--display)', fontSize:18, fontWeight:900,
                  letterSpacing:'-0.02em', lineHeight:1.1, color: ink,
                }}>{ev.n[lang]}</div>
                <div style={{
                  fontFamily:'var(--mono)', fontSize:10, fontWeight:600,
                  letterSpacing:'0.04em', color: mute,
                }}>{ev.sub[lang]}</div>
                <div style={{
                  fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                  letterSpacing:'0.1em', color: ink, marginTop:2,
                }}>{t('entry')}: {priceLabel}</div>
              </div>
            </button>
          );
        })}
      </div>

      {open && (
        <div onClick={() => setOpen(null)} style={{
          position:'absolute', inset:0, background:'rgba(0,0,0,0.55)',
          display:'flex', alignItems:'flex-end', zIndex:30,
        }}>
          <div onClick={(e)=>e.stopPropagation()} style={{
            width:'100%', background: bg, borderTop:`2px solid ${ink}`,
            padding:'14px 16px 22px',
          }}>
            <CornerTag theme={theme}>{open.tag} · {String(open.day).padStart(2,'0')} {T[lang].months[open.mon]} · {open.t}</CornerTag>
            <div style={{
              fontFamily:'var(--display)', fontSize:28, fontWeight:900,
              letterSpacing:'-0.02em', color: ink, marginTop:8, lineHeight:1.0,
            }}>{open.n[lang]}</div>
            <div style={{
              fontFamily:'var(--mono)', fontSize:11, fontWeight:600,
              color: mute, marginTop:8, lineHeight:1.5,
            }}>{open.sub[lang]}</div>
            <div style={{ marginTop:14 }}>
              <RowKV k={t('entry')} v={open.free ? t('free_entry') : open.donate ? t('by_donation') : fmtPrice(open.price, lang)} theme={theme} />
            </div>
            <div style={{ marginTop:16, display:'flex', gap:8 }}>
              <Btn full theme={theme} onClick={() => setOpen(null)}>{t('back')}</Btn>
              {/* «Записаться» = напоминание в день события; вне Telegram напоминать некому */}
              {chatId > 0 && (
                signups.includes(open.id)
                  ? <Btn full theme={theme} disabled>{t('signed_up')}</Btn>
                  : <Btn kind="primary" full theme={theme} onClick={() => signup(open)}>{t('register')}</Btn>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ─── MY BOOKINGS ──────────────────────────────────────────
function MyScreen({ theme, t, lang, go, bookings }) {
  const { ink, mute } = theme;
  return (
    <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      <ScreenHeader title={t('my')} onBack={() => go('home')} theme={theme} t={t} />
      <Hr ink={ink} />
      <div style={{ flex:1, overflow:'auto', padding:'16px 16px 20px' }}>
        {bookings.length === 0 ? (
          <div style={{
            border:`1.5px dashed ${ink}`, padding:'40px 14px', textAlign:'center',
            fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
            letterSpacing:'0.14em', color: mute,
          }}>
            ─── {t('no_bookings')} ───
            <div style={{ height:12 }} />
            <Btn theme={theme} onClick={() => go('book')}>{t('book')} →</Btn>
          </div>
        ) : (
          bookings.map((b, i) => (
            <div key={i} style={{
              border:`1.5px solid ${ink}`, padding:'14px',
              marginBottom:10, display:'flex', flexDirection:'column', gap:6,
            }}>
              <div style={{
                display:'flex', justifyContent:'space-between', alignItems:'baseline',
              }}>
                <div style={{
                  fontFamily:'var(--display)', fontSize:22, fontWeight:900,
                  letterSpacing:'-0.02em', color: ink,
                }}>{String(b.date.d).padStart(2,'0')} {T[lang].months[b.date.m]} · {b.time}</div>
                <div style={{
                  fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
                  letterSpacing:'0.14em', color: mute,
                }}>{b.code}</div>
              </div>
              <RowKV k={t('table')} v={`${b.table} · ${b.guests} ${t('persons')}`} theme={theme} />
              <RowKV k={t('status')} v={t(b.status === 'pending' ? 'st_pending' : b.status === 'cancelled' ? 'st_cancelled' : 'st_confirmed')} theme={theme} />
              {b.total != null && <RowKV k={t('paid')} v={fmtPrice(b.total, lang)} theme={theme} />}
            </div>
          ))
        )}
      </div>
    </div>
  );
}

// ─── LOYALTY (NUKUS CLUB) ─────────────────────────────────
function LoyaltyScreen({ theme, t, lang, go }) {
  const { ink, bg, mute } = theme;
  const [state, setState] = useState('loading'); // loading | guest | member | error
  const [data, setData] = useState(null);
  const [busy, setBusy] = useState(false);
  const [birthday, setBirthday] = useState('');

  const chatId = (typeof window !== 'undefined' && window.Telegram && window.Telegram.WebApp
    && window.Telegram.WebApp.initDataUnsafe && window.Telegram.WebApp.initDataUnsafe.user)
    ? window.Telegram.WebApp.initDataUnsafe.user.id : 0;
  const userName = (typeof window !== 'undefined' && window.Telegram && window.Telegram.WebApp
    && window.Telegram.WebApp.initDataUnsafe && window.Telegram.WebApp.initDataUnsafe.user)
    ? window.Telegram.WebApp.initDataUnsafe.user.first_name : '';

  const load = async () => {
    if (!chatId) { setState('guest'); return; }
    try {
      const r = await nkFetch(`/api/loyalty/${chatId}`);
      if (!r.ok) { setState('guest'); return; }
      const j = await r.json();
      setData(j); setState('member');
    } catch (e) { setState('guest'); }
  };
  useEffect(() => { load(); }, []);

  const join = async () => {
    setBusy(true);
    try {
      const r = await nkFetch('/api/loyalty/register', {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name: userName, birthday: birthday || null }),
      });
      const j = await r.json();
      setData(j.loyalty ? { ...j.loyalty, tx: [] } : null);
      await load();
    } catch (e) {} finally { setBusy(false); }
  };

  const txLabel = (type) => ({
    welcome: t('tx_welcome'), accrue: t('tx_accrue'),
    redeem: t('tx_redeem'), gift: t('tx_gift'),
  }[type] || type);

  const Header = (
    <React.Fragment>
      <ScreenHeader title={t('club')} onBack={() => go('home')} theme={theme} t={t} />
      <Hr ink={ink} />
    </React.Fragment>
  );

  if (state === 'loading') {
    return <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      {Header}
      <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center',
        fontFamily:'var(--mono)', fontSize:11, letterSpacing:'0.16em', color:mute }}>···</div>
    </div>;
  }

  if (state === 'guest') {
    return <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
      {Header}
      <div style={{ flex:1, display:'flex', flexDirection:'column', alignItems:'center',
        justifyContent:'center', padding:'0 24px', gap:18, textAlign:'center' }}>
        <div style={{ fontFamily:'var(--display)', fontSize:34, fontWeight:900,
          letterSpacing:'-0.02em', color:ink }}>NUKUS CLUB</div>
        <div style={{ fontFamily:'var(--mono)', fontSize:11, fontWeight:700,
          letterSpacing:'0.08em', color:mute, lineHeight:1.6 }}>{t('loyalty_join_note')}</div>
        <div style={{ width:'100%', textAlign:'left' }}>
          <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
            letterSpacing:'0.14em', color:mute, marginBottom:6 }}>{t('birthday_label')}</div>
          <input type="date" value={birthday} onChange={(e) => setBirthday(e.target.value)}
            max={new Date().toISOString().slice(0, 10)}
            style={{ width:'100%', boxSizing:'border-box', padding:'12px 12px',
              fontFamily:'var(--mono)', fontSize:14, fontWeight:700,
              border:`1.5px solid ${ink}`, borderRadius:0, background:bg, color:ink, outline:'none' }} />
        </div>
        <div style={{ height:6 }} />
        <Btn kind="primary" full theme={theme} onClick={join}>
          {busy ? t('joining') : t('loyalty_join')}
        </Btn>
      </div>
    </div>;
  }

  // member
  const d = data || {};
  const isMax = !d.next_tier;
  return <div style={{ height:'100%', display:'flex', flexDirection:'column' }}>
    {Header}
    <div style={{ flex:1, overflow:'auto', padding:'16px 16px 24px' }}>
      {/* Карта */}
      <div style={{ border:`1.5px solid ${ink}`, padding:'18px 16px', marginBottom:14 }}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'flex-start' }}>
          <div>
            <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
              letterSpacing:'0.2em', color:mute }}>{t('level')}</div>
            <div style={{ fontFamily:'var(--display)', fontSize:24, fontWeight:900,
              letterSpacing:'-0.02em', color:ink, marginTop:4 }}>{d.tier}</div>
          </div>
          <div style={{ textAlign:'right' }}>
            <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
              letterSpacing:'0.2em', color:mute }}>{t('cashback')}</div>
            <div style={{ fontFamily:'var(--display)', fontSize:24, fontWeight:900,
              color:ink, marginTop:4 }}>{d.pct}%</div>
          </div>
        </div>

        <div style={{ height:14 }} />
        <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
          letterSpacing:'0.2em', color:mute }}>{t('bonus_balance')}</div>
        <div style={{ fontFamily:'var(--display)', fontSize:38, fontWeight:900,
          letterSpacing:'-0.02em', color:ink, lineHeight:1.05 }}>{fmtPrice(d.balance, lang)}</div>

        {/* Прогресс к след. уровню */}
        <div style={{ height:14 }} />
        {isMax ? (
          <div style={{ fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
            letterSpacing:'0.14em', color:mute }}>★ {t('max_level')}</div>
        ) : (
          <React.Fragment>
            <div style={{ display:'flex', justifyContent:'space-between',
              fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
              letterSpacing:'0.1em', color:mute, marginBottom:5 }}>
              <span>{t('to_next_level')}: {d.next_tier} ({d.next_pct}%)</span>
              <span>{fmtPrice(d.to_next, lang)}</span>
            </div>
            <div style={{ height:6, background:theme.hover, position:'relative' }}>
              <div style={{ position:'absolute', left:0, top:0, bottom:0,
                width: `${(() => { const thr=d.threshold||1; const done=thr-(d.to_next||0); return Math.max(3, Math.min(100, Math.round(done/thr*100))); })()}%`,
                background:ink }} />
            </div>
          </React.Fragment>
        )}
      </div>

      {/* Номер карты */}
      <div style={{ border:`1.5px solid ${ink}`, padding:'14px 16px', marginBottom:14,
        textAlign:'center' }}>
        <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
          letterSpacing:'0.2em', color:mute }}>{t('card_number')}</div>
        <div style={{ fontFamily:'var(--display)', fontSize:30, fontWeight:900,
          letterSpacing:'0.14em', color:ink, marginTop:6 }}>{d.card_no}</div>
        <div style={{ fontFamily:'var(--mono)', fontSize:9, fontWeight:700,
          letterSpacing:'0.16em', color:mute, marginTop:6 }}>{t('show_to_staff')}</div>
      </div>

      <RowKV k={t('total_spent_label')} v={fmtPrice(d.total_spent, lang)} theme={theme} />

      {/* История */}
      <div style={{ height:16 }} />
      <div style={{ fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
        letterSpacing:'0.18em', color:ink, marginBottom:8 }}>{t('history')}</div>
      {(!d.tx || d.tx.length === 0) ? (
        <div style={{ fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
          letterSpacing:'0.14em', color:mute, padding:'10px 0' }}>─── {t('no_history')} ───</div>
      ) : (
        d.tx.map((x) => (
          <div key={x.id} style={{ display:'flex', justifyContent:'space-between',
            alignItems:'baseline', padding:'8px 0', borderBottom:`1px solid ${theme.hover}` }}>
            <div>
              <div style={{ fontFamily:'var(--mono)', fontSize:10, fontWeight:700,
                letterSpacing:'0.1em', color:ink }}>{txLabel(x.type)}</div>
              <div style={{ fontFamily:'var(--mono)', fontSize:9, color:mute }}>
                {String(x.created_at || '').slice(0, 10)}</div>
            </div>
            {/* Чек со списанием даёт и −redeem, и +cashback — показываем обе суммы */}
            <div style={{ textAlign:'right' }}>
              {x.redeemed > 0 && (
                <div style={{ fontFamily:'var(--display)', fontSize:15, fontWeight:900, color: mute }}>
                  −{fmtPrice(x.redeemed, lang)}
                </div>
              )}
              {(x.accrued > 0 || x.redeemed === 0) && (
                <div style={{ fontFamily:'var(--display)', fontSize:15, fontWeight:900, color: ink }}>
                  +{fmtPrice(x.accrued, lang)}
                </div>
              )}
            </div>
          </div>
        ))
      )}
    </div>
  </div>;
}

Object.assign(window, {
  HomeScreen, BookingFlow, MenuScreen, EventsScreen, MyScreen, LoyaltyScreen,
});
