/* global React, I, S */
/* Pamp — Modals + form primitives */

const { useState: useModalState, useEffect: useModalEffect, createContext, useContext } = React;

/* ------------------------------ MODAL ------------------------------ */
function Modal({ open, onClose, title, subtitle, icon, size, children, footer, danger }) {
  useModalEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === "Escape") onClose?.(); };
    document.addEventListener("keydown", onKey);
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = ""; };
  }, [open]);
  if (!open) return null;
  return (
    <div className="modal-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose?.(); }}>
      <div className={`modal${size === "lg" ? " modal-lg" : size === "xl" ? " modal-xl" : ""}`} onClick={(e) => e.stopPropagation()}>
        {(title || icon) && (
          <div className="modal-head">
            <div style={{ flex: 1, display: "flex", gap: 14, alignItems: "flex-start" }}>
              {icon && <span style={{
                width: 44, height: 44, borderRadius: 12, flexShrink: 0,
                background: danger ? "var(--neg-soft)" : "var(--accent-soft)",
                color: danger ? "var(--neg)" : "var(--accent-ink)",
                display: "flex", alignItems: "center", justifyContent: "center",
              }}>{React.cloneElement(icon, { size: 20 })}</span>}
              <div style={{ flex: 1, paddingTop: icon ? 2 : 0 }}>
                {title && <h2 className="modal-title">{title}</h2>}
                {subtitle && <div className="modal-sub">{subtitle}</div>}
              </div>
            </div>
            <button onClick={onClose} className="icon-btn"><I.x size={18} /></button>
          </div>
        )}
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-foot">{footer}</div>}
      </div>
    </div>
  );
}

/* ------------------------------ FIELDS ----------------------------- */
function Field({ label, hint, error, children, full }) {
  return (
    <div style={{ gridColumn: full ? "span 2" : undefined }}>
      {label && <label className="field-label">{label}</label>}
      {children}
      {error ? <div className="field-error">{error}</div> : hint ? <div className="field-hint">{hint}</div> : null}
    </div>
  );
}

function FormGrid({ children, cols = 2 }) {
  return <div style={{ display: "grid", gridTemplateColumns: `repeat(${cols}, 1fr)`, gap: 16 }}>{children}</div>;
}

function TextField({ value, onChange, placeholder, type = "text", icon, suffix, ...p }) {
  if (icon || suffix) {
    return (
      <div className="text-input" style={{ display: "flex", alignItems: "center", gap: 8, padding: "0 14px" }}>
        {icon && <span style={{ color: "var(--ink-3)" }}>{icon}</span>}
        <input
          type={type} value={value} placeholder={placeholder}
          onChange={(e) => onChange?.(e.target.value)}
          style={{ flex: 1, height: "100%", border: 0, background: "transparent", outline: "none", color: "inherit", fontSize: 13.5 }}
          {...p}
        />
        {suffix}
      </div>
    );
  }
  return (
    <input className="text-input" type={type}
      value={value} placeholder={placeholder}
      onChange={(e) => onChange?.(e.target.value)} {...p} />
  );
}

function Textarea({ value, onChange, placeholder, rows = 4, ...p }) {
  return (
    <textarea className="text-input" rows={rows}
      value={value} placeholder={placeholder}
      onChange={(e) => onChange?.(e.target.value)} {...p} />
  );
}

function SelectPill({ value, onClick, placeholder = "Select…" }) {
  return (
    <button type="button" className="select-pill" onClick={onClick}>
      <span style={{ color: value ? "var(--ink)" : "var(--ink-4)" }}>{value || placeholder}</span>
      <I.chevD size={14} className="chev" />
    </button>
  );
}

function RadioCards({ value, onChange, options }) {
  return (
    <div className="radio-card-grid">
      {options.map((o) => (
        <div key={o.value} className="radio-card" data-checked={value === o.value} onClick={() => onChange(o.value)}>
          <div className="rc-dot" />
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 13.5, fontWeight: 500 }}>{o.label}</div>
            {o.desc && <div className="muted tiny" style={{ marginTop: 2 }}>{o.desc}</div>}
          </div>
          {o.right}
        </div>
      ))}
    </div>
  );
}

function Checkbox({ checked, onChange, label }) {
  return (
    <label style={{ display: "flex", alignItems: "center", gap: 10, cursor: "pointer", fontSize: 13 }}>
      <span className="cb-box" data-checked={!!checked} onClick={() => onChange?.(!checked)}>
        {checked && <I.check size={12} />}
      </span>
      <span>{label}</span>
    </label>
  );
}

function Segmented({ value, onChange, options }) {
  return (
    <div className="segmented">
      {options.map((o) => (
        <button key={o.value} className={value === o.value ? "active" : ""} onClick={() => onChange(o.value)}>{o.label}</button>
      ))}
    </div>
  );
}

function FileDrop({ label = "Drag & drop or click to upload", hint, icon = <I.export /> }) {
  return (
    <button type="button" className="file-drop" style={{ width: "100%" }}>
      <div style={{
        width: 44, height: 44, borderRadius: 12, margin: "0 auto 12px",
        background: "var(--accent-soft)", color: "var(--accent-ink)",
        display: "flex", alignItems: "center", justifyContent: "center",
      }}>{React.cloneElement(icon, { size: 18 })}</div>
      <div style={{ fontSize: 13.5, fontWeight: 500 }}>{label}</div>
      {hint && <div className="muted tiny" style={{ marginTop: 4 }}>{hint}</div>}
    </button>
  );
}

/* ------------------------------ MODAL HELPERS --------------------- */
function useModal(initialOpen = false) {
  const [open, setOpen] = React.useState(initialOpen);
  return { open, openModal: () => setOpen(true), closeModal: () => setOpen(false), setOpen };
}

/* ------------------------------ Confirm dialog -------------------- */
function ConfirmModal({ open, onClose, onConfirm, title, body, confirmLabel = "Confirm", cancelLabel = "Cancel", danger = false, icon }) {
  return (
    <Modal open={open} onClose={onClose} title={title} subtitle={body} icon={icon} danger={danger}
      footer={<>
        <S.Btn variant="ghost" onClick={onClose}>{cancelLabel}</S.Btn>
        <S.Btn variant={danger ? "primary" : "primary"} onClick={onConfirm}
          style={danger ? { background: "var(--neg)", color: "white", boxShadow: "none" } : {}}>{confirmLabel}</S.Btn>
      </>}
    />
  );
}

window.M = { Modal, Field, FormGrid, TextField, Textarea, SelectPill, RadioCards, Checkbox, Segmented, FileDrop, ConfirmModal, useModal };

/* ============================================================
   BOOKING MODAL  — vendor gallery + identity + services + staff + CTA
   ============================================================ */
function BookingModal({ open, vendor, onClose, onContinue }) {
  const [slide, setSlide] = React.useState(0);
  const [picked, setPicked] = React.useState([]);          // array of service indices
  const [pickedStaff, setPickedStaff] = React.useState(null); // staff index or null
  const slides = vendor?.gallery || [];

  React.useEffect(() => {
    if (open) { setSlide(0); setPicked([0]); setPickedStaff(null); }
  }, [open, vendor]);

  if (!open || !vendor) return null;

  const services = vendor.services || [];
  const staff = vendor.staff || [];

  const toggleSvc = (i) => setPicked((p) =>
    p.includes(i) ? p.filter((x) => x !== i) : [...p, i]
  );

  const pickedSvcs = picked.map((i) => services[i]).filter(Boolean);
  const total = pickedSvcs.reduce((s, x) => s + (x.price || 0), 0);
  const totalMin = pickedSvcs.reduce((s, x) => s + parseDuration(x.duration), 0);

  const next = () => setSlide((s) => (s + 1) % slides.length);
  const prev = () => setSlide((s) => (s - 1 + slides.length) % slides.length);

  const handleContinue = () => {
    onContinue?.({
      services: pickedSvcs,
      serviceIndices: picked,
      staff: pickedStaff != null ? staff[pickedStaff] : null,
    });
  };

  return (
    <Modal open={open} onClose={onClose} size="lg"
      footer={
        <div className="row between" style={{ width: "100%", alignItems: "center" }}>
          <div>
            {pickedSvcs.length > 0 ? (
              <>
                <div className="muted tiny">
                  {pickedSvcs.length} {pickedSvcs.length === 1 ? "service" : "services"} · {formatDuration(totalMin)}
                  {pickedStaff != null && staff[pickedStaff] && ` · with ${staff[pickedStaff].name.split(" ")[0]}`}
                </div>
                <div className="display" style={{ fontSize: 22, marginTop: 2 }}>${total}</div>
              </>
            ) : (
              <div className="muted tiny">Select at least one service to continue</div>
            )}
          </div>
          <div className="row" style={{ gap: 8 }}>
            <S.Btn variant="ghost" onClick={onClose}>Close</S.Btn>
            <S.Btn variant="primary" size="lg"
              iconRight={<I.arrowR size={14} />}
              onClick={handleContinue}
              disabled={pickedSvcs.length === 0}
              style={pickedSvcs.length === 0 ? { opacity: 0.5, pointerEvents: "none" } : {}}
            >
              Continue booking
            </S.Btn>
          </div>
        </div>
      }
    >
      {/* SECTION 1 — Gallery */}
      <div style={{
        position: "relative", borderRadius: 16, overflow: "hidden",
        height: 280, background: "var(--surface-3)", marginBottom: 24,
      }}>
        <div className="placeholder-img" style={{
          position: "absolute", inset: 0, borderRadius: 0, fontSize: 12,
          display: "flex", alignItems: "center", justifyContent: "center",
        }}>{slides[slide] || "vendor photo"}</div>

        {/* nav arrows */}
        {slides.length > 1 && <>
          <button onClick={prev} aria-label="Previous"
            style={bookNavBtn("left")}>
            <I.chevR size={16} style={{ transform: "rotate(180deg)" }} />
          </button>
          <button onClick={next} aria-label="Next"
            style={bookNavBtn("right")}>
            <I.chevR size={16} />
          </button>
        </>}

        {/* indicators */}
        {slides.length > 1 && (
          <div style={{
            position: "absolute", bottom: 14, left: 0, right: 0,
            display: "flex", justifyContent: "center", gap: 6,
          }}>
            {slides.map((_, i) => (
              <button key={i} onClick={() => setSlide(i)}
                style={{
                  width: i === slide ? 22 : 7, height: 7, borderRadius: 999,
                  background: i === slide ? "white" : "oklch(100% 0 0 / 0.55)",
                  transition: "all 0.2s",
                }} />
            ))}
          </div>
        )}

        {/* counter */}
        <div className="mono tiny" style={{
          position: "absolute", top: 14, right: 14,
          background: "oklch(0% 0 0 / 0.50)", color: "white",
          padding: "4px 10px", borderRadius: 999,
        }}>{slide + 1} / {slides.length || 1}</div>
      </div>

      {/* SECTION 2 — Vendor identity */}
      <div className="row" style={{ gap: 16, alignItems: "flex-start", marginBottom: 22 }}>
        <S.Avatar name={vendor.name} size="xl" tone="accent" />
        <div style={{ flex: 1, minWidth: 0 }}>
          <div className="row-tight" style={{ gap: 8 }}>
            <S.Chip tone="accent">
              <I.check size={11} style={{ marginRight: 4 }} />
              Verified
            </S.Chip>
            <span className="row-tight tiny">
              <I.star size={12} color="var(--warn)" />
              <span className="mono" style={{ fontWeight: 600 }}>{vendor.rating}</span>
              <span className="muted">({vendor.reviews?.toLocaleString?.() || vendor.reviews} reviews)</span>
            </span>
          </div>
          <div className="display" style={{ fontSize: 26, letterSpacing: "-0.02em", marginTop: 8 }}>
            {vendor.name}
          </div>
          <div className="muted" style={{ fontSize: 13.5, marginTop: 4 }}>{vendor.tagline}</div>
          <div className="row" style={{ marginTop: 12, gap: 18, color: "var(--ink-3)", fontSize: 12.5 }}>
            <span className="row-tight"><I.location size={13} /> {vendor.dist} away</span>
            <span className="row-tight"><I.clock size={13} /> Next slot · {vendor.next}</span>
          </div>
        </div>
        <S.IconBtn title="Save"><I.heart size={16} /></S.IconBtn>
      </div>

      <div className="divider" style={{ marginBottom: 22 }} />

      {/* SECTION 3 — Services (multi-select) */}
      <div style={{ marginBottom: 28 }}>
        <div className="row between" style={{ marginBottom: 14 }}>
          <div>
            <div className="h3">Choose a service, staff</div>
            <div className="muted tiny" style={{ marginTop: 2 }}>
              {services.length} services · select one or more
            </div>
          </div>
          {picked.length > 1 && (
            <S.Chip tone="accent">{picked.length} selected</S.Chip>
          )}
        </div>

        <div className="col" style={{ gap: 10 }}>
          {services.map((s, i) => {
            const active = picked.includes(i);
            return (
              <button key={s.name} onClick={() => toggleSvc(i)}
                style={{
                  display: "flex", alignItems: "center", gap: 16, textAlign: "left",
                  padding: 16, borderRadius: 14,
                  border: active ? "1.5px solid var(--accent)" : "1px solid var(--border)",
                  background: active ? "var(--accent-soft)" : "var(--surface)",
                  transition: "all 0.15s",
                  width: "100%", cursor: "pointer",
                }}>
                <span style={{
                  width: 22, height: 22, borderRadius: 7, flexShrink: 0,
                  border: active ? "1.5px solid var(--accent)" : "1.5px solid var(--border-strong)",
                  background: active ? "var(--accent)" : "transparent",
                  color: "oklch(20% 0.10 145)",
                  display: "flex", alignItems: "center", justifyContent: "center",
                  transition: "all 0.15s",
                }}>
                  {active && <I.check size={13} />}
                </span>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div className="row between">
                    <div style={{ fontSize: 14, fontWeight: 500 }}>{s.name}</div>
                    <div className="mono" style={{ fontSize: 14, fontWeight: 600 }}>${s.price}</div>
                  </div>
                  <div className="row" style={{ marginTop: 6, gap: 14, color: "var(--ink-3)", fontSize: 12 }}>
                    <span className="row-tight"><I.clock size={11} /> {s.duration}</span>
                    <span style={{ flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{s.desc}</span>
                  </div>
                </div>
              </button>
            );
          })}
        </div>
      </div>

      {/* SECTION 4 — Staff (optional single-select) */}
      {staff.length > 0 && (
        <div>
          <div className="row between" style={{ marginBottom: 14 }}>
            <div>
              <div className="h3">Staff</div>
              <div className="muted tiny" style={{ marginTop: 2 }}>
                Choose a preferred professional — or skip and we'll pick the best available
              </div>
            </div>
            {pickedStaff != null && (
              <button onClick={() => setPickedStaff(null)} className="btn btn-ghost btn-sm">
                Clear
              </button>
            )}
          </div>

          <div className="staff-row" style={{
            display: "flex", gap: 10, overflowX: "auto",
            paddingBottom: 6, scrollbarWidth: "thin",
          }}>
            <StaffTile
              active={pickedStaff == null}
              onClick={() => setPickedStaff(null)}
              any
            />
            {staff.map((m, i) => (
              <StaffTile
                key={m.name}
                active={pickedStaff === i}
                onClick={() => setPickedStaff(i)}
                member={m}
              />
            ))}
          </div>
        </div>
      )}
    </Modal>
  );
}

function StaffTile({ active, onClick, member, any }) {
  return (
    <button onClick={onClick}
      style={{
        flexShrink: 0, width: 124, padding: 14,
        borderRadius: 14,
        border: active ? "1.5px solid var(--accent)" : "1px solid var(--border)",
        background: active ? "var(--accent-soft)" : "var(--surface)",
        cursor: "pointer", transition: "all 0.15s",
        display: "flex", flexDirection: "column", alignItems: "center", gap: 10,
        position: "relative",
      }}>
      {active && (
        <span style={{
          position: "absolute", top: 8, right: 8,
          width: 18, height: 18, borderRadius: "50%",
          background: "var(--accent)", color: "oklch(20% 0.10 145)",
          display: "flex", alignItems: "center", justifyContent: "center",
        }}>
          <I.check size={11} />
        </span>
      )}
      {any ? (
        <div style={{
          width: 56, height: 56, borderRadius: "50%",
          background: "var(--surface-3)", color: "var(--ink-2)",
          display: "flex", alignItems: "center", justifyContent: "center",
          border: "1.5px dashed var(--border-strong)",
        }}>
          <I.sparkle size={22} />
        </div>
      ) : (
        <S.Avatar name={member.name} size="xl" />
      )}
      <div style={{ textAlign: "center", width: "100%" }}>
        <div style={{ fontSize: 13, fontWeight: 500, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
          {any ? "Any available" : member.name}
        </div>
        <div className="muted tiny" style={{ marginTop: 2, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>
          {any ? "Auto-match" : member.role}
        </div>
      </div>
    </button>
  );
}

/* parse "90 min" or "2h 30m" to minutes */
function parseDuration(str = "") {
  let total = 0;
  const h = str.match(/(\d+)\s*h/i); if (h) total += parseInt(h[1]) * 60;
  const m = str.match(/(\d+)\s*m(in)?/i); if (m) total += parseInt(m[1]);
  return total;
}
function formatDuration(min) {
  if (!min) return "";
  const h = Math.floor(min / 60), m = min % 60;
  if (h && m) return `${h}h ${m}m`;
  if (h) return `${h}h`;
  return `${m} min`;
}

function bookNavBtn(side) {
  return {
    position: "absolute", top: "50%", transform: "translateY(-50%)",
    [side]: 14,
    width: 36, height: 36, borderRadius: "50%",
    background: "oklch(100% 0 0 / 0.95)", color: "var(--ink)",
    display: "flex", alignItems: "center", justifyContent: "center",
    boxShadow: "0 4px 14px oklch(0% 0 0 / 0.18)",
    cursor: "pointer",
  };
}

window.M.BookingModal = BookingModal;

/* ============================================================
   BOOKING CALENDAR MODAL — smart date + time picker
   ============================================================ */
const MONTH_NAMES = ["January", "February", "March", "April", "May", "June",
  "July", "August", "September", "October", "November", "December"];
const DOW_SHORT = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const DOW_FULL = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];

const TODAY = new Date(2026, 1, 2); // anchor — "today" inside Pamp
TODAY.setHours(0, 0, 0, 0);

/* deterministic pseudo-random based on date for "smart" availability */
function seedFor(date) { return (date.getFullYear() * 372 + date.getMonth() * 31 + date.getDate()); }
function isPast(d) { return d.getTime() < TODAY.getTime(); }

function BookingCalendarModal({ open, vendor, services, staff, onClose, onBack, onContinue }) {
  const [view, setView] = React.useState({ y: TODAY.getFullYear(), m: TODAY.getMonth() });
  const [date, setDate] = React.useState(new Date(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDate() + 1));
  const [time, setTime] = React.useState(null);

  React.useEffect(() => {
    if (open) {
      const d = new Date(TODAY.getFullYear(), TODAY.getMonth(), TODAY.getDate() + 1);
      setDate(d);
      setView({ y: d.getFullYear(), m: d.getMonth() });
      setTime(null);
    }
  }, [open, vendor]);

  // Next available suggestion — computed before any early return to keep hook order stable
  const nextAvailable = React.useMemo(() => {
    for (let i = 0; i < 14; i++) {
      const d = new Date(TODAY); d.setDate(TODAY.getDate() + i);
      if (d.getDay() !== 0 && !isPast(d)) {
        return d;
      }
    }
    return null;
  }, []);

  if (!open || !vendor) return null;

  const firstDow = (new Date(view.y, view.m, 1).getDay() + 6) % 7; // Mon-start
  const daysInMonth = new Date(view.y, view.m + 1, 0).getDate();
  const prevMonthDays = new Date(view.y, view.m, 0).getDate();

  const cells = [];
  for (let i = 0; i < firstDow; i++) {
    cells.push({ day: prevMonthDays - firstDow + 1 + i, outside: true,
      date: new Date(view.y, view.m - 1, prevMonthDays - firstDow + 1 + i) });
  }
  for (let d = 1; d <= daysInMonth; d++) {
    cells.push({ day: d, outside: false, date: new Date(view.y, view.m, d) });
  }
  while (cells.length % 7 !== 0 || cells.length < 42) {
    const i = cells.length - daysInMonth - firstDow + 1;
    cells.push({ day: i, outside: true, date: new Date(view.y, view.m + 1, i) });
    if (cells.length >= 42) break;
  }

  const sameDay = (a, b) => a && b && a.toDateString() === b.toDateString();
  const stepMonth = (delta) => setView((v) => {
    const nv = new Date(v.y, v.m + delta, 1);
    return { y: nv.getFullYear(), m: nv.getMonth() };
  });

  // Smart slot availability — deterministic per date
  const slotsForDate = (d) => {
    const seed = seedFor(d);
    const slots = {
      Morning: ["9:00", "9:30", "10:00", "10:30", "11:00", "11:30"],
      Afternoon: ["12:00", "12:30", "1:00", "1:30", "2:00", "2:30", "3:00", "3:30"],
      Evening: ["4:00", "4:30", "5:00", "5:30", "6:00", "6:30", "7:00"],
    };
    const out = {};
    Object.entries(slots).forEach(([k, list]) => {
      out[k] = list.map((t, i) => ({
        time: t,
        busy: ((seed + i * 7) % 5) === 0,
        recommended: ((seed + i * 11) % 13) === 1,
      }));
    });
    return out;
  };

  // Dot indicator per day — # of available slots bucket
  const availabilityDot = (d) => {
    if (isPast(d) || d.getDay() === 0) return null; // closed Sundays
    const seed = seedFor(d);
    const avail = (seed % 9);
    if (avail >= 6) return "high";
    if (avail >= 3) return "mid";
    return "low";
  };

  const slots = date ? slotsForDate(date) : null;
  const isClosed = date && date.getDay() === 0;

  const handleContinue = () => {
    onContinue?.({ date, time });
  };

  return (
    <Modal open={open} onClose={onClose} size="xl"
      footer={
        <div className="row between" style={{ width: "100%", alignItems: "center" }}>
          <S.Btn variant="ghost" onClick={onBack} icon={<I.chevR size={13} style={{ transform: "rotate(180deg)" }} />}>
            Back
          </S.Btn>
          <div className="row" style={{ gap: 16, alignItems: "center" }}>
            {date && time ? (
              <div style={{ textAlign: "right" }}>
                <div className="muted tiny">{DOW_FULL[date.getDay()]}, {MONTH_NAMES[date.getMonth()]} {date.getDate()}</div>
                <div className="display" style={{ fontSize: 20, marginTop: 2 }}>{time}</div>
              </div>
            ) : (
              <div className="muted tiny">Pick a date and time to continue</div>
            )}
            <S.Btn variant="primary" size="lg"
              iconRight={<I.arrowR size={14} />}
              onClick={handleContinue}
              disabled={!date || !time}
              style={(!date || !time) ? { opacity: 0.5, pointerEvents: "none" } : {}}
            >
              Continue booking
            </S.Btn>
          </div>
        </div>
      }
    >
      {/* Header */}
      <div className="row between" style={{ alignItems: "flex-start", marginBottom: 22 }}>
        <div>
          <div className="eyebrow" style={{ marginBottom: 6 }}>Step 2 of 3 · Date &amp; time</div>
          <div className="display" style={{ fontSize: 28, letterSpacing: "-0.02em" }}>
            When works for you?
          </div>
          <div className="muted" style={{ fontSize: 13.5, marginTop: 4 }}>
            {vendor.name} · {services.length} {services.length === 1 ? "service" : "services"}
            {staff && ` · with ${staff.name.split(" ")[0]}`}
          </div>
        </div>
        {nextAvailable && (
          <button onClick={() => {
            setDate(nextAvailable);
            setView({ y: nextAvailable.getFullYear(), m: nextAvailable.getMonth() });
          }} style={{
            display: "flex", alignItems: "center", gap: 10, padding: "10px 14px",
            borderRadius: 12, border: "1px solid var(--accent)",
            background: "var(--accent-soft)", color: "var(--accent-ink)",
            cursor: "pointer",
          }}>
            <I.sparkle size={14} />
            <div style={{ textAlign: "left" }}>
              <div className="mono tiny" style={{ opacity: 0.7, letterSpacing: "0.06em" }}>SUGGESTED</div>
              <div style={{ fontSize: 13, fontWeight: 500, marginTop: 1 }}>
                Next available · {DOW_SHORT[(nextAvailable.getDay() + 6) % 7]} {nextAvailable.getDate()}
              </div>
            </div>
          </button>
        )}
      </div>

      <div style={{ display: "grid", gridTemplateColumns: "1.05fr 1fr", gap: 28, alignItems: "flex-start" }}>
        {/* ---------- CALENDAR ---------- */}
        <div>
          <div className="row between" style={{ marginBottom: 16 }}>
            <div className="display" style={{ fontSize: 20 }}>
              {MONTH_NAMES[view.m]} <span className="muted">{view.y}</span>
            </div>
            <div className="row" style={{ gap: 4 }}>
              <button onClick={() => stepMonth(-1)} className="icon-btn" aria-label="Previous month">
                <I.chevR size={14} style={{ transform: "rotate(180deg)" }} />
              </button>
              <button onClick={() => stepMonth(1)} className="icon-btn" aria-label="Next month">
                <I.chevR size={14} />
              </button>
            </div>
          </div>

          <div style={{
            display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4,
            marginBottom: 6,
          }}>
            {DOW_SHORT.map((d) => (
              <div key={d} className="mono tiny muted" style={{
                textAlign: "center", padding: "6px 0", letterSpacing: "0.06em",
              }}>{d.toUpperCase()}</div>
            ))}
          </div>

          <div style={{ display: "grid", gridTemplateColumns: "repeat(7, 1fr)", gap: 4 }}>
            {cells.map((c, i) => {
              const past = isPast(c.date);
              const closed = c.date.getDay() === 0;
              const today = sameDay(c.date, TODAY);
              const selected = sameDay(c.date, date);
              const disabled = past || closed;
              const dot = availabilityDot(c.date);

              return (
                <button key={i}
                  disabled={disabled}
                  onClick={() => {
                    setDate(c.date);
                    if (c.outside) setView({ y: c.date.getFullYear(), m: c.date.getMonth() });
                    setTime(null);
                  }}
                  style={{
                    position: "relative",
                    aspectRatio: "1/1",
                    borderRadius: 12,
                    border: selected ? "1.5px solid var(--accent)" :
                            today ? "1.5px solid var(--border-strong)" :
                            "1px solid transparent",
                    background: selected ? "var(--accent)" :
                                today ? "var(--surface-2)" : "transparent",
                    color: selected ? "oklch(20% 0.10 145)" :
                           disabled ? "var(--ink-4)" :
                           c.outside ? "var(--ink-4)" : "var(--ink)",
                    cursor: disabled ? "not-allowed" : "pointer",
                    transition: "all 0.15s",
                    fontFamily: "var(--font-mono)",
                    fontSize: 13,
                    fontWeight: today || selected ? 600 : 400,
                    display: "flex", alignItems: "center", justifyContent: "center",
                    textDecoration: disabled && !closed ? "line-through" : "none",
                  }}
                  onMouseEnter={(e) => {
                    if (!disabled && !selected) e.currentTarget.style.background = "var(--surface-3)";
                  }}
                  onMouseLeave={(e) => {
                    if (!disabled && !selected) e.currentTarget.style.background = today ? "var(--surface-2)" : "transparent";
                  }}
                >
                  {c.day}
                  {dot && !selected && !c.outside && (
                    <span style={{
                      position: "absolute", bottom: 6, left: "50%", transform: "translateX(-50%)",
                      width: 4, height: 4, borderRadius: "50%",
                      background: dot === "high" ? "var(--pos)" :
                                  dot === "mid" ? "var(--warn)" : "var(--ink-4)",
                    }} />
                  )}
                </button>
              );
            })}
          </div>

          {/* Legend */}
          <div className="row" style={{ gap: 16, marginTop: 20, fontSize: 11.5 }}>
            <span className="row-tight muted">
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--pos)" }} />
              Open
            </span>
            <span className="row-tight muted">
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--warn)" }} />
              Limited
            </span>
            <span className="row-tight muted">
              <span style={{ width: 6, height: 6, borderRadius: "50%", background: "var(--ink-4)" }} />
              Few left
            </span>
            <span className="row-tight muted" style={{ marginLeft: "auto" }}>
              <I.clock size={11} />
              Closed Sundays
            </span>
          </div>
        </div>

        {/* ---------- TIME SLOTS ---------- */}
        <div style={{
          borderLeft: "1px solid var(--border)",
          paddingLeft: 28,
        }}>
          <div className="row between" style={{ marginBottom: 18 }}>
            <div>
              <div className="display" style={{ fontSize: 18 }}>
                {date ? DOW_FULL[date.getDay()] : "Select a date"}
              </div>
              {date && (
                <div className="muted tiny" style={{ marginTop: 2 }}>
                  {MONTH_NAMES[date.getMonth()]} {date.getDate()}, {date.getFullYear()}
                </div>
              )}
            </div>
            <S.Chip tone="pos">
              <span className="dot" style={{ background: "currentColor" }} />
              {isClosed ? "Closed" : "Open today"}
            </S.Chip>
          </div>

          {isClosed ? (
            <div style={{
              padding: 32, textAlign: "center", borderRadius: 14,
              border: "1px dashed var(--border)", color: "var(--ink-3)",
            }}>
              <I.clock size={22} style={{ marginBottom: 10, opacity: 0.5 }} />
              <div style={{ fontSize: 13.5 }}>This vendor is closed on Sundays.</div>
              <div className="muted tiny" style={{ marginTop: 4 }}>Pick another day to see available times.</div>
            </div>
          ) : slots && (
            <div className="col" style={{ gap: 18 }}>
              {Object.entries(slots).map(([bucket, list]) => (
                <div key={bucket}>
                  <div className="row between" style={{ marginBottom: 10 }}>
                    <div className="eyebrow">{bucket}</div>
                    <span className="muted tiny">
                      {list.filter((s) => !s.busy).length} available
                    </span>
                  </div>
                  <div style={{
                    display: "grid",
                    gridTemplateColumns: "repeat(auto-fill, minmax(70px, 1fr))",
                    gap: 6,
                  }}>
                    {list.map((s) => {
                      const active = time === s.time;
                      return (
                        <button key={s.time}
                          disabled={s.busy}
                          onClick={() => setTime(s.time)}
                          style={{
                            position: "relative",
                            padding: "10px 6px", borderRadius: 10,
                            fontSize: 12.5, fontFamily: "var(--font-mono)",
                            fontWeight: active ? 600 : 500,
                            border: active ? "1.5px solid var(--accent)" :
                                    s.busy ? "1px dashed var(--border)" :
                                    "1px solid var(--border)",
                            background: active ? "var(--accent)" :
                                        s.busy ? "transparent" : "var(--surface)",
                            color: active ? "oklch(20% 0.10 145)" :
                                   s.busy ? "var(--ink-4)" : "var(--ink)",
                            cursor: s.busy ? "not-allowed" : "pointer",
                            textDecoration: s.busy ? "line-through" : "none",
                            transition: "all 0.12s",
                          }}>
                          {s.time}
                          {s.recommended && !s.busy && !active && (
                            <span style={{
                              position: "absolute", top: -4, right: -4,
                              width: 10, height: 10, borderRadius: "50%",
                              background: "var(--accent)",
                              border: "2px solid var(--surface)",
                            }} />
                          )}
                        </button>
                      );
                    })}
                  </div>
                </div>
              ))}

              <div style={{
                padding: 12, borderRadius: 12,
                background: "var(--accent-soft)", color: "var(--accent-ink)",
                fontSize: 12.5, lineHeight: 1.5,
                display: "flex", gap: 10, alignItems: "flex-start",
              }}>
                <I.sparkle size={14} style={{ flexShrink: 0, marginTop: 2 }} />
                <div>
                  <strong>Smart tip · </strong>
                  Slots with a dot are recommended based on your past bookings and shorter wait times.
                </div>
              </div>
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
}

window.M.BookingCalendarModal = BookingCalendarModal;

/* ============================================================
   BOOKING DETAILS MODAL — summary + make payment
   ============================================================ */
function BookingDetailsModal({ open, vendor, services, staff, date, time, onClose, onBack, onPay }) {
  if (!open || !vendor) return null;

  const subtotal = (services || []).reduce((s, x) => s + (x.price || 0), 0);
  const totalMin = (services || []).reduce((s, x) => s + parseDuration(x.duration), 0);
  const serviceFee = Math.round(subtotal * 0.04);
  const total = subtotal + serviceFee;

  return (
    <Modal open={open} onClose={onClose} size="lg"
      footer={
        <div className="row between" style={{ width: "100%", alignItems: "center" }}>
          <S.Btn variant="ghost" onClick={onBack} icon={<I.chevR size={13} style={{ transform: "rotate(180deg)" }} />}>
            Back
          </S.Btn>
          <div className="row" style={{ gap: 16, alignItems: "center" }}>
            <div style={{ textAlign: "right" }}>
              <div className="muted tiny">Total</div>
              <div className="display" style={{ fontSize: 24, marginTop: 2 }}>${total}</div>
            </div>
            <S.Btn variant="primary" size="lg"
              iconRight={<I.arrowR size={14} />}
              onClick={onPay}>
              Make payment
            </S.Btn>
          </div>
        </div>
      }
    >
      <div className="eyebrow" style={{ marginBottom: 6 }}>Step 3 of 3 · Review &amp; pay</div>
      <div className="display" style={{ fontSize: 28, letterSpacing: "-0.02em", marginBottom: 4 }}>
        Confirm your booking
      </div>
      <div className="muted" style={{ fontSize: 13.5, marginBottom: 24 }}>
        Review every detail. You can cancel up to 24h before for a full refund.
      </div>

      {/* Vendor + when */}
      <div style={{
        padding: 18, borderRadius: 14, border: "1px solid var(--border)",
        marginBottom: 18,
      }}>
        <div className="row" style={{ gap: 14, alignItems: "flex-start" }}>
          <S.Avatar name={vendor.name} size="lg" tone="accent" />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 16, fontWeight: 500 }}>{vendor.name}</div>
            <div className="muted tiny" style={{ marginTop: 2 }}>{vendor.tagline}</div>
            <div className="row" style={{ marginTop: 8, gap: 16, color: "var(--ink-3)", fontSize: 12.5 }}>
              <span className="row-tight"><I.location size={12} /> {vendor.dist} away</span>
              <span className="row-tight"><I.star size={12} color="var(--warn)" /> {vendor.rating}</span>
            </div>
          </div>
        </div>

        <div className="divider" style={{ margin: "16px 0" }} />

        <div className="grid-2" style={{ gap: 14 }}>
          <DetailLine icon={<I.calendar size={13} />} label="Date">
            {date ? `${DOW_FULL[date.getDay()]}, ${MONTH_NAMES[date.getMonth()]} ${date.getDate()}` : "—"}
          </DetailLine>
          <DetailLine icon={<I.clock size={13} />} label="Time">
            {time || "—"} · {formatDuration(totalMin)}
          </DetailLine>
          <DetailLine icon={<I.users size={13} />} label="Staff">
            {staff ? (
              <span className="row-tight" style={{ gap: 8 }}>
                <S.Avatar name={staff.name} size="sm" />
                {staff.name}
              </span>
            ) : "Any available"}
          </DetailLine>
          <DetailLine icon={<I.location size={13} />} label="Location">
            21 Mott St, London
          </DetailLine>
        </div>
      </div>

      {/* Services */}
      <div style={{
        padding: 18, borderRadius: 14, border: "1px solid var(--border)",
        marginBottom: 18,
      }}>
        <div className="eyebrow" style={{ marginBottom: 14 }}>
          {services.length} {services.length === 1 ? "service" : "services"}
        </div>
        <div className="col" style={{ gap: 12 }}>
          {services.map((s, i) => (
            <div key={i} className="row between" style={{ alignItems: "flex-start" }}>
              <div style={{ flex: 1 }}>
                <div style={{ fontSize: 13.5, fontWeight: 500 }}>{s.name}</div>
                <div className="muted tiny" style={{ marginTop: 2 }}>
                  {s.duration} · {s.desc}
                </div>
              </div>
              <div className="mono" style={{ fontSize: 13.5, fontWeight: 500 }}>${s.price}</div>
            </div>
          ))}
        </div>
      </div>

      {/* Price breakdown */}
      <div style={{
        padding: 18, borderRadius: 14, border: "1px solid var(--border)",
        background: "var(--surface-2)", marginBottom: 18,
      }}>
        <div className="col" style={{ gap: 10 }}>
          <div className="row between">
            <span className="muted" style={{ fontSize: 13 }}>Subtotal</span>
            <span className="mono" style={{ fontSize: 13.5 }}>${subtotal}</span>
          </div>
          <div className="row between">
            <span className="muted" style={{ fontSize: 13 }}>Pamp service fee</span>
            <span className="mono" style={{ fontSize: 13.5 }}>${serviceFee}</span>
          </div>
          <div className="divider" style={{ margin: "4px 0" }} />
          <div className="row between">
            <span style={{ fontSize: 14, fontWeight: 500 }}>Total</span>
            <span className="display" style={{ fontSize: 22 }}>${total}</span>
          </div>
        </div>
      </div>

      {/* Payment method */}
      <div className="eyebrow" style={{ marginBottom: 10 }}>Payment method</div>
      <div style={{
        padding: 14, borderRadius: 12, border: "1.5px solid var(--accent)",
        background: "var(--accent-soft)",
        display: "flex", alignItems: "center", gap: 14,
      }}>
        <div style={{
          width: 40, height: 28, borderRadius: 6, background: "var(--ink)",
          color: "white", display: "flex", alignItems: "center", justifyContent: "center",
          fontFamily: "var(--font-display)", fontSize: 10, fontWeight: 600,
        }}>VISA</div>
        <div style={{ flex: 1 }}>
          <div style={{ fontSize: 13.5, fontWeight: 500 }}>Visa ····2118</div>
          <div className="muted tiny" style={{ marginTop: 1 }}>Default · expires 11/27</div>
        </div>
        <button className="btn btn-ghost btn-sm">Change</button>
      </div>
    </Modal>
  );
}

function DetailLine({ icon, label, children }) {
  return (
    <div>
      <div className="row-tight muted tiny" style={{ gap: 6, marginBottom: 4 }}>
        {icon} {label}
      </div>
      <div style={{ fontSize: 13.5, fontWeight: 500 }}>{children}</div>
    </div>
  );
}

window.M.BookingDetailsModal = BookingDetailsModal;

/* ============================================================
   BOOKING SUCCESS MODAL — confirmation with smart info
   ============================================================ */
function BookingSuccessModal({ open, vendor, services, staff, date, time, onClose, onView }) {
  if (!open || !vendor) return null;

  const totalMin = (services || []).reduce((s, x) => s + parseDuration(x.duration), 0);
  const total = (services || []).reduce((s, x) => s + (x.price || 0), 0);
  const total2 = total + Math.round(total * 0.04);
  const ref = "PB-" + (28000 + Math.floor(seedFor(date || TODAY) % 999)).toString();

  // Smart info computed from booking context
  const smartInfo = [];
  if (staff) smartInfo.push({
    icon: <I.users size={14} />,
    text: <>Your professional <strong>{staff.name}</strong> has been notified.</>,
  });
  smartInfo.push({
    icon: <I.bell size={14} />,
    text: <>You'll get a reminder <strong>1 day before</strong> your appointment.</>,
  });
  if (date) {
    const arriveDate = date;
    const arriveBy = time ? `${time}` : "your slot";
    smartInfo.push({
      icon: <I.clock size={14} />,
      text: <>Arrive <strong>5 minutes before {arriveBy}</strong> on {MONTH_NAMES[arriveDate.getMonth()]} {arriveDate.getDate()}.</>,
    });
  }
  smartInfo.push({
    icon: <I.card size={14} />,
    text: <>Payment of <strong>${total2}</strong> charged to Visa ····2118. Receipt sent to your inbox.</>,
  });

  return (
    <Modal open={open} onClose={onClose} size="lg"
      footer={
        <div className="row between" style={{ width: "100%" }}>
          <S.Btn variant="ghost" onClick={onClose}>Close</S.Btn>
          <div className="row" style={{ gap: 8 }}>
            <S.Btn variant="outline" icon={<I.calendar size={13} />}>Add to calendar</S.Btn>
            <S.Btn variant="primary" iconRight={<I.arrowR size={13} />} onClick={onView}>
              View my bookings
            </S.Btn>
          </div>
        </div>
      }
    >
      {/* Hero */}
      <div style={{
        textAlign: "center", padding: "12px 0 28px",
      }}>
        <div style={{
          width: 88, height: 88, borderRadius: "50%",
          background: "var(--accent-soft)", color: "var(--accent-ink)",
          display: "inline-flex", alignItems: "center", justifyContent: "center",
          marginBottom: 22,
          position: "relative",
        }}>
          <span style={{
            position: "absolute", inset: -8, borderRadius: "50%",
            border: "1.5px solid var(--accent)", opacity: 0.4,
          }} />
          <span style={{
            position: "absolute", inset: -18, borderRadius: "50%",
            border: "1px solid var(--accent)", opacity: 0.18,
          }} />
          <I.check size={40} />
        </div>

        <S.Chip tone="pos" style={{ marginBottom: 14 }}>
          <span className="dot" style={{ background: "currentColor" }} />
          Confirmed · ref {ref}
        </S.Chip>

        <div className="display" style={{ fontSize: 32, letterSpacing: "-0.025em", lineHeight: 1.15, maxWidth: 540, margin: "8px auto 0" }}>
          You have successfully booked an appointment with <span style={{ color: "var(--accent-ink)" }}>{vendor.name}</span>.
        </div>

        {date && time && (
          <div className="muted" style={{ fontSize: 14, marginTop: 14 }}>
            {DOW_FULL[date.getDay()]}, {MONTH_NAMES[date.getMonth()]} {date.getDate()} · {time} · {formatDuration(totalMin)}
          </div>
        )}
      </div>

      {/* Quick summary strip */}
      <div className="row" style={{
        background: "var(--surface-2)", borderRadius: 14,
        padding: "16px 20px", marginBottom: 22,
        border: "1px solid var(--border)",
      }}>
        <SummaryStat label="Services" value={services.length} />
        <Divider />
        <SummaryStat label="Staff" value={staff ? staff.name.split(" ")[0] : "Any"} />
        <Divider />
        <SummaryStat label="Duration" value={formatDuration(totalMin)} />
        <Divider />
        <SummaryStat label="Total" value={`$${total2}`} accent />
      </div>

      {/* Smart info */}
      <div className="eyebrow" style={{ marginBottom: 12, display: "flex", alignItems: "center", gap: 6 }}>
        <I.sparkle size={12} /> Smart info
      </div>
      <div className="col" style={{ gap: 0, border: "1px solid var(--border)", borderRadius: 14, overflow: "hidden" }}>
        {smartInfo.map((s, i) => (
          <div key={i} className="row" style={{
            gap: 14, padding: "14px 16px",
            borderBottom: i === smartInfo.length - 1 ? "none" : "1px solid var(--border)",
            alignItems: "flex-start",
          }}>
            <div style={{
              width: 30, height: 30, borderRadius: 9, flexShrink: 0,
              background: "var(--accent-soft)", color: "var(--accent-ink)",
              display: "flex", alignItems: "center", justifyContent: "center",
            }}>{s.icon}</div>
            <div style={{ flex: 1, fontSize: 13.5, lineHeight: 1.55, paddingTop: 5 }}>{s.text}</div>
          </div>
        ))}
      </div>
    </Modal>
  );
}

function SummaryStat({ label, value, accent }) {
  return (
    <div style={{ flex: 1, textAlign: "center" }}>
      <div className="mono tiny muted" style={{ letterSpacing: "0.06em", textTransform: "uppercase" }}>{label}</div>
      <div className="display" style={{
        fontSize: 18, marginTop: 4,
        color: accent ? "var(--accent-ink)" : "var(--ink)",
      }}>{value}</div>
    </div>
  );
}
function Divider() {
  return <div style={{ width: 1, background: "var(--border)", margin: "4px 8px" }} />;
}

window.M.BookingSuccessModal = BookingSuccessModal;
