/* =========================================================
   Our Journey - Wedding Planner Application
   Full React 18 SPA with PHP API backend
   ========================================================= */

const { useState, useEffect, useRef, useCallback, useMemo, createContext, useContext } = React;

// ---- API Helper ----
const API_BASE = '/api';

async function api(path, options = {}) {
  const url = API_BASE + path;
  const config = {
    credentials: 'include',
    headers: {},
    ...options,
  };

  if (options.body && !(options.body instanceof FormData)) {
    config.headers['Content-Type'] = 'application/json';
    config.body = JSON.stringify(options.body);
  }

  const res = await fetch(url, config);
  const data = await res.json().catch(() => ({}));

  if (!res.ok) {
    throw new Error(data.error || `HTTP ${res.status}`);
  }
  return data;
}

// ---- Toast Context ----
const ToastContext = createContext();

function ToastProvider({ children }) {
  const [toasts, setToasts] = useState([]);
  const idRef = useRef(0);

  const addToast = useCallback((message, type = 'success') => {
    const id = ++idRef.current;
    setToasts(prev => [...prev, { id, message, type }]);
    setTimeout(() => {
      setToasts(prev => prev.filter(t => t.id !== id));
    }, 3000);
  }, []);

  return React.createElement(ToastContext.Provider, { value: addToast },
    children,
    React.createElement('div', { className: 'toast-container' },
      toasts.map(t =>
        React.createElement('div', { key: t.id, className: `toast ${t.type}` }, t.message)
      )
    )
  );
}

function useToast() {
  return useContext(ToastContext);
}

// ---- Wedding Quotes ----
const QUOTES = [
  "\"And among His signs is that He created for you mates from among yourselves, that you may dwell in tranquility with them.\" - Quran 30:21",
  "\"A great marriage is not when the perfect couple comes together, but when an imperfect couple learns to enjoy their differences.\" - Dave Meurer",
  "\"The best thing to hold onto in life is each other.\" - Audrey Hepburn",
  "\"In all the world, there is no heart for me like yours. In all the world, there is no love for you like mine.\" - Maya Angelou",
  "\"A successful marriage requires falling in love many times, always with the same person.\" - Mignon McLaughlin",
  "\"Whatever our souls are made of, his and hers are the same.\" - Emily Bronte",
  "\"You don't marry someone you can live with — you marry someone you cannot live without.\"",
  "\"A happy marriage is a long conversation which always seems too short.\" - Andre Maurois",
  "\"To love is nothing. To be loved is something. But to love and be loved — that's everything.\" - T. Tolis",
  "\"The real act of marriage takes place in the heart, not in the ballroom or church.\" - Barbara de Angelis",
  "\"Grow old along with me; the best is yet to be.\" - Robert Browning",
  "\"I choose you. And I'll choose you over and over, without pause, without a doubt, in a heartbeat.\"",
  "\"Marriage is not a noun; it's a verb. It isn't something you get, it's something you do.\" - Barbara De Angelis",
  "\"Two are better than one, because they have a good return for their labour.\" - Ecclesiastes 4:9",
  "\"He who finds a wife finds a good thing, and obtains favour from the Lord.\" - Proverbs 18:22",
  "\"The most important thing in the world is family and love.\" - John Wooden",
  "\"There is no more lovely, friendly or charming relationship than a good marriage.\" - Martin Luther",
  "\"Being deeply loved by someone gives you strength, while loving someone deeply gives you courage.\" - Lao Tzu",
  "\"Come what may — and love it.\" - Joseph B. Wirthlin",
  "\"Where there is love there is life.\" - Mahatma Gandhi"
];

function getRandomQuote() {
  return QUOTES[Math.floor(Math.random() * QUOTES.length)];
}

// ---- Geometric Background ----
function GeoBg() {
  return React.createElement('div', { className: 'geo-bg' },
    React.createElement('svg', { viewBox: '0 0 800 600', preserveAspectRatio: 'xMidYMid slice' },
      React.createElement('defs', null,
        React.createElement('pattern', { id: 'geo', x: 0, y: 0, width: 100, height: 100, patternUnits: 'userSpaceOnUse' },
          React.createElement('polygon', { points: '50,5 95,27.5 95,72.5 50,95 5,72.5 5,27.5', fill: 'none', stroke: '#D4AF37', strokeWidth: '0.5' }),
          React.createElement('circle', { cx: 50, cy: 50, r: 20, fill: 'none', stroke: '#2E8B57', strokeWidth: '0.3' }),
          React.createElement('line', { x1: 0, y1: 50, x2: 100, y2: 50, stroke: '#D4AF37', strokeWidth: '0.2' }),
          React.createElement('line', { x1: 50, y1: 0, x2: 50, y2: 100, stroke: '#D4AF37', strokeWidth: '0.2' })
        )
      ),
      React.createElement('rect', { width: '100%', height: '100%', fill: 'url(#geo)' })
    )
  );
}

// ---- SVG Icons (inline) ----
function CheckIcon({ size = 12 }) {
  return React.createElement('svg', { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 3, strokeLinecap: 'round', strokeLinejoin: 'round' },
    React.createElement('polyline', { points: '20 6 9 17 4 12' })
  );
}

function ChevronIcon({ direction = 'right', size = 16 }) {
  const rotation = { right: 0, down: 90, left: 180, up: 270 };
  return React.createElement('svg', {
    width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round',
    style: { transform: `rotate(${rotation[direction]}deg)`, transition: 'transform 0.2s' }
  },
    React.createElement('polyline', { points: '9 18 15 12 9 6' })
  );
}

// ---- Format Currency ----
function formatCurrency(amount) {
  return new Intl.NumberFormat('en-GB', { style: 'currency', currency: 'EUR' }).format(amount || 0);
}

// ---- Color presets ----
const COLOR_PRESETS = ['#D4AF37', '#2E8B57', '#E74C3C', '#3498DB', '#9B59B6', '#E67E22', '#1ABC9C', '#F39C12', '#C0392B', '#8E44AD'];

// ================================================================
//  LOGIN SCREEN
// ================================================================
function LoginScreen({ onLogin, joinToken }) {
  const [mode, setMode] = useState(joinToken ? 'join' : 'login');
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const [quote] = useState(getRandomQuote);

  async function handleSubmit(e) {
    e.preventDefault();
    setError('');
    setLoading(true);
    try {
      let result;
      if (mode === 'login') {
        result = await api('/auth/login', { method: 'POST', body: { email, password } });
      } else if (mode === 'register') {
        result = await api('/auth/register', { method: 'POST', body: { name, email, password } });
      } else if (mode === 'join') {
        result = await api('/couple/join?token=' + joinToken, { method: 'POST', body: { name, email, password } });
      }
      onLogin(result);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }

  const titles = { login: 'Log in', register: 'Create account', join: 'Join your partner' };

  return React.createElement(React.Fragment, null,
    React.createElement(GeoBg),
    React.createElement('div', { className: 'auth-screen' },
      React.createElement('form', { className: 'auth-card', onSubmit: handleSubmit },
        React.createElement('h1', { className: 'font-display' }, "Our Journey \u0645\u0639\u0627\u064B"),
        React.createElement('div', { className: 'subtitle' }, titles[mode]),
        React.createElement('div', { className: 'quote' }, quote),

        error && React.createElement('div', { className: 'auth-error' }, error),

        (mode === 'register' || mode === 'join') && React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'First name'),
          React.createElement('input', {
            type: 'text', className: 'form-input', placeholder: 'Your first name',
            value: name, onChange: e => setName(e.target.value), required: true, autoComplete: 'given-name'
          })
        ),

        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Email'),
          React.createElement('input', {
            type: 'email', className: 'form-input', placeholder: 'your@email.com',
            value: email, onChange: e => setEmail(e.target.value), required: true, autoComplete: 'email'
          })
        ),

        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Password'),
          React.createElement('input', {
            type: 'password', className: 'form-input', placeholder: 'Minimum 6 characters',
            value: password, onChange: e => setPassword(e.target.value), required: true, minLength: 6
          })
        ),

        React.createElement('button', {
          type: 'submit', className: 'btn btn-primary', disabled: loading
        }, loading ? 'Loading...' : (mode === 'login' ? 'Log in' : (mode === 'join' ? 'Join' : 'Sign up'))),

        mode !== 'join' && React.createElement('div', { className: 'auth-toggle' },
          mode === 'login'
            ? React.createElement(React.Fragment, null, "Don't have an account? ", React.createElement('a', { onClick: () => { setMode('register'); setError(''); } }, 'Sign up'))
            : React.createElement(React.Fragment, null, "Already have an account? ", React.createElement('a', { onClick: () => { setMode('login'); setError(''); } }, 'Log in'))
        )
      )
    )
  );
}

// ================================================================
//  ONBOARDING SCREENS
// ================================================================
function OnboardingScreen({ user, couple, onComplete }) {
  const [step, setStep] = useState(0);
  const [name, setName] = useState(user.name || '');
  const [color, setColor] = useState(user.color || '#D4AF37');
  const [avatarPreview, setAvatarPreview] = useState(user.photo_path ? '/' + user.photo_path : null);
  const [avatarFile, setAvatarFile] = useState(null);
  const [inviteUrl, setInviteUrl] = useState('');
  const [copied, setCopied] = useState(false);
  const [saving, setSaving] = useState(false);
  const toast = useToast();
  const fileInputRef = useRef(null);

  useEffect(() => {
    if (couple && couple.invite_token) {
      setInviteUrl('https://wedding.boubacarbarry.fr/?join=' + couple.invite_token);
    }
  }, [couple]);

  function handleAvatarChange(e) {
    const file = e.target.files[0];
    if (!file) return;
    if (file.size > 5 * 1024 * 1024) {
      toast('Image too large (max 5MB)', 'error');
      return;
    }
    setAvatarFile(file);
    const reader = new FileReader();
    reader.onload = () => setAvatarPreview(reader.result);
    reader.readAsDataURL(file);
  }

  async function saveProfile() {
    setSaving(true);
    try {
      await api('/users/profile', { method: 'PUT', body: { name, color } });
      if (avatarFile) {
        const formData = new FormData();
        formData.append('file', avatarFile);
        await api('/users/avatar', { method: 'POST', body: formData });
      }
      setStep(2);
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setSaving(false);
    }
  }

  function copyInvite() {
    navigator.clipboard.writeText(inviteUrl).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  }

  function shareWhatsApp() {
    const text = encodeURIComponent("Join me to plan our wedding! " + inviteUrl);
    window.open('https://wa.me/?text=' + text, '_blank');
  }

  // Step 0: Welcome
  if (step === 0) {
    return React.createElement(React.Fragment, null,
      React.createElement(GeoBg),
      React.createElement('div', { className: 'onboarding' },
        React.createElement('div', { className: 'onboarding-card' },
          React.createElement('div', { style: { fontSize: 48, marginBottom: 16 } }, String.fromCodePoint(0x1F48D)),
          React.createElement('h2', null, 'Welcome to Our Journey'),
          React.createElement('p', null, 'Plan your wedding together, step by step. Organise your tasks, manage your budget and share everything with your partner.'),
          React.createElement('button', { className: 'btn btn-primary', onClick: () => setStep(1) }, 'Get started'),
          React.createElement('div', { className: 'onboarding-dots' },
            [0, 1, 2].map(i =>
              React.createElement('div', { key: i, className: `onboarding-dot ${i === 0 ? 'active' : ''}` })
            )
          )
        )
      )
    );
  }

  // Step 1: Profile setup
  if (step === 1) {
    return React.createElement(React.Fragment, null,
      React.createElement(GeoBg),
      React.createElement('div', { className: 'onboarding' },
        React.createElement('div', { className: 'onboarding-card' },
          React.createElement('h2', null, 'Your profile'),
          React.createElement('p', null, 'Customise your profile so you can be identified as a couple.'),

          React.createElement('div', { className: 'avatar-upload', onClick: () => fileInputRef.current.click() },
            React.createElement('div', { className: 'avatar-circle', style: { borderColor: color } },
              avatarPreview
                ? React.createElement('img', { src: avatarPreview, alt: 'Avatar' })
                : name.charAt(0).toUpperCase() || '?'
            ),
            React.createElement('div', { className: 'avatar-badge' }, '+'),
            React.createElement('input', { ref: fileInputRef, type: 'file', accept: 'image/*', style: { display: 'none' }, onChange: handleAvatarChange })
          ),

          React.createElement('div', { className: 'form-group' },
            React.createElement('label', null, 'Your first name'),
            React.createElement('input', {
              type: 'text', className: 'form-input', value: name,
              onChange: e => setName(e.target.value), style: { textAlign: 'center' }
            })
          ),

          React.createElement('label', { style: { fontSize: 13, color: 'var(--text-secondary)' } }, 'Your colour'),
          React.createElement('div', { className: 'color-picker-row' },
            COLOR_PRESETS.map(c =>
              React.createElement('div', {
                key: c,
                className: `color-swatch ${color === c ? 'active' : ''}`,
                style: { backgroundColor: c },
                onClick: () => setColor(c)
              })
            )
          ),

          React.createElement('button', {
            className: 'btn btn-primary', onClick: saveProfile, disabled: saving || !name.trim()
          }, saving ? 'Saving...' : 'Continue'),

          React.createElement('div', { className: 'onboarding-dots' },
            [0, 1, 2].map(i =>
              React.createElement('div', { key: i, className: `onboarding-dot ${i === 1 ? 'active' : ''}` })
            )
          )
        )
      )
    );
  }

  // Step 2: Invite partner
  return React.createElement(React.Fragment, null,
    React.createElement(GeoBg),
    React.createElement('div', { className: 'onboarding' },
      React.createElement('div', { className: 'onboarding-card' },
        React.createElement('div', { style: { fontSize: 48, marginBottom: 16 } }, String.fromCodePoint(0x1F495)),
        React.createElement('h2', null, 'Invite your partner'),
        React.createElement('p', null, 'Share this link so your partner can join you and collaborate on the planning.'),

        React.createElement('div', { className: 'invite-section' },
          React.createElement('div', { className: 'invite-link-box' },
            React.createElement('input', { type: 'text', readOnly: true, value: inviteUrl }),
            React.createElement('button', { onClick: copyInvite }, copied ? 'Copied!' : 'Copy')
          ),
          React.createElement('button', { className: 'btn btn-secondary', onClick: shareWhatsApp },
            'Share via WhatsApp'
          )
        ),

        React.createElement('button', {
          className: 'btn btn-ghost', style: { marginTop: 16 },
          onClick: onComplete
        }, 'Skip for now'),

        React.createElement('div', { className: 'onboarding-dots' },
          [0, 1, 2].map(i =>
            React.createElement('div', { key: i, className: `onboarding-dot ${i === 2 ? 'active' : ''}` })
          )
        )
      )
    )
  );
}

// ================================================================
//  STEP MODAL (Create / Edit)
// ================================================================
function StepModal({ step, onSave, onClose }) {
  const isEdit = !!step;
  const [emoji, setEmoji] = useState(step?.emoji || '');
  const [title, setTitle] = useState(step?.title || '');
  const [subtitle, setSubtitle] = useState(step?.subtitle || '');
  const [color, setColor] = useState(step?.color || '#D4AF37');
  const [deadline, setDeadline] = useState(step?.deadline || '');
  const [saving, setSaving] = useState(false);
  const toast = useToast();

  const EMOJI_SUGGESTIONS = ['', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];

  async function handleSave() {
    if (!title.trim()) return;
    setSaving(true);
    try {
      const body = { emoji, title: title.trim(), subtitle: subtitle.trim(), color, deadline: deadline || null };
      if (isEdit) {
        const updated = await api('/steps/' + step.id, { method: 'PUT', body });
        onSave(updated, 'update');
      } else {
        const created = await api('/steps/', { method: 'POST', body });
        onSave(created, 'create');
      }
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setSaving(false);
    }
  }

  return React.createElement('div', { className: 'modal-overlay', onClick: e => e.target === e.currentTarget && onClose() },
    React.createElement('div', { className: 'modal-content' },
      React.createElement('div', { className: 'modal-header' },
        React.createElement('h3', null, isEdit ? 'Edit step' : 'New step'),
        React.createElement('button', { className: 'modal-close', onClick: onClose }, String.fromCharCode(10005))
      ),

      React.createElement('div', { style: { marginBottom: 16 } },
        React.createElement('label', { style: { fontSize: 13, color: 'var(--text-secondary)', marginBottom: 6, display: 'block' } }, 'Icon'),
        React.createElement('div', { style: { display: 'flex', gap: 8, flexWrap: 'wrap' } },
          EMOJI_SUGGESTIONS.map(em =>
            React.createElement('button', {
              key: em, type: 'button',
              style: {
                fontSize: 24, background: emoji === em ? 'var(--bg-secondary)' : 'transparent',
                border: emoji === em ? '2px solid var(--gold)' : '2px solid transparent',
                borderRadius: 8, padding: '4px 8px', cursor: 'pointer'
              },
              onClick: () => setEmoji(em)
            }, em)
          )
        )
      ),

      React.createElement('div', { className: 'form-group' },
        React.createElement('label', null, 'Title *'),
        React.createElement('input', { className: 'form-input', value: title, onChange: e => setTitle(e.target.value), placeholder: 'E.g. Wedding venue', required: true })
      ),

      React.createElement('div', { className: 'form-group' },
        React.createElement('label', null, 'Subtitle'),
        React.createElement('input', { className: 'form-input', value: subtitle, onChange: e => setSubtitle(e.target.value), placeholder: 'Optional description' })
      ),

      React.createElement('div', { style: { display: 'flex', gap: 16 } },
        React.createElement('div', { className: 'form-group', style: { flex: 1 } },
          React.createElement('label', null, 'Colour'),
          React.createElement('div', { className: 'color-picker-row', style: { justifyContent: 'flex-start', margin: '8px 0 0' } },
            COLOR_PRESETS.map(c =>
              React.createElement('div', {
                key: c,
                className: `color-swatch ${color === c ? 'active' : ''}`,
                style: { backgroundColor: c, width: 28, height: 28 },
                onClick: () => setColor(c)
              })
            )
          )
        ),
        React.createElement('div', { className: 'form-group', style: { flex: 1 } },
          React.createElement('label', null, 'Deadline'),
          React.createElement('input', { type: 'date', className: 'form-input', value: deadline, onChange: e => setDeadline(e.target.value) })
        )
      ),

      React.createElement('div', { className: 'modal-actions' },
        React.createElement('button', { className: 'btn btn-secondary', onClick: onClose }, 'Cancel'),
        React.createElement('button', { className: 'btn btn-primary', style: { width: 'auto' }, onClick: handleSave, disabled: saving || !title.trim() },
          saving ? 'Saving...' : (isEdit ? 'Update' : 'Create'))
      )
    )
  );
}

// ================================================================
//  EXPENSE MODAL
// ================================================================
function ExpenseModal({ expense, stepId, userRole, onSave, onClose }) {
  const isEdit = !!expense;
  const [form, setForm] = useState({
    name: expense?.name || '',
    category: expense?.category || 'Other',
    vendor: expense?.vendor || '',
    notes: expense?.notes || '',
    link: expense?.link || '',
    estimated: expense?.estimated || 0,
    quoted: expense?.quoted || 0,
    paid: expense?.paid || 0,
    status: expense?.status || 'estimating',
    visibility: expense?.visibility || 'both',
    payer: expense?.payer || 'shared'
  });
  const [saving, setSaving] = useState(false);
  const toast = useToast();

  function updateField(field, value) {
    setForm(prev => ({ ...prev, [field]: value }));
  }

  async function handleSave() {
    if (!form.name.trim()) return;
    setSaving(true);
    try {
      if (isEdit) {
        const updated = await api('/expenses/' + expense.id, { method: 'PUT', body: form });
        onSave(updated, 'update');
      } else {
        const created = await api('/expenses/', { method: 'POST', body: { ...form, step_id: stepId } });
        onSave(created, 'create');
      }
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setSaving(false);
    }
  }

  const categories = ['Venue','Catering','Photography','Attire','Jewellery','Ceremony','Music','Flowers','Travel','Home','Admin','Other'];
  const statuses = ['estimating','quoted','deposit','paid'];
  const visibilities = ['both','user1','user2'];
  const payers = ['user1','user2','user1-family','user2-family','shared'];

  const statusLabels = { estimating: 'Estimating', quoted: 'Quote received', deposit: 'Deposit paid', paid: 'Fully paid' };
  const visibilityLabels = { both: 'Visible to both', user1: 'Partner 1 only', user2: 'Partner 2 only' };
  const payerLabels = { user1: 'Partner 1', user2: 'Partner 2', 'user1-family': 'Partner 1 family', 'user2-family': 'Partner 2 family', shared: 'Shared' };

  return React.createElement('div', { className: 'modal-overlay', onClick: e => e.target === e.currentTarget && onClose() },
    React.createElement('div', { className: 'modal-content' },
      React.createElement('div', { className: 'modal-header' },
        React.createElement('h3', null, isEdit ? 'Edit expense' : 'New expense'),
        React.createElement('button', { className: 'modal-close', onClick: onClose }, String.fromCharCode(10005))
      ),

      React.createElement('div', { className: 'expense-form-grid' },
        React.createElement('div', { className: 'form-group full-width' },
          React.createElement('label', null, 'Name *'),
          React.createElement('input', { className: 'form-input', value: form.name, onChange: e => updateField('name', e.target.value), placeholder: 'E.g. Wedding venue' })
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Category'),
          React.createElement('select', { className: 'form-select', value: form.category, onChange: e => updateField('category', e.target.value) },
            categories.map(c => React.createElement('option', { key: c, value: c }, c))
          )
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Vendor'),
          React.createElement('input', { className: 'form-input', value: form.vendor, onChange: e => updateField('vendor', e.target.value), placeholder: 'Vendor name' })
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Estimated'),
          React.createElement('input', { type: 'number', className: 'form-input', value: form.estimated, onChange: e => updateField('estimated', parseFloat(e.target.value) || 0), min: 0, step: '0.01' })
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Quoted'),
          React.createElement('input', { type: 'number', className: 'form-input', value: form.quoted, onChange: e => updateField('quoted', parseFloat(e.target.value) || 0), min: 0, step: '0.01' })
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Paid'),
          React.createElement('input', { type: 'number', className: 'form-input', value: form.paid, onChange: e => updateField('paid', parseFloat(e.target.value) || 0), min: 0, step: '0.01' })
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Status'),
          React.createElement('select', { className: 'form-select', value: form.status, onChange: e => updateField('status', e.target.value) },
            statuses.map(s => React.createElement('option', { key: s, value: s }, statusLabels[s]))
          )
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Visibility'),
          React.createElement('select', { className: 'form-select', value: form.visibility, onChange: e => updateField('visibility', e.target.value) },
            visibilities.map(v => React.createElement('option', { key: v, value: v }, visibilityLabels[v]))
          )
        ),
        React.createElement('div', { className: 'form-group' },
          React.createElement('label', null, 'Payer'),
          React.createElement('select', { className: 'form-select', value: form.payer, onChange: e => updateField('payer', e.target.value) },
            payers.map(p => React.createElement('option', { key: p, value: p }, payerLabels[p]))
          )
        ),
        React.createElement('div', { className: 'form-group full-width' },
          React.createElement('label', null, 'Link'),
          React.createElement('input', { className: 'form-input', value: form.link, onChange: e => updateField('link', e.target.value), placeholder: 'https://...' })
        ),
        React.createElement('div', { className: 'form-group full-width' },
          React.createElement('label', null, 'Notes'),
          React.createElement('textarea', { className: 'form-textarea', value: form.notes, onChange: e => updateField('notes', e.target.value), placeholder: 'Additional notes...' })
        )
      ),

      React.createElement('div', { className: 'modal-actions' },
        React.createElement('button', { className: 'btn btn-secondary', onClick: onClose }, 'Cancel'),
        React.createElement('button', { className: 'btn btn-primary', style: { width: 'auto' }, onClick: handleSave, disabled: saving || !form.name.trim() },
          saving ? 'Saving...' : (isEdit ? 'Update' : 'Add'))
      )
    )
  );
}

// ================================================================
//  IMAGE STRIP + UPLOAD
// ================================================================
function ExpenseImageStrip({ expense, onUpdate }) {
  const [lightboxIdx, setLightboxIdx] = useState(null);
  const [uploading, setUploading] = useState(false);
  const fileRef = useRef(null);
  const toast = useToast();
  const images = expense.images || [];

  async function handleUpload(e) {
    const file = e.target.files[0];
    if (!file) return;
    if (file.size > 5 * 1024 * 1024) { toast('Max 5MB', 'error'); return; }
    setUploading(true);
    try {
      const formData = new FormData();
      formData.append('file', file);
      formData.append('expense_id', expense.id);
      const img = await api('/images/upload', { method: 'POST', body: formData });
      onUpdate({ ...expense, images: [...images, img] });
      toast('Image added');
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setUploading(false);
      if (fileRef.current) fileRef.current.value = '';
    }
  }

  async function handleDelete(imgId) {
    try {
      await api('/images/delete?id=' + imgId, { method: 'DELETE' });
      onUpdate({ ...expense, images: images.filter(i => i.id !== imgId) });
      toast('Image deleted');
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  return React.createElement(React.Fragment, null,
    React.createElement('div', { className: 'image-strip' },
      images.map((img, idx) =>
        React.createElement('div', { key: img.id, className: 'image-strip-item' },
          React.createElement('img', { src: '/' + img.file_path, alt: img.original_name, onClick: () => setLightboxIdx(idx) }),
          React.createElement('button', { className: 'delete-img', onClick: e => { e.stopPropagation(); handleDelete(img.id); } }, String.fromCharCode(10005))
        )
      ),
      React.createElement('div', { className: 'image-upload-btn', onClick: () => fileRef.current?.click() },
        uploading ? '...' : '+',
        React.createElement('input', { ref: fileRef, type: 'file', accept: 'image/jpeg,image/png,image/webp', style: { display: 'none' }, onChange: handleUpload })
      )
    ),

    lightboxIdx !== null && React.createElement('div', { className: 'lightbox', onClick: () => setLightboxIdx(null) },
      React.createElement('img', { src: '/' + images[lightboxIdx].file_path, alt: '' }),
      React.createElement('button', { className: 'lightbox-close', onClick: () => setLightboxIdx(null) }, String.fromCharCode(10005)),
      lightboxIdx > 0 && React.createElement('button', { className: 'lightbox-nav prev', onClick: e => { e.stopPropagation(); setLightboxIdx(lightboxIdx - 1); } }, String.fromCharCode(8592)),
      lightboxIdx < images.length - 1 && React.createElement('button', { className: 'lightbox-nav next', onClick: e => { e.stopPropagation(); setLightboxIdx(lightboxIdx + 1); } }, String.fromCharCode(8594))
    )
  );
}

// ================================================================
//  TASK ROW
// ================================================================
function TaskRow({ task, onUpdate, onDelete }) {
  const [expanded, setExpanded] = useState(task.expanded || false);
  const [newSubtask, setNewSubtask] = useState('');
  const toast = useToast();
  const subtasks = task.subtasks || [];

  async function toggleDone() {
    const newDone = !task.done;
    onUpdate({ ...task, done: newDone });
    try {
      await api('/tasks/' + task.id, { method: 'PUT', body: { done: newDone } });
    } catch (err) {
      onUpdate({ ...task, done: !newDone });
      toast(err.message, 'error');
    }
  }

  async function toggleExpanded() {
    const newExp = !expanded;
    setExpanded(newExp);
    try {
      await api('/tasks/' + task.id, { method: 'PUT', body: { expanded: newExp } });
    } catch (err) { /* non-critical */ }
  }

  async function addSubtask(e) {
    e.preventDefault();
    if (!newSubtask.trim()) return;
    try {
      const st = await api('/subtasks/', { method: 'POST', body: { task_id: task.id, text: newSubtask.trim() } });
      onUpdate({ ...task, subtasks: [...subtasks, st] });
      setNewSubtask('');
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  async function toggleSubtask(st) {
    const newDone = !st.done;
    const newSubs = subtasks.map(s => s.id === st.id ? { ...s, done: newDone } : s);
    onUpdate({ ...task, subtasks: newSubs });
    try {
      await api('/subtasks/' + st.id, { method: 'PUT', body: { done: newDone } });
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  async function deleteSubtask(stId) {
    const newSubs = subtasks.filter(s => s.id !== stId);
    onUpdate({ ...task, subtasks: newSubs });
    try {
      await api('/subtasks/' + stId, { method: 'DELETE' });
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  async function handleDelete() {
    try {
      await api('/tasks/' + task.id, { method: 'DELETE' });
      onDelete(task.id);
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  return React.createElement('div', { className: 'task-row' },
    React.createElement('div', { className: 'task-main' },
      React.createElement('div', { className: 'task-drag', title: 'Drag to reorder' }, String.fromCharCode(9776)),
      React.createElement('div', {
        className: `task-checkbox ${task.done ? 'checked' : ''}`,
        onClick: toggleDone
      }, task.done && React.createElement(CheckIcon)),
      React.createElement('span', { className: `task-text ${task.done ? 'done' : ''}` }, task.text),
      subtasks.length > 0 || true ? React.createElement('button', {
        className: 'task-expand-btn',
        onClick: toggleExpanded
      }, React.createElement(ChevronIcon, { direction: expanded ? 'down' : 'right', size: 14 })) : null,
      React.createElement('button', { className: 'task-delete-btn', onClick: handleDelete }, String.fromCharCode(10005))
    ),

    expanded && React.createElement('div', { className: 'subtasks-container' },
      subtasks.map(st =>
        React.createElement('div', { key: st.id, className: 'subtask-row' },
          React.createElement('div', {
            className: `subtask-checkbox ${st.done ? 'checked' : ''}`,
            onClick: () => toggleSubtask(st)
          }, st.done && React.createElement(CheckIcon, { size: 10 })),
          React.createElement('span', { className: `subtask-text ${st.done ? 'done' : ''}` }, st.text),
          React.createElement('button', { className: 'subtask-delete', onClick: () => deleteSubtask(st.id) }, String.fromCharCode(10005))
        )
      ),
      React.createElement('form', { className: 'add-subtask-input', onSubmit: addSubtask },
        React.createElement('input', {
          type: 'text', placeholder: 'Add a subtask...',
          value: newSubtask, onChange: e => setNewSubtask(e.target.value)
        })
      )
    )
  );
}

// ================================================================
//  BUDGET PANEL (per step)
// ================================================================
function BudgetPanel({ stepId, userRole }) {
  const [expenses, setExpenses] = useState([]);
  const [showModal, setShowModal] = useState(false);
  const [editExpense, setEditExpense] = useState(null);
  const [loading, setLoading] = useState(true);
  const toast = useToast();

  useEffect(() => {
    loadExpenses();
  }, [stepId]);

  async function loadExpenses() {
    try {
      const data = await api('/expenses/?step_id=' + stepId);
      setExpenses(data);
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setLoading(false);
    }
  }

  function handleSave(exp, action) {
    if (action === 'create') {
      setExpenses(prev => [exp, ...prev]);
    } else {
      setExpenses(prev => prev.map(e => e.id === exp.id ? exp : e));
    }
    setShowModal(false);
    setEditExpense(null);
  }

  async function handleDelete(expId) {
    if (!confirm('Delete this expense?')) return;
    try {
      await api('/expenses/' + expId, { method: 'DELETE' });
      setExpenses(prev => prev.filter(e => e.id !== expId));
      toast('Expense deleted');
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  function handleExpenseUpdate(updatedExpense) {
    setExpenses(prev => prev.map(e => e.id === updatedExpense.id ? updatedExpense : e));
  }

  const totals = useMemo(() => {
    return expenses.reduce((acc, e) => ({
      estimated: acc.estimated + (e.estimated || 0),
      quoted: acc.quoted + (e.quoted || 0),
      paid: acc.paid + (e.paid || 0)
    }), { estimated: 0, quoted: 0, paid: 0 });
  }, [expenses]);

  const statusLabels = { estimating: 'Estimating', quoted: 'Quoted', deposit: 'Deposit', paid: 'Paid' };

  if (loading) {
    return React.createElement('div', { style: { textAlign: 'center', padding: 40, color: 'var(--text-muted)' } }, 'Loading budget...');
  }

  return React.createElement(React.Fragment, null,
    React.createElement('div', { className: 'budget-summary' },
      React.createElement('h4', null, 'Budget summary'),
      React.createElement('div', { className: 'budget-grid' },
        React.createElement('div', { className: 'budget-stat' },
          React.createElement('div', { className: 'label' }, 'Estimated'),
          React.createElement('div', { className: 'value estimated' }, formatCurrency(totals.estimated))
        ),
        React.createElement('div', { className: 'budget-stat' },
          React.createElement('div', { className: 'label' }, 'Quoted'),
          React.createElement('div', { className: 'value quoted' }, formatCurrency(totals.quoted))
        ),
        React.createElement('div', { className: 'budget-stat' },
          React.createElement('div', { className: 'label' }, 'Paid'),
          React.createElement('div', { className: 'value paid' }, formatCurrency(totals.paid))
        )
      )
    ),

    React.createElement('div', { className: 'expense-list' },
      expenses.map(exp =>
        React.createElement('div', { key: exp.id, className: 'expense-row' },
          React.createElement('div', { className: 'expense-row-header' },
            React.createElement('span', { className: 'expense-name', onClick: () => { setEditExpense(exp); setShowModal(true); } }, exp.name),
            React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 8 } },
              React.createElement('span', { className: 'expense-amount' }, formatCurrency(exp.quoted || exp.estimated)),
              React.createElement('button', {
                className: 'btn-ghost', style: { padding: 4, fontSize: 12, color: 'var(--text-muted)' },
                onClick: () => handleDelete(exp.id)
              }, String.fromCharCode(128465))
            )
          ),
          React.createElement('div', { className: 'expense-row-meta' },
            React.createElement('span', { className: 'expense-tag' }, exp.category),
            React.createElement('span', { className: `expense-tag status-${exp.status}` }, statusLabels[exp.status]),
            exp.vendor && React.createElement('span', { className: 'expense-tag' }, exp.vendor)
          ),
          React.createElement(ExpenseImageStrip, { expense: exp, onUpdate: handleExpenseUpdate })
        )
      ),

      React.createElement('button', { className: 'add-expense-btn', onClick: () => { setEditExpense(null); setShowModal(true); } },
        '+ Add expense'
      )
    ),

    showModal && React.createElement(ExpenseModal, {
      expense: editExpense,
      stepId: stepId,
      userRole: userRole,
      onSave: handleSave,
      onClose: () => { setShowModal(false); setEditExpense(null); }
    })
  );
}

// ================================================================
//  STEP DETAIL VIEW
// ================================================================
function StepDetailView({ step, userRole, onBack, onUpdate, onDelete }) {
  const [activeTab, setActiveTab] = useState('tasks');
  const [tasks, setTasks] = useState([]);
  const [newTaskText, setNewTaskText] = useState('');
  const [loading, setLoading] = useState(true);
  const [showEditModal, setShowEditModal] = useState(false);
  const toast = useToast();

  useEffect(() => {
    loadTasks();
  }, [step.id]);

  async function loadTasks() {
    try {
      const data = await api('/tasks/?step_id=' + step.id);
      setTasks(data);
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setLoading(false);
    }
  }

  async function addTask(e) {
    e.preventDefault();
    if (!newTaskText.trim()) return;
    try {
      const task = await api('/tasks/', { method: 'POST', body: { step_id: step.id, text: newTaskText.trim() } });
      setTasks(prev => [...prev, task]);
      setNewTaskText('');
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  function handleTaskUpdate(updatedTask) {
    setTasks(prev => prev.map(t => t.id === updatedTask.id ? updatedTask : t));
  }

  function handleTaskDelete(taskId) {
    setTasks(prev => prev.filter(t => t.id !== taskId));
  }

  function handleStepUpdate(updatedStep) {
    onUpdate(updatedStep);
    setShowEditModal(false);
  }

  async function handleStepDelete() {
    if (!confirm('Delete this step and all its tasks/expenses?')) return;
    try {
      await api('/steps/' + step.id, { method: 'DELETE' });
      onDelete(step.id);
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  const doneCount = tasks.filter(t => t.done).length;
  const totalCount = tasks.length;
  const pct = totalCount > 0 ? Math.round((doneCount / totalCount) * 100) : 0;

  return React.createElement('div', { className: 'step-detail' },
    React.createElement('div', { className: 'step-detail-header' },
      React.createElement('button', { className: 'back-btn', onClick: onBack }, React.createElement(ChevronIcon, { direction: 'left' })),
      React.createElement('div', { className: 'step-detail-title' },
        React.createElement('h2', null, step.emoji, ' ', step.title),
        step.subtitle && React.createElement('div', { className: 'sub' }, step.subtitle)
      ),
      React.createElement('div', { className: 'step-actions' },
        React.createElement('button', { className: 'btn-icon', onClick: () => setShowEditModal(true), title: 'Edit' }, String.fromCharCode(9998)),
        React.createElement('button', { className: 'btn-icon', onClick: handleStepDelete, title: 'Delete', style: { color: 'var(--danger)' } }, String.fromCharCode(128465))
      )
    ),

    totalCount > 0 && React.createElement('div', { style: { marginBottom: 16 } },
      React.createElement('div', { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 4, fontSize: 12, color: 'var(--text-muted)' } },
        React.createElement('span', null, `${doneCount}/${totalCount} tasks`),
        React.createElement('span', null, `${pct}%`)
      ),
      React.createElement('div', { className: 'progress-bar' },
        React.createElement('div', { className: 'progress-fill', style: { width: pct + '%', backgroundColor: step.color || 'var(--gold)' } })
      )
    ),

    step.deadline && React.createElement('div', { className: 'step-deadline', style: { marginBottom: 16 } },
      'Deadline: ', new Date(step.deadline).toLocaleDateString('en-GB', { day: 'numeric', month: 'long', year: 'numeric' })
    ),

    React.createElement('div', { className: 'tab-bar' },
      React.createElement('button', { className: `tab-item ${activeTab === 'tasks' ? 'active' : ''}`, onClick: () => setActiveTab('tasks') }, 'Tasks'),
      React.createElement('button', { className: `tab-item ${activeTab === 'budget' ? 'active' : ''}`, onClick: () => setActiveTab('budget') }, 'Budget')
    ),

    activeTab === 'tasks' && React.createElement('div', { className: 'tasks-section' },
      loading
        ? React.createElement('div', { style: { textAlign: 'center', padding: 40, color: 'var(--text-muted)' } }, 'Loading...')
        : React.createElement(React.Fragment, null,
            tasks.map(task =>
              React.createElement(TaskRow, {
                key: task.id,
                task: task,
                onUpdate: handleTaskUpdate,
                onDelete: handleTaskDelete
              })
            ),
            tasks.length === 0 && React.createElement('div', { style: { textAlign: 'center', padding: 30, color: 'var(--text-muted)', fontSize: 14 } },
              'No tasks yet. Add your first task below.'
            ),
            React.createElement('form', { className: 'add-task-input', onSubmit: addTask },
              React.createElement('input', {
                type: 'text', placeholder: 'Add a task...',
                value: newTaskText, onChange: e => setNewTaskText(e.target.value)
              }),
              React.createElement('button', { type: 'submit', disabled: !newTaskText.trim() }, 'Add')
            )
          )
    ),

    activeTab === 'budget' && React.createElement(BudgetPanel, { stepId: step.id, userRole: userRole }),

    showEditModal && React.createElement(StepModal, {
      step: step,
      onSave: (updated) => handleStepUpdate(updated),
      onClose: () => setShowEditModal(false)
    })
  );
}

// ================================================================
//  GLOBAL BUDGET VIEW
// ================================================================
function GlobalBudgetView({ userRole }) {
  const [expenses, setExpenses] = useState([]);
  const [loading, setLoading] = useState(true);
  const toast = useToast();

  useEffect(() => {
    loadAllExpenses();
  }, []);

  async function loadAllExpenses() {
    try {
      const data = await api('/expenses/');
      setExpenses(data);
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setLoading(false);
    }
  }

  const totals = useMemo(() => {
    return expenses.reduce((acc, e) => ({
      estimated: acc.estimated + (e.estimated || 0),
      quoted: acc.quoted + (e.quoted || 0),
      paid: acc.paid + (e.paid || 0)
    }), { estimated: 0, quoted: 0, paid: 0 });
  }, [expenses]);

  const byCategory = useMemo(() => {
    const map = {};
    expenses.forEach(e => {
      if (!map[e.category]) map[e.category] = { estimated: 0, quoted: 0, paid: 0 };
      map[e.category].estimated += e.estimated || 0;
      map[e.category].quoted += e.quoted || 0;
      map[e.category].paid += e.paid || 0;
    });
    return Object.entries(map).sort((a, b) => (b[1].quoted || b[1].estimated) - (a[1].quoted || a[1].estimated));
  }, [expenses]);

  const byPayer = useMemo(() => {
    const map = {};
    expenses.forEach(e => {
      if (!map[e.payer]) map[e.payer] = 0;
      map[e.payer] += e.quoted || e.estimated || 0;
    });
    return Object.entries(map).sort((a, b) => b[1] - a[1]);
  }, [expenses]);

  const payerLabels = { user1: 'Partner 1', user2: 'Partner 2', 'user1-family': 'Partner 1 family', 'user2-family': 'Partner 2 family', shared: 'Shared' };

  if (loading) {
    return React.createElement('div', { style: { textAlign: 'center', padding: 60, color: 'var(--text-muted)' } }, 'Loading global budget...');
  }

  return React.createElement('div', { className: 'global-budget' },
    React.createElement('div', { className: 'global-budget-header' },
      React.createElement('h2', null, 'Global Budget')
    ),

    React.createElement('div', { className: 'budget-total-card' },
      React.createElement('div', { className: 'total-label' }, 'Total Estimated Budget'),
      React.createElement('div', { className: 'total-value' }, formatCurrency(totals.quoted || totals.estimated)),
      React.createElement('div', { className: 'total-sub' },
        React.createElement('div', { className: 'stat' },
          React.createElement('div', { className: 'val', style: { color: 'var(--text-secondary)' } }, formatCurrency(totals.estimated)),
          React.createElement('div', { className: 'lbl' }, 'Estimated')
        ),
        React.createElement('div', { className: 'stat' },
          React.createElement('div', { className: 'val', style: { color: 'var(--warning)' } }, formatCurrency(totals.quoted)),
          React.createElement('div', { className: 'lbl' }, 'Quoted')
        ),
        React.createElement('div', { className: 'stat' },
          React.createElement('div', { className: 'val', style: { color: 'var(--success)' } }, formatCurrency(totals.paid)),
          React.createElement('div', { className: 'lbl' }, 'Paid')
        )
      )
    ),

    expenses.length === 0 && React.createElement('div', { className: 'empty-state' },
      React.createElement('div', { className: 'icon' }, String.fromCodePoint(0x1F4B0)),
      React.createElement('h3', null, 'No expenses'),
      React.createElement('p', null, 'Add expenses to your steps to see the global budget here.')
    ),

    byCategory.length > 0 && React.createElement(React.Fragment, null,
      React.createElement('h4', { style: { fontFamily: "'Playfair Display', serif", color: 'var(--text-secondary)', fontSize: 15, marginBottom: 12 } }, 'By category'),
      React.createElement('div', { className: 'budget-category-list' },
        byCategory.map(([cat, vals]) =>
          React.createElement('div', { key: cat, className: 'budget-category-row' },
            React.createElement('span', { className: 'budget-category-name' }, cat),
            React.createElement('span', { className: 'budget-category-amount' }, formatCurrency(vals.quoted || vals.estimated))
          )
        )
      )
    ),

    byPayer.length > 0 && React.createElement('div', { className: 'budget-payer-breakdown' },
      React.createElement('h4', null, 'By payer'),
      React.createElement('div', { className: 'budget-category-list' },
        byPayer.map(([payer, amount]) =>
          React.createElement('div', { key: payer, className: 'budget-category-row' },
            React.createElement('span', { className: 'budget-category-name' }, payerLabels[payer] || payer),
            React.createElement('span', { className: 'budget-category-amount' }, formatCurrency(amount))
          )
        )
      )
    )
  );
}

// ================================================================
//  SETTINGS VIEW
// ================================================================
function SettingsView({ user, couple, userRole, onLogout, onUserUpdate }) {
  const [name, setName] = useState(user.name);
  const [color, setColor] = useState(user.color || '#D4AF37');
  const [saving, setSaving] = useState(false);
  const [inviteUrl, setInviteUrl] = useState('');
  const [copied, setCopied] = useState(false);
  const toast = useToast();
  const fileRef = useRef(null);

  useEffect(() => {
    if (couple && couple.invite_token) {
      setInviteUrl('https://wedding.boubacarbarry.fr/?join=' + couple.invite_token);
    }
  }, [couple]);

  async function saveProfile() {
    setSaving(true);
    try {
      const updated = await api('/users/profile', { method: 'PUT', body: { name, color } });
      onUserUpdate(updated);
      toast('Profile updated');
    } catch (err) {
      toast(err.message, 'error');
    } finally {
      setSaving(false);
    }
  }

  async function uploadAvatar(e) {
    const file = e.target.files[0];
    if (!file) return;
    const formData = new FormData();
    formData.append('file', file);
    try {
      const updated = await api('/users/avatar', { method: 'POST', body: formData });
      onUserUpdate(updated);
      toast('Avatar updated');
    } catch (err) {
      toast(err.message, 'error');
    }
  }

  function copyInvite() {
    navigator.clipboard.writeText(inviteUrl).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  }

  return React.createElement('div', { className: 'settings-panel' },
    React.createElement('h2', null, 'Settings'),

    React.createElement('div', { className: 'settings-section' },
      React.createElement('h4', null, 'Profile'),
      React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 16, marginBottom: 16 } },
        React.createElement('div', {
          className: 'avatar-upload', style: { width: 64, height: 64, margin: 0 },
          onClick: () => fileRef.current?.click()
        },
          React.createElement('div', { className: 'avatar-circle', style: { borderColor: color } },
            user.photo_path
              ? React.createElement('img', { src: '/' + user.photo_path, alt: user.name })
              : user.name.charAt(0).toUpperCase()
          ),
          React.createElement('input', { ref: fileRef, type: 'file', accept: 'image/*', style: { display: 'none' }, onChange: uploadAvatar })
        ),
        React.createElement('div', null,
          React.createElement('div', { style: { fontWeight: 600 } }, user.name),
          React.createElement('div', { style: { fontSize: 13, color: 'var(--text-muted)' } }, user.email)
        )
      ),

      React.createElement('div', { className: 'form-group' },
        React.createElement('label', null, 'Name'),
        React.createElement('input', { className: 'form-input', value: name, onChange: e => setName(e.target.value) })
      ),

      React.createElement('label', { style: { fontSize: 13, color: 'var(--text-secondary)' } }, 'Colour'),
      React.createElement('div', { className: 'color-picker-row', style: { justifyContent: 'flex-start', margin: '8px 0 16px' } },
        COLOR_PRESETS.map(c =>
          React.createElement('div', {
            key: c,
            className: `color-swatch ${color === c ? 'active' : ''}`,
            style: { backgroundColor: c, width: 28, height: 28 },
            onClick: () => setColor(c)
          })
        )
      ),

      React.createElement('button', { className: 'btn btn-primary btn-small', onClick: saveProfile, disabled: saving },
        saving ? 'Saving...' : 'Save')
    ),

    React.createElement('div', { className: 'settings-section' },
      React.createElement('h4', null, 'Couple'),
      couple && couple.partner
        ? React.createElement('div', { className: 'settings-row' },
            React.createElement('span', { className: 'label' }, 'Partner'),
            React.createElement('span', { className: 'value' }, couple.partner.name)
          )
        : React.createElement(React.Fragment, null,
            React.createElement('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginBottom: 12 } }, 'Your partner hasn\'t joined yet. Share this link:'),
            React.createElement('div', { className: 'invite-link-box' },
              React.createElement('input', { type: 'text', readOnly: true, value: inviteUrl }),
              React.createElement('button', { onClick: copyInvite }, copied ? 'Copied!' : 'Copy')
            )
          ),
      React.createElement('div', { className: 'settings-row' },
        React.createElement('span', { className: 'label' }, 'Your role'),
        React.createElement('span', { className: 'value' }, userRole === 'user1' ? 'Partner 1' : 'Partner 2')
      )
    ),

    React.createElement('button', {
      className: 'btn btn-danger', style: { width: '100%', marginTop: 8 },
      onClick: onLogout
    }, 'Log out')
  );
}

// ================================================================
//  MAIN APP
// ================================================================
function App() {
  const [authState, setAuthState] = useState('loading'); // 'loading' | 'unauthenticated' | 'onboarding' | 'authenticated'
  const [user, setUser] = useState(null);
  const [couple, setCouple] = useState(null);
  const [userRole, setUserRole] = useState(null);
  const [joinToken, setJoinToken] = useState(null);

  // App state
  const [steps, setSteps] = useState([]);
  const [activeStep, setActiveStep] = useState(null);
  const [activeView, setActiveView] = useState('steps'); // 'steps' | 'budget' | 'settings'
  const [showStepModal, setShowStepModal] = useState(false);
  const [stepsLoading, setStepsLoading] = useState(false);

  // Check for join token in URL
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const token = params.get('join');
    if (token) {
      setJoinToken(token);
    }
  }, []);

  // Check auth on mount
  useEffect(() => {
    checkAuth();
  }, []);

  async function checkAuth() {
    try {
      const data = await api('/auth/me');
      setUser(data.user);
      setCouple(data.couple);
      setUserRole(data.user_role);

      // Check if onboarding needed (new user with no steps)
      // For simplicity, go straight to authenticated
      setAuthState('authenticated');
      loadSteps();
    } catch (err) {
      setAuthState('unauthenticated');
    }
  }

  async function loadSteps() {
    setStepsLoading(true);
    try {
      const data = await api('/steps/');
      setSteps(data);
    } catch (err) {
      // may fail if not yet in couple
    } finally {
      setStepsLoading(false);
    }
  }

  function handleLogin(result) {
    setUser(result.user);
    setCouple(result.couple);
    setUserRole(result.user_role);

    // Clean URL
    if (joinToken) {
      window.history.replaceState({}, '', '/');
      setJoinToken(null);
    }

    // If new registration, show onboarding
    if (!result.couple?.partner && result.user_role === 'user1') {
      setAuthState('onboarding');
    } else {
      setAuthState('authenticated');
      loadSteps();
    }
  }

  function handleOnboardingComplete() {
    setAuthState('authenticated');
    loadSteps();
  }

  async function handleLogout() {
    try {
      await api('/auth/logout', { method: 'POST' });
    } catch (err) { /* ignore */ }
    setUser(null);
    setCouple(null);
    setUserRole(null);
    setSteps([]);
    setActiveStep(null);
    setAuthState('unauthenticated');
  }

  function handleStepSave(step, action) {
    if (action === 'create') {
      setSteps(prev => [...prev, { ...step, task_count: 0, done_count: 0 }]);
    } else {
      setSteps(prev => prev.map(s => s.id === step.id ? { ...s, ...step } : s));
      if (activeStep && activeStep.id === step.id) {
        setActiveStep(prev => ({ ...prev, ...step }));
      }
    }
    setShowStepModal(false);
  }

  function handleStepDelete(stepId) {
    setSteps(prev => prev.filter(s => s.id !== stepId));
    setActiveStep(null);
  }

  function handleStepUpdate(updatedStep) {
    setSteps(prev => prev.map(s => s.id === updatedStep.id ? { ...s, ...updatedStep } : s));
    setActiveStep(prev => prev ? { ...prev, ...updatedStep } : null);
  }

  function handleUserUpdate(updatedUser) {
    setUser(prev => ({ ...prev, ...updatedUser }));
  }

  // ---- Render ----

  if (authState === 'loading') {
    return React.createElement('div', { className: 'app-loader' },
      React.createElement('div', { className: 'spinner' }),
      React.createElement('div', { className: 'text' }, 'Loading...')
    );
  }

  if (authState === 'unauthenticated') {
    return React.createElement(LoginScreen, { onLogin: handleLogin, joinToken: joinToken });
  }

  if (authState === 'onboarding') {
    return React.createElement(OnboardingScreen, {
      user: user,
      couple: couple,
      onComplete: handleOnboardingComplete
    });
  }

  // ---- Authenticated App ----

  // Step detail view
  if (activeStep && activeView === 'steps') {
    return React.createElement(React.Fragment, null,
      React.createElement(GeoBg),
      React.createElement('div', { className: 'app-container' },
        React.createElement('div', { className: 'app-header' },
          React.createElement('div', { className: 'header-left' },
            React.createElement('span', { className: 'header-logo' }, "Our Journey \u0645\u0639\u0627\u064B")
          ),
          React.createElement('div', { className: 'header-right' },
            couple && couple.partner && React.createElement('div', { className: 'header-avatar', style: { borderColor: couple.partner.color || '#D4AF37' } },
              couple.partner.photo_path
                ? React.createElement('img', { src: '/' + couple.partner.photo_path, alt: '' })
                : (couple.partner.name || '?').charAt(0).toUpperCase()
            ),
            React.createElement('div', { className: 'header-avatar', style: { borderColor: user.color || '#D4AF37' }, onClick: () => { setActiveStep(null); setActiveView('settings'); } },
              user.photo_path
                ? React.createElement('img', { src: '/' + user.photo_path, alt: '' })
                : (user.name || '?').charAt(0).toUpperCase()
            )
          )
        ),
        React.createElement('div', { className: 'app-main' },
          React.createElement(StepDetailView, {
            step: activeStep,
            userRole: userRole,
            onBack: () => setActiveStep(null),
            onUpdate: handleStepUpdate,
            onDelete: handleStepDelete
          })
        ),
        React.createElement('div', { className: 'bottom-nav' },
          React.createElement('button', { className: `bottom-nav-item active`, onClick: () => setActiveStep(null) },
            React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x1F4CB)),
            React.createElement('span', null, 'Steps')
          ),
          React.createElement('button', { className: 'bottom-nav-item', onClick: () => { setActiveStep(null); setActiveView('budget'); } },
            React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x1F4B0)),
            React.createElement('span', null, 'Budget')
          ),
          React.createElement('button', { className: 'bottom-nav-item', onClick: () => { setActiveStep(null); setActiveView('settings'); } },
            React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x2699)),
            React.createElement('span', null, 'Settings')
          )
        )
      )
    );
  }

  // Main views
  return React.createElement(React.Fragment, null,
    React.createElement(GeoBg),
    React.createElement('div', { className: 'app-container' },
      React.createElement('div', { className: 'app-header' },
        React.createElement('div', { className: 'header-left' },
          React.createElement('span', { className: 'header-logo' }, "Our Journey \u0645\u0639\u0627\u064B")
        ),
        React.createElement('div', { className: 'header-right' },
          couple && couple.partner && React.createElement('div', { className: 'header-avatar', style: { borderColor: couple.partner.color || '#D4AF37' } },
            couple.partner.photo_path
              ? React.createElement('img', { src: '/' + couple.partner.photo_path, alt: '' })
              : (couple.partner.name || '?').charAt(0).toUpperCase()
          ),
          React.createElement('div', { className: 'header-avatar', style: { borderColor: user.color || '#D4AF37' }, onClick: () => setActiveView('settings') },
            user.photo_path
              ? React.createElement('img', { src: '/' + user.photo_path, alt: '' })
              : (user.name || '?').charAt(0).toUpperCase()
          )
        )
      ),

      React.createElement('div', { className: 'app-main' },

        // Steps list view
        activeView === 'steps' && React.createElement('div', { className: 'steps-list' },
          stepsLoading
            ? React.createElement('div', { style: { textAlign: 'center', padding: 40, color: 'var(--text-muted)' } }, 'Loading steps...')
            : React.createElement(React.Fragment, null,
                steps.length === 0 && React.createElement('div', { className: 'empty-state' },
                  React.createElement('div', { className: 'icon' }, String.fromCodePoint(0x1F48D)),
                  React.createElement('h3', null, 'Welcome!'),
                  React.createElement('p', null, 'Start by creating your first planning step. Each step contains tasks and a budget.')
                ),
                steps.map(s =>
                  React.createElement('div', {
                    key: s.id,
                    className: 'step-card',
                    onClick: () => setActiveStep(s),
                    style: { '--step-color': s.color || 'var(--gold)' }
                  },
                    React.createElement('div', null,
                      React.createElement('div', { style: { position: 'absolute', left: 0, top: 0, bottom: 0, width: 4, background: s.color || 'var(--gold)', borderRadius: '4px 0 0 4px' } })
                    ),
                    React.createElement('div', { className: 'step-card-header' },
                      React.createElement('span', { className: 'step-emoji' }, s.emoji || String.fromCodePoint(0x2728)),
                      React.createElement('div', { className: 'step-info' },
                        React.createElement('div', { className: 'step-title' }, s.title),
                        s.subtitle && React.createElement('div', { className: 'step-subtitle' }, s.subtitle)
                      )
                    ),
                    (s.task_count > 0) && React.createElement('div', { className: 'step-meta' },
                      React.createElement('div', { className: 'step-progress' },
                        React.createElement('div', { className: 'progress-bar' },
                          React.createElement('div', {
                            className: 'progress-fill',
                            style: {
                              width: (s.task_count > 0 ? Math.round((s.done_count / s.task_count) * 100) : 0) + '%',
                              backgroundColor: s.color || 'var(--gold)'
                            }
                          })
                        )
                      ),
                      React.createElement('span', { className: 'step-count' }, `${s.done_count}/${s.task_count}`)
                    ),
                    s.deadline && React.createElement('div', { className: 'step-deadline' },
                      new Date(s.deadline).toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })
                    )
                  )
                ),
                React.createElement('button', { className: 'add-step-btn', onClick: () => setShowStepModal(true) },
                  '+ Add a step'
                )
              )
        ),

        // Global budget view
        activeView === 'budget' && React.createElement(GlobalBudgetView, { userRole: userRole }),

        // Settings view
        activeView === 'settings' && React.createElement(SettingsView, {
          user: user,
          couple: couple,
          userRole: userRole,
          onLogout: handleLogout,
          onUserUpdate: handleUserUpdate
        })
      ),

      // Bottom nav
      React.createElement('div', { className: 'bottom-nav' },
        React.createElement('button', { className: `bottom-nav-item ${activeView === 'steps' ? 'active' : ''}`, onClick: () => setActiveView('steps') },
          React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x1F4CB)),
          React.createElement('span', null, 'Steps')
        ),
        React.createElement('button', { className: `bottom-nav-item ${activeView === 'budget' ? 'active' : ''}`, onClick: () => setActiveView('budget') },
          React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x1F4B0)),
          React.createElement('span', null, 'Budget')
        ),
        React.createElement('button', { className: `bottom-nav-item ${activeView === 'settings' ? 'active' : ''}`, onClick: () => setActiveView('settings') },
          React.createElement('span', { className: 'nav-icon' }, String.fromCodePoint(0x2699)),
          React.createElement('span', null, 'Settings')
        )
      )
    ),

    // Step creation modal
    showStepModal && React.createElement(StepModal, {
      step: null,
      onSave: handleStepSave,
      onClose: () => setShowStepModal(false)
    })
  );
}

// ================================================================
//  RENDER
// ================================================================
const rootEl = document.getElementById('root');
const root = ReactDOM.createRoot(rootEl);
root.render(
  React.createElement(ToastProvider, null,
    React.createElement(App)
  )
);
