const {Button,Badge}=window.VedutaDesignSystem_f6874f; const MOODS=['Electric','Culture','Quiet','Professional','Coast','Retreat','Adventure']; const card={background:'var(--surface-card)',border:'1px solid var(--border-subtle)',borderRadius:'var(--radius-md)',padding:'26px 28px',boxShadow:'var(--shadow-card)'}; const kicker={fontFamily:'var(--font-sans)',fontSize:'11px',letterSpacing:'0.22em',textTransform:'uppercase',fontWeight:500,color:'var(--gold)',marginBottom:'10px'}; const muted={fontFamily:'var(--font-sans)',fontWeight:300,fontSize:'14px',color:'var(--text-muted)'}; const fmtDate=s=>{if(!s)return'';const d=new Date(s+'T00:00:00');return isNaN(d)?s:d.toLocaleDateString(undefined,{month:'short',day:'numeric',year:'numeric'});}; function SignInForm({intro}){ const [email,setEmail]=React.useState(''); const [status,setStatus]=React.useState('idle'); const [error,setError]=React.useState(''); const submit=async e=>{ e.preventDefault(); const v=email.trim(); if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v)){setError('Please enter a valid email address.');return;} setStatus('sending');setError(''); const r=await window.vedutaAuth.signIn(v); if(r.ok)setStatus('sent');else{setStatus('idle');setError(r.error||'Something went wrong. Please try again.');} }; if(status==='sent')return
Check your email

We sent a sign-in link to {email.trim()}. Open it on this device and you're in. No password to remember.

; return
{intro&&

{intro}

}
{setEmail(e.target.value);setError('');}} required autoComplete="email"/>
{error&&
{error}
}
New here? The same link creates your account.
; } /* ---- Profile & preferences ---- */ function ProfileForm({profile,onSaved}){ const prefs=profile.preferences||{}; const [f,setF]=React.useState({ first_name:profile.first_name||'',last_name:profile.last_name||'',phone:profile.phone||'',home_city:profile.home_city||'', travel_style:profile.travel_style||'',loves:profile.loves||'',happiest:profile.happiest||'', moods:prefs.moods||[],pace:prefs.pace||'',party:prefs.party||'',budget:prefs.budget||'',work_travel:!!prefs.work_travel, dates:(profile.travel_dates||[]).map(d=>({start:d.start||'',end:d.end||'',note:d.note||''})) }); const [status,setStatus]=React.useState('idle'); const [error,setError]=React.useState(''); const set=k=>v=>{setF(x=>({...x,[k]:v}));if(status==='saved')setStatus('idle');}; const toggleMood=m=>set('moods')(f.moods.includes(m)?f.moods.filter(x=>x!==m):[...f.moods,m]); const setDate=(i,k,v)=>set('dates')(f.dates.map((d,j)=>j===i?{...d,[k]:v}:d)); const addDate=()=>set('dates')([...f.dates,{start:'',end:'',note:''}]); const removeDate=i=>set('dates')(f.dates.filter((_,j)=>j!==i)); const submit=async e=>{ e.preventDefault(); for(const d of f.dates){if(d.start&&d.end&&d.endd.start||d.end||d.note) }); if(r.ok){setStatus('saved');onSaved&&onSaved();}else{setStatus('idle');setError(r.error||'Could not save. Please try again.');} }; const label={fontFamily:'var(--font-sans)',fontSize:'11px',letterSpacing:'0.2em',textTransform:'uppercase',fontWeight:500,color:'var(--navy)'}; return
About you
How you travel
Moods you're drawn to
{MOODS.map(m=>toggleMood(m)} style={{cursor:'pointer',opacity:f.moods.includes(m)?1:.4,transition:'opacity var(--dur-fast) var(--ease-out)'}}>)}
When you're likely to travel
Dates you already know you'll be away. We use them to hold space and plan ahead. Nothing is booked.
{f.dates.map((d,i)=>
)}
{error&&{error}}
; } /* ---- Trip requests (the Concie-agent) ---- */ function TripRequestForm({cities,onCreated}){ const [f,setF]=React.useState({city_slug:'',start_date:'',end_date:'',guests:'2',notes:''}); const [status,setStatus]=React.useState('idle'); const [error,setError]=React.useState(''); const set=k=>v=>setF(x=>({...x,[k]:v})); const submit=async e=>{ e.preventDefault(); if(f.start_date&&f.end_date&&f.end_date
Request received.

The Concie-agent will come back to you with a first itinerary. It shows under your requests below.

; return
[c.slug,c.name]))}/>
{error&&{error}}
; } function RequestList({requests,cities,onCancel}){ const cityName=slug=>{const c=cities.find(x=>x.slug===slug);return c?c.name:(slug||'City to be decided');}; const statusColor={requested:'var(--gold)',confirmed:'var(--olive)',declined:'var(--terracotta)',canceled:'var(--text-muted)'}; if(!requests.length)return
No requests yet. Your first one shows here with its status.
; return
{requests.map(r=>
{r.kind==='concierge'?'Trip':r.kind==='property'?'Stay':'Excursion'} · {cityName(r.city_slug)}
{r.start_date?fmtDate(r.start_date)+(r.end_date?' – '+fmtDate(r.end_date):''):'Dates open'}{r.guests?' · '+r.guests+' guest'+(r.guests===1?'':'s'):''}
{r.notes&&
{r.notes}
}
{r.status} {r.status==='requested'&&}
)}
; } function PortalPage({onNavigate,section}){ const auth=window.vedutaAuth.use(); const [profile,setProfile]=React.useState(null); const [requests,setRequests]=React.useState([]); const [cities,setCities]=React.useState([]); const tab=['profile','requests'].includes(section)?section:'overview'; const loadProfile=()=>window.vedutaData.myProfile().then(setProfile); const loadRequests=()=>window.vedutaData.myRequests().then(setRequests); React.useEffect(()=>{ if(!auth.user){setProfile(null);setRequests([]);return;} loadProfile();loadRequests();window.vedutaData.listCities().then(setCities); },[auth.user&&auth.user.id]); const name=(profile&&profile.first_name)||''; const goTab=t=>onNavigate&&onNavigate(t==='overview'?'Portal':'Portal:'+t); const tabStyle=t=>({fontFamily:'var(--font-sans)',fontSize:'12px',letterSpacing:'0.18em',textTransform:'uppercase',fontWeight:500,color:tab===t?'var(--gold)':'var(--navy)',textDecoration:'none',padding:'18px 22px',borderBottom:'2px solid '+(tab===t?'var(--gold)':'transparent'),cursor:'pointer'}); const complete=profile&&profile.first_name&&profile.home_city&&(profile.preferences||{}).moods&&(profile.preferences.moods||[]).length>0; return
Member Portal

{auth.user?('Welcome'+(name?', '+name:'')):'Sign in'}

{auth.user&&
{[['overview','Overview'],['profile','Profile & Preferences'],['requests','Trip Requests']].map(([t,l])=>{e.preventDefault();goTab(t);}} style={tabStyle(t)}>{l})}
} {!auth.ready?
Loading…
:!auth.enabled?
Member sign-in opens soon

This is where members will sign in to their profile, preferences, the city guides, and the Concie-agent. Until launch, the waitlist is the way in, and founding members hear first.

:!auth.user? :tab==='profile'?
{profile?:
Loading your profile…
}
:tab==='requests'?
Request a trip
Tell the Concie-agent where and when. Everything else is handled.
Your requests
{await window.vedutaData.cancelRequest(id);loadRequests();}}/>
:
Your account
{profile&&(profile.first_name||profile.last_name)?[profile.first_name,profile.last_name].filter(Boolean).join(' '):'Profile not filled in yet'}
{auth.user.email}
{profile&&profile.home_city&&
{profile.home_city}
}
Membership
{auth.membership?auth.membership.tierName:'Waitlist'}
{auth.membership?('Status: '+auth.membership.status+(auth.membership.billing_interval?' · billed '+(auth.membership.billing_interval==='year'?'yearly':'monthly'):'')):'Founding access opens Spring 2027.'}
Travel dates
{profile&&profile.travel_dates&&profile.travel_dates.length ?
{profile.travel_dates.slice(0,4).map((d,i)=>
{fmtDate(d.start)}{d.end?' – '+fmtDate(d.end):''}{d.note?' · '+d.note:''}
)}
:
None yet. Add the dates you know you'll be away.
}
Trip requests
{requests.filter(r=>r.status==='requested').length} open
{requests.length?requests.length+' in total':'Hand a trip to the Concie-agent.'}
}
; } Object.assign(window,{PortalPage,SignInForm});