// booking-mobile.jsx — Friends SUP, мобильный визард брони (3 шага).
// Источник дизайна — отдельный mobile-bundle от UI-инструмента.
// Зависимости: window.fmtRub, window.FriendsLogo (из widgets.jsx),
//              window.BOOKING_ROUTES_DATA (из BookingApp в booking.jsx
//              после fetch /api/routes).
//
// Отличия от исходного bundle:
//   • ROUTES не хардкодятся, читаются из window.BOOKING_ROUTES_DATA;
//   • времена шага 1 приходят с /api/availability (паттерны + оверрайды);
//   • кнопка «← Назад» сверху на шагах 2/3 убрана (как в десктопе) —
//     навигация назад есть только в нижней sticky-панели;
//   • в нижней секции карточки-билета увеличен padding-top, чтобы
//     подписи «ДАТА»/«ВРЕМЯ» не пересекались с пунктирной перфорацией;
//   • вместо alert на шаге 3 — POST /api/bookings и колбек onBooked.

(function () {
  const { useState, useEffect } = React;

  const getRoutes = () => window.BOOKING_ROUTES_DATA || [];
  const TIMES_FALLBACK = ['09:00', '11:00', '13:00', '15:00', '17:00', '19:00'];

  // ─── Helpers ───────────────────────────────────────────────────────
  const isWeekend = (iso) => {
    if (!iso) return false;
    const d = new Date(iso); const w = d.getDay(); return w === 0 || w === 6;
  };
  const MONTHS = ['янв', 'фев', 'мар', 'апр', 'мая', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек'];
  const fmtDate = (iso) => {
    if (!iso) return '—';
    const d = new Date(iso); return `${d.getDate()} ${MONTHS[d.getMonth()]}`;
  };
  const fmtRub = (n) => (window.fmtRub ? window.fmtRub(n) : `${n.toLocaleString('ru-RU')} ₽`);

  // Валидаторы — те же, что использует бэк (EMAIL_RE в server.js).
  // Телефон считаем валидным при ≥ 11 цифр (российский формат с кодом страны).
  const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  const phoneDigits = (s) => (s || '').replace(/\D/g, '');
  const isValidEmail = (s) => !!s && EMAIL_RE.test(String(s).trim());
  const isValidPhone = (s) => phoneDigits(s).length >= 11;

  // ─── Step dots ─────────────────────────────────────────────────────
  function StepDots({ active }) {
    return (
      <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, color: '#5a6660', fontWeight: 500 }}>
        {[
          { n: '1', label: 'Маршрут' },
          { n: '2', label: 'Детали' },
          { n: '3', label: 'Оплата' }
        ].map((s, i) => {
          const done = i + 1 < active;
          const isActive = i + 1 === active;
          return (
            <React.Fragment key={i}>
              {i > 0 && <span style={{ width: 8, height: 1, background: 'rgba(10,46,31,.16)' }} />}
              <span style={{
                display: 'flex', alignItems: 'center', gap: 5,
                color: isActive ? '#0A2E1F' : '#5a6660',
                opacity: !isActive && !done ? .5 : 1,
                fontWeight: isActive ? 500 : 400
              }}>
                <span style={{
                  width: 16, height: 16, borderRadius: '50%',
                  background: isActive ? '#DD3310' : done ? '#0A2E1F' : 'rgba(10,46,31,.12)',
                  color: isActive || done ? '#fff' : '#0A2E1F',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  fontSize: 9, fontWeight: 600
                }}>{done ? '✓' : s.n}</span>
                {isActive && s.label}
              </span>
            </React.Fragment>
          );
        })}
      </div>
    );
  }

  // ─── Mobile shell ──────────────────────────────────────────────────
  function MobileShell({ children, step }) {
    return (
      <div style={{
        height: '100%', minHeight: '100%',
        display: 'flex', flexDirection: 'column',
        background: '#F4F1EA',
        fontFamily: '"NT Somic", system-ui, -apple-system, sans-serif',
        color: '#0A2E1F'
      }}>
        <header style={{
          padding: '50px 20px 14px',
          background: '#fff',
          borderBottom: '1px solid rgba(10,46,31,.06)',
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          flexShrink: 0
        }}>
          {window.FriendsLogo ? <window.FriendsLogo size={20} /> : <span />}
          <StepDots active={step} />
        </header>
        <div style={{ flex: 1, overflow: 'auto', WebkitOverflowScrolling: 'touch', minHeight: 0 }}>
          {children}
        </div>
      </div>
    );
  }

  // ─── Compact mobile ticket ─────────────────────────────────────────
  function MobileTicket({ route, date, time, persons }) {
    if (!route) return null;
    const weekend = isWeekend(date);
    // Используем общий калькулятор (window.calcBookingTotal из booking.jsx) —
    // в нём учитывается групповая скидка от 5 человек. Промо в билете
    // не учитываем, оно применяется на шаге 3.
    const calc = window.calcBookingTotal && window.calcBookingTotal({ route, date, persons });
    const total = date && calc ? calc.total : null;
    return (
      <div style={{
        position: 'relative',
        borderRadius: 16, overflow: 'hidden',
        background: '#FAF7EF',
        boxShadow: '0 14px 32px -16px rgba(10,46,31,.28), 0 1px 0 rgba(0,0,0,.04)',
        color: '#0A2E1F'
      }}>
        {/* Notches — на той же высоте что и пунктир */}
        <div style={{
          position: 'absolute', left: -8, top: 'calc(60% - 8px)', width: 16, height: 16,
          borderRadius: '50%', background: '#F4F1EA', zIndex: 2,
          boxShadow: 'inset 0 0 0 1px rgba(10,46,31,.06)'
        }} />
        <div style={{
          position: 'absolute', right: -8, top: 'calc(60% - 8px)', width: 16, height: 16,
          borderRadius: '50%', background: '#F4F1EA', zIndex: 2,
          boxShadow: 'inset 0 0 0 1px rgba(10,46,31,.06)'
        }} />
        {/* Photo */}
        <div style={{ position: 'relative', height: 200, overflow: 'hidden', background: '#1d2a26' }}>
          <img src={route.photo} alt="" style={{
            position: 'absolute', inset: 0, width: '100%', height: '100%',
            objectFit: 'cover', objectPosition: 'center 60%', display: 'block'
          }} />
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(rgba(0,0,0,0) 50%, rgba(0,0,0,.32) 100%)'
          }} />
          <img src={window.__resources?.logoOrange} alt="Friends SUP" style={{
            position: 'absolute', top: 14, left: 14, height: 18, width: 'auto'
          }} />
          <div style={{ position: 'absolute', bottom: 14, left: 16, right: 16, color: '#fff' }}>
            <div style={{
              fontSize: 9, letterSpacing: '.22em', textTransform: 'uppercase',
              fontWeight: 500, opacity: .85, marginBottom: 3
            }}>Билет на прогулку</div>
            <div style={{
              fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
              fontSize: 22, fontWeight: 800, letterSpacing: '-.01em', lineHeight: 1.05,
              textShadow: '0 2px 12px rgba(0,0,0,.4)'
            }}>{route.name}</div>
          </div>
        </div>
        {/* Dashed seam */}
        <div style={{
          position: 'absolute', left: 14, right: 14, top: 200,
          borderTop: '1.5px dashed rgba(10,46,31,.22)', zIndex: 4
        }} />
        {/* Details — увеличенный padding-top, чтобы подписи не касались пунктира */}
        <div style={{ padding: '34px 16px 14px', background: '#fff' }}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', columnGap: 12, rowGap: 12, marginBottom: 12 }}>
            <TF label="Дата" value={fmtDate(date)} sub={date ? (weekend ? 'выходной' : 'будни') : null} />
            <TF label="Время" value={time || '—'} mono />
            <TF label="Персон" value={persons || '—'} mono />
            <TF label="К оплате" value={total != null ? fmtRub(total) : '—'} accent mono />
          </div>
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'center',
            paddingTop: 10, borderTop: '1px solid rgba(10,46,31,.08)',
            fontSize: 10, color: '#5a6660'
          }}>
            <div>
              <span style={{ opacity: .55, textTransform: 'uppercase', letterSpacing: '.14em' }}>№ </span>
              <span style={{ color: '#0A2E1F', fontWeight: 500, letterSpacing: '.08em' }}>*******</span>
            </div>
            <div style={{ color: '#0A2E1F', fontWeight: 500 }}>friends-sup.ru</div>
          </div>
        </div>
      </div>
    );
  }

  function TF({ label, value, mono, accent, sub }) {
    return (
      <div style={{ minWidth: 0 }}>
        <div style={{
          fontSize: 9, color: '#5a6660',
          letterSpacing: '.18em', textTransform: 'uppercase', fontWeight: 500,
          marginBottom: 4
        }}>{label}</div>
        <div style={{
          fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
          fontSize: 18, fontWeight: accent ? 800 : 700,
          color: accent ? '#DD3310' : '#0A2E1F',
          letterSpacing: '-.01em', lineHeight: 1.05,
          fontVariantNumeric: mono ? 'tabular-nums' : 'normal',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'
        }}>{value}</div>
        {sub && (
          <div style={{ fontSize: 10, color: '#5a6660', marginTop: 2 }}>{sub}</div>
        )}
      </div>
    );
  }

  // ─── Field primitive (light, mobile-first) ─────────────────────────
  function Field({ label, value, onChange, placeholder, type = 'text', error, onBlur, inputMode }) {
    return (
      <label style={{ display: 'block' }}>
        <div style={{ fontSize: 12, color: '#5a6660', marginBottom: 6 }}>{label}</div>
        <input
          type={type}
          inputMode={inputMode || (type === 'tel' ? 'tel' : type === 'email' ? 'email' : undefined)}
          value={value}
          onChange={(e) => onChange(e.target.value)}
          onBlur={onBlur}
          placeholder={placeholder}
          style={{
            width: '100%', boxSizing: 'border-box',
            background: '#fff', border: 'none', outline: 'none',
            borderRadius: 12, padding: '13px 14px',
            boxShadow: error ? '0 0 0 1.5px #DD3310' : '0 1px 0 rgba(0,0,0,.04)',
            fontFamily: 'inherit', fontSize: 15, color: '#0A2E1F'
          }} />
        {error && <div style={{ fontSize: 11, color: '#DD3310', marginTop: 4 }}>{error}</div>}
      </label>
    );
  }

  // ─── Sticky CTA bar ────────────────────────────────────────────────
  function StickyCTA({ total, subtotal, ctaLabel, onCta, ctaDisabled, onBack, hint, discount = 0 }) {
    return (
      <div style={{
        position: 'sticky', bottom: 0,
        background: '#fff',
        borderTop: '1px solid rgba(10,46,31,.08)',
        padding: '14px 20px 28px',
        boxShadow: '0 -4px 16px rgba(10,46,31,.04)'
      }}>
        {hint && (
          <div style={{ fontSize: 11, color: '#7a8682', marginBottom: 8, lineHeight: 1.4 }}>
            {hint}
          </div>
        )}
        {discount > 0 && (
          <div style={{
            display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
            fontSize: 11, color: '#0B5D3B', marginBottom: 4, fontWeight: 500
          }}>
            <span>Скидка (промо + групповая)</span><span>−{fmtRub(discount)}</span>
          </div>
        )}
        {total != null && (
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 12 }}>
            <span style={{ fontSize: 12, color: '#5a6660', letterSpacing: '.1em', textTransform: 'uppercase' }}>К оплате</span>
            <span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 8 }}>
              {subtotal != null && subtotal !== total && (
                <span style={{
                  fontSize: 14, color: '#9aa39e',
                  textDecoration: 'line-through', textDecorationThickness: '1px',
                  fontVariantNumeric: 'tabular-nums'
                }}>{fmtRub(subtotal)}</span>
              )}
              <span style={{
                fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
                fontSize: 24, fontWeight: 800,
                // Когда есть зачёркнутая исходная цена — итог красим в
                // акцентный красный, чтобы выделить «новую» цену.
                color: subtotal != null && subtotal !== total ? '#DD3310' : '#0A2E1F',
                letterSpacing: '-.01em',
                fontVariantNumeric: 'tabular-nums'
              }}>{fmtRub(total)}</span>
            </span>
          </div>
        )}
        <div style={{ display: 'flex', gap: 8 }}>
          {onBack && (
            <button
              onClick={onBack}
              style={{
                appearance: 'none', border: 'none', cursor: 'pointer',
                background: 'rgba(10,46,31,.06)', color: '#0A2E1F',
                padding: '15px 18px', borderRadius: 14,
                fontFamily: 'inherit', fontSize: 15, fontWeight: 500,
                flexShrink: 0
              }}>Назад</button>
          )}
          <button
            onClick={onCta}
            disabled={ctaDisabled}
            style={{
              flex: 1, appearance: 'none', border: 'none',
              cursor: ctaDisabled ? 'default' : 'pointer',
              background: ctaDisabled ? 'rgba(10,46,31,.16)' : '#DD3310',
              color: '#fff', padding: '15px 20px', borderRadius: 14,
              fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
              letterSpacing: '.01em', transition: 'background .15s'
            }}>{ctaLabel}</button>
        </div>
      </div>
    );
  }

  // ─── Info popover (route description) ──────────────────────────────
  function RouteInfoSheet({ route, onClose }) {
    if (!route) return null;
    return (
      <div
        onClick={onClose}
        style={{
          position: 'absolute', inset: 0, zIndex: 30,
          background: 'rgba(10,46,31,.45)',
          display: 'flex', flexDirection: 'column', justifyContent: 'flex-end'
        }}>
        <div
          onClick={(e) => e.stopPropagation()}
          style={{
            background: '#fff', borderTopLeftRadius: 20, borderTopRightRadius: 20,
            padding: '8px 20px 28px',
            boxShadow: '0 -16px 40px rgba(10,46,31,.18)',
            animation: 'sheet-in .25s ease-out'
          }}>
          <div style={{ width: 40, height: 4, background: 'rgba(10,46,31,.16)', borderRadius: 2, margin: '6px auto 16px' }} />
          <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 6 }}>
            Маршрут
          </div>
          <div style={{
            fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
            fontSize: 22, fontWeight: 800, letterSpacing: '-.01em', marginBottom: 12
          }}>{route.name}</div>
          <div style={{ fontSize: 14, lineHeight: 1.55, color: '#3a4a44', marginBottom: 16 }}>
            {route.description}
          </div>
          <div style={{
            display: 'flex', gap: 14, paddingTop: 14, borderTop: '1px solid rgba(10,46,31,.08)',
            fontSize: 13, color: '#3a4a44'
          }}>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, letterSpacing: '.18em', textTransform: 'uppercase', color: '#7a8682', marginBottom: 4 }}>Длительность</div>
              <div style={{ fontWeight: 600, color: '#0A2E1F' }}>{route.duration}</div>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, letterSpacing: '.18em', textTransform: 'uppercase', color: '#7a8682', marginBottom: 4 }}>Будни</div>
              <div style={{ fontWeight: 600, color: '#0A2E1F' }}>{fmtRub(route.weekday)}</div>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, letterSpacing: '.18em', textTransform: 'uppercase', color: '#7a8682', marginBottom: 4 }}>Выходные</div>
              <div style={{ fontWeight: 600, color: '#0A2E1F' }}>{fmtRub(route.weekend)}</div>
            </div>
          </div>
          <button
            onClick={onClose}
            style={{
              width: '100%', marginTop: 18,
              appearance: 'none', border: 'none', cursor: 'pointer',
              background: 'rgba(10,46,31,.06)', color: '#0A2E1F',
              padding: '13px 18px', borderRadius: 12,
              fontFamily: 'inherit', fontSize: 14, fontWeight: 500
            }}>Закрыть</button>
        </div>
        <style>{`@keyframes sheet-in { from { transform: translateY(20px); opacity: 0; } to { transform: translateY(0); opacity: 1; } }`}</style>
      </div>
    );
  }

  // ─── STEP 1 ────────────────────────────────────────────────────────
  function MobileBookingStep1({
    initialRouteId, initialDate = '', initialTime = '', initialPersons = 2,
    initialInfoRouteId = null,
    onContinue
  }) {
    const ROUTES = getRoutes();
    const [routeId, setRouteId] = useState(initialRouteId || (ROUTES[0] && ROUTES[0].id));
    const [date, setDate] = useState(initialDate);
    const [time, setTime] = useState(initialTime);
    const [persons, setPersons] = useState(initialPersons);
    const [infoRouteId, setInfoRouteId] = useState(initialInfoRouteId);

    // Времена приходят с бэка с учётом паттернов и оверрайдов.
    const [availTimes, setAvailTimes] = useState([]);
    const [availClosed, setAvailClosed] = useState(false);
    const [availLoading, setAvailLoading] = useState(false);
    useEffect(() => {
      if (!routeId || !date) { setAvailTimes([]); setAvailClosed(false); return; }
      let off = false;
      setAvailLoading(true);
      fetch(`/api/availability?routeId=${encodeURIComponent(routeId)}&date=${encodeURIComponent(date)}`)
        .then(r => r.ok ? r.json() : { times: [], closed: false })
        .then(d => { if (!off) { setAvailTimes(d.times || []); setAvailClosed(!!d.closed); } })
        .catch(() => { if (!off) { setAvailTimes([]); setAvailClosed(false); } })
        .finally(() => { if (!off) setAvailLoading(false); });
      return () => { off = true; };
    }, [routeId, date]);
    useEffect(() => {
      if (time && availTimes.length && !availTimes.includes(time)) setTime('');
    }, [availTimes]);

    const route = ROUTES.find((r) => r.id === routeId) || ROUTES[0];
    const weekend = isWeekend(date);
    const price = route ? (date ? (weekend ? route.weekend : route.weekday) : route.weekday) : 0;
    const calc1 = route && window.calcBookingTotal
      ? window.calcBookingTotal({ route, date, persons })
      : { subtotal: price * persons, groupDiscount: 0, total: price * persons };
    const subtotal = calc1.subtotal;
    const groupDiscount = calc1.groupDiscount;
    const totalWithGroup = calc1.total;
    const canContinue = !!date && !!time && persons >= 1 && availTimes.includes(time);
    const infoRoute = ROUTES.find((r) => r.id === infoRouteId);
    const TIMES = availTimes.length ? availTimes : TIMES_FALLBACK;
    const timesDisabled = !date || availClosed;

    if (!route) {
      return (
        <MobileShell step={1}>
          <div style={{ padding: 40, textAlign: 'center', color: '#5a6660' }}>
            Маршруты загружаются…
          </div>
        </MobileShell>
      );
    }

    return (
      <div style={{ position: 'relative', height: '100%' }}>
        <MobileShell step={1}>
          <div style={{ padding: '20px 20px 0' }}>
            <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 6 }}>
              Шаг 1 из 3
            </div>
            <div style={{
              fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
              fontSize: 24, fontWeight: 800, letterSpacing: '-.01em', marginBottom: 16
            }}>Забронировать прогулку</div>

            {/* Ticket preview */}
            <div style={{ marginBottom: 26 }}>
              <MobileTicket route={route} date={date} time={time} persons={persons} />
            </div>

            {/* Routes */}
            <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
              Маршруты
            </div>
            <div style={{ display: 'grid', gap: 8, marginBottom: 26 }}>
              {ROUTES.map((r) => {
                const active = r.id === routeId;
                return (
                  <div key={r.id} style={{
                    background: active ? '#DD3310' : '#fff',
                    color: active ? '#fff' : '#0A2E1F',
                    borderRadius: 14, padding: '12px 12px 12px 14px',
                    boxShadow: active ? '0 8px 24px -8px rgba(221,51,16,.4)' : '0 1px 0 rgba(0,0,0,.04)',
                    display: 'flex', alignItems: 'center', gap: 10
                  }}>
                    <button
                      onClick={() => setRouteId(r.id)}
                      style={{
                        appearance: 'none', cursor: 'pointer', border: 'none', background: 'transparent',
                        flex: 1, minWidth: 0, padding: 0, textAlign: 'left',
                        color: 'inherit', fontFamily: 'inherit',
                        display: 'flex', alignItems: 'center', gap: 12
                      }}>
                      <span style={{
                        width: 18, height: 18, borderRadius: '50%',
                        border: `1.5px solid ${active ? '#fff' : 'rgba(10,46,31,.25)'}`,
                        background: active ? '#fff' : 'transparent',
                        flexShrink: 0, position: 'relative'
                      }}>
                        {active && (
                          <span style={{ position: 'absolute', inset: 4, borderRadius: '50%', background: '#DD3310' }} />
                        )}
                      </span>
                      <div style={{ flex: 1, minWidth: 0 }}>
                        <div style={{ fontSize: 14, fontWeight: 600, marginBottom: 2 }}>{r.name}</div>
                        <div style={{
                          fontSize: 11, opacity: active ? .9 : .65, lineHeight: 1.3,
                          textOverflow: 'ellipsis', overflow: 'hidden', whiteSpace: 'nowrap'
                        }}>{r.subtitle}</div>
                      </div>
                      <div style={{
                        fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
                        fontSize: 14, fontWeight: 800, letterSpacing: '-.01em',
                        flexShrink: 0, textAlign: 'right'
                      }}>
                        <div>{r.weekday.toLocaleString('ru-RU')} / {r.weekend.toLocaleString('ru-RU')}</div>
                        <div style={{ fontSize: 9, fontWeight: 500, opacity: .7, marginTop: 2 }}>будни / вых</div>
                      </div>
                    </button>
                    <button
                      aria-label="Подробнее"
                      onClick={() => setInfoRouteId(r.id)}
                      style={{
                        appearance: 'none', border: 'none', cursor: 'pointer',
                        background: active ? 'rgba(255,255,255,.18)' : 'rgba(10,46,31,.06)',
                        color: active ? '#fff' : '#0A2E1F',
                        width: 30, height: 30, borderRadius: '50%',
                        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                        flexShrink: 0, fontFamily: '"RF Dewi Extended", "NT Somic", serif',
                        fontStyle: 'italic', fontWeight: 700, fontSize: 14, lineHeight: 1
                      }}>i</button>
                  </div>
                );
              })}
            </div>

            {/* Date — узкое поле + popover-календарь (общий компонент с десктопом). */}
            <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
              Дата прогулки
            </div>
            {window.DateField
              ? <window.DateField routeId={routeId} value={date} onChange={setDate} />
              : <div style={{ fontSize: 12, color: '#5a6660' }}>Календарь загружается…</div>}
            {date && (
              <div style={{ fontSize: 12, color: '#5a6660', marginTop: 8, marginBottom: 26 }}>
                {fmtDate(date)} · {weekend ? 'выходной' : 'будни'} · тариф {fmtRub(price)}/чел
              </div>
            )}
            {!date && <div style={{ marginBottom: 26 }} />}

            {/* Time */}
            <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
              Время
            </div>
            {availClosed ? (
              <div style={{
                background: 'rgba(221,51,16,.08)', color: '#DD3310',
                borderRadius: 12, padding: '12px 14px',
                fontSize: 13, lineHeight: 1.45, marginBottom: 26
              }}>В этот день маршрут закрыт. Выберите другую дату.</div>
            ) : (
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 6, marginBottom: 8 }}>
                {TIMES.map((t) => {
                  const active = time === t;
                  const disabled = timesDisabled || !availTimes.includes(t);
                  return (
                    <button
                      key={t}
                      onClick={() => !disabled && setTime(t)}
                      disabled={disabled}
                      style={{
                        appearance: 'none',
                        cursor: disabled ? 'not-allowed' : 'pointer',
                        border: 'none',
                        background: active ? '#0A2E1F' : '#fff',
                        color: active ? '#fff' : '#0A2E1F',
                        opacity: disabled ? .35 : 1,
                        padding: '12px 0', borderRadius: 12,
                        fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
                        fontSize: 15, fontWeight: 700, fontVariantNumeric: 'tabular-nums',
                        boxShadow: active ? 'none' : '0 1px 0 rgba(0,0,0,.04)'
                      }}>{t}</button>
                  );
                })}
              </div>
            )}
            {date && !availClosed && availLoading && (
              <div style={{ fontSize: 11, color: '#5a6660', marginBottom: 18 }}>загружаем расписание…</div>
            )}
            {date && !availClosed && !availLoading && availTimes.length === 0 && (
              <div style={{ fontSize: 11, color: '#5a6660', marginBottom: 18 }}>на этот день расписания нет — выберите другую дату.</div>
            )}
            {!availClosed && (date || availTimes.length) && <div style={{ marginBottom: 18 }} />}

            {/* Persons */}
            <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
              Количество человек
            </div>
            <div style={{
              display: 'flex', alignItems: 'center', gap: 12,
              background: '#fff', borderRadius: 14, padding: '8px 14px',
              boxShadow: '0 1px 0 rgba(0,0,0,.04)', marginBottom: 22
            }}>
              <button
                onClick={() => setPersons((p) => Math.max(1, p - 1))}
                style={{
                  appearance: 'none', border: '1.5px solid rgba(10,46,31,.16)', background: 'transparent',
                  width: 38, height: 38, borderRadius: '50%', cursor: 'pointer',
                  fontSize: 18, color: '#0A2E1F', fontFamily: 'inherit'
                }}>−</button>
              <div style={{
                flex: 1, textAlign: 'center',
                fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
                fontSize: 22, fontWeight: 800, letterSpacing: '-.01em'
              }}>{persons}</div>
              <button
                onClick={() => setPersons((p) => Math.min(12, p + 1))}
                style={{
                  appearance: 'none', border: '1.5px solid rgba(10,46,31,.16)', background: 'transparent',
                  width: 38, height: 38, borderRadius: '50%', cursor: 'pointer',
                  fontSize: 18, color: '#0A2E1F', fontFamily: 'inherit'
                }}>+</button>
            </div>

            {/* Подсказка о групповой скидке. */}
            <div style={{
              fontSize: 12, lineHeight: 1.45, marginBottom: 14,
              color: groupDiscount > 0 ? '#0B5D3B' : '#5a6660'
            }}>
              {groupDiscount > 0
                ? `✓ Групповая скидка 15% применена (−${fmtRub(groupDiscount)})`
                : 'От 5 человек — скидка 15% на бронь.'}
            </div>
          </div>

          <StickyCTA
            total={canContinue ? totalWithGroup : null}
            subtotal={canContinue && groupDiscount > 0 ? subtotal : null}
            ctaLabel="Далее →"
            ctaDisabled={!canContinue}
            hint="Перед оплатой проверьте, что VPN выключен."
            onCta={() => canContinue && onContinue && onContinue({ routeId, date, time, persons })} />
        </MobileShell>
        {infoRoute && <RouteInfoSheet route={infoRoute} onClose={() => setInfoRouteId(null)} />}
      </div>
    );
  }

  // ─── STEP 2 ────────────────────────────────────────────────────────
  function MobileBookingStep2({
    routeId, date = '', time = '', persons = 1,
    initialFirstName = '', initialLastName = '',
    initialPhone = '', initialEmail = '',
    initialAdult = false,
    onBack, onContinue
  }) {
    const ROUTES = getRoutes();
    const [firstName, setFirstName] = useState(initialFirstName);
    const [lastName, setLastName] = useState(initialLastName);
    const [phone, setPhone] = useState(initialPhone);
    const [email, setEmail] = useState(initialEmail);
    const [adult, setAdult] = useState(initialAdult);
    const [phoneTouched, setPhoneTouched] = useState(!!initialPhone);
    const [emailTouched, setEmailTouched] = useState(!!initialEmail);
    const route = ROUTES.find((r) => r.id === routeId) || ROUTES[0];
    if (!route) return null;
    const weekend = isWeekend(date);
    const calc = window.calcBookingTotal({ route, date, persons });
    const subtotal = calc.subtotal;
    const groupDiscount = calc.groupDiscount;
    const total = calc.total;

    const phoneError = phoneTouched && !!phone && !isValidPhone(phone) ? 'Полный номер: +7 и 10 цифр' : null;
    const emailError = emailTouched && !!email && !isValidEmail(email) ? 'В email нужны @ и точка, например name@mail.ru' : null;
    const canContinue =
      firstName.trim() && lastName.trim() &&
      isValidPhone(phone) && isValidEmail(email) && adult;

    return (
      <MobileShell step={2}>
        <div style={{ padding: '16px 20px 20px' }}>
          {/* Кнопка «← Назад» сверху убрана: навигация назад остаётся
              только в нижней sticky-панели (как в десктопе). */}
          <div style={{
            fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase',
            color: '#5a6660', marginBottom: 8
          }}>
            Шаг 2 из 3
          </div>

          <div style={{
            fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
            fontSize: 24, fontWeight: 800, letterSpacing: '-.01em', marginBottom: 4
          }}>Детали бронирования</div>
          <div style={{ fontSize: 13, color: '#5a6660', marginBottom: 18 }}>
            {route.name} · {fmtDate(date)} · {time} · {persons} {persons === 1 ? 'человек' : 'чел.'}
          </div>

          <div style={{ display: 'grid', gap: 12, marginBottom: 16 }}>
            <Field label="Имя*" value={firstName} onChange={setFirstName} placeholder="Имя" />
            <Field label="Фамилия*" value={lastName} onChange={setLastName} placeholder="Фамилия" />
            <Field
              label="Номер телефона*"
              value={phone}
              onChange={(v) => {
                const fmt = window.formatRussianPhone ? window.formatRussianPhone(v) : v;
                setPhone(fmt);
                if (phoneTouched) setPhoneTouched(true);
              }}
              onBlur={() => setPhoneTouched(true)}
              placeholder="+7 (___) ___-__-__"
              type="tel"
              error={phoneError} />
            <Field
              label="Электронная почта*"
              value={email}
              onChange={(v) => { setEmail(v); if (emailTouched) setEmailTouched(true); }}
              onBlur={() => setEmailTouched(true)}
              placeholder="name@mail.ru"
              type="email"
              error={emailError} />
          </div>

          {/* 18+ */}
          <button
            onClick={() => setAdult((v) => !v)}
            style={{
              width: '100%', appearance: 'none', cursor: 'pointer', textAlign: 'left',
              background: '#fff', border: 'none', borderRadius: 12, padding: '14px',
              boxShadow: '0 1px 0 rgba(0,0,0,.04)',
              display: 'flex', alignItems: 'flex-start', gap: 12, fontFamily: 'inherit', marginBottom: 10
            }}>
            <span style={{
              width: 22, height: 22, borderRadius: 6, flexShrink: 0,
              border: `1.5px solid ${adult ? '#DD3310' : 'rgba(10,46,31,.25)'}`,
              background: adult ? '#DD3310' : 'transparent',
              display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              color: '#fff', fontSize: 13, fontWeight: 600
            }}>{adult && '✓'}</span>
            <span style={{ fontSize: 13, color: '#0A2E1F', lineHeight: 1.4, paddingTop: 2 }}>
              Подтверждаю, что всем участникам больше 18 лет
            </span>
          </button>
          <div style={{ fontSize: 11, color: '#7a8682', lineHeight: 1.5, marginBottom: 4 }}>
            Если участникам меньше 18 лет, запись производится через
            <a href="#tg" onClick={(e) => e.preventDefault()} style={{ color: '#DD3310', textDecoration: 'underline', textUnderlineOffset: 2, marginLeft: 4 }}>
              Telegram
            </a>, так как на некоторых маршрутах действуют ограничения.
          </div>
        </div>

        <StickyCTA
          total={total}
          subtotal={groupDiscount > 0 ? subtotal : null}
          ctaLabel="Далее →"
          ctaDisabled={!canContinue}
          onCta={() => canContinue && onContinue && onContinue({ firstName, lastName, phone, email })}
          onBack={onBack} />
      </MobileShell>
    );
  }

  // ─── STEP 3 ────────────────────────────────────────────────────────
  function MobileBookingStep3({
    routeId, date = '', time = '', persons = 1,
    firstName = '', lastName = '', phone = '', email = '',
    initialPromo = '', initialPromoApplied = false,
    initialComment = '',
    initialAgreements = { personal: false, oferta: false, cancel: false },
    onBack, onBooked, paymentResult
  }) {
    const ROUTES = getRoutes();
    // Способ оплаты выбирается в Paykeeper'е после редиректа, на нашей
    // стороне его дублировать не нужно — отправляем без paymentMethod.
    const [promo, setPromo] = useState(initialPromo);
    const [promoPercent, setPromoPercent] = useState(initialPromoApplied ? 10 : 0);
    const [promoError, setPromoError] = useState(null);
    const [promoChecking, setPromoChecking] = useState(false);
    const promoApplied = promoPercent > 0;
    const applyPromo = async () => {
      if (!promo.trim() || promoChecking) return;
      setPromoChecking(true);
      setPromoError(null);
      try {
        const res = await fetch('/api/booking/promo/check', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ code: promo.trim() })
        });
        const data = await res.json();
        if (data.valid) setPromoPercent(data.discountPercent);
        else { setPromoPercent(0); setPromoError(data.error || 'Промокод не подошёл'); }
      } catch { setPromoError('Ошибка сети'); }
      finally { setPromoChecking(false); }
    };
    const [comment, setComment] = useState(initialComment);
    const [agreements, setAgreements] = useState(initialAgreements);
    const [submitting, setSubmitting] = useState(false);
    const [submitError, setSubmitError] = useState(null);

    const route = ROUTES.find((r) => r.id === routeId) || ROUTES[0];
    if (!route) return null;
    const weekend = isWeekend(date);
    const calc = window.calcBookingTotal({ route, date, persons, promoPercent });
    const subtotal = calc.subtotal;
    const groupDiscount = calc.groupDiscount;
    const promoDiscount = calc.promoDiscount;
    const discount = calc.discount;
    const total = calc.total;
    const allAgreed = agreements.personal && agreements.oferta && agreements.cancel;

    const setAgr = (k) => setAgreements({ ...agreements, [k]: !agreements[k] });

    const handleSubmit = async () => {
      if (!allAgreed || submitting) return;
      setSubmitting(true);
      setSubmitError(null);
      try {
        const res = await fetch('/api/bookings', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            routeId, date, time, persons,
            firstName, lastName, phone, email,
            comment, adultConfirmed: true,
            promoCode: promoApplied ? promo : null,
            agreements
          })
        });
        const data = await res.json().catch(() => ({}));
        if (!res.ok) {
          setSubmitError(
            data.error === 'validation'
              ? 'Проверьте, что все поля заполнены.'
              : 'Не удалось создать заявку. Попробуйте ещё раз.'
          );
          return;
        }
        if (typeof onBooked === 'function') {
          onBooked({ bookingId: data.bookingId, paymentUrl: data.paymentUrl, total: data.total });
        }
      } catch (err) {
        setSubmitError('Ошибка сети. Проверьте интернет.');
      } finally {
        setSubmitting(false);
      }
    };

    return (
      <MobileShell step={3}>
        <div style={{ padding: '16px 20px 20px' }}>
          {/* Кнопка «← Назад» сверху убрана: навигация назад в нижней sticky-панели. */}
          <div style={{
            fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase',
            color: '#5a6660', marginBottom: 8
          }}>
            Шаг 3 из 3
          </div>

          <div style={{
            fontFamily: '"RF Dewi Extended", "NT Somic", sans-serif',
            fontSize: 24, fontWeight: 800, letterSpacing: '-.01em', marginBottom: 16
          }}>Подтверждение</div>

          {/* Order recap */}
          <div style={{
            background: '#fff', borderRadius: 14, padding: '14px 16px',
            boxShadow: '0 1px 0 rgba(0,0,0,.04)', marginBottom: 16
          }}>
            <Recap label="Маршрут" value={route.name} />
            <Recap label="Дата и время" value={`${fmtDate(date)}, ${time}`} sub={weekend ? 'выходной' : 'будни'} />
            <Recap label="Имя" value={`${firstName} ${lastName}`} />
            <Recap label="Телефон" value={phone} />
            <Recap label="Email" value={email} />
            <Recap label="Кол-во человек" value={persons} last />
          </div>

          {/* Promo */}
          <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
            Промокод
          </div>
          {promoApplied ? (
            <div style={{
              background: 'linear-gradient(90deg, #0B5D3B 0%, #0A2E1F 100%)', color: '#fff',
              borderRadius: 12, padding: '12px 14px',
              display: 'flex', alignItems: 'center', gap: 12, marginBottom: 16,
              boxShadow: '0 8px 22px -10px rgba(11,93,59,.5)'
            }}>
              <span style={{
                width: 26, height: 26, borderRadius: '50%',
                background: 'rgba(255,255,255,.18)',
                display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontSize: 13
              }}>✓</span>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 13, fontWeight: 500, letterSpacing: '.04em' }}>{promo.toUpperCase()} · −{promoPercent}%</div>
                <div style={{ fontSize: 11, opacity: .8, marginTop: 1 }}>−{fmtRub(promoDiscount)}</div>
              </div>
              <button
                onClick={() => { setPromoPercent(0); setPromo(''); setPromoError(null); }}
                style={{
                  appearance: 'none', border: 'none', cursor: 'pointer',
                  background: 'transparent', color: 'rgba(255,255,255,.7)',
                  fontSize: 11, fontFamily: 'inherit',
                  textDecoration: 'underline', textUnderlineOffset: 2
                }}>Убрать</button>
            </div>
          ) : (
            <div style={{ marginBottom: 16 }}>
            <div style={{
              background: '#fff', borderRadius: 12, padding: '8px 8px 8px 14px',
              boxShadow: '0 1px 0 rgba(0,0,0,.04)',
              display: 'flex', alignItems: 'center', gap: 8
            }}>
              <input
                value={promo}
                onChange={(e) => { setPromo(e.target.value); setPromoError(null); }}
                onKeyDown={(e) => { if (e.key === 'Enter') applyPromo(); }}
                placeholder="Введите промокод"
                style={{
                  flex: 1, border: 'none', outline: 'none', background: 'transparent',
                  fontFamily: 'inherit', fontSize: 14, color: '#0A2E1F',
                  letterSpacing: '.04em', textTransform: 'uppercase', minWidth: 0,
                  padding: '6px 0'
                }} />
              <button
                onClick={applyPromo}
                disabled={!promo || promoChecking}
                style={{
                  appearance: 'none', border: 'none', cursor: (promo && !promoChecking) ? 'pointer' : 'default',
                  background: 'rgba(10,46,31,.08)', color: '#0A2E1F',
                  padding: '8px 14px', borderRadius: 999,
                  fontSize: 12, fontWeight: 500, fontFamily: 'inherit',
                  opacity: (promo && !promoChecking) ? 1 : .5, flexShrink: 0
                }}>{promoChecking ? '…' : 'Применить'}</button>
            </div>
            {promoError && (
              <div style={{ fontSize: 11, color: '#DD3310', marginTop: 6 }}>{promoError}</div>
            )}
            </div>
          )}

          {/* Comment */}
          <div style={{ fontSize: 11, letterSpacing: '.18em', textTransform: 'uppercase', color: '#5a6660', marginBottom: 10 }}>
            Комментарий
          </div>
          <textarea
            value={comment}
            onChange={(e) => setComment(e.target.value)}
            rows={2}
            placeholder="Оставьте комментарий, например если у вас или ваших друзей День Рождения в день прогулки :)"
            style={{
              width: '100%', boxSizing: 'border-box',
              background: '#fff', border: 'none', outline: 'none',
              borderRadius: 12, padding: '12px 14px',
              boxShadow: '0 1px 0 rgba(0,0,0,.04)',
              fontFamily: 'inherit', fontSize: 14, color: '#0A2E1F',
              resize: 'none', lineHeight: 1.5, marginBottom: 16
            }} />

          {/* Agreements */}
          <div style={{ display: 'grid', gap: 8 }}>
            {[
              { k: 'personal', label: 'Согласен на обработку персональных данных' },
              { k: 'oferta',   label: 'Согласен с публичной офертой' },
              { k: 'cancel',   label: 'Согласен с условиями отмены и переноса' }
            ].map((a) => (
              <button
                key={a.k}
                onClick={() => setAgr(a.k)}
                style={{
                  width: '100%', appearance: 'none', cursor: 'pointer', textAlign: 'left',
                  background: 'transparent', border: 'none', padding: '6px 0',
                  display: 'flex', alignItems: 'flex-start', gap: 10, fontFamily: 'inherit'
                }}>
                <span style={{
                  width: 20, height: 20, borderRadius: 5, flexShrink: 0,
                  border: `1.5px solid ${agreements[a.k] ? '#DD3310' : 'rgba(10,46,31,.25)'}`,
                  background: agreements[a.k] ? '#DD3310' : 'transparent',
                  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
                  color: '#fff', fontSize: 12, fontWeight: 600
                }}>{agreements[a.k] && '✓'}</span>
                <span style={{
                  fontSize: 13, color: '#0A2E1F',
                  textDecoration: 'underline', textDecorationColor: 'rgba(10,46,31,.25)',
                  textUnderlineOffset: 3, lineHeight: 1.4, paddingTop: 1
                }}>{a.label}</span>
              </button>
            ))}
          </div>

          {paymentResult && paymentResult !== 'paid' && (
            <div style={{
              background: 'rgba(221,51,16,.08)', color: '#DD3310',
              borderRadius: 10, padding: '10px 14px',
              fontSize: 12, lineHeight: 1.4, marginTop: 14
            }}>
              {paymentResult === 'canceled' ? 'Оплата была отменена. Можно попробовать снова.'
                : paymentResult === 'expired' ? 'Время на оплату истекло. Можно попробовать снова.'
                : 'Оплата не прошла. Можно попробовать снова.'}
            </div>
          )}
          {submitError && (
            <div style={{
              background: 'rgba(221,51,16,.08)', color: '#DD3310',
              borderRadius: 10, padding: '10px 14px',
              fontSize: 12, lineHeight: 1.4, marginTop: 14
            }}>{submitError}</div>
          )}
        </div>

        <StickyCTA
          total={total}
          subtotal={discount > 0 ? subtotal : null}
          discount={discount}
          ctaLabel={submitting ? 'Отправляем…' : 'Забронировать'}
          ctaDisabled={!allAgreed || submitting}
          hint="После оплаты на email придёт памятка с деталями: что взять, точка сбора, время. VPN перед оплатой выключите."
          onCta={handleSubmit}
          onBack={onBack} />
      </MobileShell>
    );
  }

  function Recap({ label, value, sub, last }) {
    return (
      <div style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',
        padding: '10px 0', fontSize: 13, gap: 12,
        borderBottom: last ? 'none' : '1px solid rgba(10,46,31,.06)'
      }}>
        <span style={{ color: '#5a6660', flexShrink: 0 }}>{label}</span>
        <span style={{ color: '#0A2E1F', fontWeight: 500, textAlign: 'right' }}>
          {value}{sub && <div style={{ fontSize: 11, color: '#7a8682', fontWeight: 400, marginTop: 2 }}>{sub}</div>}
        </span>
      </div>
    );
  }

  Object.assign(window, { MobileBookingStep1, MobileBookingStep2, MobileBookingStep3 });
})();
