// Editorial Luxury — pages (real backend via window.EH_API)
const D2 = window.EH_DATA;
const API = window.EH_API;

// --- helpers ---
const isoDay = (d) => `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;

// «5 июля» из ISO-даты (короткое отображение для диапазонов)
const fmtRuDate = (iso) => {
  const d = new Date(String(iso) + 'T00:00:00');
  return isNaN(d) ? iso : d.toLocaleDateString('ru', { day: 'numeric', month: 'short' });
};

// Форматирует время брони: часовая — «13:00–14:00, 15:00–17:00»,
// дневная — «Весь день», месячная — «30 дней · по 5 авг»
function fmtHours(b) {
  if (b.kind === 'day') return `Весь день (${b.start}–${b.end})`;
  if (b.kind === 'month') return `30 дней · по ${fmtRuDate(b.dateEnd || b.date)}`;
  const hrs = Array.isArray(b.hours) && b.hours.length ? b.hours.slice().sort((a, c) => a - c) : null;
  if (!hrs) return `${b.start} — ${b.end}`;
  const runs = [];
  let s = hrs[0], p = hrs[0];
  for (let i = 1; i < hrs.length; i++) {
    if (hrs[i] === p + 1) { p = hrs[i]; }
    else { runs.push([s, p]); s = hrs[i]; p = hrs[i]; }
  }
  runs.push([s, p]);
  const f = (h) => String(h).padStart(2, '0') + ':00';
  return runs.map(([a, z]) => `${f(a)}–${f(z + 1)}`).join(', ');
}

function maskPhone(v) {
  let d = (v || '').replace(/\D/g, '');
  if (d[0] === '8') d = '7' + d.slice(1);
  if (d[0] !== '7') d = '7' + d;
  d = d.slice(0, 11);
  let out = '+7';
  if (d.length > 1) out += ' (' + d.slice(1, 4);
  if (d.length >= 4) out += ') ' + d.slice(4, 7);
  if (d.length >= 7) out += '-' + d.slice(7, 9);
  if (d.length >= 9) out += '-' + d.slice(9, 11);
  return out;
}

async function completePendingBooking() {
  const raw = sessionStorage.getItem('eh_pending_booking');
  if (!raw) return;
  sessionStorage.removeItem('eh_pending_booking');
  try { await API.createBooking(JSON.parse(raw)); } catch (e) { /* конфликт/ошибка — пропускаем */ }
}

const DashLoading = ({ label }) => (
  <div style={{minHeight: '100vh', display: 'grid', placeItems: 'center', background: 'var(--bg)'}}>
    <div style={{textAlign: 'center'}}>
      <div className="eh-spinner"></div>
      <div style={{marginTop: 18, fontSize: 12, letterSpacing: 2, textTransform: 'uppercase', color: 'var(--mute)'}}>{label}</div>
    </div>
  </div>
);

// ============================================
// CABINET PLAN — отдельные комнаты со своими планами + клик по месту → бронь
// ============================================
const CabinetPlan = ({ type, rooms, onSelectRoom, onClose }) => {
  // Цвета для разных типов объектов
  const COLOR = {
    sofa:    { fill: '#2c333d', alpha: 0.82, text: '#ffffff' }, // диваны, тёмная мягкая мебель
    wood:    { fill: '#cba374', alpha: 0.95, text: '#0f1419' }, // деревянная мебель / столики
    cabinet: { fill: '#7a5a30', alpha: 0.88, text: '#ffffff' }, // глухие шкафы
    metal:   { fill: '#5a6770', alpha: 0.85, text: '#ffffff' }, // мойка, металл
    glass:   { fill: '#9fc1d4', alpha: 0.55, text: '#0f1419' }, // зеркала
    device:  { fill: '#0f1419', alpha: 0.85, text: '#ffffff' }, // аппаратура
    chair:   { fill: '#1d3df0', alpha: 0.6,  text: '#ffffff' }, // акцентные кресла
    light:   { fill: '#f5e8c4', alpha: 0.65, text: '#0f1419' }, // окна, светлые проёмы
  };
  const c = (kind) => COLOR[kind] || COLOR.wood;

  // Helper для прямоугольной мебели
  const rect = (x, y, w, h, label, kind) => {
    const col = c(kind);
    return { x, y, w, h, label, color: col.fill, alpha: col.alpha, _textColor: col.text };
  };
  // Круглая мебель (стулья и т.п.) — рендерится через furniture с round:true
  const circ = (x, y, r, label, kind) => {
    const col = c(kind);
    return { x: x - r, y: y - r, w: r * 2, h: r * 2, label, color: col.fill, alpha: col.alpha, _textColor: col.text, round: true };
  };
  // Овал (раковины, чаши) — render через furniture с oval:true
  const oval = (cx, cy, rx, ry, label, kind) => {
    const col = c(kind);
    return { x: cx - rx, y: cy - ry, w: rx * 2, h: ry * 2, label, color: col.fill, alpha: col.alpha, _textColor: col.text, oval: true };
  };

  // ============================================================
  // КОНФИГИ ПЛАНОВ — координаты в метрах, мебель как декорация, places кликабельные
  // ============================================================
  const CFG = {
    // ---- Зона ожидания (5×4 м) — сдаётся целиком ----
    lounge: {
      title: 'Зона ожидания', planW: 5, planH: 4,
      furniture: [
        // Удобный диван во всю верхнюю стену
        rect(0.20, 0.20, 4.60, 0.55, 'Удобный диван', 'sofa'),
        // Кофе у левой стены
        rect(0.20, 1.40, 0.50, 0.90, 'Кофе', 'wood'),
      ],
      // Кликабельная зона = вся комната
      places: [{ x: 0.20, y: 0.90, w: 4.60, h: 2.85 }],
      door: { side: 'bottom', x: 2.0, w: 1.0 },
    },

    // ---- Кабинет барберов (5.5×5.5 м) — только 3 рабочих места ----
    barber: {
      title: 'Кабинет барберов', planW: 5.5, planH: 5.5,
      furniture: [],
      places: [
        { x: 0.30, y: 0.40, w: 2.30, h: 1.20 }, // Б1
        { x: 0.30, y: 2.15, w: 2.30, h: 1.20 }, // Б2
        { x: 0.30, y: 3.90, w: 2.30, h: 1.20 }, // Б3
      ],
      door: { side: 'right', y: 4.30, h: 1.00 },
    },

    // ---- Кабинет с автозагаром (5.5×5.5 м) — сдаётся целиком ----
    tan: {
      title: 'Кабинет с автозагаром', planW: 5.5, planH: 5.5,
      furniture: [],
      places: [{ x: 0.20, y: 0.20, w: 5.10, h: 5.10 }],
      door: { side: 'right', y: 4.30, h: 1.00 },
    },

    // ---- 2 кабинета с кушеткой (перманентный макияж) в линию (11×5.5 м) ----
    bed: {
      title: 'Кабинеты с кушеткой', planW: 11, planH: 5.5,
      walls: [
        { x1: 5.5, y1: 0, x2: 5.5, y2: 5.5 },
      ],
      furniture: [
        // Кушетка в каждом кабинете — прижата к верхней стене
        rect(1.55, 0.20, 2.40, 0.95, 'Кушетка', 'sofa'),
        rect(7.05, 0.20, 2.40, 0.95, 'Кушетка', 'sofa'),
      ],
      places: [
        { x: 0.20, y: 0.20, w: 5.10, h: 5.10 },
        { x: 5.70, y: 0.20, w: 5.10, h: 5.10 },
      ],
      doors: [
        { side: 'bottom', x: 2.25, w: 1.0 },
        { side: 'bottom', x: 7.75, w: 1.0 },
      ],
    },

    // ---- Кабинет косметолога (5.5×5.5 м) — сдаётся целиком ----
    cosmo: {
      title: 'Кабинет косметолога', planW: 5.5, planH: 5.5,
      furniture: [
        // Кушетка прижата к верхней стене
        rect(1.55, 0.20, 2.40, 1.00, 'Кушетка', 'sofa'),
      ],
      places: [{ x: 0.20, y: 0.20, w: 5.10, h: 5.10 }],
      door: { side: 'right', y: 4.30, h: 1.00 },
    },

    // ---- 2 свободных кабинета в линию (8×4.5 м) ----
    free4: {
      title: 'Кабинеты свободного назначения', planW: 8, planH: 4.5,
      furniture: [],
      walls: [
        { x1: 4, y1: 0, x2: 4, y2: 4.5 },
      ],
      places: [0, 1].map(i => ({ x: 0.20 + i * 4, y: 0.20, w: 3.60, h: 4.10 })),
      doors: [0, 1].map(i => ({ side: 'bottom', x: 1.5 + i * 4, w: 1.0 })),
    },
  };
  const cfg = CFG[type];
  if (!cfg) return null;
  const scale = 70;
  const W = cfg.planW * scale;
  const H = cfg.planH * scale;
  const places = cfg.places || [];

  // Цвет статуса
  const statusColor = (s) => s === 'free' ? '#5d8a72' : s === 'partial' ? '#c9913d' : '#b56666';

  // Среднее по зоне
  const minPrice = rooms.length ? Math.min(...rooms.map(r => r.prices.hour)) : null;
  const allBusy = rooms.length && rooms.every(r => r.status === 'busy');
  const anyFree = rooms.some(r => r.status === 'free');
  const zoneStatus = !rooms.length ? 'unknown' : allBusy ? 'busy' : (anyFree ? 'free' : 'partial');
  const zoneStatusLabel = zoneStatus === 'free' ? 'СВОБОДНО' : zoneStatus === 'busy' ? 'АРЕНДУЕТСЯ' : 'ЧАСТИЧНО';

  const renderDoor = (door, key = 'd') => {
    if (!door) return null;
    if (door.side === 'right') {
      const y = door.y * scale, h = door.h * scale;
      return <g key={key}><rect x={W - 4} y={y} width={8} height={h} fill="#fbfcfd" /><path d={`M ${W} ${y} A ${h} ${h} 0 0 0 ${W - h} ${y + h}`} fill="none" stroke="#0f1419" strokeWidth="0.8" strokeDasharray="3 2" opacity="0.55" /></g>;
    }
    if (door.side === 'bottom') {
      const x = door.x * scale, w = door.w * scale;
      return <g key={key}><rect x={x} y={H - 4} width={w} height={8} fill="#fbfcfd" /><path d={`M ${x} ${H} A ${w} ${w} 0 0 1 ${x + w} ${H - w}`} fill="none" stroke="#0f1419" strokeWidth="0.8" strokeDasharray="3 2" opacity="0.55" /></g>;
    }
    return null;
  };

  return (
    <div className="cabinet-plan">
      <div className="cp-head">
        <div>
          <span className={`cp-status cp-status--${zoneStatus}`}>{zoneStatusLabel}</span>
          <h3>{cfg.title}</h3>
          <p>Нажмите на место на плане — увидите фотографии, цены и свободные часы. Оплата онлайн, бронь подтверждается мгновенно.</p>
        </div>
        <button type="button" className="cp-close" onClick={onClose} aria-label="Закрыть">×</button>
      </div>

      <div className="cp-body">
        <div className="cp-svg-wrap">
          <svg viewBox={`-24 -24 ${W + 48} ${H + 48}`} className="cp-svg" xmlns="http://www.w3.org/2000/svg">
            {/* Контур комнаты */}
            <rect x="0" y="0" width={W} height={H} fill="#f3f5f8" stroke="#0f1419" strokeWidth="2.4" />
            {/* Перегородки */}
            {cfg.walls && cfg.walls.map((w, i) => (
              <line key={`w${i}`} x1={w.x1 * scale} y1={w.y1 * scale} x2={w.x2 * scale} y2={w.y2 * scale} stroke="#0f1419" strokeWidth="2" />
            ))}
            {/* Декоративная мебель */}
            {cfg.furniture && cfg.furniture.map((it, i) => {
              const px = it.x * scale, py = it.y * scale;
              const pw = it.w * scale, ph = it.h * scale;
              const cx = px + pw / 2, cy = py + ph / 2;
              const txtColor = it._textColor || '#0f1419';
              return (
                <g key={`f${i}`}>
                  {it.round ? (
                    <circle cx={cx} cy={cy} r={pw / 2} fill={it.color} fillOpacity={it.alpha || 0.85} />
                  ) : it.oval ? (
                    <ellipse cx={cx} cy={cy} rx={pw / 2} ry={ph / 2} fill={it.color} fillOpacity={it.alpha || 0.85} />
                  ) : (
                    <rect x={px} y={py} width={pw} height={ph} fill={it.color} fillOpacity={it.alpha || 0.85} rx={it.radius != null ? it.radius : 2.5} />
                  )}
                  {it.label && (
                    <text x={cx} y={cy + 3} textAnchor="middle"
                          fontFamily="Inter" fontSize={Math.min(10, Math.max(7, Math.min(pw, ph) * 0.18))}
                          fontWeight="500" letterSpacing="0.2" fill={txtColor}>
                      {it.label}
                    </text>
                  )}
                </g>
              );
            })}
            {/* Двери */}
            {cfg.door && renderDoor(cfg.door, 'door')}
            {cfg.doors && cfg.doors.map((d, i) => renderDoor(d, `door${i}`))}
            {/* Кликабельные «места» */}
            {places.map((p, i) => {
              const room = rooms[i];
              if (!room) return null;
              const stColor = statusColor(room.status);
              const disabled = room.status === 'busy';
              const cx = p.x * scale + (p.w * scale) / 2;
              const cy = p.y * scale + (p.h * scale) / 2;
              return (
                <g key={`p${i}`} style={{ cursor: disabled ? 'not-allowed' : 'pointer' }}
                   onClick={() => !disabled && onSelectRoom && onSelectRoom(room)}>
                  <rect x={p.x * scale} y={p.y * scale} width={p.w * scale} height={p.h * scale}
                        fill={stColor} fillOpacity={disabled ? 0.16 : 0.24}
                        stroke={stColor} strokeWidth="2" rx="4" />
                  <text x={cx} y={cy - 5}
                        textAnchor="middle" dominantBaseline="middle"
                        fontFamily="Bodoni Moda" fontStyle="italic"
                        fontSize="26" fontWeight="500" fill="#0f1419">{room.num}</text>
                  <text x={cx} y={cy + 18}
                        textAnchor="middle" dominantBaseline="middle"
                        fontFamily="Inter" fontSize="11" letterSpacing="0.5"
                        fill="#0f1419" fontWeight="500">{room.prices.hour}₽/ч</text>
                  <circle cx={p.x * scale + p.w * scale - 12} cy={p.y * scale + 12} r="4.5" fill={stColor} stroke="#fbfcfd" strokeWidth="1.4" />
                </g>
              );
            })}
            {/* Размеры */}
            <text x={W / 2} y={H + 18} textAnchor="middle" fontFamily="Inter" fontSize="10" letterSpacing="1" fill="#6e7682">{cfg.planW} м</text>
            <text x={-14} y={H / 2} textAnchor="middle" fontFamily="Inter" fontSize="10" letterSpacing="1" fill="#6e7682" transform={`rotate(-90 -14 ${H / 2})`}>{cfg.planH} м</text>
          </svg>
        </div>

        <aside className="cp-side">
          <div className="cp-meta"><span>Площадь</span><b>{cfg.planW} × {cfg.planH} м</b></div>
          {rooms.length > 0 && <div className="cp-meta"><span>Мест</span><b>{rooms.length}</b></div>}
          {minPrice && <div className="cp-meta"><span>Тариф</span><b>от {minPrice} ₽/ч</b></div>}
          <div className="cp-rooms-list">
            {rooms.map(r => (
              <button type="button" key={r.id} className={`cp-room-row cp-room-row--${r.status}`}
                      disabled={r.status === 'busy'}
                      onClick={() => onSelectRoom && onSelectRoom(r)}>
                <span className="cp-rr-num">{r.num}</span>
                <span className="cp-rr-name">{r.name}</span>
                <span className="cp-rr-price">{r.prices.hour}<span>₽/ч</span></span>
                <span className={`cp-rr-dot status ${r.status}`}></span>
              </button>
            ))}
          </div>
          <p className="cp-side-note">Бронь — по часам или сразу на день/неделю/месяц. Оплата на сайте, после оплаты время закрепляется за вами.</p>
        </aside>
      </div>
    </div>
  );
};

// Слайд-шоу блока «Мероприятия»: один большой горизонтальный кадр, фото меняются
// по кругу (кроссфейд + медленный «наезд камеры»). Под кадром — счётчик,
// линия-прогресс и стрелки. Пауза — при наведении курсора или касании.
const EvSlideshow = ({ slides, interval = 5200 }) => {
  const [idx, setIdx] = useState(0);
  const [paused, setPaused] = useState(false);
  const [cycle, setCycle] = useState(0); // перезапускает линию прогресса после паузы
  const touchX = useRef(null);
  const n = slides.length;

  const go = (i) => setIdx(((i % n) + n) % n);
  const resume = () => { setPaused(false); setCycle(c => c + 1); };

  // следующий кадр — через interval после показа текущего
  useEffect(() => {
    if (paused) return;
    const t = setTimeout(() => setIdx(i => (i + 1) % n), interval);
    return () => clearTimeout(t);
  }, [idx, cycle, paused, n, interval]);

  return (
    <div className={'ev-slider' + (paused ? ' is-paused' : '')}>
      <div
        className="evs-stage"
        onMouseEnter={() => setPaused(true)}
        onMouseLeave={resume}
        onTouchStart={(e) => { touchX.current = e.touches[0].clientX; setPaused(true); }}
        onTouchCancel={resume}
        onTouchEnd={(e) => {
          const dx = touchX.current == null ? 0 : e.changedTouches[0].clientX - touchX.current;
          touchX.current = null;
          if (Math.abs(dx) > 40) go(idx + (dx < 0 ? 1 : -1)); // свайп влево — следующий кадр
          resume();
        }}
      >
        {slides.map((s, i) => (
          <div key={i} className={'ev-slide' + (i === idx ? ' is-active' : '')}>
            <img src={s.src} alt={s.alt} loading={i ? 'lazy' : undefined} />
          </div>
        ))}
        <span className="ev-photo-tag">Зал · до&nbsp;30&nbsp;человек</span>
      </div>
      <div className="evs-bar">
        <span className="evs-count"><b>{String(idx + 1).padStart(2, '0')}</b>&nbsp;/&nbsp;{String(n).padStart(2, '0')}</span>
        <span className="evs-line">
          <i key={idx + '-' + cycle} className="evs-fill" style={{ animationDuration: interval + 'ms' }}></i>
        </span>
        <span className="evs-nav">
          <button type="button" className="evs-arrow" onClick={() => go(idx - 1)} aria-label="Предыдущее фото">←</button>
          <button type="button" className="evs-arrow" onClick={() => go(idx + 1)} aria-label="Следующее фото">→</button>
        </span>
      </div>
    </div>
  );
};

// ============================================
// LANDING
// ============================================
const EditorialLanding = () => {
  const [room, setRoom] = useState(null);
  const [hovered, setHovered] = useState(null);
  const [toast, setToast] = useState(null); // { type:'ok'|'err', msg }
  const [openZone, setOpenZone] = useState(null);          // аккордеон зон в плане главного зала
  const [activePlan, setActivePlan] = useState(null);      // null | 'main' | 'lounge' | 'barber' | 'tan' | 'bed' | 'cosmo' | 'free4'
  const [emailPopup, setEmailPopup] = useState(null);      // null | 'rooms' | 'events' — контекст для модалки «Написать на почту»
  const [payModal, setPayModal] = useState(null);          // данные оплаты по QR после создания брони

  // Слайдер-фон в hero (5 фото зала, кроссфейд). Каждый слайд можно заменить в админке («Картинки»).
  // Без useMemo: настройки (siteImages) приходят из БД после маунта, и фото,
  // заменённые в админке, должны подхватиться при ближайшем ре-рендере.
  const heroSlides = [
    ehSitePhoto('hero1', 'heroInterior'),
    ehSitePhoto('hero2', 'heroMoody'),
    ehSitePhoto('hero3', 'heroSoft'),
    ehSitePhoto('hero4', 'interior5'),
    ehSitePhoto('hero5', 'interior3'),
  ].filter(Boolean);
  const [heroIdx, setHeroIdx] = useState(0);
  // Видео-фон: assets/video/hero.mp4 или hero.mov. Если файла нет / формат не играет —
  // остаётся фото-слайдер (видео появится само, как только файл положат на место).
  const [videoOn, setVideoOn] = useState(false);   // true после успешного старта воспроизведения
  const videoRef = React.useRef(null);
  useEffect(() => {
    if ((heroSlides.length <= 1) || videoOn) return;
    const t = setInterval(() => setHeroIdx(i => (i + 1) % heroSlides.length), 6500);
    return () => clearInterval(t);
  }, [heroSlides.length, videoOn]);

  useEffect(() => { document.title = "Estetic Hall · бьюти-коворкинг в Тамбове"; }, []);

  const handleBook = async ({ room, kind, dateISO, dateLabel, slots, payWith }) => {
    kind = kind || 'hour';
    let me = null;
    try { me = (await API.me()).user; } catch (e) {}
    if (!me) {
      sessionStorage.setItem('eh_pending_booking', JSON.stringify({ roomId: room.id, kind, date: dateISO, slots }));
      window.location.hash = '#/login';
      return;
    }
    try {
      const resp = await API.createBooking({ roomId: room.id, kind, date: dateISO, slots, payWith: payWith || 'money' });
      const when = kind === 'day' ? 'весь день'
        : kind === 'month' ? '30 дней'
        : `${String(slots[0]).padStart(2, '0')}:00–${String(slots[slots.length - 1] + 1).padStart(2, '0')}:00`;
      setRoom(null);
      if (resp && resp.payment) {
        // Оплата деньгами: место удержано, показываем QR и реквизиты для перевода
        setPayModal(resp.payment);
      } else {
        setToast({ type: 'ok', msg: `${room.name} · ${dateLabel} · ${when}` });
        setTimeout(() => setToast(null), 6000);
      }
    } catch (ex) {
      setRoom(null);
      setToast({ type: 'err', msg: ex.message || 'Не удалось забронировать. Попробуйте другое время.' });
      setTimeout(() => setToast(null), 6000);
    }
  };

  return (
    <>
      <EditorialHeader />

      {/* HERO — Nordic-стиль текста + видео-фон (assets/video/hero.mp4 | hero.mov) с фолбэком на фото-слайдер */}
      <section className="hero hero--nordic" id="hero" data-screen-label="01 Hero">
        <div className="hero-bg">
          {heroSlides.map((src, i) => (
            <div
              key={i}
              className={`hero-bg-img${(!videoOn && i === heroIdx) ? ' is-active' : ''}`}
              style={{ backgroundImage: `url(${src})` }}
              aria-hidden={videoOn || i !== heroIdx}
            />
          ))}
          <video
            ref={videoRef}
            className={`hero-bg-video${videoOn ? ' is-active' : ''}`}
            autoPlay muted loop playsInline preload="auto"
            onPlaying={() => setVideoOn(true)}
            onError={() => setVideoOn(false)}
            aria-hidden="true"
            tabIndex={-1}
          >
            <source src="assets/video/hero.mp4" type="video/mp4" />
            <source src="assets/video/hero.mov" type="video/quicktime" />
          </video>
        </div>
        <div className="hero-overlay"></div>
        <div className="hero-inner container">
          <h1 className="hero-headline hero-headline--nordic">
            Аренда места мастера<br/>
            от&nbsp;1&nbsp;часа, <em>24/7</em>,<br/>
            без скрытых платежей
          </h1>
          <div className="hero-bullets">
            <span>Центр города</span><span className="dot"></span>
            <span>Онлайн-бронь</span><span className="dot"></span>
            <span>Гибкие тарифы</span>
          </div>
          <p className="hero-lead">Оборудованное рабочее место мастера красоты в&nbsp;центре Тамбова — без переплат и&nbsp;бюрократии.</p>
          <div className="hero-cta">
            <a href="#book" className="btn btn--gold btn--lg hero-cta-primary">
              <span className="cta-full">Хочу забронировать!</span>
              <span className="cta-short">Забронировать</span>
            </a>
            <a href="#about" className="btn btn--outline btn--lg" style={{color: 'var(--paper)', borderColor: 'rgba(255,255,255,0.6)'}}>Подробнее</a>
          </div>
        </div>
        <div className="hero-stats container">
          <div className="hstat"><b>30+</b><span>Рабочих мест</span></div>
          <div className="hstat"><b>24/7</b><span>Доступ для мастеров</span></div>
          <div className="hstat"><b>{(D2.brand.open || '09:00')}–{(D2.brand.close || '21:00')}</b><span>Ресепшн</span></div>
          <div className="hstat hstat--loc"><b>Тамбов</b><span>ул. Октябрьская, 11<br/>ТД Октябрьский (3-й этаж)</span></div>
        </div>
      </section>

      {/* Порядок блоков: «Аренда и цены» → «Для мероприятий» → «О пространстве» (через flex order) */}
      <div className="land-reorder">
      {/* ABOUT */}
      <section className="about" id="about" data-screen-label="02 About" style={{order: 3}}>
        <div className="container">
          <Reveal>
            <div className="chapter"><span className="num">III.</span><span className="label">О пространстве</span><span className="line"></span></div>
          </Reveal>
          <div className="about-grid">
            <Reveal className="about-photo">
              <img src={ehSitePhoto('about', 'reception')} alt="Интерьер ресепшна" />
              <div className="about-badge">
                <b>2026</b>
                <span>Открыты<br/>с 2026 года</span>
              </div>
            </Reveal>
            <Reveal delay={120}>
              <div className="eyebrow">Не просто аренда —</div>
              <h2>Место, где мастера<br/>работают <em>с удовольствием.</em></h2>
              <p className="lead">Мы построили пространство, в котором сами хотели бы работать. Спокойный интерьер, профессиональное оборудование уровня дорогой клиники, никаких очередей за раковину и никакой текучки клиентов.</p>
              <p className="lead">Вы платите только за то время, что используете. Полная свобода планировать день, как удобно — приходите хоть в шесть утра, хоть в полночь.</p>
              <div className="features features--6">
                <div className="feat"><div className="n">I.</div><h4>Свой ключ · 24/7</h4><p>Свободный доступ круглосуточно для арендаторов от 1 месяца.</p></div>
                <div className="feat"><div className="n">II.</div><h4>Оборудование</h4><p>Профессиональные кресла, мойки, лампы, ринг-лайты и инструмент.</p></div>
                <div className="feat"><div className="n">III.</div><h4>Гибкое расписание</h4><p>От 1 часа до месяца. Бронируйте онлайн в один клик.</p></div>
                <div className="feat"><div className="n">IV.</div><h4>Личные локеры</h4><p>Шкафчик с замком — оставляйте инструменты и приходите налегке.</p></div>
                <div className="feat"><div className="n">V.</div><h4>Кофе-аппарат</h4><p>Зерновой кофе-аппарат в зоне лаунжа для вас и ваших клиентов.</p></div>
                <div className="feat"><div className="n">VI.</div><h4>Центр города</h4><p>ул. Октябрьская, 11, 3-й этаж — рядом остановки общественного транспорта.</p></div>
              </div>
            </Reveal>
          </div>
        </div>
      </section>

      {/* EVENTS — Аренда зала для бьюти-мероприятий */}
      <section className="events" id="events" data-screen-label="02 Events" style={{order: 2}}>
        <div className="container">
          <Reveal>
            <div className="chapter"><span className="num">II.</span><span className="label">Для мероприятий</span><span className="line"></span></div>
          </Reveal>
          <div className="events-grid">
            <Reveal className="ev-photo">
              {/* Слайд-шоу: 4 горизонтальных кадра по кругу. Каждый можно заменить в админке («Картинки») */}
              <EvSlideshow slides={[
                { src: ehSitePhoto('events1', 'interior5'), alt: 'Зал для бьюти-мероприятий' },
                { src: ehSitePhoto('events2', 'chair1'),    alt: 'Рабочие места для мастер-классов' },
                { src: ehSitePhoto('events3', 'led1'),      alt: 'Видеосвет и ринг-лайты' },
                { src: ehSitePhoto('events4', 'manicure1'), alt: 'Зона маникюра' },
              ]} />
            </Reveal>
            <Reveal delay={120}>
              <div className="eyebrow">Не только аренда мест —</div>
              <h2>Аренда зала<br/>для <em>бьюти-мероприятий</em></h2>
              <p className="lead">Мастер-классы, фотосъёмки для контента, обучения, дни красоты и закрытые мероприятия — арендуйте весь зал или его часть в нерабочие часы.</p>
              <div className="ev-points">
                <div className="ev-point"><b>Мастер-классы</b><span>До 30 участников · видеосвет, ринг-лайты, демо-кресла</span></div>
                <div className="ev-point"><b>Дни красоты</b><span>Выездные мероприятия и обучающие интенсивы под ключ</span></div>
                <div className="ev-point"><b>Закрытые встречи</b><span>Корпоративы, презентации брендов, бьюти-завтраки</span></div>
              </div>
              <button type="button" className="btn btn--gold btn--lg" style={{marginTop: 20}} onClick={() => setEmailPopup('events')}>Обсудить условия →</button>
            </Reveal>
          </div>
        </div>
      </section>

      {/* BOOKING / PLAN */}
      <section className="book" id="book" data-screen-label="03 Book" style={{order: 1}}>
        <div className="container">
          <Reveal>
            <div className="chapter"><span className="num">I.</span><span className="label">Аренда и цены</span><span className="line"></span></div>
          </Reveal>
          <Reveal>
            <h2>Аренда рабочих мест<br/><em>для мастеров</em></h2>
            <p className="book-intro">Выберите зону и нажмите «Подробнее» — вы увидите схему зала, цены и сможете забронировать конкретное место за минуту.</p>
          </Reveal>

          {/* Карточки зон — 8 шт., 4 в ряд */}
          <Reveal>
            <div className="zone-cards zone-cards--4">
              {(() => {
                // Считаем минимальную цену + статус из реальных данных D2.rooms
                const zoneInfo = (zone) => {
                  const list = D2.rooms.filter(r => r.zone === zone);
                  if (!list.length) return { minPrice: null, status: 'unknown' };
                  const minPrice = Math.min(...list.map(r => r.prices.hour));
                  const allBusy = list.every(r => r.status === 'busy');
                  const anyFree = list.some(r => r.status === 'free');
                  const status = allBusy ? 'taken' : anyFree ? 'free' : 'partial';
                  return { minPrice, status, count: list.length };
                };
                // Общий зал теперь = парикмахеры + визаж (9 мест); маникюр/педикюр — отдельная карточка
                const mainMinAll = D2.rooms.filter(r => ['hair','makeup'].includes(r.zone));
                const mainMin = mainMinAll.length ? Math.min(...mainMinAll.map(r => r.prices.hour)) : 450;
                const nailsMinAll = D2.rooms.filter(r => ['nails','pedicure'].includes(r.zone));
                const nailsMin = nailsMinAll.length ? Math.min(...nailsMinAll.map(r => r.prices.hour)) : 350;
                const barb = zoneInfo('barber'), tn = zoneInfo('tan'),
                      bd = zoneInfo('bed'), cs = zoneInfo('cosmo'), fr = zoneInfo('free');
                const dynBadge = (info, fallbackBadge, fallbackKind) => {
                  if (info.status === 'unknown') return { badge: fallbackBadge, kind: fallbackKind };
                  if (info.status === 'taken') return { badge: 'арендуется', kind: 'taken' };
                  if (info.status === 'free') return { badge: info.minPrice ? `от ${info.minPrice}₽/ч` : 'свободен', kind: 'price' };
                  return { badge: 'частично', kind: 'partial' };
                };
                const CABINETS = [
                  { id: 'main',   title: 'Места в общем зале',           desc: '9 рабочих мест — 7 парикмахеров и 2 визажиста. Аренда от 1 часа.', photoKey: 'heroInterior', plan: 'main',  badge: `от ${mainMin}₽/ч`, kind: 'price' },
                  { id: 'nails',  title: 'Маникюр и педикюр',            desc: 'Отдельная зона: 5 мест для маникюра и 2 для педикюра. Аренда от 1 часа.', photoKey: 'manicure1', plan: 'nails', badge: `от ${nailsMin}₽/ч`, kind: 'price' },
                  { id: 'barber', title: 'Кабинет барберов',             desc: 'Отдельная комната ~30 м² с 3 рабочими местами для барберов. Аренда мест отдельно.',     photoKey: 'chair3',       plan: 'barber', ...dynBadge(barb, '3 места · 30 м²', 'info') },
                  { id: 'bed',    title: 'Кабинет с кушеткой',           desc: '2 кабинета с кушеткой для перманентного макияжа (также массаж, татуаж, косметология). Сдаются целиком.', photoKey: 'led2',  plan: 'bed',    ...dynBadge(bd, '2 кабинета', 'info') },
                  { id: 'cosmo',  title: 'Кабинет косметолога',          desc: 'Полноценный косметологический кабинет ~30 м² с аппаратом и кушеткой. Сдаётся целиком.', photoKey: 'led1',          plan: 'cosmo',  ...dynBadge(cs, 'арендуется', 'taken') },
                  { id: 'tan',    title: 'Кабинет с автозагаром',        desc: 'Отдельная комната ~30 м² с оборудованием для автозагара. Сдаётся целиком.',              photoKey: 'interior3',    plan: 'tan',    ...dynBadge(tn, 'арендуется', 'taken') },
                  { id: 'free4',  title: 'Кабинеты свободного назначения', desc: '2 отдельных кабинета ~20 м² каждый в зоне общего зала. Подходят под любые услуги, арендуются отдельно.', photoKey: 'interior1', plan: 'free4', ...dynBadge(fr, '2 кабинета', 'info') },
                  { id: 'more',   title: 'Больше свободных мест',        desc: 'Не нашли подходящий вариант? Свяжитесь с нами — расскажем какие места и кабинеты свободны сейчас, когда освобождаются занятые.', photoKey: 'reception', plan: 'contact', badge: 'связаться', kind: 'cta', ctaText: 'Подробнее →' },
                ];
                return CABINETS.map(c => {
                  const handleClick = () => {
                    setActivePlan(c.plan);
                    if (c.plan === 'main') setOpenZone('hair');
                    if (c.plan === 'nails') setOpenZone('nails');
                    setTimeout(() => {
                      const el = document.getElementById('book-plan');
                      if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
                    }, 120);
                  };
                  return (
                    <article key={c.id} className={`zone-card ${activePlan === c.plan ? 'zone-card--active' : ''}`}>
                      <div className="zc-photo">
                        <img src={ehSitePhoto('zc_' + c.id, c.photoKey)} alt={c.title} loading="lazy" />
                        <span className={`zc-badge zc-badge--${c.kind}`}>{c.badge}</span>
                      </div>
                      <div className="zc-body">
                        <h3>{c.title}</h3>
                        <p>{c.desc}</p>
                        <button type="button" className="btn btn--gold zc-cta" onClick={handleClick}>{c.ctaText || 'Подробнее →'}</button>
                      </div>
                    </article>
                  );
                });
              })()}
            </div>
          </Reveal>

          {/* План + список — показываются только после клика «Подробнее» */}
          <div id="book-plan"></div>
          {activePlan === 'contact' && (
            <Reveal>
              <div className="cabinet-plan contact-plan">
                <div className="cp-head">
                  <div>
                    <span className="cp-status cp-status--free">Свяжитесь с нами</span>
                    <h3>Уточнить что свободно</h3>
                    <p>Не нашли подходящий вариант? Позвоните или напишите — расскажем какие места и кабинеты свободны прямо сейчас и когда освобождаются занятые.</p>
                  </div>
                  <button type="button" className="cp-close" onClick={() => setActivePlan(null)} aria-label="Закрыть">×</button>
                </div>
                <div className="contact-plan-body">
                  <a href={`tel:${D2.brand.phone}`} className="cp-phone-block">
                    <span className="cp-phone-label">Позвоните прямо сейчас</span>
                    <b>{D2.brand.phone}</b>
                    <span className="cp-phone-hint">Ежедневно · {(D2.brand.open || '09:00')}–{(D2.brand.close || '21:00')}</span>
                  </a>
                  {D2.brand.email && (
                    <button type="button"
                       className="btn btn--gold btn--lg cp-write-cta"
                       onClick={() => setEmailPopup('rooms')}>
                      Написать нам на почту →
                    </button>
                  )}
                  <div className="cp-contact-meta">
                    {D2.brand.email && (
                      <div className="cp-meta"><span>Email</span><b>{D2.brand.email}</b></div>
                    )}
                    <div className="cp-meta"><span>Адрес</span><b style={{whiteSpace: 'pre-line', textAlign: 'right'}}>{D2.brand.address}</b></div>
                    <div className="cp-meta"><span>Режим работы</span><b>{D2.brand.hours}</b></div>
                    {D2.brand.vk && (
                      <div className="cp-meta"><span>ВКонтакте</span><a href={D2.brand.vk} target="_blank" rel="noopener noreferrer" style={{color: 'var(--gold)'}}>Написать в ВК →</a></div>
                    )}
                  </div>
                </div>
              </div>
            </Reveal>
          )}
          {activePlan && activePlan !== 'main' && activePlan !== 'nails' && activePlan !== 'contact' && (() => {
            // Соотносим тип плана с зоной БД
            const PLAN_TO_ZONE = { barber: 'barber', tan: 'tan', bed: 'bed', cosmo: 'cosmo', free4: 'free' };
            const zone = PLAN_TO_ZONE[activePlan];
            const list = D2.rooms.filter(r => r.zone === zone);
            return (
              <Reveal>
                <CabinetPlan
                  type={activePlan}
                  rooms={list}
                  onSelectRoom={(r) => setRoom(r)}
                  onClose={() => setActivePlan(null)}
                />
              </Reveal>
            );
          })()}
          {(activePlan === 'main' || activePlan === 'nails') && (() => {
          const isMain = activePlan === 'main';
          const planMode = isMain ? 'hall' : 'nails';
          const planZones = isMain ? ['hair', 'makeup'] : ['nails', 'pedicure'];
          const planRooms = D2.rooms.filter(r => planZones.includes(r.zone));
          return <>
          <Reveal>
            <div className="lead-row" style={{marginTop: 16}}>
              <p style={{margin: 0}}>Нажмите на место на плане или в списке — увидите фотографии, тарифы и свободные часы.</p>
              <div className="legend">
                <span className="free"><i></i>Свободен</span>
                <span className="partial"><i></i>Частично</span>
                <span className="busy"><i></i>Занят сегодня</span>
              </div>
            </div>
          </Reveal>
          <Reveal>
            <div className="book-layout">
              <div className="plan-frame">
                <span className="c-tl"></span><span className="c-tr"></span><span className="c-bl"></span><span className="c-br"></span>
                <div className="pl-label">ПЛАН · РАБОЧИЕ МЕСТА</div>
                <div className="pl-label-r">{isMain ? 'общий зал' : 'маникюр · педикюр'}</div>
                <FloorPlan rooms={planRooms} mode={planMode} onSelect={setRoom} hovered={hovered} setHovered={setHovered} />
              </div>
              <div className="rooms-list">
                <h3>Рабочие места</h3>
                {planZones.map(z => {
                  const list = D2.rooms.filter(r => r.zone === z);
                  if (!list.length) return null;
                  const n = list.length;
                  const word = (n >= 2 && n <= 4) ? 'места' : 'мест';
                  const open = openZone === z;
                  return (
                    <div key={z} className={`zone-group ${open ? 'open' : ''}`}>
                      <button className="zone-head" onClick={() => setOpenZone(open ? null : z)} aria-expanded={open}>
                        <span className="zh-name">{list[0].zoneRu}</span>
                        <span className="zg-meta">{n} {word} · от {list[0].prices.hour}₽/ч</span>
                        <span className="zh-arrow">{open ? '−' : '+'}</span>
                      </button>
                      {open && (
                        <div className="zone-rows">
                          {list.map(r => (
                            <div key={r.id} className="room-row" onClick={() => setRoom(r)} onMouseEnter={() => setHovered(r.id)} onMouseLeave={() => setHovered(null)}>
                              <span className="num">{r.num}</span>
                              <div className="info">
                                <b>{r.name}</b>
                                <span>{r.area} м²</span>
                              </div>
                              <span className="price">{r.prices.hour}<span>₽/ч</span></span>
                              <span className={`status ${r.status}`}></span>
                            </div>
                          ))}
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            </div>
          </Reveal>
          </>;
          })()}
        </div>
      </section>
      </div>{/* /.land-reorder */}

      {/* GALLERY */}
      <section className="gallery-section" id="gallery" data-screen-label="04 Gallery">
        <div className="container">
          <Reveal>
            <div className="chapter"><span className="num">IV.</span><span className="label">Галерея</span><span className="line"></span></div>
            <h2 className="display" style={{fontSize: 'clamp(40px, 5vw, 64px)', marginBottom: 56}}>Наше <em>пространство.</em></h2>
          </Reveal>
          <Reveal>
            <div className="gallery-grid">
              <div className="gi-1"><img src={ehSitePhoto('gal1', 'interior1')} alt="" /></div>
              <div className="gi-2"><img src={ehSitePhoto('gal2', 'chair1')} alt="" /></div>
              <div className="gi-3"><img src={ehSitePhoto('gal3', 'detail1')} alt="" /></div>
              <div className="gi-4"><img src={ehSitePhoto('gal4', 'interior3')} alt="" /></div>
              <div className="gi-5"><img src={ehSitePhoto('gal5', 'manicure1')} alt="" /></div>
              <div className="gi-6"><img src={ehSitePhoto('gal6', 'interior5')} alt="" /></div>
              <div className="gi-7"><img src={ehSitePhoto('gal7', 'led1')} alt="" /></div>
            </div>
          </Reveal>
        </div>
      </section>

      {/* PROMO — Абонемент + забота о резидентах */}
      <section className="promo" id="promo" data-screen-label="04A Promo">
        <div className="container">
          <Reveal>
            <div className="chapter"><span className="num">V.</span><span className="label">Абонемент</span><span className="line"></span></div>
            <h2 className="display promo-h2">Абонемент<br/>в&nbsp;<em>Estetic Hall</em></h2>
            <p className="promo-lead">Покупайте часы заранее по выгодной цене и оплачивайте ими брони в общем зале и кабинете барберов — прямо в личном кабинете.</p>
          </Reveal>
          <Reveal>
            <div className="promo-grid promo-grid--single">
              {/* Абонемент — единственная карточка */}
              <div className="promo-card promo-card--brand">
                <div className="pc-logo">
                  <img src="assets/logo.png" alt="Estetic Hall" />
                  <div className="pc-brand-name">ESTETIC<br/><span>HALL</span></div>
                </div>
                <div className="pc-content">
                  <h3><b>{D2.brand.abonementHours || 30} часов</b><br/>за {(D2.brand.abonementPrice || 6000).toLocaleString()} ₽</h3>
                  <p>Абонемент на {D2.brand.abonementHours || 30}&nbsp;часов аренды. Тратится на места в&nbsp;общем зале и кабинет барберов&nbsp;— 1&nbsp;час брони = 1&nbsp;час с&nbsp;абонемента. Покупка и&nbsp;оплата прямо на сайте.</p>
                  <a href="#/cabinet" className="btn btn--white pc-cta">Купить абонемент →</a>
                </div>
              </div>
            </div>
          </Reveal>
        </div>
      </section>

      {/* CARE — Мы заботимся о наших резидентах */}
      <section className="care" data-screen-label="05 Care" style={{backgroundImage: `url(${ehSitePhoto('care', 'heroMoody')})`}}>
        <div className="care-overlay"></div>
        <div className="container">
          <Reveal>
            <h2 className="care-title">Мы заботимся о&nbsp;наших резидентах</h2>
            <p className="care-lead">Работайте в удобном для Вас графике, а&nbsp;мы позаботимся о&nbsp;комфорте для Вас и&nbsp;Ваших клиентов.</p>
          </Reveal>
          <Reveal>
            <div className="care-grid">
              <div className="care-col">
                <div className="care-ico" aria-hidden="true">
                  <svg viewBox="0 0 48 48" width="34" height="34" fill="none" stroke="#ffffff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M9 20h22v8a9 9 0 0 1-9 9h-4a9 9 0 0 1-9-9z"/>
                    <path d="M31 22h4a5 5 0 0 1 0 10h-4"/>
                    <path d="M16 14c0-2 2-2 2-4M22 14c0-2 2-2 2-4M28 14c0-2 2-2 2-4"/>
                  </svg>
                </div>
                <h4>Сервис для мастеров<br/>и клиентов</h4>
                <p>Доброжелательные администраторы встретят вас и ваших клиентов, предложат чай/кофе и безлимитный Wi-Fi.</p>
              </div>
              <div className="care-col">
                <div className="care-ico" aria-hidden="true">
                  <svg viewBox="0 0 48 48" width="34" height="34" fill="none" stroke="#ffffff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M24 6l16 8-16 8L8 14z"/>
                    <path d="M8 14v20l16 8 16-8V14"/>
                    <path d="M24 22v20"/>
                  </svg>
                </div>
                <h4>Профессиональное<br/>оборудование и материалы</h4>
                <p>Свежий стильный и аккуратный ремонт, профессиональное оборудование для работы, дополнительные материалы по себестоимости прямо на месте.</p>
              </div>
              <div className="care-col">
                <div className="care-ico" aria-hidden="true">
                  <svg viewBox="0 0 48 48" width="34" height="34" fill="none" stroke="#ffffff" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
                    <rect x="12" y="8" width="24" height="32" rx="2"/>
                    <line x1="24" y1="8" x2="24" y2="40"/>
                    <circle cx="19" cy="23" r="1.6" fill="#ffffff" stroke="none"/>
                    <circle cx="29" cy="23" r="1.6" fill="#ffffff" stroke="none"/>
                  </svg>
                </div>
                <h4>Локер для<br/>личных вещей</h4>
                <p>Приходите работать налегке&nbsp;— ваши инструменты и личные вещи можно оставить в локере, который закрывается на ключ.</p>
              </div>
            </div>
          </Reveal>
        </div>
      </section>

      {/* МАП — Как нас найти (контакты) */}
      <section className="contact-map" id="contact" data-screen-label="07 Map">
        <div className="cm-container">
          <iframe
            className="cm-iframe"
            src={`https://yandex.ru/map-widget/v1/?text=${encodeURIComponent((D2.brand.address || 'г. Тамбов, ул. Октябрьская, 11').replace(/\s*\n\s*/g, ' '))}&z=17`}
            allow="geolocation"
            allowFullScreen
            loading="lazy"
            title="Карта · Estetic Hall"
          />
          <div className="cm-overlay">
            <span className="kicker">Контакты · как нас найти</span>
            <h3 style={{whiteSpace: 'pre-line'}}>{D2.brand.address}</h3>
            <p>Центр Тамбова, 3-й этаж. Остановка общественного транспорта в шаговой доступности.</p>
            <div className="cm-contacts">
              <a href={`tel:${D2.brand.phone}`}>{D2.brand.phone}</a>
              {D2.brand.email && <a href={`mailto:${D2.brand.email}`}>{D2.brand.email}</a>}
              <span>{D2.brand.hours}</span>
            </div>
            <a className="btn btn--outline btn--lg" href={`https://yandex.ru/maps/?text=${encodeURIComponent((D2.brand.address || 'г. Тамбов, ул. Октябрьская, 11').replace(/\s*\n\s*/g, ' '))}`} target="_blank" rel="noopener noreferrer">Открыть в Яндекс.Картах →</a>
          </div>
        </div>
      </section>

      <EditorialFooter />

      {room && <RoomDrawer room={room} onClose={() => setRoom(null)} onBook={handleBook} />}

      {payModal && (
        <PaymentModal
          payment={payModal}
          onClose={() => {
            setPayModal(null);
            setToast({ type: 'info', title: '⏳ Ожидается подтверждение оплаты', msg: 'Администратор проверит перевод и подтвердит бронь. Статус — в личном кабинете.' });
            setTimeout(() => setToast(null), 8000);
          }}
        />
      )}

      {toast && (
        <div className={`eh-toast ${toast.type === 'err' ? 'err' : 'ok'}`}>
          <b style={{fontFamily: 'var(--serif)', fontSize: 16}}>{toast.title || (toast.type === 'ok' ? '✓ Бронь подтверждена' : toast.type === 'info' ? '✓ Готово' : '⚠ Не получилось')}</b>
          <div style={{fontSize: 13, opacity: 0.88, marginTop: 4}}>{toast.msg}</div>
          {toast.type === 'ok' && (
            <a href="#/cabinet" style={{display: 'inline-block', marginTop: 12, fontSize: 12, letterSpacing: 1.5, textTransform: 'uppercase', textDecoration: 'underline', textUnderlineOffset: 4}}>Перейти в кабинет →</a>
          )}
        </div>
      )}

      {/* Модалка «Написать нам» — выбор почтового сервиса */}
      {emailPopup && D2.brand.email && (() => {
        const email = D2.brand.email;
        const CTX = {
          rooms:  { subject: 'Запрос свободных мест · Estetic Hall',  body: 'Здравствуйте!\nРасскажите, пожалуйста, какие места и кабинеты сейчас свободны в коворкинге.', title: 'Написать нам', desc: 'Выберите свой почтовый сервис — откроется окно с уже готовым письмом:' },
          events: { subject: 'Аренда зала для мероприятия · Estetic Hall', body: 'Здравствуйте!\nХочу узнать условия аренды зала для мероприятия.\n\nТип мероприятия: \nПредполагаемая дата: \nКоличество гостей: \nДополнительно: ', title: 'Аренда зала для мероприятия', desc: 'Опишите ваше мероприятие — мы свяжемся с вами и согласуем все детали:' },
        };
        const cfg = CTX[emailPopup] || CTX.rooms;
        const subject = cfg.subject;
        const body = cfg.body;
        const e = (s) => encodeURIComponent(s);
        const services = [
          { name: 'Gmail',        url: `https://mail.google.com/mail/?view=cm&fs=1&to=${e(email)}&su=${e(subject)}&body=${e(body)}` },
          { name: 'Яндекс.Почта', url: `https://mail.yandex.ru/compose?to=${e(email)}&subject=${e(subject)}&body=${e(body)}` },
          { name: 'Mail.ru',      url: `https://e.mail.ru/compose?to=${e(email)}&subject=${e(subject)}&body=${e(body)}` },
          { name: 'Почтовый клиент', url: `mailto:${email}?subject=${e(subject)}&body=${e(body)}` },
        ];
        const copyEmail = async () => {
          try {
            await navigator.clipboard.writeText(email);
            setToast({ type: 'info', title: '✓ Email скопирован', msg: email });
          } catch (err) {
            setToast({ type: 'info', title: '✉ Email', msg: email });
          }
          setTimeout(() => setToast(null), 5000);
        };
        return (
          <>
            <div className="drawer-overlay" onClick={() => setEmailPopup(null)}></div>
            <div className="email-popup">
              <button className="ep-close" onClick={() => setEmailPopup(null)} aria-label="Закрыть">×</button>
              <h3>{cfg.title}</h3>
              <p>{cfg.desc}</p>
              <div className="ep-services">
                {services.map((s, i) => (
                  <a key={i} href={s.url} target="_blank" rel="noopener noreferrer"
                     className="ep-service"
                     onClick={() => setTimeout(() => setEmailPopup(null), 200)}>
                    <span>{s.name}</span><b>→</b>
                  </a>
                ))}
              </div>
              <div className="ep-copy-row">
                <span className="ep-email">{email}</span>
                <button type="button" className="btn btn--outline" onClick={copyEmail}>Скопировать</button>
              </div>
            </div>
          </>
        );
      })()}
    </>
  );
};

// ============================================
// LOGIN
// ============================================
const EditorialLogin = () => {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [err, setErr] = useState(null);
  const [loading, setLoading] = useState(false);

  useEffect(() => { document.title = "Вход · Estetic Hall"; }, []);

  const submit = async (e) => {
    e.preventDefault();
    setErr(null);
    setLoading(true);
    try {
      const { user } = await API.login({ email: email.trim(), password });
      await completePendingBooking();
      window.location.hash = user.role === 'admin' ? '#/admin' : '#/cabinet';
    } catch (ex) {
      setErr(ex.network ? 'Нет связи с сервером' : (ex.message || 'Не удалось войти'));
      setLoading(false);
    }
  };

  return (
    <div className="auth-page" data-screen-label="07 Login">
      <AuthPhoto
        quote="Пространство, в котором приятно работать — это уже половина успеха."
        by="Анна Смирнова · стилист-колорист"
        bg={D2.photos.heroMoody}
      />
      <div className="auth-form">
        <a href="#/" className="back">← Назад на главную</a>
        <h1>Войти<br/><em>в кабинет.</em></h1>
        <p className="lead">Управляйте бронированиями и расписанием в одном месте.</p>
        <form onSubmit={submit}>
          <div className="field">
            <label>Email</label>
            <input type="email" value={email} onChange={e => setEmail(e.target.value)} required autoComplete="email" />
          </div>
          <div className="field">
            <label>Пароль</label>
            <input type="password" value={password} onChange={e => setPassword(e.target.value)} required autoComplete="current-password" />
          </div>
          {err && <div className="err" style={{marginTop: -10, marginBottom: 12}}>{err}</div>}
          <button className="btn btn--lg submit" disabled={loading} style={{marginTop: 8}}>{loading ? 'Входим…' : 'Войти →'}</button>
        </form>
        <div className="alt-link">Нет аккаунта? <a href="#/register">Зарегистрироваться</a></div>
      </div>
    </div>
  );
};

// ============================================
// REGISTER
// ============================================
const EditorialRegister = () => {
  const [form, setForm] = useState({ firstName: '', lastName: '', phone: '', email: '', spec: 'Парикмахер', password: '', confirm: '', agree: false });
  const [err, setErr] = useState({});
  const [loading, setLoading] = useState(false);
  const upd = (k, v) => setForm(s => ({...s, [k]: v}));

  useEffect(() => { document.title = "Регистрация · Estetic Hall"; }, []);

  const submit = async (e) => {
    e.preventDefault();
    const errs = {};
    if (!form.firstName.trim()) errs.firstName = 'Укажите имя';
    if (!form.lastName.trim()) errs.lastName = 'Укажите фамилию';
    if (!/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.email)) errs.email = 'Некорректный email';
    if (form.phone.replace(/\D/g, '').length < 11) errs.phone = 'Укажите телефон';
    if (form.password.length < 8) errs.password = 'Минимум 8 символов';
    if (form.password !== form.confirm) errs.confirm = 'Пароли не совпадают';
    if (!form.agree) errs.agree = 'Нужно согласие';
    setErr(errs);
    if (Object.keys(errs).length) return;

    setLoading(true);
    try {
      await API.register({
        firstName: form.firstName.trim(),
        lastName: form.lastName.trim(),
        email: form.email.trim(),
        phone: form.phone.trim(),
        spec: form.spec,
        password: form.password,
      });
      await completePendingBooking();
      window.location.hash = '#/cabinet';
    } catch (ex) {
      if (ex.fields) setErr(ex.fields);
      else setErr({ email: ex.network ? 'Нет связи с сервером' : (ex.message || 'Не удалось зарегистрироваться') });
      setLoading(false);
    }
  };

  return (
    <div className="auth-page" data-screen-label="08 Register">
      <AuthPhoto
        quote="Сначала вы строите своё имя — затем имя строит вас."
        by="из этикета мастерской"
        bg={D2.photos.authRegister || D2.photos.heroSoft}
      />
      <div className="auth-form auth-compact">
        <a href="#/" className="back">← Назад на главную</a>
        <h1>Создать<br/><em>аккаунт.</em></h1>
        <p className="lead">Зарегистрируйтесь, чтобы бронировать рабочие места и оплачивать на сайте.</p>
        <form onSubmit={submit}>
          <div className="row2">
            <div className={`field ${err.firstName ? 'error' : ''}`}>
              <label>Имя</label>
              <input value={form.firstName} onChange={e => upd('firstName', e.target.value)} />
              {err.firstName && <div className="err">{err.firstName}</div>}
            </div>
            <div className={`field ${err.lastName ? 'error' : ''}`}>
              <label>Фамилия</label>
              <input value={form.lastName} onChange={e => upd('lastName', e.target.value)} />
              {err.lastName && <div className="err">{err.lastName}</div>}
            </div>
          </div>
          <div className="row2">
            <div className={`field ${err.phone ? 'error' : ''}`}>
              <label>Телефон</label>
              <input value={form.phone} onChange={e => upd('phone', maskPhone(e.target.value))} placeholder="+7 (___) ___-__-__" inputMode="tel" />
              {err.phone && <div className="err">{err.phone}</div>}
            </div>
            <div className={`field ${err.email ? 'error' : ''}`}>
              <label>Email</label>
              <input type="email" value={form.email} onChange={e => upd('email', e.target.value)} />
              {err.email && <div className="err">{err.email}</div>}
            </div>
          </div>
          <div className="field">
            <label>Специализация</label>
            <select value={form.spec} onChange={e => upd('spec', e.target.value)}>
              <option>Парикмахер</option>
              <option>Мастер маникюра / педикюра</option>
              <option>Визажист</option>
              <option>Косметолог</option>
              <option>Лешмейкер</option>
              <option>Другое</option>
            </select>
          </div>
          <div className="row2">
            <div className={`field ${err.password ? 'error' : ''}`}>
              <label>Пароль</label>
              <input type="password" value={form.password} onChange={e => upd('password', e.target.value)} />
              {err.password && <div className="err">{err.password}</div>}
            </div>
            <div className={`field ${err.confirm ? 'error' : ''}`}>
              <label>Повторите</label>
              <input type="password" value={form.confirm} onChange={e => upd('confirm', e.target.value)} />
              {err.confirm && <div className="err">{err.confirm}</div>}
            </div>
          </div>
          <label style={{display: 'flex', gap: 10, fontSize: 13, color: 'var(--ink-soft)', marginTop: 8, cursor: 'pointer'}}>
            <input type="checkbox" checked={form.agree} onChange={e => upd('agree', e.target.checked)} />
            <span>Я согласен с <a href="#/offer" target="_blank" rel="noopener" style={{color: 'var(--ink)', textDecoration: 'underline'}}>офертой</a> и <a href="#/privacy" target="_blank" rel="noopener" style={{color: 'var(--ink)', textDecoration: 'underline'}}>политикой конфиденциальности</a></span>
          </label>
          {err.agree && <div className="err" style={{marginTop: 4}}>{err.agree}</div>}
          <button className="btn btn--lg submit" disabled={loading}>{loading ? 'Создаём аккаунт…' : 'Зарегистрироваться →'}</button>
        </form>
        <div className="alt-link">Уже есть аккаунт? <a href="#/login">Войти</a></div>
      </div>
    </div>
  );
};

// ============================================
// CABINET
// ============================================
const CabinetProfile = ({ user, onUpdate }) => {
  const [form, setForm] = useState({ firstName: user.first_name || '', lastName: user.last_name || '', phone: user.phone || '', spec: user.spec || 'Парикмахер' });
  const [err, setErr] = useState({});
  const [saving, setSaving] = useState(false);
  const [ok, setOk] = useState(false);
  const upd = (k, v) => setForm(s => ({ ...s, [k]: v }));

  const save = async (e) => {
    e.preventDefault();
    const errs = {};
    if (!form.firstName.trim()) errs.firstName = 'Укажите имя';
    if (!form.lastName.trim()) errs.lastName = 'Укажите фамилию';
    if (form.phone.replace(/\D/g, '').length < 11) errs.phone = 'Укажите телефон';
    setErr(errs);
    if (Object.keys(errs).length) return;
    setSaving(true); setOk(false);
    try {
      const res = await API.updateProfile({ firstName: form.firstName.trim(), lastName: form.lastName.trim(), phone: form.phone.trim(), spec: form.spec });
      onUpdate(res.user); setOk(true); setTimeout(() => setOk(false), 3000);
    } catch (ex) { setErr(ex.fields || { firstName: ex.message || 'Ошибка сохранения' }); }
    finally { setSaving(false); }
  };

  const [pw, setPw] = useState({ current: '', next: '', confirm: '' });
  const [pwErr, setPwErr] = useState({});
  const [pwSaving, setPwSaving] = useState(false);
  const [pwOk, setPwOk] = useState(false);
  const updPw = (k, v) => setPw(s => ({ ...s, [k]: v }));

  const savePw = async (e) => {
    e.preventDefault();
    const errs = {};
    if (!pw.current) errs.current = 'Введите текущий пароль';
    if (pw.next.length < 8) errs.next = 'Минимум 8 символов';
    if (pw.next !== pw.confirm) errs.confirm = 'Пароли не совпадают';
    setPwErr(errs);
    if (Object.keys(errs).length) return;
    setPwSaving(true); setPwOk(false);
    try {
      await API.updateProfile({ currentPassword: pw.current, newPassword: pw.next });
      setPw({ current: '', next: '', confirm: '' }); setPwOk(true); setTimeout(() => setPwOk(false), 3000);
    } catch (ex) { setPwErr(ex.fields || { current: ex.message || 'Ошибка' }); }
    finally { setPwSaving(false); }
  };

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Личный кабинет</div>
          <h1><em>Профиль.</em></h1>
          <p>Личные данные и безопасность аккаунта. Зарегистрирован {new Date(user.created_at).toLocaleDateString('ru')}.</p>
        </div>
      </div>

      <div className="profile-grid">
        <form className="profile-card" onSubmit={save}>
          <h4>Личные данные</h4>
          <div className="row2">
            <div className={`field ${err.firstName ? 'error' : ''}`}><label>Имя</label><input value={form.firstName} onChange={e => upd('firstName', e.target.value)} />{err.firstName && <div className="err">{err.firstName}</div>}</div>
            <div className={`field ${err.lastName ? 'error' : ''}`}><label>Фамилия</label><input value={form.lastName} onChange={e => upd('lastName', e.target.value)} />{err.lastName && <div className="err">{err.lastName}</div>}</div>
          </div>
          <div className={`field ${err.phone ? 'error' : ''}`}><label>Телефон</label><input value={form.phone} onChange={e => upd('phone', maskPhone(e.target.value))} inputMode="tel" />{err.phone && <div className="err">{err.phone}</div>}</div>
          <div className="field"><label>Специализация</label>
            <select value={form.spec} onChange={e => upd('spec', e.target.value)}>
              <option>Парикмахер</option>
              <option>Мастер маникюра / педикюра</option>
              <option>Визажист</option>
              <option>Косметолог</option>
              <option>Лешмейкер</option>
              <option>Другое</option>
            </select>
          </div>
          <div className="field"><label>Email</label><input value={user.email} disabled /><div className="hint">Email используется для входа и не меняется</div></div>
          <div className="profile-actions">
            <button className="btn" disabled={saving}>{saving ? 'Сохраняем…' : 'Сохранить'}</button>
            {ok && <span className="save-ok">✓ Сохранено</span>}
          </div>
        </form>

        <form className="profile-card" onSubmit={savePw}>
          <h4>Сменить пароль</h4>
          <div className={`field ${pwErr.current ? 'error' : ''}`}><label>Текущий пароль</label><input type="password" value={pw.current} onChange={e => updPw('current', e.target.value)} autoComplete="current-password" />{pwErr.current && <div className="err">{pwErr.current}</div>}</div>
          <div className={`field ${pwErr.next ? 'error' : ''}`}><label>Новый пароль</label><input type="password" value={pw.next} onChange={e => updPw('next', e.target.value)} autoComplete="new-password" />{pwErr.next && <div className="err">{pwErr.next}</div>}</div>
          <div className={`field ${pwErr.confirm ? 'error' : ''}`}><label>Повторите новый</label><input type="password" value={pw.confirm} onChange={e => updPw('confirm', e.target.value)} autoComplete="new-password" />{pwErr.confirm && <div className="err">{pwErr.confirm}</div>}</div>
          <div className="profile-actions">
            <button className="btn btn--outline" disabled={pwSaving}>{pwSaving ? 'Сохраняем…' : 'Обновить пароль'}</button>
            {pwOk && <span className="save-ok">✓ Пароль обновлён</span>}
          </div>
        </form>
      </div>
    </>
  );
};

// Секция «Абонемент» в личном кабинете: часовой пакет и дневной (на N дней)
const CabinetAbonement = ({ sub, buying, onBuy, onShowQR }) => {
  const balanceHours = sub ? (sub.balanceHours != null ? sub.balanceHours : sub.balance) : 0;
  const balanceDays = sub ? (sub.balanceDays || 0) : 0;
  const price = sub ? sub.price : (D2.brand.abonementPrice || 6000);
  const hours = sub ? sub.hours : (D2.brand.abonementHours || 30);
  const dayPrice = sub ? (sub.dayPrice || D2.brand.abonementDayPrice || 1000) : (D2.brand.abonementDayPrice || 1000);
  const history = (sub && sub.history) || [];
  const pending = sub && sub.pending;
  const [days, setDays] = useState(5); // выбор количества дней для дневного абонемента
  const daysNum = Math.max(1, Math.min(60, parseInt(days, 10) || 1));
  const unit = (h) => h.kind === 'days' ? 'дн.' : 'ч';
  const srcLabel = (s) => (
    s === 'admin' ? 'Выдан администратором'
    : s === 'cash' ? 'Оплата наличными'
    : s === 'revoke' ? 'Списание (администратор)'
    : s === 'refund' ? 'Возврат'
    : 'Покупка на сайте'
  );
  const stLabel = (st) => st === 'pending' ? 'Ожидает подтверждения' : st === 'rejected' ? 'Отклонено' : '';
  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Личный кабинет</div>
          <h1><em>Абонемент.</em></h1>
          <p>Покупайте часы или целые дни заранее и оплачивайте ими брони в общем зале и кабинете барберов.</p>
        </div>
      </div>

      {pending && (
        <div className="abonement-card" style={{marginBottom: 18}}>
          <div className="ab-buy" style={{width: '100%'}}>
            <div className="ab-pending">
              <b>⏳ Ожидается подтверждение оплаты</b>
              <span>Вы оформили {pending.kind === 'days' ? `дневной абонемент на ${pending.units} дн.` : `абонемент на ${pending.hours} ч`} за {pending.price.toLocaleString()} ₽. Переведите деньги по QR — администратор проверит поступление и зачислит {pending.kind === 'days' ? 'дни' : 'часы'}. Это может занять какое-то время.</span>
            </div>
            <button className="btn btn--gold" onClick={onShowQR}>Показать QR для оплаты →</button>
          </div>
        </div>
      )}

      <div className="ab-grid">
        {/* Часовой абонемент */}
        <div className="abonement-card">
          <div className="ab-balance">
            <span className="ab-balance-lbl">Остаток часов</span>
            <b>{balanceHours}<em>ч</em></b>
          </div>
          <div className="ab-buy">
            <div className="ab-offer">
              <b>{hours} часов</b>
              <span>за {price.toLocaleString()} ₽</span>
            </div>
            <button className="btn btn--gold" onClick={() => onBuy({ kind: 'hours' })}
              disabled={buying || balanceHours > 0 || !!pending}>{buying ? 'Оформляем…' : 'Купить часы →'}</button>
            {balanceHours > 0 ? (
              <p className="ab-note">У вас ещё есть {balanceHours}&nbsp;ч. Новый пакет можно купить, когда часы закончатся.</p>
            ) : (
              <p className="ab-note">1&nbsp;час брони = 1&nbsp;час с&nbsp;абонемента. Оплата по&nbsp;QR, зачисление после подтверждения администратором.</p>
            )}
          </div>
        </div>

        {/* Дневной абонемент */}
        <div className="abonement-card">
          <div className="ab-balance">
            <span className="ab-balance-lbl">Остаток дней</span>
            <b>{balanceDays}<em>дн</em></b>
          </div>
          <div className="ab-buy">
            <div className="ab-offer ab-offer--days">
              <label>
                <input type="number" min="1" max="60" value={days}
                  onChange={e => setDays(e.target.value)} aria-label="Количество дней" />
                <b>дней</b>
              </label>
              <span>× {dayPrice.toLocaleString()} ₽ = <b>{(daysNum * dayPrice).toLocaleString()} ₽</b></span>
            </div>
            <button className="btn btn--gold" onClick={() => onBuy({ kind: 'days', days: daysNum })}
              disabled={buying || balanceDays > 0 || !!pending}>{buying ? 'Оформляем…' : 'Купить дни →'}</button>
            {balanceDays > 0 ? (
              <p className="ab-note">У вас ещё есть {balanceDays}&nbsp;дн. Новый пакет можно купить, когда дни закончатся.</p>
            ) : (
              <p className="ab-note">1&nbsp;день с&nbsp;абонемента = бронь места на&nbsp;весь рабочий день. Выберите количество дней — от&nbsp;1&nbsp;до&nbsp;60.</p>
            )}
          </div>
        </div>
      </div>

      <h3 style={{fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 26, margin: '40px 0 16px', letterSpacing: '-0.5px'}}>История абонемента</h3>
      {history.length === 0 ? (
        <p style={{color: 'var(--mute)'}}>Пока нет покупок и начислений.</p>
      ) : (
        <div className="tbl-wrap"><table className="tbl">
          <thead><tr><th>#</th><th>Дата</th><th>Объём</th><th>Использовано</th><th>Тип</th><th>Сумма</th><th>Статус</th></tr></thead>
          <tbody>
            {history.map((h, i) => (
              <tr key={h.id} style={h.status === 'rejected' ? {opacity: 0.55} : undefined}>
                <td className="num">{String(i + 1).padStart(2, '0')}</td>
                <td>{new Date(h.createdAt).toLocaleDateString('ru')}</td>
                <td><b>{h.hoursTotal < 0 ? `− ${-h.hoursTotal} ${unit(h)}` : `${h.hoursTotal} ${unit(h)}`}</b></td>
                <td>{h.hoursTotal < 0 ? '—' : `${h.hoursUsed} ${unit(h)}`}</td>
                <td>{srcLabel(h.source)}</td>
                <td>{h.price ? h.price.toLocaleString() + ' ₽' : '—'}</td>
                <td>{stLabel(h.status) ? <span className={`badge ${h.status === 'pending' ? 'pending' : ''}`}>{stLabel(h.status)}</span> : '✓'}</td>
              </tr>
            ))}
          </tbody>
        </table></div>
      )}
    </>
  );
};

const EditorialCabinet = () => {
  const [user, setUser] = useState(undefined); // undefined = loading
  const [bookings, setBookings] = useState([]);
  const [tab, setTab] = useState('active');
  const [section, setSection] = useState('bookings');
  const [busyId, setBusyId] = useState(null);
  const [sub, setSub] = useState(null); // { balance, price, hours, history, pending, payment }
  const [buying, setBuying] = useState(false);
  const [payModal, setPayModal] = useState(null); // повторный показ QR для неоплаченной брони/абонемента

  useEffect(() => { document.title = "Личный кабинет · Estetic Hall"; }, []);

  useEffect(() => {
    let active = true;
    (async () => {
      try {
        const me = (await API.me()).user;
        if (!me) { window.location.hash = '#/login'; return; }
        if (!active) return;
        const { bookings } = await API.myBookings();
        if (!active) return;
        setBookings(bookings);
        try { const s = await API.mySubscription(); if (active) setSub(s); } catch (e) {}
        setUser(me);
      } catch (e) {
        window.location.hash = '#/login';
      }
    })();
    return () => { active = false; };
  }, []);

  const buyAbonement = async (body) => {
    setBuying(true);
    try {
      const s = await API.buySubscription(body || { kind: 'hours' });
      setSub(s);
      // Покупка через перевод по QR — показываем реквизиты, часы/дни зачислятся после подтверждения
      if (s && s.payment) setPayModal(s.payment);
    } catch (e) { alert(e.message || 'Не удалось купить абонемент'); }
    finally { setBuying(false); }
  };

  // QR для неподтверждённой брони: сумма и назначение по данным брони, реквизиты из настроек
  const showBookingQR = (b) => {
    setPayModal({
      amount: b.price,
      purpose: `Оплата брони №${b.id}, Estetic Hall`,
      requisites: D2.pay || {},
    });
  };

  if (user === undefined) return <DashLoading label="Загрузка кабинета…" />;
  if (!user) return null;

  const roomOf = (id) => D2.rooms.find(r => r.id === id) || { num: '—', name: 'Место', zoneRu: '', area: 0 };
  // Вкладка «Активные» включает и брони, ожидающие подтверждения оплаты (pending)
  const filtered = bookings.filter(b => tab === 'active' ? (b.status === 'active' || b.status === 'pending') : tab === 'history' ? b.status === 'completed' : b.status === 'cancelled');
  const upcoming = bookings.filter(b => b.status === 'active' || b.status === 'pending').length;
  const totalSpent = bookings.filter(b => b.paid).reduce((s, b) => s + b.price, 0);

  const logout = async () => { try { await API.logout(); } catch (e) {} window.location.hash = '#/'; };
  const cancel = async (id) => {
    setBusyId(id);
    try {
      await API.cancelBooking(id);
      setBookings(bs => bs.map(b => b.id === id ? {...b, status: 'cancelled'} : b));
    } catch (e) { alert(e.message || 'Не удалось отменить'); }
    finally { setBusyId(null); }
  };

  return (
    <div className="dash" data-screen-label="09 Cabinet">
      <aside className="dash-side">
        <a href="#/" className="logo" style={{marginBottom: 16}}>
          <span className="mark"><img src="assets/logo.png" alt="Estetic Hall" /></span>
          <span className="txt"><b>ESTETIC HALL</b><span>кабинет мастера</span></span>
        </a>
        <div className="who">
          <div className="avatar">{user.name[0]}</div>
          <b>{user.name}</b>
          <span>{user.spec}</span>
        </div>
        <nav>
          <a className={section === 'bookings' ? 'active' : ''} onClick={() => setSection('bookings')}>Бронирования</a>
          <a className={section === 'abonement' ? 'active' : ''} onClick={() => setSection('abonement')}>Абонемент</a>
          <a className={section === 'profile' ? 'active' : ''} onClick={() => setSection('profile')}>Профиль</a>
          <a href="#book">Забронировать ещё</a>
        </nav>
        <div className="bottom" onClick={logout}>← Выйти</div>
      </aside>

      <main className="dash-main">
        {section === 'profile' ? (
          <CabinetProfile user={user} onUpdate={setUser} />
        ) : section === 'abonement' ? (
          <CabinetAbonement sub={sub} buying={buying} onBuy={buyAbonement}
            onShowQR={() => { if (sub && sub.payment) setPayModal(sub.payment); }} />
        ) : (
          <>
            <div className="head-row">
              <div>
                <div className="crumbs">Личный кабинет</div>
                <h1>Здравствуйте, <em>{user.name.split(' ')[0]}.</em></h1>
                <p>У вас {upcoming} активных бронирований. Всего за всё время — {bookings.length}.</p>
              </div>
              <a href="#book" className="btn">Новая бронь →</a>
            </div>

            <div className="metrics">
              <div className="metric"><div className="lbl">Активные брони</div><div className="val">{upcoming}<em>шт.</em></div></div>
              <div className="metric"><div className="lbl">Всего за всё время</div><div className="val">{bookings.length}<em>шт.</em></div></div>
              <div className="metric"><div className="lbl">Итого оплачено</div><div className="val">{totalSpent.toLocaleString()}<em>₽</em></div></div>
              <div className="metric"><div className="lbl">Абонемент</div><div className="val">{sub ? (sub.balanceHours != null ? sub.balanceHours : sub.balance) : 0}<em>ч</em> {sub && sub.balanceDays > 0 ? <>· {sub.balanceDays}<em>дн</em></> : null}</div></div>
            </div>

            <div className="tabs">
              <button className={tab === 'active' ? 'active' : ''} onClick={() => setTab('active')}>Активные</button>
              <button className={tab === 'history' ? 'active' : ''} onClick={() => setTab('history')}>История</button>
              <button className={tab === 'cancelled' ? 'active' : ''} onClick={() => setTab('cancelled')}>Отменённые</button>
            </div>

            {filtered.length === 0 ? (
              <div className="empty">
                <div className="ico">∅</div>
                <h3>Здесь пока пусто.</h3>
                <p>{tab === 'active' ? 'Ещё нет активных бронирований.' : tab === 'history' ? 'История появится после первых сеансов.' : 'Отменённых бронирований нет.'}</p>
                <a href="#book" className="btn">Выбрать место →</a>
              </div>
            ) : (
              filtered.map(b => {
                const r = roomOf(b.roomId);
                return (
                  <div key={b.id} className="booking-card">
                    <span className="num">{r.num}</span>
                    <div className="info">
                      <b>{r.name}</b>
                      <span>{r.zoneRu} · {r.area} м²</span>
                    </div>
                    <div className="when">
                      <b>{new Date(b.date).toLocaleDateString('ru', {day: '2-digit', month: 'long'})}</b>
                      <span>{fmtHours(b)}</span>
                    </div>
                    <div className="price">
                      <b>{b.price.toLocaleString()} ₽</b>
                      {b.discountPct > 0 && b.originalPrice > b.price && (
                        <span style={{color: 'var(--gold)', fontWeight: 600, fontSize: 11, letterSpacing: 0.5}}>−{b.discountPct}% · было {b.originalPrice.toLocaleString()}₽</span>
                      )}
                      <span>{b.paid ? (b.paymentMethod === 'abonement' ? 'Оплачено абонементом' : 'Оплачено') : (b.status === 'pending' ? 'Ожидается подтверждение оплаты' : 'Ожидает оплаты')}</span>
                    </div>
                    <div style={{display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap'}}>
                      <span className={`status ${b.status === 'active' ? 'active' : b.status === 'pending' ? 'pending-pay' : ''}`}>{b.status === 'active' ? 'Активна' : b.status === 'pending' ? 'Ждёт подтверждения' : b.status === 'completed' ? 'Завершена' : 'Отменена'}</span>
                      {b.status === 'pending' && <button className="btn btn--ghost" style={{fontSize: 11, color: 'var(--gold-deep)'}} onClick={() => showBookingQR(b)}>QR·Реквизиты</button>}
                      {(b.status === 'active' || b.status === 'pending') && <button className="btn btn--ghost" style={{fontSize: 11, color: 'var(--red)'}} disabled={busyId === b.id} onClick={() => cancel(b.id)}>{busyId === b.id ? '…' : 'Отменить'}</button>}
                    </div>
                  </div>
                );
              })
            )}
          </>
        )}
      </main>

      {payModal && (
        <PaymentModal payment={payModal} onClose={() => setPayModal(null)} ctaHref="#/cabinet" ctaLabel="Я перевёл(а) — готово" />
      )}
    </div>
  );
};

// ============================================
// ADMIN
// ============================================
const EditorialAdmin = () => {
  const [user, setUser] = useState(undefined);
  const [section, setSection] = useState('dash');
  const [stats, setStats] = useState(null);
  const [clients, setClients] = useState([]);
  const [bookings, setBookings] = useState([]);
  const [payments, setPayments] = useState(null); // { pending, recent }
  const [refreshing, setRefreshing] = useState(false);

  useEffect(() => { document.title = "Админ-панель · Estetic Hall"; }, []);

  const reload = async () => {
    try {
      setRefreshing(true);
      const [s, c, b, p] = await Promise.all([API.adminStats(), API.adminClients(), API.allBookings(), API.adminPayments()]);
      setStats(s);
      setClients(c.clients);
      setBookings(b.bookings);
      setPayments(p);
    } catch (e) { /* следующий тик обновит */ }
    finally { setRefreshing(false); }
  };

  useEffect(() => {
    let active = true;
    (async () => {
      try {
        const me = (await API.me()).user;
        if (!me) { window.location.hash = '#/login'; return; }
        if (me.role !== 'admin') { window.location.hash = '#/cabinet'; return; }
        if (!active) return;
        await reload();
        if (active) setUser(me);
      } catch (e) {
        window.location.hash = '#/login';
      }
    })();
    // авто-обновление: при возврате на вкладку и каждые 30 сек
    const onFocus = () => { if (!document.hidden) reload(); };
    window.addEventListener('focus', onFocus);
    const interval = setInterval(() => { if (!document.hidden) reload(); }, 30000);
    return () => { active = false; window.removeEventListener('focus', onFocus); clearInterval(interval); };
  }, []);

  if (user === undefined) return <DashLoading label="Загрузка панели…" />;
  if (!user || user.role !== 'admin') return null;

  const logout = async () => { try { await API.logout(); } catch (e) {} window.location.hash = '#/'; };

  const handleCancel = async (id) => {
    await API.cancelBooking(id);
    await reload();
  };

  return (
    <div className="dash" data-screen-label="10 Admin">
      <aside className="dash-side">
        <a href="#/" className="logo" style={{marginBottom: 16}}>
          <span className="mark"><img src="assets/logo.png" alt="Estetic Hall" /></span>
          <span className="txt"><b>ESTETIC HALL</b><span>админ-панель</span></span>
        </a>
        <div className="who">
          <div className="avatar">A</div>
          <b>{user.name}</b>
          <span>Администратор</span>
        </div>
        <nav>
          <a className={section === 'dash' ? 'active' : ''} onClick={() => setSection('dash')}>Дашборд</a>
          <a className={section === 'payments' ? 'active' : ''} onClick={() => setSection('payments')}>
            Оплата{payments && payments.pending.length > 0 && <span className="nav-count">{payments.pending.length}</span>}
          </a>
          <a className={section === 'clients' ? 'active' : ''} onClick={() => setSection('clients')}>Клиенты</a>
          <a className={section === 'bookings' ? 'active' : ''} onClick={() => setSection('bookings')}>Бронирования</a>
          <a className={section === 'rooms' ? 'active' : ''} onClick={() => setSection('rooms')}>Места</a>
          <a className={section === 'images' ? 'active' : ''} onClick={() => setSection('images')}>Картинки</a>
          <a className={section === 'settings' ? 'active' : ''} onClick={() => setSection('settings')}>Настройки</a>
        </nav>
        <div className="bottom" onClick={logout}>← Выйти</div>
      </aside>

      <main className="dash-main">
        {section === 'dash' && <AdminDashboard stats={stats} bookings={bookings} user={user} onRefresh={reload} refreshing={refreshing} />}
        {section === 'payments' && <AdminPayments payments={payments} onChanged={reload} refreshing={refreshing} />}
        {section === 'clients' && <AdminClients clients={clients} bookings={bookings} onChanged={reload} />}
        {section === 'bookings' && <AdminBookings bookings={bookings} clients={clients} onCancel={handleCancel} onCreated={reload} />}
        {section === 'rooms' && <AdminRooms />}
        {section === 'images' && <AdminImages />}
        {section === 'settings' && <AdminSettings />}
      </main>
    </div>
  );
};

const AdminDashboard = ({ stats, bookings, user, onRefresh, refreshing }) => {
  if (!stats) return null;
  const max = Math.max(1, ...stats.revenueByDay);
  const hr = new Date().getHours();
  const greet = hr < 6 ? 'Доброй ночи' : hr < 12 ? 'Доброе утро' : hr < 18 ? 'Добрый день' : 'Добрый вечер';
  const recent = bookings.filter(b => b.status === 'active').slice(0, 6);

  // Карта забронированных на сегодня мест
  const todayISO = isoDay(new Date());
  const bookedRoomIds = Array.from(new Set(
    bookings.filter(b => b.status === 'active' && b.date === todayISO).map(b => b.roomId)
  ));
  const hallRooms = D2.rooms.filter(r => ['hair', 'makeup'].includes(r.zone));
  const nailsRooms = D2.rooms.filter(r => ['nails', 'pedicure'].includes(r.zone));

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Админ-панель</div>
          <h1><em>{greet},</em> {user.name.split(' ')[0]}.</h1>
          <p>Сводка по реальным данным базы. Обновляется автоматически при возврате на вкладку и каждые 30 секунд.</p>
        </div>
        <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
          <button className="btn btn--ghost" onClick={onRefresh} disabled={refreshing} title="Обновить сейчас" style={{ padding: '10px 16px', fontSize: 11 }}>{refreshing ? '…' : '↻ Обновить'}</button>
          <a href="#/" className="btn btn--outline">На сайт →</a>
        </div>
      </div>

      <div className="metrics">
        <div className="metric"><div className="lbl">Всего клиентов</div><div className="val">{stats.clients}</div></div>
        <div className="metric"><div className="lbl">Броней сегодня</div><div className="val">{stats.bookingsToday}<em>акт.</em></div></div>
        <div className="metric"><div className="lbl">Выручка месяца</div><div className="val">{(stats.monthRevenue/1000).toFixed(0)}<em>тыс ₽</em></div></div>
        <div className="metric"><div className="lbl">Занятость сегодня</div><div className="val">{stats.occupancy}<em>%</em></div></div>
      </div>

      <h3 style={{fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 28, margin: '40px 0 16px', letterSpacing: '-0.5px'}}>Забронированные <em style={{fontStyle: 'italic', color: 'var(--gold-deep)'}}>сегодня</em> места · {bookedRoomIds.length}</h3>
      <div className="admin-floor-maps">
        <div className="admin-floor-map">
          <div className="afm-label">Общий зал</div>
          <FloorPlan rooms={hallRooms} mode="hall" readonly bookedRoomIds={bookedRoomIds} />
        </div>
        <div className="admin-floor-map">
          <div className="afm-label">Маникюр и педикюр</div>
          <FloorPlan rooms={nailsRooms} mode="nails" readonly bookedRoomIds={bookedRoomIds} />
        </div>
      </div>
      <div className="afm-legend">
        <span><i style={{background: '#b56666'}}></i>занято сегодня</span>
        <span><i style={{background: '#5d8a72'}}></i>свободно</span>
      </div>

      <h3 style={{fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 28, margin: '40px 0 0', letterSpacing: '-0.5px'}}>Выручка за <em style={{fontStyle: 'italic', color: 'var(--gold-deep)'}}>последние 30 дней</em></h3>
      <div className="chart">
        {stats.revenueByDay.map((v, i) => (
          <div key={i} className="bar" style={{height: `${Math.max(2, (v/max)*100)}%`}} title={`${v.toLocaleString()} ₽`}></div>
        ))}
      </div>
      <div className="chart-row"><span>30 дней назад</span><span>сегодня</span></div>

      <h3 style={{fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 28, margin: '48px 0 16px', letterSpacing: '-0.5px'}}>Ближайшие бронирования</h3>
      {recent.length === 0 ? (
        <p style={{color: 'var(--mute)'}}>Активных бронирований пока нет.</p>
      ) : (
        <div className="tbl-wrap"><table className="tbl">
          <thead><tr><th>#</th><th>Клиент</th><th>Место</th><th>Дата</th><th>Время</th><th>Сумма</th><th>Оплата</th></tr></thead>
          <tbody>
            {recent.map((b, i) => {
              const r = D2.rooms.find(x => x.id === b.roomId) || {name: '—'};
              return (
                <tr key={b.id}>
                  <td className="num">{String(i+1).padStart(2,'0')}</td>
                  <td><b>{b.clientName}</b><br/><span style={{fontSize:12, color: 'var(--mute)'}}>{b.spec}</span></td>
                  <td>{r.name}</td>
                  <td>{new Date(b.date).toLocaleDateString('ru')}</td>
                  <td>{fmtHours(b)}</td>
                  <td><b>{b.price.toLocaleString()} ₽</b></td>
                  <td><span className={`badge ${b.paid ? 'active' : 'pending'}`}>{b.paid ? 'Оплачено' : 'Ожидает'}</span></td>
                </tr>
              );
            })}
          </tbody>
        </table></div>
      )}
    </>
  );
};

// ─────── Раздел «Оплата»: переводы по QR, ожидающие проверки админом ───────
const AdminPayments = ({ payments, onChanged, refreshing }) => {
  const [busyKey, setBusyKey] = useState(null); // `${kind}:${id}`
  const pending = (payments && payments.pending) || [];
  const recent = (payments && payments.recent) || [];

  const decide = async (p, action) => {
    if (action === 'reject' && !window.confirm(`Отклонить оплату «${p.purpose}» на ${p.amount.toLocaleString()} ₽?\nБронь/абонемент будут отменены.`)) return;
    const key = `${p.kind}:${p.id}`;
    setBusyKey(key);
    try {
      await API.adminPaymentDecide({ kind: p.kind, id: p.id, action });
      if (onChanged) await onChanged();
    } catch (e) { alert(e.message || 'Не удалось сохранить решение'); }
    finally { setBusyKey(null); }
  };

  const decisionLabel = (d) => d === 'confirmed' ? 'Подтверждена' : d === 'rejected' ? 'Отклонена' : 'Просрочена';

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Касса</div>
          <h1><em>Оплата</em> переводом{pending.length > 0 ? ` · ${pending.length}` : ''}</h1>
          <p>Клиенты переводят деньги по QR на реквизиты. Проверьте поступление в банке и подтвердите — бронь/абонемент активируются. Список обновляется автоматически.</p>
        </div>
        {refreshing && <span style={{color: 'var(--mute)', fontSize: 12}}>Обновление…</span>}
      </div>

      {pending.length === 0 ? (
        <div className="empty">
          <div className="ico">✓</div>
          <h3>Нет ожидающих оплат.</h3>
          <p>Как только клиент оформит бронь или абонемент с оплатой переводом, заявка появится здесь.</p>
        </div>
      ) : (
        <div className="pay-cards">
          {pending.map(p => {
            const key = `${p.kind}:${p.id}`;
            const busy = busyKey === key;
            return (
              <div key={key} className="pay-card">
                <div className="pay-card-main">
                  <div className="pay-kind">{p.kind === 'booking' ? 'Бронь места' : 'Абонемент'} · №{p.id}</div>
                  <div className="pay-client">
                    <b>{p.clientName || '—'}</b>
                    {p.phone && <a href={`tel:${p.phone}`}>{p.phone}</a>}
                  </div>
                  <div className="pay-details">{p.details}</div>
                  <div className="pay-purpose">Назначение: «{p.purpose}»</div>
                  <div className="pay-created">Оформлено: {p.createdAt}</div>
                </div>
                <div className="pay-card-side">
                  <div className="pay-amount">{p.amount.toLocaleString()} <span>₽</span></div>
                  <div className="pay-actions">
                    <button className="btn btn--gold" disabled={busy} onClick={() => decide(p, 'confirm')}>{busy ? '…' : '✓ Подтвердить'}</button>
                    <button className="btn btn--outline pay-reject" disabled={busy} onClick={() => decide(p, 'reject')}>Отказать</button>
                  </div>
                </div>
              </div>
            );
          })}
        </div>
      )}

      <h3 style={{fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 26, margin: '44px 0 14px', letterSpacing: '-0.5px'}}>История решений</h3>
      {recent.length === 0 ? (
        <p style={{color: 'var(--mute)'}}>Здесь появятся подтверждённые и отклонённые оплаты.</p>
      ) : (
        <div className="tbl-wrap"><table className="tbl">
          <thead><tr><th>Тип</th><th>Клиент</th><th>Детали</th><th>Сумма</th><th>Решение</th><th>Когда</th></tr></thead>
          <tbody>
            {recent.map(p => (
              <tr key={`${p.kind}:${p.id}`}>
                <td>{p.kind === 'booking' ? `Бронь №${p.id}` : `Абонемент №${p.id}`}</td>
                <td><b>{p.clientName || '—'}</b></td>
                <td>{p.details}</td>
                <td><b>{p.amount.toLocaleString()} ₽</b></td>
                <td><span className={`badge ${p.decision === 'confirmed' ? 'active' : 'pending'}`}>{decisionLabel(p.decision)}</span></td>
                <td style={{color: 'var(--mute)'}}>{p.decidedAt || '—'}</td>
              </tr>
            ))}
          </tbody>
        </table></div>
      )}
    </>
  );
};

const AdminClients = ({ clients, bookings, onChanged }) => {
  const [search, setSearch] = useState('');
  const [spec, setSpec] = useState('');
  const [selected, setSelected] = useState(null);
  const [grantKind, setGrantKind] = useState('hours');  // 'hours' | 'days'
  const [grantUnits, setGrantUnits] = useState(D2.brand.abonementHours || 30);
  const [grantPrice, setGrantPrice] = useState(0);      // 0 = бесплатно; >0 = оплачено наличными
  const [revokeKind, setRevokeKind] = useState('hours');
  const [revokeUnits, setRevokeUnits] = useState('');
  const [granting, setGranting] = useState(false);
  const [revoking, setRevoking] = useState(false);
  const [subMsg, setSubMsg] = useState(null);

  const flashSub = (t) => { setSubMsg(t); setTimeout(() => setSubMsg(null), 4000); };
  const unitLbl = (k) => k === 'days' ? 'дн.' : 'ч';
  // При смене вида — подставляем разумное количество по умолчанию
  const pickGrantKind = (k) => { setGrantKind(k); setGrantUnits(k === 'days' ? 5 : (D2.brand.abonementHours || 30)); };

  const grant = async () => {
    if (!selected) return;
    const n = Math.max(1, parseInt(grantUnits, 10) || 0);
    const p = Math.max(0, parseInt(grantPrice, 10) || 0);
    setGranting(true);
    try {
      const r = await API.adminSubAction({ userId: selected.id, action: 'grant', kind: grantKind, units: n, price: p });
      setSelected(s => s ? { ...s, subHours: r.balanceHours, subDays: r.balanceDays } : s);
      flashSub((p > 0 ? `Выдано ${n} ${unitLbl(grantKind)} · наличными ${p.toLocaleString()} ₽` : `Выдано ${n} ${unitLbl(grantKind)} (бесплатно)`));
      if (onChanged) onChanged();
    } catch (e) { alert(e.message || 'Не удалось выдать абонемент'); }
    finally { setGranting(false); }
  };

  const revoke = async () => {
    if (!selected) return;
    const n = Math.max(1, parseInt(revokeUnits, 10) || 0);
    const bal = revokeKind === 'days' ? (selected.subDays || 0) : (selected.subHours || 0);
    if (n > bal) { alert(`У клиента на балансе ${bal} ${unitLbl(revokeKind)} — забрать ${n} ${unitLbl(revokeKind)} нельзя.`); return; }
    if (!window.confirm(`Забрать у клиента «${selected.name}» ${n} ${unitLbl(revokeKind)} с абонемента?`)) return;
    setRevoking(true);
    try {
      const r = await API.adminSubAction({ userId: selected.id, action: 'revoke', kind: revokeKind, units: n });
      setSelected(s => s ? { ...s, subHours: r.balanceHours, subDays: r.balanceDays } : s);
      setRevokeUnits('');
      flashSub(`Списано ${n} ${unitLbl(revokeKind)} · остаток ${revokeKind === 'days' ? r.balanceDays : r.balanceHours} ${unitLbl(revokeKind)}`);
      if (onChanged) onChanged();
    } catch (e) { alert(e.message || 'Не удалось забрать'); }
    finally { setRevoking(false); }
  };

  const filtered = clients.filter(c =>
    (search ? (c.name.toLowerCase().includes(search.toLowerCase()) || (c.email || '').includes(search) || (c.phone || '').includes(search)) : true) &&
    (spec ? c.spec === spec : true)
  );
  const clientBookings = selected ? bookings.filter(b => b.userId === selected.id) : [];

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">База данных</div>
          <h1><em>Клиенты</em> · {clients.length}</h1>
          <p>Реальная база зарегистрированных мастеров из БД.</p>
        </div>
        {clients.length > 0 && <button className="btn btn--outline" onClick={() => exportClientsCSV(filtered)}>Экспорт CSV →</button>}
      </div>

      {clients.length === 0 ? (
        <div className="empty">
          <div className="ico">∅</div>
          <h3>Пока нет мастеров.</h3>
          <p>Здесь появятся пользователи, как только зарегистрируются на сайте.</p>
        </div>
      ) : (
        <>
          <div className="search-bar">
            <input placeholder="Поиск по имени, email, телефону…" value={search} onChange={e => setSearch(e.target.value)} />
            <select value={spec} onChange={e => setSpec(e.target.value)}>
              <option value="">Все специализации</option>
              <option>Парикмахер</option>
              <option>Мастер маникюра / педикюра</option>
              <option>Визажист</option>
              <option>Косметолог</option>
              <option>Лешмейкер</option>
            </select>
          </div>

          {filtered.length === 0 ? (
            <p style={{color: 'var(--mute)'}}>По фильтру ничего не найдено.</p>
          ) : (
            <div className="tbl-wrap"><table className="tbl">
              <thead><tr><th>#</th><th>Имя</th><th>Телефон</th><th>Email</th><th>Специализация</th><th>Регистрация</th><th>Броней</th><th>Сумма</th></tr></thead>
              <tbody>
                {filtered.map((c, i) => (
                  <tr key={c.id} style={{cursor: 'pointer'}} onClick={() => setSelected(c)}>
                    <td className="num">{String(i+1).padStart(2,'0')}</td>
                    <td><b>{c.name}</b></td>
                    <td>{c.phone}</td>
                    <td>{c.email}</td>
                    <td>{c.spec}</td>
                    <td style={{color: 'var(--mute)'}}>{new Date(c.createdAt).toLocaleDateString('ru')}</td>
                    <td><b>{c.bookingsCount}</b></td>
                    <td><b>{c.totalSpent.toLocaleString()} ₽</b></td>
                  </tr>
                ))}
              </tbody>
            </table></div>
          )}
        </>
      )}

      {selected && (
        <>
          <div className="drawer-overlay" onClick={() => setSelected(null)}></div>
          <aside className="admin-drawer">
            <button className="ad-close" onClick={() => setSelected(null)} aria-label="Закрыть">✕</button>
            <div className="ad-avatar">{selected.name.trim().charAt(0)}</div>
            <h3>{selected.name}</h3>
            <div className="ad-spec">{selected.spec}</div>
            <div className="ad-contacts">
              <div><span>Телефон</span><b>{selected.phone}</b></div>
              <div><span>Email</span><b>{selected.email}</b></div>
              <div><span>Регистрация</span><b>{new Date(selected.createdAt).toLocaleDateString('ru')}</b></div>
              <div><span>Всего оплачено</span><b>{selected.totalSpent.toLocaleString()} ₽</b></div>
              <div><span>Абонемент · остаток</span><b>{(selected.subHours || 0)} ч{(selected.subDays || 0) > 0 ? ` · ${selected.subDays} дн.` : ''}</b></div>
            </div>

            <h4>Выдать абонемент</h4>
            {subMsg && <div className="save-ok" style={{marginBottom: 10}}>✓ {subMsg}</div>}
            <div className="ad-grant ad-grant--col">
              <div className="ad-grant-row">
                <label>Вид</label>
                <select value={grantKind} onChange={e => pickGrantKind(e.target.value)} aria-label="Вид абонемента">
                  <option value="hours">Часы</option>
                  <option value="days">Дни (целые дни)</option>
                </select>
              </div>
              <div className="ad-grant-row">
                <label>{grantKind === 'days' ? 'Дней' : 'Часов'}</label>
                <input type="number" min="1" value={grantUnits} onChange={e => setGrantUnits(e.target.value)} aria-label="Количество" />
              </div>
              <div className="ad-grant-row">
                <label>Оплата наличными, ₽</label>
                <input type="number" min="0" step="100" value={grantPrice} onChange={e => setGrantPrice(e.target.value)} aria-label="Сумма наличными" placeholder="0" />
              </div>
              <p className="ad-grant-hint">{grantKind === 'days'
                ? '1 день = бронь места на весь рабочий день. Если клиент оплатил наличными — укажите сумму, она попадёт в выручку. 0 — бесплатная выдача.'
                : 'Если клиент оплатил наличными — укажите сумму, она попадёт в выручку. 0 — бесплатная выдача (бонус).'}</p>
              <button className="btn btn--gold" onClick={grant} disabled={granting}>{granting ? 'Выдаём…' : 'Выдать →'}</button>
            </div>

            <h4>Забрать часы или дни (исправить ошибку)</h4>
            <div className="ad-grant">
              <select value={revokeKind} onChange={e => { setRevokeKind(e.target.value); setRevokeUnits(''); }} aria-label="Вид для списания">
                <option value="hours">Часы</option>
                <option value="days">Дни</option>
              </select>
              <input type="number" min="1" max={revokeKind === 'days' ? (selected.subDays || 0) : (selected.subHours || 0)} value={revokeUnits}
                onChange={e => setRevokeUnits(e.target.value)} aria-label="Сколько забрать" placeholder="0" />
              <span>{unitLbl(revokeKind)}</span>
              <button className="btn btn--outline ad-revoke" onClick={revoke}
                disabled={revoking || !((revokeKind === 'days' ? selected.subDays : selected.subHours) > 0)}>
                {revoking ? 'Списываем…' : 'Забрать ←'}
              </button>
            </div>
            {!((revokeKind === 'days' ? selected.subDays : selected.subHours) > 0) && <p className="ad-grant-hint" style={{marginTop: 6}}>На балансе клиента нет {revokeKind === 'days' ? 'дней' : 'часов'}.</p>}

            <h4>Бронирования · {clientBookings.length}</h4>
            {clientBookings.length === 0 ? (
              <p style={{color: 'var(--mute)', fontSize: 14}}>Бронирований пока нет.</p>
            ) : (
              <div className="ad-bookings">
                {clientBookings.map(b => {
                  const r = D2.rooms.find(x => x.id === b.roomId) || { name: '—' };
                  return (
                    <div key={b.id} className="ad-book">
                      <div>
                        <b>{r.name}</b>
                        <span>{new Date(b.date).toLocaleDateString('ru')} · {fmtHours(b)}</span>
                      </div>
                      <div className="ad-book-right">
                        <b>{b.price.toLocaleString()} ₽</b>
                        <span className={`badge ${b.status === 'active' ? 'active' : b.status === 'completed' ? 'completed' : 'pending'}`}>{b.status === 'active' ? 'Активна' : b.status === 'pending' ? 'Ждёт оплаты' : b.status === 'completed' ? 'Завершена' : 'Отменена'}</span>
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </aside>
        </>
      )}
    </>
  );
};

function exportClientsCSV(clients) {
  const head = ['Имя', 'Телефон', 'Email', 'Специализация', 'Регистрация', 'Броней', 'Сумма'];
  const rows = clients.map(c => [c.name, c.phone, c.email, c.spec, c.createdAt, c.bookingsCount, c.totalSpent]);
  const csv = [head, ...rows].map(r => r.map(x => `"${String(x).replace(/"/g, '""')}"`).join(';')).join('\n');
  const blob = new Blob(['﻿' + csv], { type: 'text/csv;charset=utf-8' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'estetic-hall-clients.csv';
  a.click();
}

// ─────── Drawer: ручное создание брони админом ───────
const AdminBookingForm = ({ clients, onClose, onCreated }) => {
  const today = isoDay(new Date());
  const maxDate = isoDay(new Date(Date.now() + 90 * 24 * 60 * 60 * 1000)); // +90 дней
  const [mode, setMode] = useState('existing');           // 'existing' | 'new'
  const [kind, setKind] = useState('hour');                // 'hour' | 'day' | 'month'
  const [userId, setUserId] = useState('');
  const [clientName, setClientName] = useState('');
  const [spec, setSpec] = useState('');
  const [query, setQuery] = useState('');
  const [roomId, setRoomId] = useState(D2.rooms[0] ? D2.rooms[0].id : '');
  const [date, setDate] = useState(today);
  const [slots, setSlots] = useState([]);
  const [busy, setBusy] = useState({});
  const [busyDates, setBusyDates] = useState(null);        // занятые даты (для дня/месяца); null = загрузка
  const [busyLoading, setBusyLoading] = useState(false);
  const [payAbonement, setPayAbonement] = useState(false); // списать с абонемента клиента
  const [submitting, setSubmitting] = useState(false);
  const [err, setErr] = useState(null);
  const [nowH, setNowH] = useState(new Date().getHours());

  const openHour  = parseInt(String(D2.brand.open || '09:00').slice(0, 2), 10);
  const closeHour = parseInt(String(D2.brand.close || '21:00').slice(0, 2), 10);
  const hoursList = [];
  for (let h = openHour; h < closeHour; h++) hoursList.push(h);

  // Загружает занятость для текущего room+date (+30 дней в режиме «месяц»)
  const reloadBusy = React.useCallback(async () => {
    if (!roomId || !date) return null;
    try {
      const r = await API.availability(roomId, date, kind === 'month' ? 30 : 1);
      const b = {}; (r.busy || []).forEach(h => { b[h] = true; });
      setBusy(b);
      setBusyDates(kind === 'month' ? (r.busyDates || []) : ((r.busy || []).length ? [date] : []));
      setSlots(curr => curr.filter(h => !b[h]));
      return b;
    } catch (e) {
      setBusy({});
      setBusyDates([]);
      return null;
    }
  }, [roomId, date, kind]);

  // Подтягиваем занятость при смене места/даты
  useEffect(() => {
    let active = true;
    setBusyLoading(true);
    reloadBusy().finally(() => { if (active) setBusyLoading(false); });
    return () => { active = false; };
  }, [reloadBusy]);

  // Авто-обновление: каждые 20 сек и при возврате фокуса — на случай если кто-то параллельно бронирует
  useEffect(() => {
    const onFocus = () => { if (!document.hidden) reloadBusy(); };
    window.addEventListener('focus', onFocus);
    const interval = setInterval(() => { if (!document.hidden) reloadBusy(); }, 20000);
    // Также обновляем текущий час раз в минуту (для disabled past-hours)
    const hourTick = setInterval(() => setNowH(new Date().getHours()), 60 * 1000);
    return () => {
      window.removeEventListener('focus', onFocus);
      clearInterval(interval);
      clearInterval(hourTick);
    };
  }, [reloadBusy]);

  const room = D2.rooms.find(r => r.id === Number(roomId));
  const pricePerHour = (room && room.prices && room.prices.hour) || 0;
  const total = kind === 'hour' ? slots.length * pricePerHour
    : kind === 'day' ? ((room && room.prices && room.prices.day) || 0)
    : ((room && room.prices && room.prices.month) || 0);

  const isToday = date === today;
  const isPastHour = (h) => isToday && h <= nowH;
  const slotState = (h) => {
    if (busy[h]) return 'busy';        // уже занято — серое, перечёркнутое
    if (isPastHour(h)) return 'past';  // час уже прошёл сегодня — серое
    return 'free';
  };

  const toggleSlot = (h) => {
    if (slotState(h) !== 'free') return;
    setSlots(s => s.includes(h) ? s.filter(x => x !== h) : [...s, h].sort((a, b) => a - b));
  };

  const filteredClients = (clients || []).filter(c => {
    if (!query.trim()) return true;
    const q = query.trim().toLowerCase();
    return (String(c.name || '').toLowerCase().includes(q))
        || (String(c.phone || '').toLowerCase().includes(q))
        || (String(c.email || '').toLowerCase().includes(q));
  });

  const pickClient = (c) => {
    setUserId(c.id);
    setClientName(c.name || '');
    setSpec(c.spec || '');
  };

  // Доступность даты в режимах «день»/«месяц» (busyDates = null пока грузится)
  const rangeFree = busyDates !== null && busyDates.length === 0;
  // Абонемент клиента: часовой для часовых броней, дневной для дневных
  const selClient = mode === 'existing' && userId ? (clients || []).find(c => c.id === Number(userId)) : null;
  const abBalance = selClient ? (kind === 'hour' ? (selClient.subHours || 0) : (selClient.subDays || 0)) : 0;
  const abZoneOk = room && EH_ABONEMENT_ZONES.includes(room.zone);
  const abVisible = !!selClient && kind !== 'month' && abZoneOk && abBalance > 0;
  const abNeed = kind === 'hour' ? slots.length : 1;
  const abEnough = abBalance >= abNeed;

  const submit = async () => {
    setErr(null);
    let cleanedSlots = [];
    if (kind === 'hour') {
      // Локальная проверка: убираем часы которые стали недоступны
      cleanedSlots = slots.filter(h => slotState(h) === 'free');
      if (cleanedSlots.length !== slots.length) {
        setSlots(cleanedSlots);
        setErr('Некоторые часы стали недоступны — обновите выбор.');
        return;
      }
      if (!cleanedSlots.length) { setErr('Выберите хотя бы один час'); return; }
    } else if (!rangeFree) {
      setErr(kind === 'day' ? 'На эту дату уже есть брони — выберите другую.' : 'В выбранном месяце есть занятые дни — выберите другую дату начала.');
      return;
    }
    if (mode === 'existing' && !userId) { setErr('Выберите клиента из списка'); return; }
    if (mode === 'new' && !clientName.trim()) { setErr('Введите имя клиента'); return; }
    setSubmitting(true);
    try {
      const body = { roomId: Number(roomId), kind, date, slots: cleanedSlots, spec: spec.trim() };
      if (mode === 'existing') body.userId = Number(userId);
      else body.clientName = clientName.trim();
      if (payAbonement && abVisible && abEnough) body.payWith = 'abonement';
      const r = await API.adminCreateBooking(body);
      onCreated && onCreated(r.booking);
      onClose && onClose();
    } catch (ex) {
      // На 409 — конфликт: кто-то параллельно забронировал. Подтянем свежую занятость.
      if (ex.status === 409) {
        await reloadBusy();
        setErr('Время уже занято — кто-то опередил. Освежили занятость, выберите другое время.');
      } else {
        setErr(ex.message || 'Не удалось создать бронь');
      }
    } finally {
      setSubmitting(false);
    }
  };

  const tabBtn = (active) => ({
    flex: 1, padding: '11px 14px', fontSize: 11, letterSpacing: '1.5px', textTransform: 'uppercase',
    border: '1px solid var(--ink)',
    background: active ? 'var(--ink)' : 'transparent',
    color: active ? 'var(--paper)' : 'var(--ink)',
    cursor: 'pointer', borderRadius: 0, transition: 'all 0.18s',
  });

  return (
    <>
      <div className="drawer-overlay" onClick={onClose}></div>
      <aside className="admin-drawer" role="dialog" aria-modal="true">
        <button className="ad-close" onClick={onClose} aria-label="Закрыть">×</button>
        <h3 style={{fontStyle: 'italic', marginBottom: 6}}>Новая бронь</h3>
        <p style={{fontSize: 13, color: 'var(--mute)', margin: '0 0 22px'}}>Создайте бронь от лица клиента — например, если он пришёл на ресепшн.</p>

        <div style={{display: 'flex', gap: 8, marginBottom: 22}}>
          <button type="button" style={tabBtn(mode === 'existing')} onClick={() => setMode('existing')}>Из списка</button>
          <button type="button" style={tabBtn(mode === 'new')} onClick={() => setMode('new')}>Walk-in (без аккаунта)</button>
        </div>

        {mode === 'existing' ? (
          <>
            <div className="field">
              <label>Поиск клиента</label>
              <input type="text" placeholder="Имя, телефон или email…" value={query} onChange={e => setQuery(e.target.value)} />
            </div>
            <div style={{maxHeight: 220, overflowY: 'auto', border: '1px solid var(--line)', marginBottom: 18}}>
              {filteredClients.length === 0
                ? <div style={{padding: 14, color: 'var(--mute)', fontSize: 13}}>Никого не нашли. Переключитесь в Walk-in.</div>
                : filteredClients.map(c => (
                  <div key={c.id} onClick={() => pickClient(c)}
                       style={{
                         padding: '10px 14px', cursor: 'pointer',
                         borderBottom: '1px solid var(--line-soft)',
                         background: Number(userId) === c.id ? 'rgba(6,106,171,0.10)' : 'transparent',
                         fontSize: 13,
                       }}>
                    <b>{c.name}</b> <span style={{color: 'var(--mute)', fontSize: 11}}>· {c.spec || '—'}</span>
                    <div style={{color: 'var(--mute)', fontSize: 11, marginTop: 2}}>{c.phone || '—'}</div>
                  </div>
                ))
              }
            </div>
          </>
        ) : (
          <>
            <div className="field"><label>Имя клиента *</label>
              <input type="text" placeholder="Анна Иванова" value={clientName} onChange={e => setClientName(e.target.value)} />
            </div>
            <div className="field"><label>Специализация</label>
              <input type="text" placeholder="Парикмахер · 5 лет (необязательно)" value={spec} onChange={e => setSpec(e.target.value)} />
            </div>
          </>
        )}

        <div className="field">
          <label>Рабочее место</label>
          <select value={roomId} onChange={e => setRoomId(e.target.value)}>
            {D2.rooms.map(r => (
              <option key={r.id} value={r.id}>{r.name} · {r.zoneRu} · {(r.prices && r.prices.hour) || 0}₽/ч</option>
            ))}
          </select>
        </div>

        <div className="field">
          <label>Формат брони</label>
          <div style={{display: 'flex', gap: 8}}>
            {[['hour', 'По часам'], ['day', 'На день'], ['month', 'На месяц']].map(([k, lbl]) => (
              <button key={k} type="button" style={tabBtn(kind === k)} onClick={() => { setKind(k); setErr(null); setPayAbonement(false); }}>{lbl}</button>
            ))}
          </div>
        </div>

        <div className="field">
          <label>{kind === 'month' ? 'Дата начала (бронь на 30 дней)' : 'Дата'}</label>
          <input type="date" value={date} min={today} max={maxDate} onChange={e => setDate(e.target.value)} />
          <div style={{fontSize: 11, color: 'var(--mute)', marginTop: 4}}>Можно бронировать на ближайшие 90 дней</div>
        </div>

        {kind === 'hour' ? (
        <div className="field">
          <label>
            Часы {busyLoading && <span style={{color: 'var(--mute)', fontSize: 11}}>· обновляем занятость…</span>}
          </label>
          <div className="cal-slots" style={{gridTemplateColumns: 'repeat(7, 1fr)'}}>
            {hoursList.map(h => {
              const state = slotState(h);            // 'free' | 'busy' | 'past'
              const isSelected = slots.includes(h);
              const title = state === 'busy' ? 'Уже забронировано'
                          : state === 'past' ? 'Этот час уже прошёл сегодня' : '';
              return (
                <button key={h} type="button" onClick={() => toggleSlot(h)}
                  disabled={state !== 'free'}
                  title={title}
                  className={`cal-slot ${state === 'busy' || state === 'past' ? 'busy' : ''} ${isSelected ? 'selected' : ''}`}>
                  {String(h).padStart(2, '0')}
                </button>
              );
            })}
          </div>
          <div style={{display: 'flex', gap: 14, fontSize: 11, color: 'var(--mute)', marginTop: 8}}>
            <span><span style={{display: 'inline-block', width: 8, height: 8, background: 'var(--gold)', verticalAlign: 'middle', marginRight: 4}}></span>выбрано</span>
            <span><span style={{display: 'inline-block', width: 8, height: 8, background: 'rgba(15,20,25,0.05)', border: '1px solid var(--line)', verticalAlign: 'middle', marginRight: 4}}></span>занято / прошло</span>
          </div>
        </div>
        ) : (
        <div className="field">
          <label>Занятость {busyLoading && <span style={{color: 'var(--mute)', fontSize: 11}}>· проверяем…</span>}</label>
          {busyDates === null ? (
            <div style={{padding: '12px 14px', fontSize: 13, color: 'var(--mute)', border: '1px dashed var(--line)'}}>Проверяем занятость…</div>
          ) : rangeFree ? (
            <div style={{padding: '12px 14px', fontSize: 13, background: 'rgba(46,125,50,0.08)', color: '#2e7d32'}}>
              ✓ {kind === 'day' ? 'День свободен — бронь займёт весь рабочий день' : 'Все 30 дней свободны'}
            </div>
          ) : (
            <div style={{padding: '12px 14px', fontSize: 13, background: 'rgba(198,40,40,0.07)', color: 'var(--red)'}}>
              {kind === 'day' ? 'На эту дату уже есть брони — выберите другую.' : `Заняты дни: ${busyDates.slice(0, 4).map(fmtRuDate).join(', ')}${busyDates.length > 4 ? '…' : ''}`}
            </div>
          )}
        </div>
        )}

        {abVisible && (
          <label style={{display: 'flex', gap: 9, alignItems: 'flex-start', fontSize: 13, color: 'var(--ink-soft)', margin: '4px 0 8px', cursor: 'pointer'}}>
            <input type="checkbox" checked={payAbonement && abEnough} disabled={!abEnough}
              onChange={e => setPayAbonement(e.target.checked)} style={{width: 16, height: 16, accentColor: 'var(--gold)', flexShrink: 0, marginTop: 1}} />
            <span>Списать с абонемента клиента · осталось {abBalance} {kind === 'hour' ? 'ч' : 'дн.'}
              {!abEnough && <em style={{color: 'var(--red)', fontStyle: 'normal'}}> (не хватает)</em>}</span>
          </label>
        )}

        {(kind !== 'hour' || slots.length > 0) && (
          <div style={{padding: '14px 16px', background: 'var(--bg-2)', marginTop: 6, fontSize: 14}}>
            {payAbonement && abVisible && abEnough ? (
              <>Оплата: <b>абонемент · {kind === 'hour' ? `${slots.length} ч` : '1 день'}</b></>
            ) : kind === 'hour' ? (
              <>Итого: <b>{slots.length} ч × {pricePerHour.toLocaleString()} ₽ = {total.toLocaleString()} ₽</b></>
            ) : (
              <>Итого: <b>{kind === 'day' ? 'день' : '30 дней'} · {total.toLocaleString()} ₽</b></>
            )}
          </div>
        )}

        {err && <div style={{marginTop: 14, color: 'var(--red)', fontSize: 13}}>{err}</div>}

        <div className="profile-actions" style={{marginTop: 24, display: 'flex', gap: 10}}>
          <button className="btn btn--gold" disabled={submitting || (kind === 'hour' ? !slots.length : !rangeFree)} onClick={submit}>
            {submitting ? 'Создаём…' : 'Создать бронь →'}
          </button>
          <button className="btn btn--ghost" onClick={onClose}>Отмена</button>
        </div>
      </aside>
    </>
  );
};

const AdminBookings = ({ bookings, clients, onCancel, onCreated }) => {
  const [showNew, setShowNew] = useState(false);
  const [filter, setFilter] = useState('all');
  const [roomFilter, setRoomFilter] = useState('');
  const [busyId, setBusyId] = useState(null);
  const filtered = bookings.filter(b =>
    (filter === 'all' ? true : b.status === filter) &&
    (roomFilter ? b.roomId === Number(roomFilter) : true)
  );

  const cancel = async (id) => {
    if (!window.confirm('Отменить эту бронь?')) return;
    setBusyId(id);
    try { await onCancel(id); } catch (e) { alert(e.message || 'Не удалось отменить'); }
    finally { setBusyId(null); }
  };

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Управление</div>
          <h1><em>Бронирования</em> · {bookings.length}</h1>
          <p>Все брони из базы — текущие, завершённые, отменённые.</p>
        </div>
        <button className="btn btn--gold" onClick={() => setShowNew(true)} style={{flexShrink: 0}}>+ Новая бронь</button>
      </div>

      {bookings.length === 0 ? (
        <div className="empty">
          <div className="ico">∅</div>
          <h3>Бронирований пока нет.</h3>
          <p>Появятся здесь, как только мастера начнут бронировать места.</p>
        </div>
      ) : (
        <>
          <div className="search-bar">
            <select value={filter} onChange={e => setFilter(e.target.value)}>
              <option value="all">Все статусы</option>
              <option value="active">Активные</option>
              <option value="pending">Ждут подтверждения оплаты</option>
              <option value="completed">Завершённые</option>
              <option value="cancelled">Отменённые</option>
            </select>
            <select value={roomFilter} onChange={e => setRoomFilter(e.target.value)}>
              <option value="">Все места</option>
              {D2.rooms.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
            </select>
          </div>

          {filtered.length === 0 ? (
            <p style={{color: 'var(--mute)'}}>По фильтру ничего не найдено.</p>
          ) : (
            <div className="tbl-wrap"><table className="tbl">
              <thead><tr><th>#</th><th>Клиент</th><th>Место</th><th>Дата</th><th>Время</th><th>Сумма</th><th>Оплата</th><th>Статус</th><th></th></tr></thead>
              <tbody>
                {filtered.map((b, i) => {
                  const r = D2.rooms.find(x => x.id === b.roomId) || {name: '—', zoneRu: ''};
                  return (
                    <tr key={b.id}>
                      <td className="num">{String(i+1).padStart(2,'0')}</td>
                      <td><b>{b.clientName}</b><br/><span style={{fontSize:11, color: 'var(--mute)'}}>{b.spec}</span></td>
                      <td>{r.name} · {r.zoneRu}</td>
                      <td>{new Date(b.date).toLocaleDateString('ru')}</td>
                      <td>{fmtHours(b)}</td>
                      <td><b>{b.price.toLocaleString()} ₽</b></td>
                      <td><span className={`badge ${b.paid ? 'active' : 'pending'}`}>{b.paid ? 'Оплачено' : (b.status === 'pending' ? 'Ждёт перевода' : 'Ожидает')}</span></td>
                      <td><span className={`badge ${b.status === 'active' ? 'active' : b.status === 'completed' ? 'completed' : 'pending'}`}>{b.status === 'active' ? 'Активна' : b.status === 'pending' ? 'Не подтверждена' : b.status === 'completed' ? 'Завершена' : 'Отменена'}</span></td>
                      <td>{(b.status === 'active' || b.status === 'pending') && <button className="btn btn--ghost" style={{fontSize: 11, padding: '6px 10px', color: 'var(--red)'}} disabled={busyId === b.id} onClick={() => cancel(b.id)}>{busyId === b.id ? '…' : 'Отменить'}</button>}</td>
                    </tr>
                  );
                })}
              </tbody>
            </table></div>
          )}
        </>
      )}

      {showNew && (
        <AdminBookingForm
          clients={clients || []}
          onClose={() => setShowNew(false)}
          onCreated={() => { setShowNew(false); onCreated && onCreated(); }}
        />
      )}
    </>
  );
};

const AdminRooms = () => {
  const [edit, setEdit] = useState(null); // черновик редактируемого кабинета
  const [, setTick] = useState(0);
  const [saving, setSaving] = useState(false);
  const [uploading, setUploading] = useState(false);
  const [msg, setMsg] = useState(null);
  const [applyZone, setApplyZone] = useState(true);
  const rooms = D2.rooms; // живые данные (из БД после загрузки)

  const openEdit = (r) => { setApplyZone(true); setEdit(JSON.parse(JSON.stringify(r))); };
  const close = () => setEdit(null);
  const setField = (k, v) => setEdit(e => ({ ...e, [k]: v }));
  const setPrice = (k, v) => setEdit(e => ({ ...e, prices: { ...e.prices, [k]: v } }));

  const addPhoto = async (file) => {
    if (!file) return;
    setUploading(true);
    try {
      const { url } = await API.uploadPhoto(file);
      setEdit(e => ({ ...e, photos: [...(e.photos || []), url] }));
    } catch (ex) { alert(ex.message || 'Не удалось загрузить фото'); }
    finally { setUploading(false); }
  };
  const removePhoto = (i) => setEdit(e => ({ ...e, photos: e.photos.filter((_, idx) => idx !== i) }));

  const save = async () => {
    setSaving(true);
    const num = edit.num;
    try {
      await API.updateRoom({
        id: edit.id, name: edit.name, area: Number(edit.area) || 0, desc: edit.desc, status: edit.status,
        prices: {
          hour: +edit.prices.hour, halfDay: +edit.prices.halfDay, day: +edit.prices.day,
          week: +edit.prices.week, month: +edit.prices.month,
        },
        photos: edit.photos || [],
        applyZone: applyZone,
      });
      // перезагрузить все места (на случай применения ко всей зоне)
      try { const { rooms } = await API.rooms(); if (rooms && rooms.length) window.EH_DATA.rooms = rooms; } catch (e) {}
      setEdit(null);
      setTick(t => t + 1);
      setMsg(applyZone ? `Зона обновлена (место ${num} и вся зона)` : `Место ${num} обновлено`);
      setTimeout(() => setMsg(null), 3500);
    } catch (ex) { alert(ex.message || 'Не удалось сохранить'); }
    finally { setSaving(false); }
  };

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Управление</div>
          <h1><em>Места</em> · {rooms.length}</h1>
          <p>Редактируйте цены, описание, фотографии и статус. Внутри зоны можно применить ко всем местам сразу. Изменения сразу видны на сайте.</p>
        </div>
        {msg && <span className="save-ok">✓ {msg}</span>}
      </div>

      <div className="rooms-admin">
        {rooms.map(r => (
          <div key={r.id} className="room-admin-card">
            <img src={ehPhoto(r.photos[0])} alt="" />
            <div className="ra-body">
              <div className="ra-head">
                <span className="ra-num">{r.num}</span>
                <h4>{r.name}</h4>
                <span className={`status-dot ${r.status}`} title={r.status}></span>
              </div>
              <div className="ra-meta">{r.zoneRu} · {r.area} м²</div>
              <div className="ra-prices">
                <span>{r.prices.hour}₽/ч</span>
                <span>{r.prices.day.toLocaleString()}₽/день</span>
                <span>{r.prices.month.toLocaleString()}₽/мес</span>
              </div>
              <button className="btn btn--ghost" style={{ fontSize: 11, padding: '7px 14px', alignSelf: 'flex-start' }} onClick={() => openEdit(r)}>Редактировать →</button>
            </div>
          </div>
        ))}
      </div>

      {edit && (
        <>
          <div className="drawer-overlay" onClick={close}></div>
          <aside className="admin-drawer room-edit">
            <button className="ad-close" onClick={close} aria-label="Закрыть">✕</button>
            <div className="crumbs">Место {edit.num} · {edit.zoneRu}</div>
            <h3>Редактирование</h3>

            <h4>Основное</h4>
            <div className="field"><label>Название</label><input value={edit.name} onChange={e => setField('name', e.target.value)} /></div>
            <div className="row2">
              <div className="field"><label>Площадь, м²</label><input type="number" min="0" value={edit.area} onChange={e => setField('area', e.target.value)} /></div>
              <div className="field"><label>Статус</label>
                <select value={edit.status} onChange={e => setField('status', e.target.value)}>
                  <option value="free">Свободен</option>
                  <option value="partial">Частично занят</option>
                  <option value="busy">Занят</option>
                </select>
              </div>
            </div>
            <div className="field"><label>Описание</label><textarea rows="4" value={edit.desc} onChange={e => setField('desc', e.target.value)} /></div>

            <h4>Тарифы, ₽</h4>
            <div className="price-grid">
              <div className="field"><label>Час</label><input type="number" min="0" value={edit.prices.hour} onChange={e => setPrice('hour', e.target.value)} /></div>
              <div className="field"><label>День</label><input type="number" min="0" value={edit.prices.day} onChange={e => setPrice('day', e.target.value)} /></div>
              <div className="field"><label>Месяц</label><input type="number" min="0" value={edit.prices.month} onChange={e => setPrice('month', e.target.value)} /></div>
            </div>

            <h4>Фотографии</h4>
            <div className="photo-grid">
              {(edit.photos || []).map((p, i) => (
                <div key={i} className="photo-thumb">
                  <img src={ehPhoto(p)} alt="" />
                  <button onClick={() => removePhoto(i)} title="Удалить">✕</button>
                </div>
              ))}
              <label className={`photo-add ${uploading ? 'busy' : ''}`}>
                {uploading ? '…' : '+ Фото'}
                <input type="file" accept="image/jpeg,image/png,image/webp" style={{ display: 'none' }} disabled={uploading}
                  onChange={e => { addPhoto(e.target.files[0]); e.target.value = ''; }} />
              </label>
            </div>

            <label className="zone-apply">
              <input type="checkbox" checked={applyZone} onChange={e => setApplyZone(e.target.checked)} />
              <span>Применить цену, описание и оснащение ко всем местам зоны «{edit.zoneRu}»</span>
            </label>

            <div className="profile-actions" style={{ marginTop: 18 }}>
              <button className="btn" onClick={save} disabled={saving}>{saving ? 'Сохраняем…' : 'Сохранить'}</button>
              <button className="btn btn--outline" onClick={close} disabled={saving}>Отмена</button>
            </div>
          </aside>
        </>
      )}
    </>
  );
};

// ─────── Картинки сайта: замена фотографий на публичных страницах ───────
// Каждый «слот» — конкретное место на сайте. Загруженное фото хранится в settings (img_<слот>).
const EH_IMAGE_SLOTS = [
  { group: 'Главный экран (слайдер под видео)', hint: 'Показываются по очереди, пока не загружено видео, и на устройствах без видео.', slots: [
    { id: 'hero1', label: 'Слайд 1', fb: 'heroInterior' },
    { id: 'hero2', label: 'Слайд 2', fb: 'heroMoody' },
    { id: 'hero3', label: 'Слайд 3', fb: 'heroSoft' },
    { id: 'hero4', label: 'Слайд 4', fb: 'interior5' },
    { id: 'hero5', label: 'Слайд 5', fb: 'interior3' },
  ]},
  { group: 'Карточки «Аренда рабочих мест»', hint: 'Фото на 8 карточках зон в разделе «Аренда и цены».', slots: [
    { id: 'zc_main',   label: 'Места в общем зале',            fb: 'heroInterior' },
    { id: 'zc_nails',  label: 'Маникюр и педикюр',             fb: 'manicure1' },
    { id: 'zc_barber', label: 'Кабинет барберов',              fb: 'chair3' },
    { id: 'zc_bed',    label: 'Кабинет с кушеткой',            fb: 'led2' },
    { id: 'zc_cosmo',  label: 'Кабинет косметолога',           fb: 'led1' },
    { id: 'zc_tan',    label: 'Кабинет с автозагаром',         fb: 'interior3' },
    { id: 'zc_free4',  label: 'Кабинеты свободного назначения', fb: 'interior1' },
    { id: 'zc_more',   label: 'Больше свободных мест',         fb: 'reception' },
  ]},
  { group: 'Аренда зала для мероприятий (слайд-шоу)', hint: 'Четыре горизонтальных кадра — меняются по кругу.', slots: [
    { id: 'events1', label: 'Кадр 1 (показывается первым)', fb: 'interior5' },
    { id: 'events2', label: 'Кадр 2', fb: 'chair1' },
    { id: 'events3', label: 'Кадр 3', fb: 'led1' },
    { id: 'events4', label: 'Кадр 4', fb: 'manicure1' },
  ]},
  { group: 'О пространстве', slots: [
    { id: 'about', label: 'Фото слева (с плашкой «2026»)', fb: 'reception' },
  ]},
  { group: 'Галерея «Наше пространство»', slots: [
    { id: 'gal1', label: 'Фото 1 (большое)', fb: 'interior1' },
    { id: 'gal2', label: 'Фото 2', fb: 'chair1' },
    { id: 'gal3', label: 'Фото 3', fb: 'detail1' },
    { id: 'gal4', label: 'Фото 4', fb: 'interior3' },
    { id: 'gal5', label: 'Фото 5', fb: 'manicure1' },
    { id: 'gal6', label: 'Фото 6', fb: 'interior5' },
    { id: 'gal7', label: 'Фото 7', fb: 'led1' },
  ]},
  { group: 'Фон «Мы заботимся о резидентах»', slots: [
    { id: 'care', label: 'Фоновое фото', fb: 'heroMoody' },
  ]},
  { group: 'Страница «Отзывы» — фото мастеров', hint: 'Фотографии Веры, Виктории и Марии.', slots: [
    { id: 'rev1', label: 'Вера · тату-мастер',        fb: 'review1' },
    { id: 'rev2', label: 'Виктория · лешмейкер',      fb: 'review2' },
    { id: 'rev3', label: 'Мария · мастер автозагара', fb: 'review3' },
  ]},
];

const AdminImages = () => {
  const [, setTick] = useState(0);           // перерисовка после изменения EH_DATA.siteImages
  const [busySlot, setBusySlot] = useState(null);
  const [msg, setMsg] = useState(null);

  const overrides = D2.siteImages || {};
  const flash = (t) => { setMsg(t); setTimeout(() => setMsg(null), 3500); };

  const saveSlot = async (slotId, url) => {
    const body = {}; body['img_' + slotId] = url;
    await API.saveSettings(body);
    const si = Object.assign({}, D2.siteImages);
    if (url) si[slotId] = url; else delete si[slotId];
    D2.siteImages = si;
    setTick(t => t + 1);
  };

  const upload = async (slot, file) => {
    if (!file) return;
    setBusySlot(slot.id);
    try {
      const { url } = await API.uploadPhoto(file);
      await saveSlot(slot.id, url);
      flash(`Фото «${slot.label}» заменено`);
    } catch (ex) { alert(ex.message || 'Не удалось загрузить фото'); }
    finally { setBusySlot(null); }
  };

  const reset = async (slot) => {
    setBusySlot(slot.id);
    try {
      await saveSlot(slot.id, '');
      flash(`«${slot.label}» — возвращено стандартное фото`);
    } catch (ex) { alert(ex.message || 'Не удалось сбросить'); }
    finally { setBusySlot(null); }
  };

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Управление</div>
          <h1><em>Картинки</em> сайта</h1>
          <p>Замените любую фотографию на главной странице и странице отзывов. Изменения видны сразу после обновления страницы.</p>
        </div>
        {msg && <span className="save-ok">✓ {msg}</span>}
      </div>

      {EH_IMAGE_SLOTS.map(g => (
        <div key={g.group} className="img-group">
          <h4 className="img-group-title">{g.group}</h4>
          {g.hint && <p className="img-group-hint">{g.hint}</p>}
          <div className="img-slot-grid">
            {g.slots.map(s => {
              const overridden = !!overrides[s.id];
              const src = ehSitePhoto(s.id, s.fb);
              const busy = busySlot === s.id;
              return (
                <div key={s.id} className={`img-slot ${overridden ? 'img-slot--custom' : ''}`}>
                  <div className="img-slot-photo">
                    {src ? <img src={src} alt={s.label} loading="lazy" /> : <div className="img-slot-none">нет фото</div>}
                    {overridden && <span className="img-slot-badge">своё фото</span>}
                  </div>
                  <div className="img-slot-label">{s.label}</div>
                  <div className="img-slot-actions">
                    <label className={`btn btn--ghost img-slot-btn ${busy ? 'busy' : ''}`}>
                      {busy ? '…' : 'Заменить'}
                      <input type="file" accept="image/jpeg,image/png,image/webp" style={{ display: 'none' }} disabled={busy}
                        onChange={e => { upload(s, e.target.files[0]); e.target.value = ''; }} />
                    </label>
                    {overridden && (
                      <button type="button" className="btn btn--ghost img-slot-btn img-slot-reset" disabled={busy}
                        onClick={() => reset(s)}>Вернуть стандарт</button>
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>
      ))}
    </>
  );
};

const AdminSettings = () => {
  const [form, setForm] = useState(null);
  const [saving, setSaving] = useState(false);
  const [ok, setOk] = useState(false);
  const [err, setErr] = useState({});

  useEffect(() => {
    let active = true;
    API.settings()
      .then(r => { if (active) setForm({
        address: r.settings.address || '', phone: r.settings.phone || '', email: r.settings.email || '',
        open: r.settings.open || '09:00', close: r.settings.close || '21:00', weekend: r.settings.weekend || '—',
        abonement_price: r.settings.abonement_price || '6000', abonement_hours: r.settings.abonement_hours || '30',
        abonement_day_price: r.settings.abonement_day_price || '1000',
        vk: r.settings.vk || '', instagram: r.settings.instagram || '', max_group: r.settings.max_group || '', max_dm: r.settings.max_dm || '',
        pay_name: r.settings.pay_name || '', pay_inn: r.settings.pay_inn || '', pay_account: r.settings.pay_account || '',
        pay_bank: r.settings.pay_bank || '', pay_bic: r.settings.pay_bic || '', pay_corr: r.settings.pay_corr || '',
      }); })
      .catch(() => { if (active) setForm({ address: D2.brand.address, phone: D2.brand.phone, email: D2.brand.email, open: '09:00', close: '21:00', weekend: '—',
        abonement_price: '6000', abonement_hours: '30', abonement_day_price: '1000', vk: D2.brand.vk || '', instagram: '', max_group: '', max_dm: D2.brand.maxDm || '',
        pay_name: '', pay_inn: '', pay_account: '', pay_bank: '', pay_bic: '', pay_corr: '' }); });
    return () => { active = false; };
  }, []);

  if (!form) return <p style={{ color: 'var(--mute)' }}>Загрузка настроек…</p>;

  const upd = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const save = async () => {
    const e = {};
    if (form.email && !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(form.email)) e.email = 'Некорректный email';
    if (!/^\d{1,2}:\d{2}$/.test(form.open)) e.open = 'Формат ЧЧ:ММ';
    if (!/^\d{1,2}:\d{2}$/.test(form.close)) e.close = 'Формат ЧЧ:ММ';
    setErr(e);
    if (Object.keys(e).length) return;
    setSaving(true); setOk(false);
    try {
      const r = await API.saveSettings(form);
      const st = r.settings;
      const open = st.open || '09:00', close = st.close || '21:00';
      window.EH_DATA.brand = Object.assign({}, window.EH_DATA.brand, {
        address: st.address, phone: st.phone, email: st.email,
        open, close, weekend: st.weekend || '—',
        hours: (st.weekend && st.weekend !== '—') ? `Пн–Сб · ${open}–${close}` : `Пн–Вс · ${open}–${close}`,
        abonementPrice: parseInt(st.abonement_price, 10) || 6000,
        abonementHours: parseInt(st.abonement_hours, 10) || 30,
        abonementDayPrice: parseInt(st.abonement_day_price, 10) || 1000,
        vk: st.vk || window.EH_DATA.brand.vk,
        instagram: st.instagram || '', maxGroup: st.max_group || '', maxDm: st.max_dm || window.EH_DATA.brand.maxDm,
      });
      window.EH_DATA.pay = {
        name: st.pay_name || '', inn: st.pay_inn || '', account: st.pay_account || '',
        bank: st.pay_bank || '', bic: st.pay_bic || '', corr: st.pay_corr || '',
      };
      setOk(true); setTimeout(() => setOk(false), 3000);
    } catch (ex) { setErr(ex.fields || { address: ex.message || 'Ошибка сохранения' }); }
    finally { setSaving(false); }
  };

  const hLabel = { fontFamily: 'var(--serif)', fontStyle: 'italic', fontSize: 20, fontWeight: 400, marginBottom: 16 };

  return (
    <>
      <div className="head-row">
        <div>
          <div className="crumbs">Настройки</div>
          <h1><em>Параметры</em> сайта</h1>
          <p>Контакты и рабочее время. Меняются сразу на сайте — футер, блок контактов, карточки.</p>
        </div>
      </div>

      <div className="settings-grid">
        <div>
          <h4 style={hLabel}>Рабочее время</h4>
          <div className={`field ${err.open ? 'error' : ''}`}><label>Открытие</label><input value={form.open} onChange={e => upd('open', e.target.value)} placeholder="09:00" />{err.open && <div className="err">{err.open}</div>}</div>
          <div className={`field ${err.close ? 'error' : ''}`}><label>Закрытие</label><input value={form.close} onChange={e => upd('close', e.target.value)} placeholder="21:00" />{err.close && <div className="err">{err.close}</div>}</div>
          <div className="field"><label>Выходные дни</label><input value={form.weekend} onChange={e => upd('weekend', e.target.value)} placeholder="—" /></div>
        </div>
        <div>
          <h4 style={hLabel}>Контактная информация</h4>
          <div className="field"><label>Адрес</label><input value={form.address} onChange={e => upd('address', e.target.value)} /></div>
          <div className="field"><label>Телефон</label><input value={form.phone} onChange={e => upd('phone', e.target.value)} /></div>
          <div className={`field ${err.email ? 'error' : ''}`}><label>Email</label><input value={form.email} onChange={e => upd('email', e.target.value)} />{err.email && <div className="err">{err.email}</div>}</div>
        </div>
      </div>

      <div className="settings-grid" style={{ marginTop: 8 }}>
        <div>
          <h4 style={hLabel}>Абонемент</h4>
          <div className="field"><label>Стоимость, ₽</label><input type="number" min="0" value={form.abonement_price} onChange={e => upd('abonement_price', e.target.value)} placeholder="6000" /></div>
          <div className="field"><label>Количество часов</label><input type="number" min="1" value={form.abonement_hours} onChange={e => upd('abonement_hours', e.target.value)} placeholder="30" /></div>
          <div className="field"><label>Дневной абонемент · цена 1 дня, ₽</label><input type="number" min="0" value={form.abonement_day_price} onChange={e => upd('abonement_day_price', e.target.value)} placeholder="1000" />
            <div className="hint">Клиент выбирает количество дней; итог = дни × эта цена. Цены брони «на день»/«на месяц» для каждого места меняются в разделе «Места».</div></div>
        </div>
        <div>
          <h4 style={hLabel}>Соцсети и мессенджеры</h4>
          <div className="field"><label>ВКонтакте (ссылка)</label><input value={form.vk} onChange={e => upd('vk', e.target.value)} placeholder="https://vk.com/…" /></div>
          <div className="field"><label>Instagram (ссылка)</label><input value={form.instagram} onChange={e => upd('instagram', e.target.value)} placeholder="https://instagram.com/…" /></div>
          <div className="field"><label>Группа в MAX (ссылка)</label><input value={form.max_group} onChange={e => upd('max_group', e.target.value)} placeholder="https://max.ru/…" /></div>
          <div className="field"><label>MAX — личные сообщения (ссылка)</label><input value={form.max_dm} onChange={e => upd('max_dm', e.target.value)} placeholder="https://max.ru/+79204770302" /></div>
        </div>
      </div>

      <div className="settings-grid" style={{ marginTop: 8 }}>
        <div>
          <h4 style={hLabel}>Реквизиты для оплаты по QR</h4>
          <p style={{fontSize: 13, color: 'var(--mute)', marginTop: -8, marginBottom: 14}}>Клиенты переводят оплату на эти реквизиты. По ним же строится QR-код (стандарт банков РФ).</p>
          <div className="field"><label>Получатель</label><input value={form.pay_name} onChange={e => upd('pay_name', e.target.value)} placeholder="ИП Фамилия Имя Отчество" /></div>
          <div className="field"><label>ИНН</label><input value={form.pay_inn} onChange={e => upd('pay_inn', e.target.value)} placeholder="683301372855" /></div>
          <div className="field"><label>Расчётный счёт</label><input value={form.pay_account} onChange={e => upd('pay_account', e.target.value)} placeholder="40802…" /></div>
        </div>
        <div>
          <h4 style={hLabel}>Банк получателя</h4>
          <div className="field"><label>Банк</label><input value={form.pay_bank} onChange={e => upd('pay_bank', e.target.value)} placeholder="ПАО Сбербанк…" /></div>
          <div className="field"><label>БИК</label><input value={form.pay_bic} onChange={e => upd('pay_bic', e.target.value)} placeholder="042007681" /></div>
          <div className="field"><label>Корсчёт</label><input value={form.pay_corr} onChange={e => upd('pay_corr', e.target.value)} placeholder="30101…" /></div>
        </div>
      </div>
      <div className="profile-actions" style={{ marginTop: 24 }}>
        <button className="btn" onClick={save} disabled={saving}>{saving ? 'Сохраняем…' : 'Сохранить'}</button>
        {ok && <span className="save-ok">✓ Сохранено</span>}
      </div>
    </>
  );
};

// ============================================
// REVIEWS — страница «Отзывы» (#/reviews)
// ============================================
const ReviewPhoto = ({ src, name }) => {
  const [failed, setFailed] = useState(false);
  // Пока файл фото не добавлен (или битый) — показываем аккуратную заглушку с инициалом
  if (!src || failed) {
    return <div className="rv-photo rv-photo--empty" aria-hidden="true"><span>{(name || '·').charAt(0)}</span></div>;
  }
  return (
    <div className="rv-photo">
      <img src={src} alt={`Фото — ${name}`} loading="lazy" onError={() => setFailed(true)} />
    </div>
  );
};

const Stars = ({ n }) => (
  <span className="rv-stars" aria-label={`Оценка ${n} из 5`}>
    {Array.from({ length: 5 }, (_, i) => <i key={i} className={i < n ? 'on' : ''}>★</i>)}
  </span>
);

const EditorialReviews = () => {
  useEffect(() => { document.title = "Отзывы · Estetic Hall"; }, []);
  const reviews = D2.reviews || [];
  return (
    <>
      <EditorialHeader />
      <section className="reviews-page" data-screen-label="13 Reviews">
        <div className="container">
          <Reveal>
            <div className="chapter" style={{marginTop: 40}}><span className="num">★</span><span className="label">Отзывы резидентов</span><span className="line"></span></div>
            <h1 className="display reviews-title">Нам доверяют <em>мастера.</em></h1>
            <p className="reviews-lead">Настоящие отзывы мастеров, которые работают в Estetic Hall каждый день.</p>
          </Reveal>

          <div className="reviews-list">
            {reviews.map((r, i) => (
              <Reveal key={i} delay={i * 90}>
                <article className={`rv-card ${i % 2 === 1 ? 'rv-card--flip' : ''}`}>
                  <ReviewPhoto src={ehSitePhoto(r.slot || ('rev' + (i + 1)), r.photo)} name={r.name} />
                  <div className="rv-body">
                    <Stars n={r.rating || 5} />
                    <p className="rv-text">«{r.text}»</p>
                    <div className="rv-who">
                      <b>{r.name}</b>
                      <span>{r.role}</span>
                      <em>{r.date}</em>
                    </div>
                  </div>
                </article>
              </Reveal>
            ))}
          </div>

          <Reveal>
            <div className="reviews-cta">
              <h3>Хотите работать так же?</h3>
              <p>Забронируйте место на час — почувствуйте пространство, а потом решайте.</p>
              <a href="#book" className="btn btn--gold btn--lg">Выбрать место →</a>
            </div>
          </Reveal>
        </div>
      </section>
      <EditorialFooter />
    </>
  );
};

// ============================================
// LEGAL PAGES — Политика конфиденциальности / Оферта
// ============================================
const LEGAL = {
  privacy: {
    title: 'Политика конфиденциальности',
    updated: 'май 2026',
    intro: 'Настоящая Политика описывает, как бьюти-коворкинг Estetic Hall (далее — «Сайт») собирает, использует и защищает персональные данные пользователей. Регистрируясь и пользуясь сайтом, вы соглашаетесь с условиями настоящей Политики.',
    sections: [
      { h: 'Общие положения', body: [
        'Настоящая Политика разработана в соответствии с Федеральным законом РФ № 152-ФЗ «О персональных данных».',
        'Оператором персональных данных является бьюти-коворкинг Estetic Hall, г. Тамбов, ул. Октябрьская, 11 (3-й этаж). По любым вопросам обработки данных вы можете обратиться по контактам, указанным в конце документа.',
      ] },
      { h: 'Какие данные мы собираем', body: [
        'При регистрации и использовании сайта мы можем собирать:',
        ['имя и фамилию', 'адрес электронной почты', 'номер телефона', 'специализацию', 'данные о бронированиях рабочих мест', 'технические данные сессии (cookie)'],
      ] },
      { h: 'Цели обработки', body: [
        ['регистрация и идентификация пользователя', 'бронирование рабочих мест и управление им', 'связь с пользователем по вопросам бронирований', 'ведение истории операций и оплат'],
      ] },
      { h: 'Правовые основания', body: [
        'Обработка осуществляется на основании согласия пользователя, которое даётся при регистрации, а также в целях исполнения договора (публичной оферты).',
      ] },
      { h: 'Хранение и защита', body: [
        'Пароли хранятся в виде необратимого криптографического хеша (bcrypt) и недоступны в открытом виде.',
        'Данные хранятся на защищённом сервере, доступ к ним ограничен. Срок хранения — до отзыва согласия или прекращения использования сервиса.',
      ] },
      { h: 'Передача третьим лицам', body: [
        'Мы не передаём персональные данные третьим лицам, за исключением платёжных провайдеров (при оплате на сайте) и случаев, прямо предусмотренных законодательством РФ.',
      ] },
      { h: 'Права пользователя', body: [
        'Вы вправе запросить доступ к своим данным, их изменение или удаление, а также отозвать согласие, направив запрос на email из раздела «Контакты».',
        'Часть данных можно изменить самостоятельно в личном кабинете, раздел «Профиль».',
      ] },
      { h: 'Файлы cookie', body: [
        'Сайт использует cookie для поддержания авторизованной сессии. Отключение cookie может ограничить функциональность сайта.',
      ] },
      { h: 'Изменения политики', body: [
        'Оператор вправе изменять настоящую Политику. Актуальная редакция всегда доступна на этой странице.',
      ] },
    ],
  },
  offer: {
    title: 'Публичная оферта',
    updated: 'май 2026',
    intro: 'Настоящий документ является публичной офертой бьюти-коворкинга Estetic Hall (далее — «Исполнитель») и содержит условия предоставления в аренду оборудованных рабочих мест для мастеров бьюти-индустрии. Регистрация на сайте и/или оформление бронирования являются полным и безоговорочным акцептом настоящей оферты.',
    sections: [
      { h: 'Термины', body: [
        ['«Исполнитель» — владелец бьюти-коворкинга Estetic Hall.', '«Пользователь» («Мастер») — физическое лицо, зарегистрированное на сайте.', '«Рабочее место» — оборудованное место в одной из зон: парикмахер, маникюр, визаж, педикюр.', '«Бронирование» — резервирование рабочего места на выбранные дату и часы.'],
      ] },
      { h: 'Предмет договора', body: [
        'Исполнитель предоставляет Пользователю во временное пользование рабочее место согласно выбранной зоне, дате и времени. Состав оборудования каждой зоны указан в карточке места на сайте.',
      ] },
      { h: 'Порядок бронирования', body: [
        ['Бронирование оформляется через сайт после регистрации.', 'Пользователь выбирает зону, конкретное место, дату и часы.', 'Бронь считается оформленной после подтверждения на сайте.', 'Часы работы указаны в разделе контактов.'],
      ] },
      { h: 'Стоимость и оплата', body: [
        'Стоимость определяется тарифами, указанными на сайте для каждой зоны (час, день, месяц). Также доступен абонемент на часы аренды.',
        'Тарифы могут изменяться; к оформленной брони применяется тариф, действовавший на момент её оформления.',
      ] },
      { h: 'Права и обязанности сторон', body: [
        'Пользователь обязуется бережно относиться к оборудованию, соблюдать чистоту и правила коворкинга, освобождать место по окончании брони.',
        'Исполнитель обязуется предоставить исправное оборудованное рабочее место в забронированное время.',
      ] },
      { h: 'Отмена и перенос', body: [
        'Пользователь может отменить активную бронь в личном кабинете в любой момент до её начала.',
        'При отмене не позднее чем за 24 часа до начала забронированного времени внесённая оплата возвращается в полном объёме. При отмене менее чем за 24 часа или при неявке оплата за это бронирование не возвращается. Перенос брони на другое время возможен при наличии свободных мест.',
      ] },
      { h: 'Ответственность', body: [
        'Стороны несут ответственность в соответствии с законодательством РФ. Исполнитель не несёт ответственности за сохранность личных вещей Пользователя.',
      ] },
      { h: 'Прочие условия', body: [
        'Во всём, что не урегулировано офертой, стороны руководствуются законодательством РФ. Исполнитель вправе изменять условия оферты, публикуя новую редакцию на сайте.',
      ] },
    ],
  },
};

const EditorialLegal = ({ kind }) => {
  const data = LEGAL[kind] || LEGAL.privacy;
  useEffect(() => { document.title = `${data.title} · Estetic Hall`; window.scrollTo(0, 0); }, [kind]);
  return (
    <>
      <div className="legal-top">
        <EditorialLogo />
        <a href="#/" className="back">← На главную</a>
      </div>
      <div className="legal" data-screen-label={kind === 'offer' ? '11 Offer' : '12 Privacy'}>
        <div className="crumbs">Документы · Estetic Hall</div>
        <h1>{data.title}</h1>
        <p className="legal-upd">Редакция: {data.updated} · г. {D2.brand.city || 'Тамбов'}</p>
        <p className="legal-lead">{data.intro}</p>
        {data.sections.map((s, i) => (
          <section key={i} className="legal-sec">
            <h3><span className="ls-num">{i + 1}.</span> {s.h}</h3>
            {s.body.map((b, j) => Array.isArray(b)
              ? <ul key={j}>{b.map((li, k) => <li key={k}>{li}</li>)}</ul>
              : <p key={j}>{b}</p>)}
          </section>
        ))}
        <div className="legal-contacts">
          <b>Контакты</b>
          {D2.brand.name} · {(D2.brand.address || '').replace(/\s*\n\s*/g, ' ')}<br />
          Телефон: {D2.brand.phone}{D2.brand.email ? ` · Email: ${D2.brand.email}` : ''}<br />
          Режим работы: {D2.brand.hours}
        </div>
      </div>
      <EditorialFooter />
    </>
  );
};

Object.assign(window, { EditorialLanding, EditorialLogin, EditorialRegister, EditorialCabinet, EditorialAdmin, EditorialLegal });
