/* Shared UI - icons, context, event card, sheet, tab bar, buttons */
const { useState, useEffect, useRef, useContext, useMemo, createContext } = React;
function rnd(seed) { let s = seed; return () => { s = (s * 9301 + 49297) % 233280; return s / 233280; }; }
window.rnd = rnd;
window.tierSummary = (b, unit = 'ticket') => { const c = b.cats || String(b.tier || '').split(' + ').length; return c > 1 ? `${b.n} ${unit}s · ${c} types` : `${b.n} × ${b.tier}`; };
const AppCtx = createContext(null);
const useApp = () => useContext(AppCtx);
let useAppShare = () => {}; // set by App to open the share sheet

const P = (d, extra = {}) => ({ size = 22, stroke = 1.6, style, fill = 'none', ...rest }) => (
  <svg width={size} height={size} viewBox="0 0 24 24" fill={fill} stroke="currentColor" strokeWidth={stroke} strokeLinecap="round" strokeLinejoin="round" style={style} aria-hidden="true" {...rest}>{d}</svg>
);
const Ico = {
  home: P(<><path d="M9.02 2.84 3.63 7.04C2.73 7.74 2 9.23 2 10.36v7.41c0 2.32 1.89 4.22 4.21 4.22h11.58c2.32 0 4.21-1.9 4.21-4.21v-7.28c0-1.21-.81-2.76-1.8-3.45l-6.18-4.33c-1.4-.98-3.65-.93-5 .12Z"/><path d="M12 17.99v-3"/></>),
  ticket: P(<><path d="M19.5 12.5c0-1.38 1.12-2.5 2.5-2.5V9c0-4-1-5-5-5H7C3 4 2 5 2 9v.5a2.5 2.5 0 0 1 0 5V15c0 4 1 5 5 5h10c4 0 5-1 5-5a2.5 2.5 0 0 1-2.5-2.5Z"/><path d="M10 4v16" strokeDasharray="2.5 2.5"/></>),
  heart: P(<path d="M12.62 20.81c-.34.12-.9.12-1.24 0C8.48 19.82 2 15.69 2 8.69 2 5.6 4.49 3.1 7.56 3.1c1.82 0 3.43.88 4.44 2.24a5.53 5.53 0 0 1 4.44-2.24C19.51 3.1 22 5.6 22 8.69c0 7-6.48 11.13-9.38 12.12Z"/>),
  grid: P(<><circle cx="7.5" cy="7.5" r="2.6" fill="currentColor" stroke="none"/><circle cx="16.5" cy="7.5" r="2.6" fill="currentColor" stroke="none"/><circle cx="7.5" cy="16.5" r="2.6" fill="currentColor" stroke="none"/><circle cx="16.5" cy="16.5" r="2.6" fill="currentColor" stroke="none"/></>),
  cheers: P(<><path d="M5 3h6l-1 8a2.5 2.5 0 0 1-4 0L5 3Z"/><path d="M8 13v7M5.5 20h5"/><path d="M13 5h6l-1 8a2.5 2.5 0 0 1-4 0l-1-8Z"/><path d="M16 15v5M13.5 20h5"/></>),
  wand: P(<><path d="m4 20 11-11"/><path d="m13 7 4 4"/><path d="M18 2v3M16.5 3.5h3M20 8v2M19 9h2M12 2v2M11 3h2"/></>),
  info: P(<><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></>),
  seat: P(<><path d="M6 11V6a3 3 0 0 1 3-3h6a3 3 0 0 1 3 3v5"/><rect x="3" y="11" width="18" height="6" rx="2"/><path d="M6 17v4M18 17v4"/></>),
  play: P(<path d="M8 5v14l11-7Z"/>),
  map: P(<><path d="m9 3-6 2v16l6-2 6 2 6-2V3l-6 2-6-2Z"/><path d="M9 3v16M15 5v16"/></>),
  lock: P(<><rect x="4" y="10" width="16" height="11" rx="3"/><path d="M8 10V7a4 4 0 0 1 8 0v3"/></>),
  user: P(<><path d="M12.12 12.78a.96.96 0 0 0-.24 0 3.27 3.27 0 0 1-3.16-3.28A3.28 3.28 0 1 1 12.12 12.78Z"/><path d="M18.74 19.38A9.93 9.93 0 0 1 12 22c-2.6 0-4.96-.99-6.74-2.62.1-.94.7-1.86 1.77-2.58 2.74-1.82 7.22-1.82 9.94 0 1.07.72 1.67 1.64 1.77 2.58Z"/><path d="M12 22c5.52 0 10-4.48 10-10S17.52 2 12 2 2 6.48 2 12s4.48 10 10 10Z"/></>),
  search: P(<><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></>),
  bell: P(<><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></>),
  pin: P(<><path d="M12 22s7-6.5 7-12a7 7 0 1 0-14 0c0 5.5 7 12 7 12Z"/><circle cx="12" cy="10" r="2.5"/></>),
  back: P(<path d="m15 5-7 7 7 7"/>),
  chev: P(<path d="m9 5 7 7-7 7"/>),
  down: P(<path d="m6 9 6 6 6-6"/>),
  share: P(<><path d="M12 3v12"/><path d="m8 7 4-4 4 4"/><path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-7"/></>),
  cal: P(<><rect x="3" y="5" width="18" height="16" rx="3"/><path d="M3 10h18M8 3v4M16 3v4"/></>),
  clock: P(<><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></>),
  x: P(<path d="M6 6l12 12M18 6 6 18"/>),
  check: P(<path d="m5 12 5 5 9-10"/>),
  globe: P(<><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></>),
  moon: P(<path d="M20 14.5A8 8 0 0 1 9.5 4a8 8 0 1 0 10.5 10.5Z"/>),
  mail: P(<><rect x="3" y="5" width="18" height="14" rx="3"/><path d="m3 8 9 6 9-6"/></>),
  phone: P(<><rect x="6" y="2" width="12" height="20" rx="2.5"/><path d="M11 18h2"/></>),
  help: P(<><circle cx="12" cy="12" r="9"/><path d="M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.7.4-1 1-1 1.7M12 17h.01"/></>),
  users: P(<><circle cx="9" cy="8" r="3.5"/><path d="M2.5 20a6.5 6.5 0 0 1 13 0"/><path d="M16 4.5a3.5 3.5 0 0 1 0 7M21.5 20a6.5 6.5 0 0 0-5-6.3"/></>),
  trash: P(<><path d="M4 7h16M10 11v6M14 11v6M6 7l1 13h10l1-13M9 7V4h6v3"/></>),
  out: P(<><path d="M10 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h4"/><path d="m15 8 5 4-5 4M20 12H9"/></>),
  coin: P(<><circle cx="12" cy="12" r="9"/><path d="M14.5 9.5A2.5 2.5 0 0 0 12 8c-1.5 0-2.5.8-2.5 2 0 2.6 5 1.4 5 4 0 1.2-1 2-2.5 2a2.5 2.5 0 0 1-2.5-1.5M12 6v2M12 16v2"/></>),
  qr: P(<><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><path d="M14 14h3v3h-3zM19 14h2M21 17v4h-4M14 21h2"/></>),
  wallet: P(<><rect x="3" y="6" width="18" height="14" rx="3"/><path d="M3 10h18M16 15h2"/></>),
  star: P(<path d="m12 3 2.7 5.6 6.1.9-4.4 4.3 1 6.1L12 17l-5.4 2.9 1-6.1L3.2 9.5l6.1-.9Z"/>),
  sliders: P(<><path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/></>),
  sparkle: P(<path d="M12 3c.5 4 2.5 6 7 7-4.5 1-6.5 3-7 7-.5-4-2.5-6-7-7 4.5-1 6.5-3 7-7Z"/>),
  spark: P(<path d="M13 2 4 14h7l-1 8 9-12h-7Z"/>),
  copy: P(<><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a1 1 0 0 1 1-1h10"/></>),
};
const Brand = {
  google: () => <svg width="20" height="20" viewBox="0 0 24 24"><path fill="#4285F4" d="M23.5 12.3c0-.8-.1-1.6-.2-2.3H12v4.4h6.5a5.6 5.6 0 0 1-2.4 3.6v3h3.9c2.3-2.1 3.5-5.2 3.5-8.7Z"/><path fill="#34A853" d="M12 24c3.2 0 6-1.1 7.9-2.9l-3.9-3c-1.1.7-2.4 1.2-4 1.2-3.1 0-5.7-2.1-6.7-4.9H1.4v3.1A12 12 0 0 0 12 24Z"/><path fill="#FBBC05" d="M5.3 14.3a7.2 7.2 0 0 1 0-4.6V6.6H1.4a12 12 0 0 0 0 10.8l3.9-3.1Z"/><path fill="#EA4335" d="M12 4.8c1.8 0 3.3.6 4.6 1.8l3.4-3.4A12 12 0 0 0 1.4 6.6l3.9 3.1c1-2.8 3.6-4.9 6.7-4.9Z"/></svg>,
  apple: ({ color = '#fff' }) => <svg width="20" height="20" viewBox="0 0 24 24" fill={color}><path d="M16.6 12.7c0-2.4 2-3.6 2.1-3.7-1.1-1.7-2.9-1.9-3.5-1.9-1.5-.2-2.9.9-3.7.9-.8 0-1.9-.9-3.2-.8-1.6 0-3.1 1-4 2.4-1.7 3-.4 7.3 1.2 9.7.8 1.2 1.8 2.5 3 2.4 1.2 0 1.7-.8 3.2-.8s1.9.8 3.2.8c1.3 0 2.2-1.2 3-2.4.9-1.3 1.3-2.6 1.3-2.7 0 0-2.6-1-2.6-3.9ZM14.2 5.5c.7-.8 1.1-1.9 1-3-1 0-2.1.7-2.8 1.5-.6.7-1.2 1.8-1 2.9 1.1.1 2.1-.6 2.8-1.4Z"/></svg>,
  facebook: () => <svg width="20" height="20" viewBox="0 0 24 24" fill="#1877F2"><path d="M24 12.07C24 5.44 18.63.07 12 .07S0 5.44 0 12.07c0 5.99 4.39 10.95 10.13 11.85v-8.38H7.08v-3.47h3.05V9.43c0-3.01 1.79-4.67 4.53-4.67 1.31 0 2.69.24 2.69.24v2.95h-1.51c-1.49 0-1.96.93-1.96 1.87v2.25h3.33l-.53 3.47h-2.8v8.38C19.61 23.02 24 18.06 24 12.07z"/></svg>,
};

function Logo({ h = 22, white, style }) {
  const { dark } = useApp();
  const src = white || dark ? DP.A + 'desipass-logo-white.svg' : DP.A + 'desipass-logo.svg';
  return <img src={src} alt="DesiPass" style={{ height: h, width: 'auto', ...style }} />;
}

function Btn({ children, kind = 'primary', onClick, style, disabled, icon, small }) {
  const base = { display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8, height: small ? 40 : 52, padding: small ? '0 18px' : '0 24px', borderRadius: small ? 10 : 12, fontWeight: 600, fontSize: small ? 14 : 15.5, width: small ? 'auto' : '100%', transition: 'background .2s,opacity .2s', opacity: disabled ? .5 : 1, pointerEvents: disabled ? 'none' : 'auto' };
  const kinds = {
    primary: { background: 'var(--red)', color: '#fff' },
    dark: { background: 'var(--fill)', color: '#fff' },
    ghost: { background: 'var(--chip)', color: 'var(--ink)' },
    outline: { background: 'transparent', color: 'var(--ink)', boxShadow: 'inset 0 0 0 1px var(--line)' },
    white: { background: '#fff', color: '#1D0D31' },
  };
  return <button className="press" onClick={onClick} style={{ ...base, ...kinds[kind], ...style }}>{icon}{children}</button>;
}

function GradChip({ on, onClick, icon: I, svg, children, style }) {
  return <button className="press" onClick={onClick} style={{ flex: 'none', display: 'inline-flex', alignItems: 'center', gap: 6, height: 34, padding: '0 14px', borderRadius: 9999, fontSize: 12.5, fontWeight: 600, whiteSpace: 'nowrap', border: on ? '1px solid transparent' : '1px solid var(--line)', background: on ? 'linear-gradient(90deg,#E54558,#3B2E4D)' : 'var(--sur)', color: on ? '#fff' : 'var(--ink)', transition: 'all .2s', ...style }}>{svg && <span style={{ width: 18, height: 18, display: 'inline-flex', opacity: on ? 1 : .55 }} dangerouslySetInnerHTML={{ __html: svg.replace('<svg ', '<svg width="18" height="18" ') }}></span>}{I && <I size={16} style={{ opacity: on ? 1 : .55 }} />}{children}</button>;
}
function OptIn({ on, set }) {
  return <label style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%', textAlign: 'left', padding: '12px 14px', minHeight: 56, borderRadius: 12, background: 'var(--sur2)', border: '1px solid var(--line)', cursor: 'pointer' }}><span style={{ flex: 1, minWidth: 0, display: 'grid', gap: 2 }}><span style={{ fontSize: 13, fontWeight: 600, lineHeight: 1.4, letterSpacing: '-.01em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>Insider offers & must-see events near me</span><span style={{ fontSize: 11.5, color: 'var(--mut)', fontWeight: 500, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>By email. No spam, unsubscribe anytime.</span></span><input type="checkbox" checked={on} onChange={e => set(e.target.checked)} style={{ position: 'absolute', opacity: 0, pointerEvents: 'none' }} /><span aria-hidden="true" style={{ width: 22, height: 22, flex: 'none', borderRadius: 6, border: on ? '1.5px solid var(--purple)' : '1.5px solid #C4BDCC', background: on ? 'var(--purple)' : 'var(--sur)', display: 'grid', placeItems: 'center', color: '#fff', transition: 'all .15s' }}>{on && <Ico.check size={14} stroke={2.6} />}</span></label>;
}
function IconBtn({ icon: I, onClick, dim, bg, color, size = 40, style, label }) {
  return <button className="press" aria-label={label} onClick={onClick} style={{ width: size, height: size, borderRadius: 9999, display: 'inline-flex', alignItems: 'center', justifyContent: 'center', background: bg || 'transparent', color: color || 'var(--ink)', flex: 'none', ...style }}><I size={dim || 22} /></button>;
}

/* heart with burst */
function HeartBtn({ ev, size = 32, glass = true, style }) {
  const { saved, toggleSave } = useApp();
  const on = saved.has(ev.id);
  const [burst, setBurst] = useState(0);
  const click = (e) => { e.stopPropagation(); e.preventDefault(); if (!on) setBurst(Date.now()); toggleSave(ev.id); };
  const bg = on ? '#fff' : glass ? 'rgba(59,46,77,.55)' : 'var(--chip)';
  return (
    <button className="press" aria-label="Save" onClick={click} style={{ position: 'relative', width: size, height: size, borderRadius: 9999, background: bg, backdropFilter: glass ? 'blur(8px)' : 'none', border: glass ? '1px solid rgba(255,255,255,.25)' : 'none', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: on ? '#E54558' : glass ? '#fff' : 'var(--ink)', transition: 'background .2s,color .2s', ...style }}>
      <Ico.heart size={size * .47} fill={on ? 'currentColor' : 'none'} style={{ transition: 'transform .3s cubic-bezier(.34,1.56,.64,1)', transform: on ? 'scale(1.08)' : 'none' }} />
      {burst > 0 && <span key={burst} className="burst"><b></b>{[...Array(8)].map((_, i) => { const a = i / 8 * Math.PI * 2; return <i key={i} style={{ '--dx': Math.cos(a) * 22 + 'px', '--dy': Math.sin(a) * 22 + 'px' }}></i>; })}</span>}
    </button>
  );
}

/* Event card - same proportions as the mobile web card (index.html .app .ev) */
function EventCard({ ev, onOpen, style }) {
  const { city: myCity } = useApp();
  const abroad = DP.CITIES[ev.city].cc !== DP.CITIES[myCity].cc;
  const sk = ev.st || (ev.approval ? 'appr' : null); const SM = { fast: ['Filling fast!', 'fast.svg', 'var(--warn-bg)', 'var(--warn-fg)'], few: ['Only few left!', 'fire.svg', 'var(--warn-bg)', 'var(--warn-fg)'], out: ['Sold out!', '', 'var(--out-bg)', 'var(--out-fg)'], wait: ['Waiting list open', 'clock.svg', 'var(--purple-soft)', 'var(--purple)'], appr: ['Approval required', '', 'var(--purple-soft)', 'var(--purple)'] }[sk]; const stat = SM ? SM[0] : null;
  const statCol = ev.st === 'out' ? { background: 'var(--out-bg)', color: 'var(--out-fg)' } : (ev.st === 'wait' || ev.approval) ? { background: 'var(--purple-soft)', color: 'var(--purple)' } : { background: 'var(--warn-bg)', color: 'var(--warn-fg)' };
  const free = !ev.price && !ev.pmax;
  return (
    <article className="press" onClick={() => onOpen(ev)} style={{ display: 'flex', flexDirection: 'column', minWidth: 0, border: '1px solid transparent', borderRadius: 14, padding: 8, background: 'linear-gradient(var(--sur),var(--sur)) padding-box,var(--card-ring) border-box', boxShadow: 'var(--shadow)', cursor: 'pointer', ...style }}>
      <div style={{ position: 'relative', aspectRatio: '4/5', borderRadius: 9, overflow: 'hidden', background: 'var(--chip)' }}>
        <img src={ev.img} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
        {ev.tag && <span style={{ position: 'absolute', top: 10, left: 10, fontFamily: 'var(--dp-font-mono)', fontWeight: 700, fontSize: 8.5, letterSpacing: '.08em', textTransform: 'uppercase', padding: '5px 9px', borderRadius: 6, background: ev.tag === 'New' ? '#7B5CFF' : '#E54558', color: '#fff' }}>{ev.tag}</span>}
        <div style={{ position: 'absolute', bottom: stat ? 38 : 10, right: 10 }}><HeartBtn ev={ev} /></div>
        {stat && <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0, height: 28, display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 5, fontSize: 11.5, fontWeight: 600, background: SM[2], color: SM[3] }}>{SM[1] && <img src={DP.A + 'icons/' + SM[1]} alt="" style={{ height: 13, width: 'auto' }} />}{stat}</div>}
      </div>
      <div style={{ padding: '9px 3px 2px', minWidth: 0 }}>
        <div style={{ fontFamily: 'var(--dp-font-mono)', fontSize: 9.5, letterSpacing: '.05em', color: 'var(--red)', fontWeight: 600, display: 'flex', alignItems: 'center', gap: 5, marginBottom: 5, textTransform: 'uppercase' }}><img src={DP.A + 'icons/calendar.svg'} alt="" style={{ width: 11, height: 11 }} />{ev.day} · {ev.dateLbl}</div>
        <div style={{ fontWeight: 700, fontSize: 14, letterSpacing: '-.01em', color: 'var(--ink)', lineHeight: 1.2, display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden', minHeight: 34 }}>{ev.nm}</div>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginTop: 8 }}>
          <div style={{ fontSize: 11.5, color: 'var(--mut)', fontWeight: 500, display: 'inline-flex', alignItems: 'center', gap: 4, overflow: 'hidden', minWidth: 0 }}><img src={DP.A + 'icons/location.svg'} alt="" style={{ width: 11, height: 11, flex: 'none' }} /><span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{ev.city}{abroad ? ', ' + ev.cc : ''}</span></div>
          <div style={{ fontSize: free ? 12.5 : 14, fontWeight: 700, color: free ? 'var(--green)' : 'var(--price)', flex: 'none', whiteSpace: 'nowrap', textTransform: free ? 'uppercase' : 'none', letterSpacing: free ? '.04em' : 0 }}>{DP.priceLbl(ev)}</div>
        </div>
      </div>
    </article>
  );
}

function CardSkeleton() {
  return <div style={{ border: '1px solid var(--line)', borderRadius: 14, padding: 8, background: 'var(--sur)' }}><div className="skel" style={{ aspectRatio: '4/5', borderRadius: 9 }}></div><div style={{ padding: '10px 3px 4px', display: 'grid', gap: 7 }}><div className="skel" style={{ height: 9, width: '45%', borderRadius: 4 }}></div><div className="skel" style={{ height: 12, width: '90%', borderRadius: 4 }}></div><div className="skel" style={{ height: 12, width: '60%', borderRadius: 4 }}></div></div></div>;
}

function SecHead({ title, em, action, onAction, sub }) {
  return <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', padding: '0 16px', marginBottom: 16 }}><div><h2 style={{ margin: 0, fontFamily: 'var(--dp-font-display)', fontWeight: 600, fontSize: 21, letterSpacing: '-.02em', lineHeight: 1.15, color: 'var(--ink)' }}>{title} {em && <em style={{ fontStyle: 'italic', fontWeight: 500, color: 'var(--red)' }}>{em}</em>}</h2>{sub && <div style={{ fontSize: 12.5, color: 'var(--mut)', marginTop: 4, fontWeight: 500 }}>{sub}</div>}</div>{action && <button onClick={onAction} style={{ fontSize: 13, fontWeight: 600, color: 'var(--red)' }}>{action}</button>}</div>;
}

/* bottom sheet with spring-ish slide */
function Sheet({ open, onClose, title, children, full, pad = true }) {
  const [show, setShow] = useState(open);
  const [closing, setClosing] = useState(false);
  useEffect(() => { if (open) { setShow(true); setClosing(false); } else if (show) { setClosing(true); const t = setTimeout(() => { setShow(false); setClosing(false); }, 300); return () => clearTimeout(t); } }, [open]);
  const { bottomInset } = useApp();
  if (!show) return null;
  const host = typeof document !== 'undefined' && document.querySelector('.ph');
  const node = (
    <div style={{ position: 'absolute', inset: 0, zIndex: 80 }}>
      <div className={'sheet-bg' + (closing ? ' out' : '')} onClick={onClose} style={{ position: 'absolute', inset: 0, background: 'rgba(14,4,32,.45)', backdropFilter: 'blur(3px)' }}></div>
      <div className={'sheet' + (closing ? ' out' : '')} style={{ position: 'absolute', left: 0, right: 0, bottom: 0, maxHeight: full ? '94%' : '86%', display: 'flex', flexDirection: 'column', background: 'var(--sur)', borderRadius: '24px 24px 0 0', boxShadow: '0 -12px 40px rgba(29,13,49,.25)', paddingBottom: bottomInset }}>
        <div style={{ width: 40, height: 4, borderRadius: 4, background: 'var(--line)', margin: '10px auto 0', flex: 'none' }}></div>
        {title && <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 20px 6px', flex: 'none' }}><div style={{ fontFamily: 'var(--dp-font-display)', fontWeight: 600, fontSize: 20, letterSpacing: '-.02em' }}>{title}</div><IconBtn icon={Ico.x} onClick={onClose} size={34} dim={18} bg="var(--chip)" label="Close" /></div>}
        <div className="scr" style={{ position: 'relative', flex: '1 1 auto', minHeight: 0, background: 'transparent', padding: pad ? '8px 20px 20px' : 0 }}>{children}</div>
      </div>
    </div>
  );
  return host ? ReactDOM.createPortal(node, host) : node;
}

function TopBar({ title, onBack, right, transparent, sub }) {
  const { topInset } = useApp();
  return <div style={{ position: 'sticky', top: 0, zIndex: 5, display: 'flex', alignItems: 'center', gap: 8, padding: `${topInset + 6}px 12px 8px`, background: transparent ? 'transparent' : 'inherit' }}><IconBtn icon={Ico.back} onClick={onBack} bg={transparent ? 'rgba(0,0,0,.35)' : 'var(--chip)'} color={transparent ? '#fff' : 'var(--ink)'} label="Back" /><div style={{ flex: 1, minWidth: 0 }}>{title && <div style={{ fontWeight: 700, fontSize: 16, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{title}</div>}{sub && <div style={{ fontSize: 12, color: 'var(--mut)' }}>{sub}</div>}</div>{right}</div>;
}

const TABS = [{ k: 'home', l: 'Home', I: Ico.home }, { k: 'bookings', l: 'Bookings', I: Ico.ticket }, { k: 'favorites', l: 'Favorites', I: Ico.heart }, { k: 'profile', l: 'Profile', I: Ico.user }];
function TabBar({ tab, setTab }) {
  const { bottomInset, saved } = useApp();
  return (
    <nav style={{ position: 'absolute', left: 0, right: 0, bottom: 0, zIndex: 20, display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', padding: `6px 8px ${bottomInset + 6}px`, background: 'var(--tabbg)', backdropFilter: 'blur(18px)', borderTop: '1px solid var(--line)' }}>
      {TABS.map(({ k, l, I }) => { const on = tab === k; return <button key={k} className="press" onClick={() => setTab(k)} style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3, padding: '6px 0 2px', color: on ? 'var(--red)' : 'var(--mut)', transition: 'color .2s', position: 'relative', minHeight: 44 }}><span style={{ position: 'relative', display: 'inline-flex' }}><I size={23} stroke={on ? 1.9 : 1.6} fill={on && k === 'favorites' ? 'currentColor' : 'none'} style={{ transition: 'transform .3s cubic-bezier(.34,1.56,.64,1)', transform: on ? 'translateY(-1px)' : 'none' }} />{k === 'favorites' && saved.size > 0 && <span style={{ position: 'absolute', top: -4, right: -8, minWidth: 15, height: 15, padding: '0 4px', borderRadius: 8, background: 'var(--red)', color: '#fff', fontSize: 9.5, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>{saved.size}</span>}</span><span style={{ fontSize: 10.5, fontWeight: on ? 700 : 500 }}>{l}</span></button>; })}
    </nav>
  );
}

function Field({ icon: I, children, style }) {
  return <div style={{ display: 'flex', alignItems: 'center', gap: 10, height: 50, padding: '0 14px', borderRadius: 12, background: 'var(--sur)', border: '1px solid var(--line)', ...style }}>{I && <I size={18} style={{ color: 'var(--mut)', flex: 'none' }} />}{children}</div>;
}
const inputStyle = { flex: 1, minWidth: 0, border: 0, outline: 0, background: 'transparent', fontSize: 15, color: 'var(--ink)', fontWeight: 500 };

function Row({ icon: I, label, value, onClick, danger, last, right }) {
  return <button className="press" onClick={onClick} style={{ display: 'flex', alignItems: 'center', gap: 14, width: '100%', minHeight: 54, padding: '0 4px', textAlign: 'left', borderBottom: last ? 0 : '1px solid var(--line)', color: danger ? 'var(--red)' : 'var(--ink)' }}>{I && <span style={{ width: 36, height: 36, borderRadius: 10, background: danger ? 'var(--red-soft)' : 'var(--chip)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', color: danger ? 'var(--red)' : 'var(--purple)' }}><I size={18} /></span>}<span style={{ flex: 1, fontSize: 14.5, fontWeight: 600 }}>{label}</span>{value && <span style={{ fontSize: 13, color: 'var(--mut)', fontWeight: 500 }}>{value}</span>}{right || <Ico.chev size={16} style={{ color: 'var(--mut2)' }} />}</button>;
}

function Toast({ msg }) { if (!msg) return null; return <div className="rise" style={{ position: 'absolute', left: 16, right: 16, bottom: 100, zIndex: 95, padding: '12px 16px', borderRadius: 14, background: 'var(--fill)', color: '#fff', fontSize: 13.5, fontWeight: 600, boxShadow: '0 12px 30px rgba(29,13,49,.35)', display: 'flex', gap: 8, alignItems: 'center' }}><Ico.check size={16} />{msg}</div>; }

window.setShareHandler = (fn) => { useAppShare = fn; };
Object.assign(window, { GradChip, OptIn, AppCtx, useApp, Ico, Brand, Logo, Btn, IconBtn, HeartBtn, EventCard, CardSkeleton, SecHead, Sheet, TopBar, TabBar, Field, inputStyle, Row, Toast });
