/* OnSumn — Map components: MapView, MapEventCard, EventsMapSection
   Extracted from index.html. Behavior unchanged. */

/* ───── Map Event Card (slides up when a pin is clicked) ───── */
function MapEventCard({ event, darkMode, radius, onClose, position }) {
  if (!event || !position) return null;
  const tagColor = TAG_COLORS[event.tag] || 'var(--international-orange)';
  const bgHue = { MUSIC: 30, ART: 340, FOOD: 25, NIGHTLIFE: 260, COMMUNITY: 200, CARDS: 220 }[event.category] || 25;
  const cardW = 240, cardH = 220;

  return (
    <div style={{
      position: 'absolute',
      left: Math.max(6, Math.min(position.x - cardW / 2, position.containerW - cardW - 6)),
      top: Math.max(6, position.y - cardH - 14),
      zIndex: 600,
      width: cardW,
      animation: 'mapCardPop 0.18s cubic-bezier(0.16, 1, 0.3, 1) forwards',
    }}>
      <div style={{
        background: darkMode ? 'rgba(18,20,20,0.45)' : 'rgba(255,255,255,0.45)',
        backdropFilter: 'blur(24px)', WebkitBackdropFilter: 'blur(24px)',
        border: darkMode ? '1px solid rgba(255,255,255,0.12)' : '1px solid rgba(0,0,0,0.08)',
        borderRadius: Math.max(radius - 4, 10),
        overflow: 'hidden',
        boxShadow: darkMode
          ? '0 8px 32px rgba(0,0,0,0.5), inset 0 0 0 0.5px rgba(255,255,255,0.06)'
          : '0 8px 32px rgba(0,0,0,0.15), inset 0 0 0 0.5px rgba(255,255,255,0.5)',
      }}>
        {/* Close */}
        <button onClick={onClose} aria-label="Close event card" style={{
          position: 'absolute', top: 4, right: 4, zIndex: 10,
          width: 32, height: 32, borderRadius: '50%',
          background: 'rgba(0,0,0,0.5)', border: 'none',
          color: '#fff', fontSize: 12, cursor: 'pointer',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>✕</button>
        {/* Image */}
        <div style={{
          height: 90, position: 'relative', overflow: 'hidden',
          background: `oklch(${darkMode ? 0.3 : 0.45} 0.06 ${bgHue})`,
        }}>
          {event.image && <img src={event.image} alt="" style={{
            width: '100%', height: '100%', objectFit: 'cover',
          }} />}
          <div style={{
            position: 'absolute', inset: 0,
            background: 'linear-gradient(to top, rgba(0,0,0,0.5) 0%, transparent 50%)',
          }} />
          {/* Category pill */}
          <div style={{
            position: 'absolute', top: 6, left: 6,
            background: tagColor, color: '#fff',
            fontFamily: 'var(--font-body)', fontSize: 9, fontWeight: 700,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            padding: '4px 10px', borderRadius: 100,
          }}>{event.category}</div>
        </div>

        {/* Content */}
        <div style={{ padding: '8px 10px 10px' }}>
          <div style={{
            fontFamily: 'var(--font-body)', fontSize: 10, fontWeight: 700,
            letterSpacing: '0.06em', textTransform: 'uppercase',
            color: tagColor, marginBottom: 2,
          }}>{event.tag} · {event.date} · {event.time}</div>

          <div style={{
            fontFamily: 'var(--font-display)', fontSize: 13, fontWeight: 700,
            color: darkMode ? '#fff' : '#1a1a1a',
            lineHeight: 1.2, marginBottom: 3,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>{event.title}</div>

          <div style={{
            fontFamily: 'var(--font-body)', fontSize: 10,
            color: darkMode ? 'rgba(255,255,255,0.4)' : '#888',
            marginBottom: 8,
            display: 'flex', alignItems: 'center', gap: 3,
          }}>
            <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor"
              strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
              <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
              <circle cx="12" cy="10" r="3"/>
            </svg>
            {event.venue}
          </div>

          <button
            onClick={() => { if (event.link && /^https?:\/\//i.test(event.link)) window.open(event.link, '_blank', 'noopener,noreferrer'); }}
            style={{
              width: '100%', padding: '7px 0',
              background: 'var(--international-orange)',
              color: '#fff', border: 'none', cursor: 'pointer',
              fontFamily: 'var(--font-body)', fontSize: 10, fontWeight: 700,
              letterSpacing: '0.04em', textTransform: 'uppercase',
              borderRadius: Math.max(radius - 8, 6),
              transition: 'transform 0.15s, box-shadow 0.15s',
              boxShadow: '0 2px 10px rgba(255,79,0,0.25)',
            }}
            onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = '0 4px 14px rgba(255,79,0,0.35)'; }}
            onMouseLeave={e => { e.currentTarget.style.transform = 'none'; e.currentTarget.style.boxShadow = '0 2px 10px rgba(255,79,0,0.25)'; }}
          >I'M SLIDING →</button>
        </div>
      </div>
    </div>
  );
}

/* ───── Map View (Leaflet, uses glass tokens) ───── */
function MapView({ events, darkMode, height = 480, radius }) {
  const ref = React.useRef(null);
  const mapRef = React.useRef(null);
  const layerRef = React.useRef(null);
  const tileRef = React.useRef(null);
  const [selectedEvent, setSelectedEvent] = React.useState(null);
  const [cardPos, setCardPos] = React.useState(null);

  React.useEffect(() => {
    if (!ref.current || !window.L) return;
    if (mapRef.current) { mapRef.current.remove(); mapRef.current = null; }
    const m = window.L.map(ref.current, {
      center: [37.76, -122.20],
      zoom: 10,
      scrollWheelZoom: window.innerWidth > 768,
      attributionControl: false,
    });
    const tileUrl = darkMode
      ? 'https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png'
      : 'https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png';
    tileRef.current = window.L.tileLayer(tileUrl, { maxZoom: 19 }).addTo(m);
    mapRef.current = m;
    m.on('click', () => { setSelectedEvent(null); setCardPos(null); });
    return () => { m.remove(); mapRef.current = null; };
  }, [darkMode]);

  React.useEffect(() => {
    const m = mapRef.current;
    if (!m || !window.L) return;
    if (layerRef.current) { m.removeLayer(layerRef.current); }
    const group = window.L.markerClusterGroup({
      maxClusterRadius: 45,
      spiderfyOnMaxZoom: true,
      showCoverageOnHover: false,
      zoomToBoundsOnClick: true,
      animate: true,
    });
    events.forEach(ev => {
      if (ev.lat == null || ev.lng == null) return;
      const catColors = {
        'MUSIC': '#FF4F00', 'CARDS': '#7A99AC', 'ART': '#5A7A68',
        'FOOD': '#D57800', 'NIGHTLIFE': '#5B7A8D', 'COMMUNITY': '#395542',
      };
      const rawColor = catColors[ev.category] || TAG_COLORS[ev.tag] || '#FF4F00';
      const color = /^#[0-9A-Fa-f]{3,6}$/.test(rawColor) ? rawColor : '#FF4F00';
      const icon = window.L.divIcon({
        className: 'onsumn-pin',
        html: `<span class="onsumn-pin-dot" style="background:${color}"></span><span class="onsumn-pin-ring" style="border-color:${color}"></span>`,
        iconSize: [18, 18],
        iconAnchor: [9, 9],
      });
      const marker = window.L.marker([ev.lat, ev.lng], { icon });
      marker.on('click', (e) => {
        window.L.DomEvent.stopPropagation(e);
        const pt = m.latLngToContainerPoint(e.latlng);
        const container = ref.current;
        setCardPos({ x: pt.x, y: pt.y, containerW: container ? container.clientWidth : 600 });
        setSelectedEvent(ev);
      });
      group.addLayer(marker);
    });
    group.addTo(m);
    layerRef.current = group;
    /* Fit map to show all event pins */
    const validEvents = events.filter(e => e.lat != null && e.lng != null);
    if (validEvents.length > 1) {
      const bounds = window.L.latLngBounds(validEvents.map(e => [e.lat, e.lng]));
      m.fitBounds(bounds, { padding: [40, 40], maxZoom: 12 });
    }
  }, [events]);

  return (
    <div style={{
      position: 'relative',
      ...glassStyle(darkMode, 'card'),
      border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.06)',
      borderRadius: radius,
      overflow: 'hidden',
      boxShadow: darkMode
        ? 'inset 0 0 30px rgba(0,0,0,0.4), 0 8px 32px rgba(0,0,0,0.3)'
        : 'inset 0 0 20px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.08)',
    }}>
      <div ref={ref} style={{ width: '100%', height, background: darkMode ? '#0A0A0A' : '#EFEFEF' }} />
      {/* Event count badge */}
      {!selectedEvent && <div style={{
        position: 'absolute', bottom: 12, left: 12, zIndex: 500,
        ...glassStyle(darkMode, 'btn'),
        border: darkMode ? '1px solid rgba(255,255,255,0.1)' : '1px solid rgba(255,255,255,0.3)',
        borderRadius: Math.max(radius - 4, 8),
        padding: '8px 14px',
        fontFamily: 'var(--font-body)', fontSize: 10, fontWeight: 700,
        letterSpacing: '0.1em', textTransform: 'uppercase',
        color: darkMode ? 'rgba(255,255,255,0.7)' : 'rgba(0,0,0,0.5)',
      }}>
        <span style={{
          display: 'inline-block', width: 6, height: 6, borderRadius: '50%',
          background: 'var(--international-orange)',
          boxShadow: '0 0 6px var(--international-orange)',
          animation: 'livePulse 2s ease-in-out infinite',
          marginRight: 6, verticalAlign: 'middle',
        }} />{events.length} EVENTS · THE BAY, RIGHT NOW
      </div>}
      {/* Selected event card */}
      <MapEventCard
        event={selectedEvent}
        darkMode={darkMode}
        radius={radius}
        position={cardPos}
        onClose={() => { setSelectedEvent(null); setCardPos(null); }}
      />
    </div>
  );
}

/* ───── Time-of-day filter helper ───── */
function matchesTimeFilter(event, times) {
  if (!times || times.length === 0) return true;
  const t = event.time || '';
  const match = t.match(/(\d+)\s*(AM|PM)/i);
  if (!match) return true;
  let hour = parseInt(match[1]);
  const period = match[2].toUpperCase();
  if (period === 'PM' && hour !== 12) hour += 12;
  if (period === 'AM' && hour === 12) hour = 0;

  return times.some(slot => {
    if (slot === 'Morning') return hour >= 6 && hour < 12;
    if (slot === 'Afternoon') return hour >= 12 && hour < 17;
    if (slot === 'Evening') return hour >= 17 && hour < 21;
    if (slot === 'Late Night') return hour >= 21 || hour < 6;
    return true;
  });
}

/* ───── Date filter helper ───── */
function matchesDateFilter(event, filter) {
  if (filter === 'ALL') return true;
  const now = new Date();
  const eventDateStr = event.date || '';
  // Parse "Mon DD" format (e.g., "Apr 25")
  const match = eventDateStr.match(/^(\w+)\s+(\d+)$/);
  if (!match) return true; // Can't parse, show it
  const months = { Jan:0, Feb:1, Mar:2, Apr:3, May:4, Jun:5, Jul:6, Aug:7, Sep:8, Oct:9, Nov:10, Dec:11 };
  const monthIdx = months[match[1]];
  if (monthIdx === undefined) return true;
  const day = parseInt(match[2]);
  const eventDate = new Date(now.getFullYear(), monthIdx, day);
  // If event month is far behind current month, it's probably next year
  if (monthIdx < now.getMonth() - 1) eventDate.setFullYear(now.getFullYear() + 1);

  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
  const tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1);
  const eventDay = new Date(eventDate.getFullYear(), eventDate.getMonth(), eventDate.getDate());

  if (filter === 'TODAY') return eventDay.getTime() === today.getTime();
  if (filter === 'TOMORROW') return eventDay.getTime() === tomorrow.getTime();
  if (filter === 'WEEKEND') {
    const sat = new Date(today);
    sat.setDate(today.getDate() + (6 - today.getDay()));
    const sun = new Date(sat); sun.setDate(sat.getDate() + 1);
    return eventDay.getTime() === sat.getTime() || eventDay.getTime() === sun.getTime();
  }
  return true;
}

/* ───── Events + Map Section (merges glass grid + Leaflet map) ───── */
function EventsMapSection({ darkMode, activeCity, radius, taste }) {
  const { events: allEvents, loading } = useEvents();
  const [view, setView] = React.useState('split');
  const [switching, setSwitching] = React.useState(false);
  const [filterOpen, setFilterOpen] = React.useState(false);
  const [dateFilter, setDateFilter] = React.useState('ALL');
  const [activeFilters, setActiveFilters] = React.useState({ categories: [], cities: [], times: [], sounds: [] });
  const [page, setPage] = React.useState(0);
  const [isMobile, setIsMobile] = React.useState(window.innerWidth <= 768);
  const carouselRef = React.useRef(null);
  const EVENTS_PER_PAGE = 12;

  /* Draggable section order — persisted to localStorage */
  const DEFAULT_ORDER = ['MUSIC', 'ART', 'FOOD', 'NIGHTLIFE', 'COMMUNITY', 'CARDS'];
  const [sectionOrder, setSectionOrder] = React.useState(() => {
    try {
      const saved = localStorage.getItem('onsumn-section-order');
      if (saved) {
        const parsed = JSON.parse(saved);
        const missing = DEFAULT_ORDER.filter(c => !parsed.includes(c));
        return [...parsed.filter(c => DEFAULT_ORDER.includes(c)), ...missing];
      }
    } catch {}
    return DEFAULT_ORDER;
  });
  const [liftedCat, setLiftedCat] = React.useState(null);
  const rowRefs = React.useRef({});
  const dragState = React.useRef(null);
  const scrollAnimRef = React.useRef(null);

  /* Batch-fetch interest counts for visible events */
  React.useEffect(() => {
    if (!allEvents.length) return;
    const slugs = allEvents.filter(e => e.slug).map(e => e.slug).slice(0, 50);
    fetchInterestCounts(slugs);
  }, [allEvents]);

  /* Pointer-based drag: smooth, no HTML5 DnD ghost */
  const handlePointerDown = React.useCallback((e, cat) => {
    e.preventDefault();
    const rowEl = rowRefs.current[cat];
    if (!rowEl) return;

    const rect = rowEl.getBoundingClientRect();
    const startY = e.clientY;
    const startScroll = window.scrollY;

    /* Lift the row */
    setLiftedCat(cat);
    rowEl.style.transition = 'transform 0.08s ease-out, box-shadow 0.2s ease';
    rowEl.style.zIndex = '50';
    rowEl.style.transform = 'scale(1.015)';
    rowEl.style.boxShadow = '0 12px 40px rgba(0,0,0,0.35)';
    rowEl.style.borderRadius = '16px';
    rowEl.style.pointerEvents = 'none';
    document.body.style.cursor = 'grabbing';
    document.body.style.userSelect = 'none';

    let currentY = startY;

    /* Auto-scroll near edges */
    const scrollLoop = () => {
      const edgeZone = 100;
      const speed = 6;
      if (currentY > 0 && currentY < edgeZone) {
        const intensity = 1 - (currentY / edgeZone);
        window.scrollBy(0, -speed * intensity);
      } else if (currentY > window.innerHeight - edgeZone) {
        const intensity = 1 - ((window.innerHeight - currentY) / edgeZone);
        window.scrollBy(0, speed * intensity);
      }
      scrollAnimRef.current = requestAnimationFrame(scrollLoop);
    };
    scrollAnimRef.current = requestAnimationFrame(scrollLoop);

    const onPointerMove = (ev) => {
      currentY = ev.clientY;
      const scrollDelta = window.scrollY - startScroll;
      const dy = ev.clientY - startY + scrollDelta;
      rowEl.style.transform = `translateY(${dy}px) scale(1.015)`;

      /* Hit-test other rows for swap */
      const liftedMid = rect.top + rect.height / 2 + dy;
      setSectionOrder(prev => {
        const curIdx = prev.indexOf(cat);
        let swapIdx = -1;
        for (let i = 0; i < prev.length; i++) {
          if (prev[i] === cat) continue;
          const otherEl = rowRefs.current[prev[i]];
          if (!otherEl) continue;
          const otherRect = otherEl.getBoundingClientRect();
          const otherMid = otherRect.top + otherRect.height / 2;
          if (curIdx < i && liftedMid > otherMid) swapIdx = i;
          if (curIdx > i && liftedMid < otherMid) swapIdx = i;
        }
        if (swapIdx !== -1 && swapIdx !== curIdx) {
          const next = [...prev];
          next.splice(curIdx, 1);
          next.splice(swapIdx, 0, cat);
          localStorage.setItem('onsumn-section-order', JSON.stringify(next));
          return next;
        }
        return prev;
      });
    };

    const onPointerUp = () => {
      /* Settle the row back into place */
      rowEl.style.transition = 'transform 0.35s cubic-bezier(0.2, 0, 0, 1), box-shadow 0.35s ease, border-radius 0.35s ease';
      rowEl.style.transform = '';
      rowEl.style.boxShadow = '';
      rowEl.style.borderRadius = '';
      rowEl.style.zIndex = '';
      rowEl.style.pointerEvents = '';
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
      setLiftedCat(null);

      if (scrollAnimRef.current) cancelAnimationFrame(scrollAnimRef.current);
      document.removeEventListener('pointermove', onPointerMove);
      document.removeEventListener('pointerup', onPointerUp);
    };

    document.addEventListener('pointermove', onPointerMove);
    document.addEventListener('pointerup', onPointerUp);
  }, []);

  React.useEffect(() => {
    const onResize = () => setIsMobile(window.innerWidth <= 768);
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  const handleViewChange = (v) => {
    if (v === view) return;
    setSwitching(true);
    setTimeout(() => { setView(v); setSwitching(false); }, 250);
  };

  const totalFilters = activeFilters.categories.length + activeFilters.cities.length + activeFilters.times.length + activeFilters.sounds.length;

  /* City mapping for filter chips (display name → data value) */
  const cityNameMap = {
    'Oakland': 'OAKLAND', 'SF': 'SF', 'San Jose': 'SAN JOSE',
    'Vallejo': 'VALLEJO', 'Richmond': 'RICHMOND', 'Daly City': 'DALY CITY',
  };

  const filtered = allEvents.filter(e => {
    /* Header city nav still works */
    const headerCityMatch = activeCity === 'ALL' || e.city === activeCity;
    /* Popup category filter */
    const catMatch = activeFilters.categories.length === 0
      || activeFilters.categories.some(c => c.toUpperCase() === e.category);
    /* Popup city filter */
    const popupCityMatch = activeFilters.cities.length === 0
      || activeFilters.cities.some(c => cityNameMap[c] === e.city);
    /* Popup time filter */
    const timeMatch = matchesTimeFilter(e, activeFilters.times);
    /* Sub-genre filter (only when Concerts + sounds selected) */
    const soundMatch = activeFilters.sounds.length === 0
      || !e.subgenres || e.subgenres.length === 0
      || activeFilters.sounds.some(s => e.subgenres.includes(s));

    /* Date quick-pick filter */
    const dateMatch = matchesDateFilter(e, dateFilter);

    /* Popup date filter (from filter popup "When" section) */
    const popupDateMatch = !activeFilters.dates || activeFilters.dates.length === 0
      || activeFilters.dates.some(d => {
        const key = d.toUpperCase().replace('THIS ', '');
        return matchesDateFilter(e, key);
      });

    return headerCityMatch && catMatch && popupCityMatch && timeMatch && soundMatch && dateMatch && popupDateMatch;
  });

  /* Apply taste-based sorting — matching events float up, nothing gets hidden */
  const sorted = taste && !taste.skipped ? [...filtered].sort((a, b) => {
    return tasteBoost(b, taste) - tasteBoost(a, taste);
  }) : filtered;

  const totalPages = Math.ceil(sorted.length / EVENTS_PER_PAGE);
  const currentPage = Math.min(page, Math.max(totalPages - 1, 0));
  const visible = sorted.slice(currentPage * EVENTS_PER_PAGE, (currentPage + 1) * EVENTS_PER_PAGE);

  /* Reset page when filters change */
  React.useEffect(() => { setPage(0); }, [activeCity, activeFilters, dateFilter]);

  /* Carousel scroll navigation */
  const scrollCarousel = (dir) => {
    const c = carouselRef.current;
    if (!c) return;
    const amount = c.clientWidth * 0.85;
    c.scrollBy({ left: dir === 'left' ? -amount : amount, behavior: 'smooth' });
  };

  return (
    <section style={{ padding: 'clamp(40px, 8vw, 80px) clamp(16px, 4vw, 32px)', maxWidth: 1280, margin: '0 auto' }}>
      {/* Filter Popup */}
      <FilterPopup
        darkMode={darkMode}
        radius={radius}
        activeFilters={activeFilters}
        onApply={(filters) => { setActiveFilters(filters); setPage(0); }}
        onClose={() => setFilterOpen(false)}
        visible={filterOpen}
      />

      {/* Header */}
      <div style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end',
        marginBottom: 40, flexWrap: 'wrap', gap: 16,
      }}>
        <div>
          <h2 id="main-content" style={{
            fontFamily: 'var(--font-display)', fontSize: 32, fontWeight: 700,
            color: darkMode ? 'var(--on-surface)' : '#1a1a1a', lineHeight: 1,
            marginBottom: 8,
          }}>What's Good This Weekend</h2>
          <p style={{
            fontFamily: 'var(--font-body)', fontSize: 14,
            color: darkMode ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.45)',
          }}>Fresh every day. The whole Bay.</p>
        </div>

      </div>

      {/* Glass view toggle */}
      <div className="view-toggle" style={{
        display: 'inline-flex', gap: 2, marginBottom: 24,
        ...glassStyle(darkMode, 'panel'),
        border: darkMode ? '1px solid rgba(255,255,255,0.08)' : '1px solid rgba(0,0,0,0.06)',
        borderRadius: radius, padding: 3,
      }}>
        {[['map', 'Map'], ['split', 'Split'], ['grid', 'Grid']].map(([v, label]) => (
          <button key={v} aria-label={`${label} view`} aria-pressed={view === v} onClick={() => handleViewChange(v)} style={{
            fontFamily: 'var(--font-body)', fontSize: 11, fontWeight: 700,
            letterSpacing: '0.08em', textTransform: 'uppercase',
            padding: '8px 16px', minHeight: 44, borderRadius: Math.max(radius - 4, 0),
            background: view === v ? (darkMode ? 'rgba(255,255,255,0.12)' : 'rgba(0,0,0,0.08)') : 'transparent',
            color: view === v ? (darkMode ? '#fff' : '#0A0A0A') : (darkMode ? 'rgba(255,255,255,0.4)' : 'rgba(0,0,0,0.35)'),
            border: 'none', cursor: 'pointer',
            transition: 'all 0.25s',
          }}>{label}</button>
        ))}
      </div>

      {/* Temporal quick-pick pills */}
      <div style={{
        display: 'flex', flexWrap: 'wrap', gap: 6, marginBottom: 20, alignItems: 'center',
      }}>
        {['TODAY', 'TOMORROW', 'WEEKEND', 'ALL'].map(label => (
          <button key={label} aria-label={`Filter by ${label === 'ALL' ? 'all dates' : label.toLowerCase()}`} aria-pressed={dateFilter === label} onClick={() => setDateFilter(label)} style={{
            padding: '10px 18px', borderRadius: 100, minHeight: 40,
            fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
            letterSpacing: '0.04em',
            cursor: 'pointer', transition: 'all 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
            border: dateFilter === label
              ? '2px solid var(--international-orange)'
              : (darkMode ? '2px solid rgba(255,255,255,0.12)' : '2px solid rgba(0,0,0,0.08)'),
            background: dateFilter === label
              ? 'var(--international-orange)'
              : (darkMode ? 'rgba(255,255,255,0.04)' : 'rgba(0,0,0,0.03)'),
            color: dateFilter === label
              ? '#fff'
              : (darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.45)'),
            transform: dateFilter === label ? 'scale(1.05)' : 'scale(1)',
          }}>{label === 'ALL' ? 'ALL DATES' : label}</button>
        ))}

        {/* MORE filter button */}
        <button onClick={() => setFilterOpen(true)} style={{
          padding: '10px 16px', borderRadius: 100, minHeight: 40,
          fontFamily: 'var(--font-body)', fontSize: 12, fontWeight: 700,
          letterSpacing: '0.04em',
          cursor: 'pointer', transition: 'all 0.25s',
          border: totalFilters > 0
            ? '2px solid var(--international-orange)'
            : (darkMode ? '2px solid rgba(255,255,255,0.12)' : '2px solid rgba(0,0,0,0.08)'),
          background: 'transparent',
          color: totalFilters > 0
            ? 'var(--international-orange)'
            : (darkMode ? 'rgba(255,255,255,0.55)' : 'rgba(0,0,0,0.45)'),
          display: 'inline-flex', alignItems: 'center', gap: 6,
        }}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor"
            strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
            <path d="M4 6h16M6 12h12M9 18h6"/>
          </svg>
          {totalFilters > 0 ? `MORE (${totalFilters})` : 'MORE'}
        </button>
      </div>

      <div className={`view-content ${switching ? 'switching' : ''}`}>
        {/* Map */}
        {view !== 'grid' && (
          <div style={{ marginBottom: view === 'split' ? 32 : 0 }}>
            <MapView events={filtered} darkMode={darkMode} height={view === 'map' ? 620 : 440} radius={radius} />
          </div>
        )}

        {/* Loading skeleton */}
        {loading && view !== 'map' ? (
          <div className="events-grid" style={{
            display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(min(280px, 100%), 1fr))', gap: 16,
          }}>
            {[1,2,3,4].map(i => <SkeletonCard key={i} darkMode={darkMode} radius={radius} />)}
          </div>
        ) : view === 'map' ? null : sorted.length === 0 ? (
          <EmptyState darkMode={darkMode} radius={radius} city={activeCity} category={activeFilters.categories.length === 1 ? activeFilters.categories[0] : null} />
        ) : (
          /* ── Category-Sectioned Carousels ── */
          <div>
            {/* YOUR WAVE row — taste-matched events surfaced first */}
            {taste && !taste.skipped && (() => {
              const forYouEvents = sorted.filter(e => tasteBoost(e, taste) > 0).slice(0, 15);
              return forYouEvents.length > 0 ? (
                <CategoryCarouselRow
                  title="YOUR WAVE"
                  events={forYouEvents}
                  darkMode={darkMode} radius={radius} taste={taste}
                />
              ) : null;
            })()}

            {/* Category rows — user-reorderable */}
            {sectionOrder.map(cat => {
              const catEvents = sorted.filter(e => e.category === cat);
              return (
                <CategoryCarouselRow
                  key={cat}
                  title={cat}
                  events={catEvents}
                  darkMode={darkMode} radius={radius} taste={taste}
                  rowRef={el => { if (el) rowRefs.current[cat] = el; }}
                  isLifted={liftedCat === cat}
                  onHandlePointerDown={handlePointerDown}
                />
              );
            })}
          </div>
        )}
      </div>
    </section>
  );
}

window.MapEventCard = MapEventCard;
window.MapView = MapView;
window.EventsMapSection = EventsMapSection;
window.matchesTimeFilter = matchesTimeFilter;
window.matchesDateFilter = matchesDateFilter;
