// Saraya Events — shared component atoms.
// React + Babel; exports to window for sibling <script> files.
// Visual language follows the Saraya design system (colors_and_type.css).

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

/* ---------- i18n hook ---------- */
// window.LangContext is created in the HTML bridge before this file loads.
function useLang() {
  const ctx = useContext(window.LangContext);
  return ctx;
}

/* ---------- Icon (Lucide via CDN) ---------- */
function Icon({ name, size = 20, stroke = 1.5, style, className }) {
  const ref = useRef(null);
  useEffect(() => {
    if (window.lucide && ref.current) {
      ref.current.innerHTML = '';
      const i = document.createElement('i');
      i.setAttribute('data-lucide', name);
      ref.current.appendChild(i);
      window.lucide.createIcons({ attrs: { width: size, height: size, 'stroke-width': stroke } });
    }
  }, [name, size, stroke]);
  return <span ref={ref} className={className} style={{ display: 'inline-flex', width: size, height: size, color: 'inherit', ...style }} />;
}

/* ---------- Logo ---------- */
function Logo({ height = 44, style }) {
  const logoSrc = (window.__resources && window.__resources.sarayaLogo) || 'assets/logo-primary.png';
  return <img src={logoSrc} alt="Saraya Events" style={{ ...{ height, display: 'block', ...style, width: "46px" }, height: "55px", width: "65px" }} />;
}

/* ---------- Branded image placeholder ----------
   Every image on the site is one of these. Pass `src` to show a real photo;
   otherwise a warm on-brand placeholder renders (gradient + faint icon + label).
   To use real photography later: set src on the data entry, e.g.
     <Img src="assets/photo/raw-022.jpg" ... />                                  */
const TONES = {
  cream: { g: 'linear-gradient(135deg,#FAF6F0 0%,#F1E8D9 55%,#E2D3BC 100%)', fg: 'var(--gold-deep)', dark: false },
  blush: { g: 'linear-gradient(135deg,#FBEFEF 0%,#F0D6D6 55%,#DBAFAF 100%)', fg: 'var(--rose-deep)', dark: false },
  rose: { g: 'linear-gradient(135deg,#F1D3D3 0%,#DDA9A9 55%,#B07878 100%)', fg: '#7A4B4B', dark: false },
  champagne: { g: 'linear-gradient(135deg,#F8F0DA 0%,#E7D29C 55%,#C9A961 100%)', fg: '#7A5E28', dark: false },
  sage: { g: 'linear-gradient(135deg,#ECE6D7 0%,#CFC6A9 55%,#9CAA8C 100%)', fg: '#4F5A43', dark: false },
  espresso: { g: 'linear-gradient(135deg,#4A3930 0%,#33251E 55%,#231914 100%)', fg: 'var(--gold-light)', dark: true }
};

function Img({ src, tone = 'cream', icon = 'flower-2', label, ratio = '4/3', radius = 16, className = '', style, caption, plain, slot, fit = 'cover' }) {
  const tk = TONES[tone] || TONES.cream;
  const admin = React.useContext(window.AdminContext);
  const override = slot && admin && admin.getImg ? admin.getImg(slot) : null;
  const finalSrc = override || src;
  const showOverlay = slot && admin && admin.editMode;
  const common = { aspectRatio: ratio, borderRadius: radius, overflow: 'hidden', position: 'relative', display: 'block' };

  if (finalSrc) {
    return (
      <div className={className} style={{ ...common, background: fit === 'contain' ? 'var(--cream)' : undefined, ...style }}>
        <img src={finalSrc} alt={label || ''} style={{ width: '100%', height: '100%', objectFit: fit, objectPosition: 'center', display: 'block' }} />
        {caption && <CaptionRibbon>{caption}</CaptionRibbon>}
        {showOverlay && <ImgEditOverlay admin={admin} slot={slot} hasImg={true} />}
      </div>);

  }
  return (
    <div className={className} style={{ ...common, background: tk.g, color: tk.fg, ...style }}>
      {/* soft light */}
      <span style={{ position: 'absolute', inset: 0, background: 'radial-gradient(120% 90% at 30% 18%, rgba(255,255,255,0.45), transparent 60%)' }} />
      {!plain &&
      <>
        {/* faint ornament icon */}
        <span style={{ position: 'absolute', inset: 0, display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: tk.dark ? 0.32 : 0.22 }}>
          <Icon name={icon} size={56} stroke={1} />
        </span>
        {/* hairline frame */}
        <span style={{ position: 'absolute', inset: 10, borderRadius: Math.max(radius - 8, 4), border: `1px solid ${tk.dark ? 'rgba(226,206,154,0.35)' : 'rgba(168,136,74,0.30)'}` }} />
        </>
      }
      {!plain && label &&
      <span style={{
        position: 'absolute', insetInlineStart: 16, bottom: 14, color: tk.fg,
        fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 600,
        letterSpacing: '0.18em', textTransform: 'uppercase'
      }}>{label}</span>
      }
      {showOverlay && <ImgEditOverlay admin={admin} slot={slot} hasImg={false} />}
    </div>);

}

/* Admin-only overlay: upload / replace / remove an image in a slot */
function ImgEditOverlay({ admin, slot, hasImg }) {
  const stop = (e) => { e.stopPropagation(); e.preventDefault(); };
  return (
    <div className="saraya-img-overlay" onClick={stop}
      style={{ position: 'absolute', inset: 0, zIndex: 15, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 8, background: 'rgba(42,31,26,0.5)', opacity: 0, transition: 'opacity 180ms var(--ease-out)', cursor: 'default' }}>
      <button onClick={(e) => { stop(e); admin.pickImage(slot); }}
        style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '9px 16px', borderRadius: 8, background: 'var(--white)', color: 'var(--fg-primary)', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 13, fontWeight: 600, boxShadow: 'var(--shadow-soft)' }}>
        <Icon name={hasImg ? 'repeat' : 'upload'} size={15} style={{ color: 'var(--gold-deep)' }} />{hasImg ? 'Replace' : 'Upload'}
      </button>
      {hasImg &&
      <button onClick={(e) => { stop(e); admin.removeImg(slot); }}
        style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '7px 14px', borderRadius: 8, background: 'rgba(255,255,255,0.15)', color: '#fff', border: '1px solid rgba(255,255,255,0.5)', cursor: 'pointer', fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 500 }}>
        <Icon name="trash-2" size={13} />Remove
      </button>
      }
    </div>);

}

function CaptionRibbon({ children }) {
  return (
    <span style={{
      position: 'absolute', insetInlineStart: 12, bottom: 10,
      background: 'rgba(42,31,26,0.66)', color: 'var(--ivory)',
      padding: '4px 9px', borderRadius: 4, fontSize: 10,
      letterSpacing: '0.1em', textTransform: 'uppercase', fontFamily: 'var(--font-body)'
    }}>{children}</span>);

}

/* ---------- Button ---------- */
function Button({ variant = 'primary', size = 'md', icon, iconRight, children, onClick, href, type = 'button', disabled, full, style, ...rest }) {
  const sizes = {
    sm: { padding: '9px 16px', fontSize: 13 },
    md: { padding: '13px 24px', fontSize: 14 },
    lg: { padding: '16px 32px', fontSize: 15 }
  };
  const variants = {
    primary: { background: 'var(--espresso)', color: 'var(--ivory)', border: '1px solid var(--espresso)' },
    gold: { background: 'var(--gold)', color: 'var(--espresso)', border: '1px solid var(--gold)' },
    whatsapp: { background: '#188041', color: '#fff', border: '1px solid #188041' },
    secondary: { background: 'transparent', color: 'var(--fg-primary)', border: '1px solid var(--line-strong)' },
    onDark: { background: 'var(--ivory)', color: 'var(--espresso)', border: '1px solid var(--ivory)' },
    ghost: { background: 'transparent', color: 'var(--fg-primary)', border: '1px solid transparent', padding: '8px 12px' }
  };
  const Comp = href ? 'a' : 'button';
  return (
    <Comp
      href={href} type={href ? undefined : type} onClick={onClick} disabled={disabled}
      className="saraya-btn"
      style={{
        fontFamily: 'var(--font-body)', fontWeight: 500, letterSpacing: '0.04em',
        borderRadius: 8, cursor: disabled ? 'not-allowed' : 'pointer', opacity: disabled ? 0.5 : 1,
        transition: 'all 200ms var(--ease-out)', display: full ? 'flex' : 'inline-flex',
        width: full ? '100%' : undefined, justifyContent: 'center',
        alignItems: 'center', gap: 9, textDecoration: 'none', whiteSpace: 'nowrap',
        ...sizes[size], ...variants[variant], ...style
      }}
      {...rest}>
      
      {icon && <Icon name={icon} size={size === 'lg' ? 19 : 17} />}
      {children}
      {iconRight && <Icon name={iconRight} size={size === 'lg' ? 19 : 17} />}
    </Comp>);

}

/* ---------- Eyebrow ---------- */
function Eyebrow({ children, color = 'var(--gold-deep)', center, style }) {
  return (
    <div style={{
      fontFamily: 'var(--font-body)', fontSize: 11.5, fontWeight: 600,
      letterSpacing: '0.28em', textTransform: 'uppercase', color,
      display: 'flex', alignItems: 'center', gap: 10,
      justifyContent: center ? 'center' : 'flex-start', ...style
    }}>
      <span style={{ width: 22, height: 1, background: 'currentColor', opacity: 0.55 }} />
      {children}
    </div>);

}

/* ---------- OrnamentRule (gold hairline + diamond) ---------- */
function OrnamentRule({ width = 200, style }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 12, width, color: 'var(--gold)', ...style }}>
      <span style={{ flex: 1, height: 1, background: 'currentColor', opacity: 0.5 }} />
      <span style={{ width: 6, height: 6, background: 'currentColor', transform: 'rotate(45deg)' }} />
      <span style={{ flex: 1, height: 1, background: 'currentColor', opacity: 0.5 }} />
    </div>);

}

/* ---------- Layout ---------- */
function Container({ children, narrow, wide, style, className }) {
  const max = narrow ? 880 : wide ? 1320 : 1200;
  return (
    <div className={className} style={{ maxWidth: max, margin: '0 auto', paddingInline: 'var(--page-gutter)', ...style }}>
      {children}
    </div>);

}

function Section({ children, bg = 'canvas', style, id, className }) {
  const bgs = {
    canvas: 'var(--bg-canvas)', muted: 'var(--cream)', tint: 'var(--gold-tint)',
    rose: 'var(--rose-tint)', dark: 'var(--espresso)', white: 'var(--white)'
  };
  return (
    <section id={id} className={className} style={{ background: bgs[bg], color: bg === 'dark' ? 'var(--ivory)' : undefined, paddingBlock: 'clamp(56px, 8vw, 104px)', ...style }}>
      {children}
    </section>);

}

/* SectionHead — eyebrow + serif title + optional intro */
function SectionHead({ eyebrow, title, intro, center, onDark, style }) {
  return (
    <div style={{ maxWidth: center ? 720 : 760, margin: center ? '0 auto' : undefined, textAlign: center ? 'center' : 'start', ...style }}>
      {eyebrow && <Eyebrow center={center} color={onDark ? 'var(--gold-light)' : 'var(--gold-deep)'}>{eyebrow}</Eyebrow>}
      {title && <h2 style={{ fontFamily: 'var(--font-display)', fontSize: 'clamp(30px,4.5vw,52px)', lineHeight: 1.08, fontWeight: 500, margin: '16px 0 0', color: onDark ? 'var(--ivory)' : 'var(--fg-primary)', letterSpacing: '-0.01em' }}>{title}</h2>}
      {intro && <p style={{ marginTop: 18, fontSize: 'clamp(16px,1.6vw,19px)', lineHeight: 1.65, color: onDark ? 'rgba(250,246,240,0.78)' : 'var(--fg-secondary)' }}>{intro}</p>}
    </div>);

}

/* ---------- Reveal (gentle fade-up on scroll) ---------- */
function Reveal({ children, delay = 0, as = 'div', style, className }) {
  const ref = useRef(null);
  const [seen, setSeen] = useState(false);
  useEffect(() => {
    const el = ref.current;if (!el) return;
    // already in view on mount?
    const r = el.getBoundingClientRect();
    if (r.top < (window.innerHeight || 800)) {setSeen(true);return;}
    const io = new IntersectionObserver((es) => {
      es.forEach((e) => {if (e.isIntersecting) {setSeen(true);io.disconnect();}});
    }, { threshold: 0.12, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    // safety: never leave content hidden
    const tm = setTimeout(() => setSeen(true), 1400);
    return () => {io.disconnect();clearTimeout(tm);};
  }, []);
  const Comp = as;
  return (
    <Comp ref={ref} className={className} style={{
      opacity: seen ? 1 : 0, transform: seen ? 'none' : 'translateY(18px)',
      transition: `opacity 600ms var(--ease-out) ${delay}ms, transform 600ms var(--ease-out) ${delay}ms`,
      ...style
    }}>{children}</Comp>);

}

/* ---------- Form atoms ---------- */
function inputStyle(err) {
  return {
    fontFamily: 'var(--font-body)', fontSize: 15, padding: '13px 15px', width: '100%',
    boxSizing: 'border-box', maxWidth: '100%',
    border: '1px solid ' + (err ? 'var(--error)' : 'var(--line-strong)'),
    background: 'var(--white)', borderRadius: 8, color: 'var(--fg-primary)', outline: 'none',
    transition: 'border-color 180ms var(--ease-out), box-shadow 180ms var(--ease-out)'
  };
}
function Field({ label, hint, optional, required, error, children }) {
  const { t } = useLang();
  return (
    <label style={{ display: 'grid', gap: 7 }}>
      {label && <span style={{ fontSize: 13.5, fontWeight: 500, color: error ? 'var(--error)' : 'inherit' }}>{label}{required && <span style={{ color: 'var(--error)', fontWeight: 700 }}> *</span>}{optional && <span style={{ color: 'var(--fg-muted)', fontWeight: 400 }}> · {t('form.optional')}</span>}</span>}
      {children}
      {hint && <span style={{ fontSize: 12, color: 'var(--fg-muted)' }}>{hint}</span>}
    </label>);

}
function TextInput(props) {
  const [f, setF] = useState(false);
  return <input {...props} onFocus={(e) => {setF(true);props.onFocus && props.onFocus(e);}} onBlur={(e) => {setF(false);props.onBlur && props.onBlur(e);}}
  style={{ ...inputStyle(props.error), ...(f ? { borderColor: 'var(--gold)', boxShadow: '0 0 0 3px var(--gold-tint)' } : {}), ...(props.style || {}) }} />;
}
function Textarea(props) {
  const [f, setF] = useState(false);
  return <textarea rows={props.rows || 4} {...props} onFocus={(e) => {setF(true);props.onFocus && props.onFocus(e);}} onBlur={(e) => {setF(false);props.onBlur && props.onBlur(e);}}
  style={{ ...inputStyle(props.error), resize: 'vertical', minHeight: 96, ...(f ? { borderColor: 'var(--gold)', boxShadow: '0 0 0 3px var(--gold-tint)' } : {}), ...(props.style || {}) }} />;
}
function Select({ children, ...props }) {
  return (
    <div style={{ position: 'relative' }}>
      <select {...props} style={{ ...inputStyle(props.error), appearance: 'none', paddingInlineEnd: 38, cursor: 'pointer', ...(props.style || {}) }}>{children}</select>
      <Icon name="chevron-down" size={16} style={{ position: 'absolute', insetInlineEnd: 14, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none', color: 'var(--fg-secondary)' }} />
    </div>);

}

/* Selectable tile (icon + label) — used in builder/quote */
function OptionTile({ active, icon, children, onClick, style }) {
  return (
    <button type="button" onClick={onClick} style={{
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 9,
      padding: '18px 12px', minHeight: 96, textAlign: 'center', cursor: 'pointer',
      background: active ? 'var(--white)' : 'var(--bg-canvas)',
      border: '1px solid ' + (active ? 'transparent' : 'var(--line)'),
      boxShadow: active ? '0 0 0 2px var(--gold), var(--shadow-soft)' : 'none',
      borderRadius: 14, color: 'var(--fg-primary)', fontFamily: 'var(--font-body)',
      transition: 'all 180ms var(--ease-out)'
    }}>
      {icon && <span style={{ color: active ? 'var(--gold-deep)' : 'var(--slate)' }}><Icon name={icon} size={24} /></span>}
      <span style={{ fontSize: 13.5, fontWeight: 500 }}>{children}</span>
    </button>);

}

/* Pill chip (multi-select) */
function Chip({ active, onClick, icon, children }) {
  return (
    <button type="button" onClick={onClick} style={{
      display: 'inline-flex', alignItems: 'center', gap: 7, padding: '9px 16px', borderRadius: 999,
      fontFamily: 'var(--font-body)', fontSize: 13.5, fontWeight: 500,
      background: active ? 'var(--espresso)' : 'var(--white)',
      color: active ? 'var(--ivory)' : 'var(--fg-primary)',
      border: '1px solid ' + (active ? 'var(--espresso)' : 'var(--line-strong)'),
      cursor: 'pointer', transition: 'all 180ms var(--ease-out)'
    }}>
      {icon && <Icon name={icon} size={15} />}{children}
    </button>);

}

function Badge({ tone = 'gold', children, style }) {
  const tones = {
    gold: { bg: 'var(--gold-tint)', fg: 'var(--gold-deep)' },
    rose: { bg: 'var(--rose-tint)', fg: 'var(--rose-deep)' },
    dark: { bg: 'var(--espresso)', fg: 'var(--ivory)' },
    cream: { bg: 'var(--cream)', fg: 'var(--fg-primary)' }
  };
  const tk = tones[tone] || tones.gold;
  return <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 12px', borderRadius: 999, fontFamily: 'var(--font-body)', fontSize: 11.5, fontWeight: 600, letterSpacing: '0.06em', background: tk.bg, color: tk.fg, ...style }}>{children}</span>;
}

/* ---------- RentalImageFrame (RENTALS ONLY) ----------
   Shared frame for rental item images so every rental card + the rental detail
   image look consistent: item shown in full (contain), centered, never cropped,
   on a pale-gold wash. Do NOT use for products, services, or marketplace cards.
   Pass `variant="detail"` for the rental detail-page main image.
   `src` = image url; `children` = fallback (e.g. an <Img> icon tile) when no src. */
function RentalImageFrame({ src, alt, variant, onClick, style, children }) {
  const cls = variant === 'detail' ? 'rental-detail-image-frame' : 'rental-image-frame';
  return (
    <div className={cls} onClick={onClick} style={{ ...(onClick ? { cursor: 'pointer' } : null), ...style }}>
      {src ? <img src={src} alt={alt || ''} /> : (children || null)}
    </div>
  );
}

/* ---------- Shared month calendar for availability / booking ----------
   mode="block"  → vendor taps a date to block/unblock it (blocked shown red).
   mode="select" → customer taps an available date to pick it; unavailable,
                   past, before-lead-time and non-working days are disabled.
   Props: value (selected YYYY-MM-DD), blocked/unavailable (array|Set of
   YYYY-MM-DD), workingDays (array 0..6, Sun..Sat), minDate (YYYY-MM-DD),
   onPick(dateStr), onMonthChange(fromStr,toStr), ar, busy. */
function AvailabilityCalendar({ mode = 'select', value, blocked, unavailable, workingDays, minDate, onPick, onMonthChange, ar, busy, rangeStart, rangeEnd, onRangePick, minDays }) {
  const today = new Date();
  const seed = (value || rangeStart) ? new Date((value || rangeStart) + 'T00:00:00') : today;
  const [cur, setCur] = useState({ y: seed.getFullYear(), m: seed.getMonth() });
  const blockedSet = blocked instanceof Set ? blocked : new Set(blocked || []);
  const unavailSet = unavailable instanceof Set ? unavailable : new Set(unavailable || []);
  const wd = Array.isArray(workingDays) && workingDays.length ? workingDays : [0, 1, 2, 3, 4, 5, 6];

  const pad = (n) => String(n).padStart(2, '0');
  const toStr = (y, m, d) => y + '-' + pad(m + 1) + '-' + pad(d);
  const todayStr = toStr(today.getFullYear(), today.getMonth(), today.getDate());
  const firstDow = new Date(cur.y, cur.m, 1).getDay();
  const daysInMonth = new Date(cur.y, cur.m + 1, 0).getDate();

  useEffect(() => {
    if (onMonthChange) onMonthChange(toStr(cur.y, cur.m, 1), toStr(cur.y, cur.m, daysInMonth));
    // eslint-disable-next-line
  }, [cur.y, cur.m]);

  const cells = [];
  for (let i = 0; i < firstDow; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);

  const monthLabel = new Date(cur.y, cur.m, 1).toLocaleDateString(ar ? 'ar' : 'en', { month: 'long', year: 'numeric' });
  const prev = () => setCur((p) => (p.m === 0 ? { y: p.y - 1, m: 11 } : { y: p.y, m: p.m - 1 }));
  const next = () => setCur((p) => (p.m === 11 ? { y: p.y + 1, m: 0 } : { y: p.y, m: p.m + 1 }));
  const dow = ar ? ['أحد', 'إثن', 'ثلا', 'أرب', 'خمي', 'جمع', 'سبت'] : ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
  const navBtn = { border: '1px solid var(--line)', background: 'var(--white)', cursor: 'pointer', padding: '4px 12px', borderRadius: 7, fontSize: 18, lineHeight: 1, color: 'var(--fg-primary)' };

  const dayStyle = (o) => {
    const base = { border: '1px solid var(--line)', borderRadius: 8, padding: '9px 0', fontSize: 13, fontFamily: 'var(--font-body)', cursor: o.disabled ? 'not-allowed' : 'pointer', background: 'var(--white)', color: 'var(--fg-primary)', width: '100%' };
    if (o.selected) return { ...base, background: 'var(--gold)', color: '#fff', borderColor: 'var(--gold)', fontWeight: 700 };
    if (o.inRange) return { ...base, background: 'var(--gold-tint)', color: 'var(--gold-deep)', borderColor: 'var(--gold-light)', fontWeight: 600 };
    if (o.blocked) return { ...base, background: '#FEE2E2', color: '#B91C1C', borderColor: '#FCA5A5', fontWeight: 600 };
    if (o.disabled) return { ...base, background: '#F3F4F6', color: '#C7C0B8', cursor: 'not-allowed' };
    return base;
  };

  // ── range-mode (multi-day) helpers ──
  const addDaysStr = (ds, n) => { const dt = new Date(ds + 'T00:00:00'); dt.setDate(dt.getDate() + n); return toStr(dt.getFullYear(), dt.getMonth(), dt.getDate()); };
  const spanHasUnavail = (a, b) => { let c = a; while (c <= b) { if (unavailSet.has(c)) return true; c = addDaysStr(c, 1); } return false; };
  const pickRange = (ds) => {
    if (!onRangePick) return;
    const md = Math.max(1, Number(minDays) || 1);
    const startFresh = () => { const auto = addDaysStr(ds, md - 1); (md > 1 && !spanHasUnavail(ds, auto)) ? onRangePick(ds, auto) : onRangePick(ds, null); };
    if (!rangeStart || (rangeStart && rangeEnd)) { startFresh(); return; }
    if (ds < rangeStart) { startFresh(); return; }
    if (!spanHasUnavail(rangeStart, ds)) onRangePick(rangeStart, ds); else onRangePick(ds, null);
  };

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
        <button type="button" onClick={prev} style={navBtn}>‹</button>
        <span style={{ fontWeight: 600, fontSize: 14 }}>{monthLabel}{busy ? ' …' : ''}</span>
        <button type="button" onClick={next} style={navBtn}>›</button>
      </div>
      <div style={{ border: '1px solid var(--line)', borderRadius: 12, padding: '12px 8px', background: 'var(--bg-tint)' }}>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 3, marginBottom: 6 }}>
          {dow.map((d, i) => <div key={i} style={{ textAlign: 'center', fontSize: 10.5, fontWeight: 700, color: 'var(--fg-muted)', padding: '2px 0' }}>{d}</div>)}
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: 3 }}>
          {cells.map((d, i) => {
            if (!d) return <div key={'e' + i} />;
            const ds = toStr(cur.y, cur.m, d);
            const dowIdx = new Date(cur.y, cur.m, d).getDay();
            const isPast = ds < todayStr;
            if (mode === 'block') {
              const isBlocked = blockedSet.has(ds);
              return <button key={ds} type="button" disabled={isPast} onClick={() => onPick && onPick(ds)} title={isBlocked ? (ar ? 'محجوب — اضغط للإتاحة' : 'Blocked — tap to open') : (ar ? 'متاح — اضغط للحجب' : 'Open — tap to block')} style={dayStyle({ disabled: isPast, blocked: isBlocked })}>{d}</button>;
            }
            const beforeMin = minDate ? ds < minDate : false;
            const nonWorking = !wd.includes(dowIdx);
            const off = unavailSet.has(ds) || isPast || beforeMin || nonWorking;
            if (mode === 'range') {
              const isEdge = rangeStart === ds || rangeEnd === ds;
              const inRange = rangeStart && rangeEnd && ds > rangeStart && ds < rangeEnd;
              return <button key={ds} type="button" disabled={off} onClick={() => pickRange(ds)} style={dayStyle({ disabled: off, selected: isEdge, inRange: inRange })}>{d}</button>;
            }
            return <button key={ds} type="button" disabled={off} onClick={() => onPick && onPick(ds)} style={dayStyle({ disabled: off, selected: value === ds })}>{d}</button>;
          })}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  useLang, Icon, Logo, Img, CaptionRibbon, Button, Eyebrow, OrnamentRule,
  Container, Section, SectionHead, Reveal, Field, TextInput, Textarea, Select,
  OptionTile, Chip, Badge, TONES, RentalImageFrame, AvailabilityCalendar
});

/* ---------- Vendor Agreement (shared: canonical bilingual text + downloadable document) ---------- */
/* Used by the vendor dashboard (sign + download) and the admin vendor detail (download). */
window.SarayaAgreement = (function () {
  var VERSION = '2.0';
  var TEXT_EN = `Saraya Events Marketplace — Vendor Agreement (v${VERSION})

This Vendor Agreement ("Agreement") is made between Saraya Events LLC, a company licensed in the Emirate of Abu Dhabi, United Arab Emirates ("Saraya", "the Platform"), and the vendor who accepts it electronically ("Vendor"). By ticking the acceptance box and signing electronically, the Vendor confirms that it has read, understood, and agreed to be legally bound by this Agreement.

1. Definitions
"Platform" means the Saraya Events website, marketplace and vendor dashboard. "Listing" means any product, rental or service the Vendor publishes. "Customer" means a buyer placing an Order. "Order" means a confirmed purchase or booking. "Commission" means Saraya's fee on each sale as set by the Vendor's subscription tier. "Payout" means the Vendor's share of an Order after Commission. "Content" means text, images and materials the Vendor uploads.

2. Eligibility and Vendor Representations
The Vendor represents that: (a) it is an authorised representative of a business validly registered in the UAE; (b) it holds and will maintain a valid UAE trade licence covering the activities it lists; (c) all registration and profile information is true and kept up to date; (d) it has full legal capacity and authority to enter into this Agreement; and (e) it is not subject to any sanction or legal bar preventing it from trading.

3. Nature of the Relationship
Saraya operates solely as an intermediary marketplace connecting Vendors and Customers. Saraya is not a party to the contract of sale formed between the Vendor and the Customer, and is not the seller, manufacturer, importer or supplier of any Listing. Nothing in this Agreement creates a partnership, agency, joint venture or employment relationship. The Vendor is the seller of record and is solely responsible for its Listings and their fulfilment.

4. Listings and Content
The Vendor is responsible for the accuracy, legality and completeness of every Listing, including descriptions, images, pricing, specifications, availability and delivery terms. The Vendor shall comply with all applicable UAE laws, including the Consumer Protection Law (Federal Law No. 15 of 2020) and its regulations. Saraya may review, approve, edit, reject, suspend or remove any Listing at its discretion, including for legal, safety, quality or policy reasons. The Vendor grants Saraya a non-exclusive, royalty-free licence to host, display, reproduce and promote its Content and business name across the Platform and Saraya's marketing channels for the purpose of operating and promoting the marketplace.

5. Prohibited Items and Conduct
The Vendor shall not list or supply any item that is illegal, unsafe, counterfeit, stolen, expired, recalled, or that infringes any third party's intellectual property or other rights, nor any item restricted or prohibited under UAE law. The Vendor shall not manipulate reviews, misrepresent products, or engage in any fraudulent, deceptive or anti-competitive conduct.

6. Pricing, Commission, Fees and VAT
The Vendor sets the retail price of each Listing, inclusive of any applicable Value Added Tax. Saraya charges Commission on each completed sale at the rate defined by the Vendor's subscription tier, together with any subscription or listing fees as published. Each party is responsible for its own tax obligations, including UAE VAT under Federal Decree-Law No. 8 of 2017 (as amended). Saraya may deduct Commission and applicable fees before releasing Payouts.

7. Orders and Fulfilment
The Vendor shall accept and fulfil confirmed Orders promptly and professionally, meeting the delivery, installation, timing and quality standards stated in the Listing and reasonably expected by the Customer. The Vendor is solely responsible for product quality, delivery, installation, warranty and after-sales service, and shall keep stock levels and availability accurate at all times.

8. Cancellations, Returns and Refunds
The Vendor shall operate a fair cancellation, return and refund policy consistent with the UAE Consumer Protection Law and Saraya's policies. The Vendor bears the cost of returns, replacements or refunds arising from defective, misdescribed, damaged, late or undelivered items, and shall cooperate promptly to resolve Customer complaints.

9. Payments and Payouts
Saraya (through its payment providers) collects Customer payments. Payouts are released to the Vendor, net of Commission and fees, after the Customer confirms delivery or completion, or on expiry of the three (3) day dispute window, whichever is earlier. Where automated payouts via Stripe Connect are enabled, the Vendor must complete the required identity and bank verification (KYC) before receiving Payouts. Saraya may withhold, delay, offset or reverse Payouts in cases of dispute, refund, chargeback, suspected fraud or breach of this Agreement.

10. Anti-Circumvention and No Off-Platform Solicitation
The Vendor shall not divert, solicit or encourage any Customer introduced through the Platform to transact off-platform for the purpose of avoiding Commission, and shall not use Customer contact details for unsolicited marketing or any purpose other than fulfilling the relevant Order. Breach of this clause is a material breach entitling Saraya to charge the Commission that would have been due, and to suspend or terminate the account.

11. Intellectual Property
The Vendor retains ownership of its own Content and warrants that it owns or is licensed to use all Content and marks it uploads, and that they do not infringe any third-party rights. The Saraya Events name, logo and platform remain the exclusive property of Saraya, and the Vendor acquires no rights in them beyond the limited use expressly permitted here.

12. Data Protection and Privacy
The Vendor shall comply with the UAE Personal Data Protection Law (Federal Decree-Law No. 45 of 2021) and process any Customer personal data received through the Platform solely to fulfil the relevant Order. The Vendor shall not sell, share or retain such data beyond what is necessary, shall keep it secure and confidential, and shall delete it on Saraya's request or when no longer required.

13. Confidentiality
Each party shall keep confidential all non-public information disclosed by the other, including commercial terms, Customer data and platform information, and shall not use it except to perform this Agreement.

14. Insurance
The Vendor shall maintain adequate insurance appropriate to its products, rentals and services, including public liability cover where relevant, and shall provide evidence of such insurance on request.

15. Warranties and Disclaimers
The Vendor warrants that its Listings are genuine, safe, lawful, accurately described and fit for their stated purpose. The Platform is provided on an "as is" and "as available" basis, and Saraya makes no warranty regarding sales volumes or uninterrupted availability.

16. Limitation of Liability
To the maximum extent permitted by law, Saraya shall not be liable for any indirect, incidental or consequential loss, or for loss of profit, goodwill or data, and shall not be liable for disputes between the Vendor and any Customer. Saraya's total aggregate liability to the Vendor under this Agreement shall not exceed the total Commission paid by the Vendor to Saraya in the three (3) months preceding the event giving rise to the claim.

17. Indemnification
The Vendor shall indemnify and hold harmless Saraya, its officers and employees against all claims, losses, damages, fines and expenses (including legal fees) arising from the Vendor's Listings, products, services, breach of this Agreement, or violation of any law or third-party right.

18. Suspension and Termination
Saraya may suspend or terminate the Vendor's account, with or without notice, for breach of this Agreement, repeated Customer complaints, an expired or invalid trade licence, illegal activity, or where required by law. The Vendor may terminate by giving written notice, subject to fulfilling all open Orders. On termination, the Vendor shall complete or refund outstanding Orders, and Saraya shall release any Payouts properly due after settlement of disputes, refunds and chargebacks. Clauses that by their nature should survive termination shall continue in effect.

19. Compliance
The Vendor shall comply with all applicable laws, including anti-bribery, anti-money laundering (Federal Decree-Law No. 20 of 2018) and sanctions laws, and shall not use the Platform for any unlawful purpose.

20. Force Majeure
Neither party is liable for any failure or delay caused by events beyond its reasonable control, including acts of God, war, civil unrest, epidemic, government action, or failure of utilities or telecommunications.

21. Amendments
Saraya may update this Agreement from time to time. Material changes will be notified through the Platform or by email and published as a new version. Continued use of the Platform, or acceptance of the new version, constitutes agreement to the updated terms. The version and date accepted by the Vendor are recorded electronically.

22. Notices
Notices may be given through the Platform, the vendor dashboard, or the email address on the Vendor's account, and are deemed received when sent.

23. Governing Law and Jurisdiction
This Agreement is governed by the federal laws of the United Arab Emirates as applied in the Emirate of Abu Dhabi. The parties submit to the exclusive jurisdiction of the courts of Abu Dhabi.

24. Electronic Acceptance
The Vendor accepts this Agreement electronically. The parties agree that electronic acceptance is valid and binding under the UAE Electronic Transactions and Trust Services Law (Federal Decree-Law No. 46 of 2021), and that Saraya's records of the version accepted, the date and time, and the acceptance details constitute admissible evidence of the Vendor's acceptance.

25. General
This Agreement, together with the Platform policies and the Vendor's subscription terms, constitutes the entire agreement between the parties and supersedes any prior agreements on its subject matter. If any provision is held invalid, the remainder continues in force. The Vendor may not assign this Agreement without Saraya's consent; Saraya may assign it to an affiliate or successor. No failure to enforce a right is a waiver of it. This Agreement is provided in English and Arabic; in case of any discrepancy, the Arabic version shall prevail before the UAE courts.`;

  var TEXT_AR = `اتفاقية البائع لسوق سرايا للفعاليات (الإصدار ${VERSION})

أُبرمت هذه الاتفاقية ("الاتفاقية") بين شركة سرايا إيفنتس ذ.م.م، وهي شركة مُرخّصة في إمارة أبوظبي بدولة الإمارات العربية المتحدة ("سرايا" أو "المنصة")، والبائع الذي يقبلها إلكترونياً ("البائع"). بتحديد خانة الموافقة والتوقيع إلكترونياً، يقرّ البائع بأنه قرأ الاتفاقية وفهمها ووافق على الالتزام بها قانونياً.

1. التعريفات
"المنصة" تعني موقع سرايا إيفنتس والسوق الإلكتروني ولوحة تحكم البائع. "القائمة" تعني أي منتج أو تأجير أو خدمة ينشرها البائع. "العميل" يعني المشتري الذي يقدّم طلباً. "الطلب" يعني عملية شراء أو حجز مؤكدة. "العمولة" تعني رسوم سرايا على كل عملية بيع وفقاً لباقة اشتراك البائع. "المستحقات" تعني حصة البائع من الطلب بعد خصم العمولة. "المحتوى" يعني النصوص والصور والمواد التي يرفعها البائع.

2. الأهلية وإقرارات البائع
يقرّ البائع بأن: (أ) أنه ممثل مفوّض عن منشأة مسجّلة تسجيلاً صحيحاً في دولة الإمارات؛ (ب) أنه يحمل ويحافظ على رخصة تجارية إماراتية سارية تغطي الأنشطة التي يدرجها؛ (ج) أن جميع بيانات التسجيل والملف التعريفي صحيحة ومحدّثة؛ (د) أنه يملك الأهلية والصلاحية القانونية الكاملة لإبرام هذه الاتفاقية؛ (هـ) أنه غير خاضع لأي عقوبة أو مانع قانوني يمنعه من ممارسة النشاط.

3. طبيعة العلاقة
تعمل سرايا بصفتها وسيطاً في السوق الإلكتروني يربط بين البائعين والعملاء فقط. سرايا ليست طرفاً في عقد البيع المبرم بين البائع والعميل، وليست البائع أو المُصنّع أو المستورد أو المورّد لأي قائمة. لا ينشئ أي بند في هذه الاتفاقية علاقة شراكة أو وكالة أو مشروع مشترك أو توظيف. البائع هو البائع المسجّل ويتحمّل وحده المسؤولية عن قوائمه وتنفيذها.

4. القوائم والمحتوى
يتحمّل البائع مسؤولية دقة وقانونية واكتمال كل قائمة، بما في ذلك الأوصاف والصور والأسعار والمواصفات والتوافر وشروط التسليم. يلتزم البائع بجميع القوانين الإماراتية المعمول بها، بما في ذلك قانون حماية المستهلك (القانون الاتحادي رقم 15 لسنة 2020) ولوائحه. يحق لسرايا مراجعة أو اعتماد أو تعديل أو رفض أو تعليق أو إزالة أي قائمة وفق تقديرها، بما في ذلك لأسباب قانونية أو تتعلق بالسلامة أو الجودة أو السياسات. يمنح البائع سرايا ترخيصاً غير حصري وبدون مقابل لاستضافة وعرض واستنساخ والترويج لمحتواه واسمه التجاري عبر المنصة وقنوات سرايا التسويقية بغرض تشغيل السوق والترويج له.

5. الأصناف والسلوكيات المحظورة
لا يجوز للبائع إدراج أو توريد أي صنف غير قانوني أو غير آمن أو مقلّد أو مسروق أو منتهي الصلاحية أو مسحوب من السوق، أو ينتهك حقوق الملكية الفكرية أو غيرها لأي طرف ثالث، أو أي صنف مقيّد أو محظور بموجب القانون الإماراتي. ولا يجوز له التلاعب بالتقييمات أو تحريف المنتجات أو ممارسة أي سلوك احتيالي أو مضلل أو مخل بالمنافسة.

6. الأسعار والعمولة والرسوم وضريبة القيمة المضافة
يحدّد البائع سعر البيع لكل قائمة شاملاً أي ضريبة قيمة مضافة مطبّقة. تتقاضى سرايا عمولة على كل عملية بيع مكتملة بالنسبة المحددة في باقة اشتراك البائع، إضافةً إلى أي رسوم اشتراك أو إدراج منشورة. يتحمّل كل طرف التزاماته الضريبية الخاصة، بما في ذلك ضريبة القيمة المضافة الإماراتية بموجب المرسوم بقانون اتحادي رقم 8 لسنة 2017 وتعديلاته. يجوز لسرايا خصم العمولة والرسوم المطبّقة قبل صرف المستحقات.

7. الطلبات والتنفيذ
يلتزم البائع بقبول وتنفيذ الطلبات المؤكدة بسرعة واحترافية، بما يفي بمعايير التسليم والتركيب والتوقيت والجودة المذكورة في القائمة والمتوقعة بشكل معقول من العميل. البائع وحده مسؤول عن جودة المنتج والتسليم والتركيب والضمان وخدمة ما بعد البيع، ويلتزم بالحفاظ على دقة مستويات المخزون والتوافر في جميع الأوقات.

8. الإلغاء والإرجاع والاسترداد
يلتزم البائع بتطبيق سياسة عادلة للإلغاء والإرجاع والاسترداد بما يتوافق مع قانون حماية المستهلك الإماراتي وسياسات سرايا. يتحمّل البائع تكلفة الإرجاع أو الاستبدال أو الاسترداد الناشئة عن الأصناف المعيبة أو المخالفة للوصف أو التالفة أو المتأخرة أو غير المسلّمة، ويتعاون بسرعة لحل شكاوى العملاء.

9. المدفوعات والمستحقات
تُحصّل سرايا (عبر مزوّدي الدفع لديها) مدفوعات العملاء. تُصرف المستحقات للبائع بعد خصم العمولة والرسوم، عقب تأكيد العميل للتسليم أو الإنجاز، أو عند انتهاء نافذة النزاع البالغة ثلاثة (3) أيام، أيهما أسبق. وحيثما تُفعّل المدفوعات التلقائية عبر Stripe Connect، يجب على البائع إتمام التحقق المطلوب من الهوية والحساب البنكي (اعرف عميلك) قبل استلام المستحقات. يجوز لسرايا حجز أو تأخير أو مقاصّة أو استرداد المستحقات في حالات النزاع أو الاسترداد أو ردّ المبالغ المدفوعة أو الاشتباه بالاحتيال أو الإخلال بهذه الاتفاقية.

10. عدم الالتفاف وعدم التعامل خارج المنصة
لا يجوز للبائع تحويل أو استمالة أو تشجيع أي عميل تم التعرّف عليه عبر المنصة على التعامل خارجها بهدف تجنّب العمولة، ولا يجوز له استخدام بيانات اتصال العملاء لأي تسويق غير مرغوب أو لأي غرض بخلاف تنفيذ الطلب المعني. يُعدّ الإخلال بهذا البند إخلالاً جوهرياً يخوّل سرايا تحصيل العمولة التي كانت مستحقة وتعليق الحساب أو إنهائه.

11. الملكية الفكرية
يحتفظ البائع بملكية محتواه ويضمن أنه يملك أو مرخّص له باستخدام كل المحتوى والعلامات التي يرفعها، وأنها لا تنتهك حقوق أي طرف ثالث. يبقى اسم وشعار ومنصة سرايا إيفنتس ملكاً حصرياً لسرايا، ولا يكتسب البائع أي حقوق فيها بخلاف الاستخدام المحدود المصرّح به هنا.

12. حماية البيانات والخصوصية
يلتزم البائع بقانون حماية البيانات الشخصية الإماراتي (المرسوم بقانون اتحادي رقم 45 لسنة 2021)، ويعالج أي بيانات شخصية للعملاء يتلقاها عبر المنصة لغرض تنفيذ الطلب المعني فقط. ولا يجوز له بيع أو مشاركة أو الاحتفاظ بهذه البيانات بما يتجاوز الضرورة، ويحافظ على سريتها وأمنها، ويحذفها بناءً على طلب سرايا أو عند انتفاء الحاجة إليها.

13. السرية
يلتزم كل طرف بالحفاظ على سرية جميع المعلومات غير العلنية التي يفصح عنها الطرف الآخر، بما في ذلك الشروط التجارية وبيانات العملاء ومعلومات المنصة، وعدم استخدامها إلا لتنفيذ هذه الاتفاقية.

14. التأمين
يلتزم البائع بالحفاظ على تأمين كافٍ يتناسب مع منتجاته وتأجيراته وخدماته، بما في ذلك تغطية المسؤولية تجاه الغير حيثما كان ذلك مناسباً، وتقديم ما يثبت ذلك عند الطلب.

15. الضمانات وإخلاء المسؤولية
يضمن البائع أن قوائمه أصلية وآمنة وقانونية وموصوفة بدقة وصالحة للغرض المذكور. تُقدَّم المنصة "كما هي" و"حسب توافرها"، ولا تقدّم سرايا أي ضمان بشأن حجم المبيعات أو استمرارية التوافر دون انقطاع.

16. تحديد المسؤولية
إلى أقصى حد يسمح به القانون، لا تتحمّل سرايا المسؤولية عن أي خسارة غير مباشرة أو تبعية أو عرضية، أو عن فقدان الأرباح أو السمعة أو البيانات، ولا تتحمّل المسؤولية عن النزاعات بين البائع وأي عميل. لا تتجاوز المسؤولية الإجمالية لسرايا تجاه البائع بموجب هذه الاتفاقية إجمالي العمولة التي دفعها البائع لسرايا خلال الأشهر الثلاثة (3) السابقة للحدث المنشئ للمطالبة.

17. التعويض
يلتزم البائع بتعويض سرايا ومسؤوليها وموظفيها وإبراء ذمتهم من كل المطالبات والخسائر والأضرار والغرامات والمصاريف (بما فيها الأتعاب القانونية) الناشئة عن قوائم البائع أو منتجاته أو خدماته أو إخلاله بهذه الاتفاقية أو مخالفته لأي قانون أو حق للغير.

18. التعليق والإنهاء
يجوز لسرايا تعليق أو إنهاء حساب البائع، بإشعار أو بدونه، عند الإخلال بهذه الاتفاقية أو تكرار شكاوى العملاء أو انتهاء أو بطلان الرخصة التجارية أو ممارسة نشاط غير قانوني أو حيثما يقتضي القانون ذلك. يجوز للبائع الإنهاء بموجب إشعار كتابي، مع الالتزام بتنفيذ جميع الطلبات القائمة. عند الإنهاء، يلتزم البائع بإتمام أو استرداد الطلبات القائمة، وتصرف سرايا أي مستحقات واجبة بعد تسوية النزاعات والاستردادات وردّ المبالغ. تظل البنود التي تستمر بطبيعتها بعد الإنهاء سارية المفعول.

19. الامتثال
يلتزم البائع بجميع القوانين المعمول بها، بما في ذلك قوانين مكافحة الرشوة ومكافحة غسل الأموال (المرسوم بقانون اتحادي رقم 20 لسنة 2018) والعقوبات، ولا يستخدم المنصة لأي غرض غير قانوني.

20. القوة القاهرة
لا يتحمّل أي طرف المسؤولية عن أي إخفاق أو تأخير ناتج عن أحداث خارجة عن سيطرته المعقولة، بما في ذلك الكوارث الطبيعية أو الحرب أو الاضطرابات المدنية أو الأوبئة أو الإجراءات الحكومية أو انقطاع المرافق أو الاتصالات.

21. التعديلات
يجوز لسرايا تحديث هذه الاتفاقية من وقت لآخر. يتم الإخطار بالتغييرات الجوهرية عبر المنصة أو البريد الإلكتروني وتُنشر كإصدار جديد. يُعدّ استمرار استخدام المنصة أو قبول الإصدار الجديد موافقةً على الشروط المحدّثة. يُسجَّل إلكترونياً الإصدار والتاريخ اللذان قبِلهما البائع.

22. الإخطارات
يجوز توجيه الإخطارات عبر المنصة أو لوحة تحكم البائع أو عنوان البريد الإلكتروني المسجّل في حساب البائع، وتُعتبر مستلمة عند إرسالها.

23. القانون الحاكم والاختصاص القضائي
تخضع هذه الاتفاقية للقوانين الاتحادية لدولة الإمارات العربية المتحدة كما تُطبَّق في إمارة أبوظبي. ويخضع الطرفان للاختصاص القضائي الحصري لمحاكم أبوظبي.

24. القبول الإلكتروني
يقبل البائع هذه الاتفاقية إلكترونياً. يتفق الطرفان على أن القبول الإلكتروني صحيح ومُلزم بموجب قانون المعاملات الإلكترونية وخدمات الثقة الإماراتي (المرسوم بقانون اتحادي رقم 46 لسنة 2021)، وأن سجلات سرايا الخاصة بالإصدار المقبول وتاريخ ووقت القبول وتفاصيله تُشكّل دليلاً مقبولاً على قبول البائع.

25. أحكام عامة
تشكّل هذه الاتفاقية، مع سياسات المنصة وشروط اشتراك البائع، الاتفاق الكامل بين الطرفين وتحل محل أي اتفاقات سابقة بشأن موضوعها. إذا اعتُبر أي بند باطلاً، يظل الباقي سارياً. لا يجوز للبائع التنازل عن هذه الاتفاقية دون موافقة سرايا؛ ويجوز لسرايا التنازل عنها لشركة تابعة أو خلف. لا يُعدّ عدم إنفاذ أي حق تنازلاً عنه. تُقدَّم هذه الاتفاقية باللغتين الإنجليزية والعربية؛ وفي حال وجود أي تعارض، تسود النسخة العربية أمام محاكم دولة الإمارات.`;

  function esc(s) { return (s == null ? '' : String(s)).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }

  function buildHtml(v) {
    v = v || {};
    var ver = v.version || VERSION;
    var signed = v.signedAt ? new Date(v.signedAt).toLocaleString('en-AE', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : null;
    var body = esc(TEXT_EN).replace(/\n/g, '<br>');
    var bodyAr = esc(TEXT_AR).replace(/\n/g, '<br>');
    return '<!doctype html><html><head><meta charset="utf-8"><title>Saraya Vendor Agreement — ' + esc(v.tradeName || '') + '</title>'
      + '<style>body{font-family:Georgia,"Times New Roman",serif;color:#2A1F1A;max-width:760px;margin:0 auto;padding:48px 32px;line-height:1.7;}'
      + 'h1{color:#c19a3e;font-size:22px;margin:0 0 4px;}.sub{color:#6B7280;font-size:13px;margin-bottom:24px;}'
      + '.doc{font-size:14px;}.ar{direction:rtl;text-align:right;}.sig{margin-top:32px;padding:16px 18px;border:1px solid #ece0c4;background:#FAF7EF;border-radius:10px;font-size:13.5px;}'
      + 'hr{margin:32px 0;border:none;border-top:1px solid #ece0c4;}@media print{body{padding:24px;}}</style></head><body>'
      + '<h1>Saraya Events</h1><div class="sub">Marketplace Vendor Agreement · Version ' + esc(ver) + '</div>'
      + '<div class="doc">' + body + '</div>'
      + '<hr>'
      + '<div class="doc ar">' + bodyAr + '</div>'
      + '<div class="sig">'
      + (v.tradeName ? '<div><b>Vendor:</b> ' + esc(v.tradeName) + '</div>' : '')
      + '<div><b>Agreement version:</b> ' + esc(ver) + '</div>'
      + (signed ? '<div><b>Signed &amp; accepted on:</b> ' + esc(signed) + '</div>' : '<div><b>Status:</b> Not yet signed</div>')
      + (v.signedIp ? '<div><b>IP address:</b> ' + esc(v.signedIp) + '</div>' : '')
      + '<div style="margin-top:8px;color:#6B7280;">Accepted electronically via the Saraya Events vendor dashboard. To keep a PDF copy, use your browser&#39;s Print → Save as PDF.</div>'
      + '</div></body></html>';
  }

  function downloadDoc(v) {
    v = v || {};
    try {
      var blob = new Blob([buildHtml(v)], { type: 'text/html;charset=utf-8' });
      var url = URL.createObjectURL(blob);
      var a = document.createElement('a');
      var safe = (v.tradeName || 'vendor').replace(/[^a-z0-9]+/gi, '-').replace(/^-+|-+$/g, '') || 'vendor';
      a.href = url; a.download = 'Saraya-Vendor-Agreement-' + safe + '.html';
      document.body.appendChild(a); a.click();
      setTimeout(function () { try { document.body.removeChild(a); URL.revokeObjectURL(url); } catch (e) {} }, 1500);
    } catch (e) { try { window.alert('Could not generate the agreement document.'); } catch (_) {} }
  }

  return { VERSION: VERSION, TEXT_EN: TEXT_EN, TEXT_AR: TEXT_AR, buildHtml: buildHtml, downloadDoc: downloadDoc };
})();
