// screens-home.jsx — discovery / home feed (+ hero variants)

function HomeScreen({ app }) {
  const [chip, setChip] = React.useState('All');
  const [search, setSearch] = React.useState('');
  const [loading, setLoading] = React.useState(true);
  const [feedRevealed, setFeedRevealed] = React.useState(false);
  const deferredFeedRef = React.useRef(null);
  const heroVariant = app.tweaks.heroVariant || 'classic';

  // Delivery details (address + time) drawer
  const [deliveryOpen, setDeliveryOpen] = React.useState(false);
  const [addrId, setAddrId] = React.useState((ADDRESSES.find(a => a.default) || ADDRESSES[0]).id);
  const [whenId, setWhenId] = React.useState('asap');
  const selectedAddr = ADDRESSES.find(a => a.id === addrId) || ADDRESSES[0];
  const selectedWhen = WHEN_OPTIONS.find(w => w.id === whenId) || WHEN_OPTIONS[0];

  // Search + filter drawers
  const [searchOpen, setSearchOpen] = React.useState(false);
  const [filterOpen, setFilterOpen] = React.useState(false);
  const [filters, setFilters] = React.useState({ sort: 'recommended', maxFee: 'any', openNow: false, featured: false });
  const activeFilterCount =
    (filters.sort !== 'recommended' ? 1 : 0) +
    (filters.maxFee !== 'any' ? 1 : 0) +
    (filters.openNow ? 1 : 0) +
    (filters.featured ? 1 : 0);

  React.useEffect(() => {
    const t = setTimeout(() => setLoading(false), 700);
    return () => clearTimeout(t);
  }, []);

  // Filter
  const cuisines = ['All', 'BBQ', 'Burgers', 'Tacos', 'Coffee', 'Sweets', 'Healthy', 'Asian'];
  const restHasFavDish = (rid) => {
    const m = MENUS[rid] || GENERIC_MENU;
    return m.some(sec => sec.items.some(it => app.favourites.has(it.id)));
  };
  const filtered = sortRestaurants(RESTAURANTS.filter(r => {
    if (chip === 'Faves') {
      if (!(app.favStores.has(r.id) || restHasFavDish(r.id))) return false;
    } else if (chip !== 'All') {
      const c = r.cuisine.toLowerCase();
      const want = chip.toLowerCase();
      if (!c.includes(want) && !(want === 'sweets' && c.includes('sweet')) && !(want === 'burgers' && (c.includes('burger') || c.includes('smash')))) return false;
    }
    if (search) {
      const s = search.toLowerCase();
      const dishHit = (MENUS[r.id] || []).some(sec => sec.items.some(it =>
        it.name.toLowerCase().includes(s) || (it.desc || '').toLowerCase().includes(s)));
      if (!r.name.toLowerCase().includes(s) && !r.cuisine.toLowerCase().includes(s) && !dishHit) return false;
    }
    // Filter-drawer constraints
    if (filters.openNow && !['live', 'cooking', 'open'].includes(r.status)) return false;
    if (filters.maxFee === '2' && !(r.deliveryFee < 2)) return false;
    if (filters.maxFee === '1' && !(r.deliveryFee < 1)) return false;
    if (filters.featured && !r.featured) return false;
    return true;
  }), filters.sort);

  const featured = filtered.filter(r => r.featured);
  const filterActive = chip !== 'All' || !!search.trim();
  const [activeOffer, setActiveOffer] = React.useState(null);

  React.useEffect(() => {
    if (loading || filterActive || feedRevealed) return undefined;
    const deferredFeed = deferredFeedRef.current;
    if (!deferredFeed) return undefined;

    if (!('IntersectionObserver' in window)) {
      setFeedRevealed(true);
      return undefined;
    }

    const observer = new IntersectionObserver((entries) => {
      if (entries.some(entry => entry.isIntersecting)) {
        setFeedRevealed(true);
      }
    }, {
      rootMargin: '0px 0px -96px 0px',
      threshold: 0.01,
    });
    observer.observe(deferredFeed);
    return () => observer.disconnect();
  }, [feedRevealed, filterActive, loading]);

  // Curated discovery rails (independent of category filter)
  const hasTag = (r, t) => (r.tags || []).includes(t);
  const railFeatured = RESTAURANTS.filter(r => r.featured);
  const railTop10  = [...RESTAURANTS].sort((a, b) => b.rating - a.rating).slice(0, 10);
  const railNearby = [...RESTAURANTS].sort((a, b) => railDist(a) - railDist(b)).slice(0, 8);
  const railFree   = RESTAURANTS.filter(r => hasTag(r, 'free'));
  const railValue  = [...RESTAURANTS]
    .sort((a, b) => (a.deliveryFee - b.deliveryFee) || (a.minOrder - b.minOrder)); // cheapest delivery — "big value, small spend"
  const railPickup = RESTAURANTS.filter(r => hasTag(r, 'pickup'));
  const railVegan  = RESTAURANTS.filter(r => hasTag(r, 'vegan') || hasTag(r, 'eco'));

  return (
    <Screen>
      {/* Sticky top bar — LASSO + bell/account stay pinned together */}
      <div data-production-mobile-header="" style={{ position: 'sticky', top: 0, zIndex: 33, background: 'var(--bg-app)', overflow: 'visible' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 20px 4px' }}>
          <Wordmark size={22}/>
          <HeaderActions
            authed={app.authed}
            onBell={() => app.nav('notifications')}
            onProfile={() => app.nav('profile')}
            initials={PROFILE.initials}
          />
        </div>
        {/* Address row — stays under the header */}
        <div data-action="open-delivery" onClick={() => setDeliveryOpen(true)} style={{
          display: 'flex', alignItems: 'center', gap: 10, padding: '2px 20px 34px', cursor: 'pointer',
        }}>
          <Ico.pin c="var(--brand-orange)" s={16}/>
          <div style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
            <div style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--warm-stone)', lineHeight: 1.1 }}>Deliver to · {selectedWhen.short}</div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 13.5, color: 'var(--fg-primary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{selectedAddr.label} · {selectedAddr.line1}</div>
          </div>
          <Ico.chevD c="var(--warm-stone)" s={14}/>
        </div>
        <div data-production-mobile-header-fade="" aria-hidden="true" style={{
          position: 'absolute',
          left: 0,
          right: 0,
          bottom: -26,
          height: 26,
          pointerEvents: 'none',
          background: 'linear-gradient(180deg, var(--bg-app) 0%, rgba(245,239,231,0) 100%)',
        }}/>
      </div>

      {/* Search — floating pill tucked under the sticky bar (no gap) */}
      <div style={{ position: 'sticky', top: 96, zIndex: 34, background: 'transparent', padding: '0 0 8px', marginTop: -24 }}>
        <SearchBar
          value={search}
          elevated
          onActivate={() => setSearchOpen(true)}
          onFilter={() => setFilterOpen(true)}
          filterCount={activeFilterCount}
        />
      </div>

      {/* Category slider — image + name, scrolls right */}
      <CategorySlider active={chip} onPick={(val) => setChip(c => c === val ? 'All' : val)} favCount={app.favourites.size + app.favStores.size}/>

      {filterActive ? (
        <>
          {/* ── Filter results — right under the search, no scrolling to the bottom ── */}
          <div data-lasso="filter-results" style={{ animation: 'lasso-slide-up 260ms var(--ease-out)' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '2px 20px 12px' }}>
              <div style={{ flex: 1, minWidth: 0 }}>
                <Eyebrow color="var(--brand-orange)">{search.trim() ? `“${search.trim()}”` : chip === 'Faves' ? 'Your favourites' : chip}</Eyebrow>
                <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 20, letterSpacing: '-0.01em', color: 'var(--fg-primary)', marginTop: 4 }}>
                  {filtered.length} {filtered.length === 1 ? 'spot' : 'spots'} on the trail
                </div>
              </div>
              <button onClick={() => { setChip('All'); setSearch(''); }} style={{
                display: 'inline-flex', alignItems: 'center', gap: 6, padding: '8px 13px', borderRadius: 12, cursor: 'pointer',
                background: 'var(--dark-chocolate)', color: 'var(--dust-cream)', border: 'none', flexShrink: 0,
                fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 12,
              }}><Ico.x c="var(--dust-cream)" s={13}/> Clear</button>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: '0 20px 8px' }}>
              {chip === 'Faves' && !search.trim() && <SavedDishRail app={app}/>}
              {filtered.length === 0
                ? (chip === 'Faves'
                    ? <EmptyState
                        title="Your horse hasn't found a favourite trail"
                        body="Tap the heart on any dish or kitchen and it’ll wait for you here."
                        cta="Browse the trail"
                        onAction={() => { setChip('All'); setSearch(''); }}
                        icon={<Ico.heart c="var(--brand-orange)" s={30}/>}
                      />
                    : <EmptyState
                    title="Nothing roped in"
                    body="No kitchens match that. Try a nearby trail below."
                    cta="Clear filters"
                    onAction={() => { setChip('All'); setSearch(''); }}
                  />)
                : filtered.map(r => <RestaurantCard key={r.id} restaurant={r} onOpen={() => app.openRestaurant(r.id)}/>)}
            </div>
            {/* Keep browsing — related categories so the trail never dead-ends */}
            <SectionTitle eyebrow="Not it?" title="Try another trail"/>
            <CategorySlider active={chip} onPick={(val) => { setSearch(''); setChip(c => c === val ? 'All' : val); }}/>
            <div style={{ height: 16 }}></div>
          </div>
        </>
      ) : (
      <>
      {/* Offers — horizontal scroll of promo cards */}
      <OffersRail app={app} onOpen={setActiveOffer}/>

      {/* Discovery rails */}
      {loading ? (
        <>
          <SectionTitle eyebrow="Wrangler's Picks" title="Featured tonight" action="See all"/>
          <div style={{ display: 'flex', gap: 12, padding: '0 20px 20px', overflowX: 'auto', scrollbarWidth: 'none', scrollSnapType: 'x mandatory', scrollPaddingInline: 20 }}>
            {[0,1].map(i => <FeaturedSkeleton key={i}/>)}
          </div>
          <SectionTitle title="Near you, right now"/>
          <div data-lasso="restaurant-grid" style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: '0 20px 24px' }}>
            {[0,1,2].map(i => <RestaurantSkeleton key={i}/>)}
          </div>
        </>
      ) : (
        <>
          <DiscoveryRail app={app} variant="featured" eyebrow="Wrangler's Picks" title="Featured tonight" items={railFeatured}/>
          <TopTenRail app={app} items={railTop10}/>
          <div
            ref={deferredFeedRef}
            data-lasso="deferred-home-feed"
            aria-busy={!feedRevealed}
            style={{ minHeight: feedRevealed ? 0 : 420 }}
          >
            {feedRevealed && (
              <>
                <DiscoveryRail app={app} variant="nearby"   eyebrow="On your block" title="Closest to you" items={railNearby}/>
                <DiscoveryRail app={app} variant="free"     eyebrow="Fees on us" title="Free delivery this week" items={railFree}/>
                <ValueGrid     app={app} eyebrow="Smart spend" title="Big value, small spend" items={railValue}/>
                <DiscoveryRail app={app} variant="pickup"   eyebrow="Skip the wait" title="Ready for collection" items={railPickup}/>
                <DiscoveryRail app={app} variant="vegan"    eyebrow="Plant-first" title="Vegan & eco" items={railVegan}/>
                <SectionTitle title="Near you, right now"/>
                <div data-lasso="restaurant-grid" style={{ display: 'flex', flexDirection: 'column', gap: 14, padding: '0 20px 24px' }}>
                  {filtered.length === 0
                    ? <EmptyState
                        title="No restaurants match"
                        body="Try a different cuisine or clear your search."
                        cta="Clear filters"
                        onAction={() => { setChip('All'); setSearch(''); }}
                      />
                    : filtered.map(r => <RestaurantCard key={r.id} restaurant={r} onOpen={() => app.openRestaurant(r.id)}/>)
                  }
                </div>
              </>
            )}
          </div>
        </>
      )}
      </>
      )}

      <div
        aria-hidden="true"
        style={{
          height: 'max(132px, calc(env(safe-area-inset-bottom, 0px) + 132px))',
          flexShrink: 0,
        }}
      />

      {/* Offer detail drawer */}
      <OfferSheet
        offer={activeOffer}
        onClose={() => setActiveOffer(null)}
        app={app}
        onBrowseCategory={(c) => { setActiveOffer(null); setSearch(''); setChip(c); }}
      />

      {/* Delivery details drawer */}
      <DeliverySheet
        app={app}
        open={deliveryOpen}
        onClose={() => setDeliveryOpen(false)}
        addrId={addrId} setAddrId={setAddrId}
        whenId={whenId} setWhenId={setWhenId}
      />

      {/* Full search drawer */}
      <SearchDrawer
        app={app}
        open={searchOpen}
        onClose={() => setSearchOpen(false)}
        initial={search}
        onCommit={(q) => { setSearch(q); setSearchOpen(false); }}
      />

      {/* Filter drawer */}
      <FilterDrawer
        open={filterOpen}
        onClose={() => setFilterOpen(false)}
        filters={filters}
        setFilters={setFilters}
        resultCount={filtered.length}
      />
    </Screen>
  );
}

// ─── Delivery details drawer (address + time) ───────────────────────

const WHEN_OPTIONS = [
  { id: 'asap', label: 'Standard delivery', short: 'Now', sub: 'Arrives in 18–28 min', tag: 'Free', icon: 'clock' },
  { id: 'priority', label: 'Priority delivery', short: 'Priority', sub: 'Jump the line · ~12 min', tag: '+£2.99', icon: 'flame' },
  { id: 's1', label: 'Today, 7:30 PM', short: '7:30 PM', sub: 'Scheduled window', tag: 'Free', icon: 'cal' },
  { id: 's2', label: 'Today, 8:00 PM', short: '8:00 PM', sub: 'Scheduled window', tag: 'Free', icon: 'cal' },
  { id: 's3', label: 'Today, 8:30 PM', short: '8:30 PM', sub: 'Scheduled window', tag: 'Free', icon: 'cal' },
];

function WhenIcon({ name, c, s }) {
  if (name === 'flame') return <Ico.flame c={c} s={s}/>;
  if (name === 'cal') return <Ico.clock c={c} s={s}/>;
  return <Ico.clock c={c} s={s}/>;
}

function DeliverySheet({ app, open, onClose, addrId, setAddrId, whenId, setWhenId }) {
  const SectionLabel = ({ children }) => (
    <div style={{
      fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.08em',
      textTransform: 'uppercase', color: 'var(--warm-stone)', padding: '2px 4px 10px',
    }}>{children}</div>
  );

  const addr = ADDRESSES.find(a => a.id === addrId) || ADDRESSES[0];
  const when = WHEN_OPTIONS.find(w => w.id === whenId) || WHEN_OPTIONS[0];

  return (
    <BottomSheet open={open} onClose={onClose} title="Delivery details">
      <div data-lasso="delivery-sheet" style={{ padding: '0 20px 28px' }}>

        {/* Deliver to */}
        <SectionLabel>Deliver to</SectionLabel>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {ADDRESSES.map(a => {
            const on = a.id === addrId;
            return (
              <button key={a.id} data-action="select-address" onClick={() => setAddrId(a.id)} className={on ? 'lv-picked' : ''} style={{
                display: 'flex', alignItems: 'center', gap: 12, padding: 14, borderRadius: 14, cursor: 'pointer',
                background: on ? 'var(--bg-warm)' : 'var(--bg-surface)',
                border: '1.5px solid ' + (on ? 'var(--brand-orange)' : 'var(--soft-line)'),
                transition: 'transform 240ms var(--ease-out), box-shadow 240ms var(--ease-out)',
                textAlign: 'left',
              }}>
                <div style={{ width: 36, height: 36, borderRadius: 10, background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {a.label === 'Home' ? <Ico.home c="var(--brand-orange)" s={18}/> : <Ico.pin c="var(--brand-orange)" s={18}/>}
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>{a.label}</div>
                  <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{a.line1}{a.line2 ? `, ${a.line2}` : ''}, {a.city}</div>
                </div>
                {on && <Ico.check c="var(--brand-orange)" s={18}/>}
              </button>
            );
          })}
          <button data-action="add-address" onClick={() => app.toast('Add new address coming soon', 'info')} style={{
            display: 'flex', alignItems: 'center', gap: 12, padding: 14, borderRadius: 14, cursor: 'pointer',
            background: 'transparent', border: '1.5px dashed var(--soft-line)', textAlign: 'left',
          }}>
            <div style={{ width: 36, height: 36, borderRadius: 10, background: 'var(--bg-warm)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <Ico.plus c="var(--horse-brown)" s={18}/>
            </div>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>Add new address</div>
          </button>
        </div>

        {/* When */}
        <div style={{ marginTop: 22 }}>
          <SectionLabel>When</SectionLabel>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            {WHEN_OPTIONS.map(w => {
              const on = w.id === whenId;
              return (
                <button key={w.id} data-action="select-time" onClick={() => setWhenId(w.id)} className={on ? 'lv-picked' : ''} style={{
                  display: 'flex', alignItems: 'center', gap: 12, padding: 14, borderRadius: 14, cursor: 'pointer',
                  background: on ? 'var(--bg-warm)' : 'var(--bg-surface)',
                  border: '1.5px solid ' + (on ? 'var(--brand-orange)' : 'var(--soft-line)'),
                  transition: 'transform 240ms var(--ease-out), box-shadow 240ms var(--ease-out)',
                  textAlign: 'left',
                }}>
                  <div style={{ width: 36, height: 36, borderRadius: 10, background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                    <WhenIcon name={w.icon} c="var(--brand-orange)" s={18}/>
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>{w.label}</div>
                    <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2 }}>{w.sub}</div>
                  </div>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 11, color: w.tag === 'Free' ? 'var(--warm-stone)' : 'var(--brand-orange)' }}>{w.tag}</div>
                  {on && <Ico.check c="var(--brand-orange)" s={18}/>}
                </button>
              );
            })}
          </div>
        </div>

        {/* Confirm */}
        <div style={{ marginTop: 22 }}>
          <Button kind="primary" size="lg" full onClick={() => {
            onClose();
            app.toast(`Delivering to ${addr.label} · ${when.short}`, 'success');
          }}>Confirm details</Button>
        </div>
      </div>
    </BottomSheet>
  );
}

// ─── Category slider (image + name, horizontal scroll) ──────────────

const CATEGORIES = [
  { name: 'BBQ',     chip: 'BBQ',     hue: 'bbq',    glyph: 'bowl' },
  { name: 'Burgers', chip: 'Burgers', hue: 'bun',    glyph: 'bowl' },
  { name: 'Tacos',   chip: 'Tacos',   hue: 'orange', glyph: 'taco' },
  { name: 'Tex-Mex', chip: 'Tex-Mex', hue: 'red',    glyph: 'bowl' },
  { name: 'Noodles', chip: 'Noodles', hue: 'coffee', glyph: 'bowl' },
  { name: 'Coffee',  chip: 'Coffee',  hue: 'coffee', glyph: 'drink' },
  { name: 'Sweets',  chip: 'Sweets',  hue: 'sweet',  glyph: 'cake' },
  { name: 'Healthy', chip: 'Healthy', hue: 'green',  glyph: 'bowl' },
  { name: 'Asian',   chip: 'Asian',   hue: 'red',    glyph: 'bowl' },
];

function CategorySlider({ active, onPick, favCount = 0 }) {
  const favOn = active === 'Faves';
  return (
    <div data-lasso="category-slider-shell" style={{ position: 'relative', minHeight: 98, padding: '2px 0 16px', overflow: 'hidden' }}>
      <style>{`
        [data-lasso="category-slider"]::-webkit-scrollbar{display:none}
        [data-lasso="category-slider"]{scroll-padding-left:102px;scroll-snap-type:x mandatory}
      `}</style>
      {/* Favourites — pinned above the moving rail */}
      <button data-action="pick-category" data-lasso="category-faves-pinned" onClick={() => onPick('Faves')} style={{
        position: 'absolute', left: 20, top: 2, zIndex: 4,
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
        background: 'transparent',
        border: 'none', cursor: 'pointer', width: 82, padding: '0 16px 0 0',
      }}>
        <div style={{
          width: 62, height: 62, borderRadius: '50%', position: 'relative',
          background: 'transparent',
          border: 'none',
          boxShadow: 'none',
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          transition: 'all 180ms var(--ease-out)',
        }}>
          <GlassSurfaceStock
            width="100%"
            height="100%"
            borderRadius={999}
            displace={0.5}
            distortionScale={-180}
            redOffset={0}
            greenOffset={10}
            blueOffset={20}
            brightness={50}
            opacity={0.93}
            mixBlendMode="screen"
            backgroundOpacity={favOn ? 0.18 : 0.08}
            saturation={1.24}
            style={{
              background: favOn ? 'rgba(216,90,20,0.50)' : 'rgba(245,239,231,0.20)',
              borderColor: favOn ? 'rgba(255,255,255,0.42)' : 'rgba(255,255,255,0.30)',
            }}
          >
            <Ico.heart c={favOn ? '#fff' : 'var(--brand-orange)'} s={26} filled={favOn}/>
          </GlassSurfaceStock>
          {favCount > 0 && (
            <span style={{
              position: 'absolute', top: -2, right: -4, minWidth: 18, height: 18, borderRadius: 9, padding: '0 5px',
              background: 'var(--dark-chocolate)', color: 'var(--brand-orange)', border: '2px solid var(--bg-app)',
              fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 10,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>{favCount}</span>
          )}
        </div>
        <div style={{
          fontFamily: 'var(--font-display)', fontWeight: favOn ? 800 : 700, fontSize: 11.5,
          color: favOn ? 'var(--brand-orange)' : 'var(--fg-primary)', whiteSpace: 'nowrap',
        }}>Faves</div>
      </button>
      <div data-lasso="category-slider" style={{
        display: 'flex', gap: 16, overflowX: 'auto', padding: '0 20px 0 102px',
        scrollbarWidth: 'none', position: 'relative', zIndex: 1,
      }}>
        {CATEGORIES.map(c => {
          const on = c.chip === active;
          return (
            <button key={c.name} data-action="pick-category" onClick={() => onPick(c.chip)} style={{
              display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8,
              background: 'transparent', border: 'none', cursor: 'pointer', flexShrink: 0, width: 66, padding: 0,
              scrollSnapAlign: 'start', scrollSnapStop: 'always',
            }}>
              <div style={{
                width: 62, height: 62, borderRadius: '50%', overflow: 'hidden',
                border: '2.5px solid ' + (on ? 'var(--brand-orange)' : 'transparent'),
                boxShadow: on ? '0 6px 16px rgba(216,90,20,0.30)' : '0 4px 12px rgba(27,18,13,0.14)',
                transition: 'border-color 180ms, box-shadow 180ms',
              }}>
                <FoodPh hue={c.hue} height={62} radius={0} glyph={c.glyph}/>
              </div>
              <div style={{
                fontFamily: 'var(--font-display)', fontWeight: on ? 800 : 700, fontSize: 11.5,
                color: on ? 'var(--brand-orange)' : 'var(--fg-primary)', whiteSpace: 'nowrap',
              }}>{c.name}</div>
            </button>
          );
        })}
      </div>
    </div>
  );
}

// ─── Saved dishes rail (shown in Faves view) ────────────────────
function SavedDishRail({ app }) {
  const saved = [];
  Object.entries(MENUS).forEach(([rid, menu]) => {
    menu.forEach(sec => sec.items.forEach(it => {
      if (app.favourites.has(it.id)) saved.push({ item: it, restaurantId: rid });
    }));
  });
  GENERIC_MENU.forEach(sec => sec.items.forEach(it => {
    if (app.favourites.has(it.id)) saved.push({ item: it, restaurantId: null });
  }));
  if (saved.length === 0) return null;
  return (
    <div data-lasso="saved-dish-rail" style={{ margin: '0 -20px' }}>
      <div style={{ padding: '0 20px 8px', display: 'flex', alignItems: 'center', gap: 7 }}>
        <Ico.heart c="var(--brand-orange)" s={14} filled/>
        <span style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 14, color: 'var(--fg-primary)' }}>Saved dishes</span>
        <span style={{ fontFamily: 'var(--font-body)', fontSize: 11.5, color: 'var(--warm-stone)' }}>{saved.length} hearted</span>
      </div>
      <div style={{ display: 'flex', gap: 10, overflowX: 'auto', padding: '0 20px 6px', scrollbarWidth: 'none', scrollSnapType: 'x mandatory', scrollPaddingInline: 20 }}>
        {saved.map(({ item: it, restaurantId }) => {
          const r = restaurantId ? RESTAURANT_LOOKUP[restaurantId] : null;
          return (
            <div key={it.id} onClick={() => r ? app.openRestaurant(r.id) : app.toast('Available at several kitchens', 'info')} style={{
              width: 128, flexShrink: 0, background: 'var(--bg-surface)', border: '1.5px solid var(--soft-line)',
              borderRadius: 16, overflow: 'hidden', cursor: 'pointer',
              scrollSnapAlign: 'start', scrollSnapStop: 'always',
            }}>
              <div style={{ position: 'relative' }}>
                {it.art ? <FoodArt dish={it.art} height={76} radius={0}/> : <FoodPh height={76} hue={it.hue} radius={0} glyph={it.glyph}/>}
                <div style={{ position: 'absolute', top: 6, right: 6, width: 22, height: 22, borderRadius: '50%', background: 'rgba(193,59,31,0.92)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  <Ico.heart c="#fff" s={12} filled/>
                </div>
              </div>
              <div style={{ padding: '8px 10px 10px' }}>
                <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 12, color: 'var(--fg-primary)', lineHeight: 1.2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{it.name}</div>
                <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, color: 'var(--warm-stone)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r ? r.name : 'Multiple kitchens'} · £{it.price.toFixed(2)}</div>
              </div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ─── Sort helper ────────────────────────────────────────────────────

function sortRestaurants(list, sort) {
  const arr = [...list];
  const etaNum = (r) => parseInt((r.etaShort || r.eta || '99').replace(/[^0-9]/g, ''), 10) || 99;
  if (sort === 'rating') arr.sort((a, b) => b.rating - a.rating);
  else if (sort === 'eta') arr.sort((a, b) => etaNum(a) - etaNum(b));
  else if (sort === 'fee') arr.sort((a, b) => a.deliveryFee - b.deliveryFee);
  return arr; // 'recommended' keeps source order
}

// ─── Full search drawer ─────────────────────────────────────────────

const POPULAR_SEARCHES = ['Brisket', 'Smash burger', 'Tacos', 'Cold brew', 'Bowls', 'Noodles'];

function SearchDrawer({ app, open, onClose, initial = '', onCommit }) {
  const [q, setQ] = React.useState(initial);
  const inputRef = React.useRef(null);
  const [recents, setRecents] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem('lasso_recent_searches') || '[]'); } catch (e) { return []; }
  });
  const remember = (term) => {
    const t = term.trim();
    if (!t) return;
    setRecents(prev => {
      const next = [t, ...prev.filter(x => x.toLowerCase() !== t.toLowerCase())].slice(0, 6);
      try { localStorage.setItem('lasso_recent_searches', JSON.stringify(next)); } catch (e) {}
      return next;
    });
  };
  const commit = (term) => { remember(term); onCommit(term); };

  React.useEffect(() => { if (open) setQ(initial); }, [open]);
  React.useEffect(() => {
    if (open) { const t = setTimeout(() => inputRef.current && inputRef.current.focus(), 360); return () => clearTimeout(t); }
  }, [open]);

  const results = q.trim()
    ? RESTAURANTS.filter(r =>
        r.name.toLowerCase().includes(q.toLowerCase()) ||
        r.cuisine.toLowerCase().includes(q.toLowerCase()) ||
        (r.tagline || '').toLowerCase().includes(q.toLowerCase()))
    : [];

  // Dish-level results — search inside bespoke menus
  const ql = q.trim().toLowerCase();
  const dishResults = [];
  if (ql) {
    RESTAURANTS.forEach(r => {
      const m = MENUS[r.id];
      if (!m) return;
      m.forEach(sec => sec.items.forEach(it => {
        if (it.name.toLowerCase().includes(ql) || (it.desc || '').toLowerCase().includes(ql)) {
          dishResults.push({ item: it, r });
        }
      }));
    });
  }
  const dishes = dishResults.slice(0, 8);

  return (
    <BottomSheet open={open} onClose={onClose} maxHeight="92%">
      <div data-lasso="search-drawer" style={{ padding: '0 20px 28px', display: 'flex', flexDirection: 'column' }}>

        {/* Search field */}
        <div style={{
          display: 'flex', alignItems: 'center', gap: 10,
          background: 'var(--bg-surface)', border: '1.5px solid var(--brand-orange)',
          borderRadius: 16, padding: '12px 14px',
        }}>
          <Ico.search c="var(--brand-orange)" s={20}/>
          <input
            ref={inputRef}
            placeholder="Search restaurants & cuisines…"
            value={q}
            onChange={(e) => setQ(e.target.value)}
            onKeyDown={(e) => { if (e.key === 'Enter' && q.trim()) commit(q.trim()); }}
            style={{
              border: 'none', outline: 'none', flex: 1, background: 'transparent',
              fontFamily: 'var(--font-body)', fontSize: 15, color: 'var(--fg-primary)',
            }}/>
          {q && (
            <button onClick={() => setQ('')} style={{ border: 'none', background: 'transparent', cursor: 'pointer', display: 'flex', padding: 2 }}>
              <Ico.x c="var(--warm-stone)" s={16}/>
            </button>
          )}
        </div>

        {/* Empty state — recents + popular searches */}
        {!q.trim() && (
          <div style={{ marginTop: 22 }}>
            {recents.length > 0 && (
              <div style={{ marginBottom: 20 }}>
                <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 12 }}>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)' }}>Recent rides</div>
                  <button onClick={() => { setRecents([]); try { localStorage.setItem('lasso_recent_searches', '[]'); } catch (e) {} }} style={{ background: 'none', border: 'none', cursor: 'pointer', fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 11, color: 'var(--brand-orange)' }}>Clear</button>
                </div>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
                  {recents.map(t => (
                    <button key={t} onClick={() => setQ(t)} style={{
                      display: 'inline-flex', alignItems: 'center', gap: 6, padding: '9px 14px', borderRadius: 13, cursor: 'pointer',
                      background: 'var(--bg-warm)', border: '1.5px solid var(--soft-line)',
                      fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12.5, color: 'var(--horse-brown)',
                    }}><Ico.clock c="var(--warm-stone)" s={12}/>{t}</button>
                  ))}
                </div>
              </div>
            )}
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)', marginBottom: 12 }}>Popular right now</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
              {POPULAR_SEARCHES.map(p => (
                <button key={p} onClick={() => setQ(p)} style={{
                  padding: '9px 14px', borderRadius: 13, cursor: 'pointer',
                  background: 'var(--bg-surface)', border: '1.5px solid var(--soft-line)',
                  fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 12.5, color: 'var(--fg-primary)',
                }}>{p}</button>
              ))}
            </div>
          </div>
        )}

        {/* Results */}
        {q.trim() && (
          <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column', gap: 4 }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)', marginBottom: 8 }}>
              {results.length + dishes.length} {results.length + dishes.length === 1 ? 'result' : 'results'}
            </div>
            {results.length === 0 && dishes.length === 0 && (
              <div style={{ padding: '24px 4px', fontFamily: 'var(--font-body)', fontSize: 13.5, color: 'var(--warm-stone)', lineHeight: 1.5 }}>
                No matches for “{q}”. Try a cuisine like “tacos” or “coffee”.
              </div>
            )}
            {/* Dishes first — people search cravings, not names */}
            {dishes.length > 0 && (
              <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)', padding: '4px 6px 2px' }}>Dishes</div>
            )}
            {dishes.map(({ item, r }) => (
              <button key={item.id} data-action="search-dish" onClick={() => { remember(q); onClose(); app.openRestaurant(r.id); }} style={{
                display: 'flex', alignItems: 'center', gap: 12, padding: '10px 6px', cursor: 'pointer',
                background: 'transparent', border: 'none', borderBottom: '1px solid var(--soft-line)', textAlign: 'left',
              }}>
                <div style={{ width: 46, height: 46, borderRadius: 12, overflow: 'hidden', flexShrink: 0 }}>
                  <FoodPh hue={item.hue} height={46} radius={12}/>
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{item.name}</div>
                  <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name} · {r.etaShort || r.eta}</div>
                </div>
                <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 13, color: 'var(--fg-primary)', flexShrink: 0 }}>£{item.price.toFixed(2)}</div>
              </button>
            ))}
            {results.length > 0 && dishes.length > 0 && (
              <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)', padding: '14px 6px 2px' }}>Kitchens</div>
            )}
            {results.map(r => (
              <button key={r.id} data-action="search-result" onClick={() => { remember(q); onClose(); app.openRestaurant(r.id); }} style={{
                display: 'flex', alignItems: 'center', gap: 12, padding: '10px 6px', cursor: 'pointer',
                background: 'transparent', border: 'none', borderBottom: '1px solid var(--soft-line)', textAlign: 'left',
              }}>
                <div style={{ width: 46, height: 46, borderRadius: 12, overflow: 'hidden', flexShrink: 0 }}>
                  <FoodPh hue={r.hue} height={46} radius={12}/>
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>{r.name}</div>
                  <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.cuisine} · {r.etaShort || r.eta}</div>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 3, flexShrink: 0 }}>
                  <Ico.star s={12}/>
                  <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 12, color: 'var(--fg-primary)' }}>{r.rating}</span>
                </div>
              </button>
            ))}
          </div>
        )}
      </div>
    </BottomSheet>
  );
}

// ─── Filter drawer ──────────────────────────────────────────────────

function FilterDrawer({ open, onClose, filters, setFilters, resultCount }) {
  const set = (patch) => setFilters(f => ({ ...f, ...patch }));

  const Label = ({ children }) => (
    <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--warm-stone)', padding: '0 4px 10px' }}>{children}</div>
  );

  const RadioRow = ({ value, current, onPick, label, sub }) => {
    const on = value === current;
    return (
      <button onClick={() => onPick(value)} style={{
        display: 'flex', alignItems: 'center', gap: 12, padding: 13, borderRadius: 14, cursor: 'pointer',
        background: on ? 'var(--bg-warm)' : 'var(--bg-surface)',
        border: '1.5px solid ' + (on ? 'var(--brand-orange)' : 'var(--soft-line)'), textAlign: 'left', width: '100%',
      }}>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>{label}</div>
          {sub && <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2 }}>{sub}</div>}
        </div>
        <div style={{
          width: 20, height: 20, borderRadius: '50%', flexShrink: 0,
          border: '2px solid ' + (on ? 'var(--brand-orange)' : 'var(--soft-line)'),
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          {on && <div style={{ width: 10, height: 10, borderRadius: '50%', background: 'var(--brand-orange)' }}/>}
        </div>
      </button>
    );
  };

  const ToggleRow = ({ value, onToggle, label, sub }) => (
    <button onClick={() => onToggle(!value)} style={{
      display: 'flex', alignItems: 'center', gap: 12, padding: 13, borderRadius: 14, cursor: 'pointer',
      background: value ? 'var(--bg-warm)' : 'var(--bg-surface)',
      border: '1.5px solid ' + (value ? 'var(--brand-orange)' : 'var(--soft-line)'), textAlign: 'left', width: '100%',
    }}>
      <div style={{ flex: 1 }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 14, color: 'var(--fg-primary)' }}>{label}</div>
        {sub && <div style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'var(--warm-stone)', marginTop: 2 }}>{sub}</div>}
      </div>
      <div style={{
        width: 42, height: 25, borderRadius: 13, flexShrink: 0, position: 'relative',
        background: value ? 'var(--brand-orange)' : 'var(--soft-line)', transition: 'background 200ms',
      }}>
        <div style={{
          position: 'absolute', top: 2.5, left: value ? 19.5 : 2.5, width: 20, height: 20, borderRadius: '50%',
          background: '#fff', transition: 'left 200ms var(--ease-out)', boxShadow: '0 1px 3px rgba(0,0,0,0.25)',
        }}/>
      </div>
    </button>
  );

  return (
    <BottomSheet open={open} onClose={onClose} title="Filters" maxHeight="88%">
      <div data-lasso="filter-drawer" style={{ padding: '0 20px 24px' }}>

        <Label>Sort by</Label>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <RadioRow value="recommended" current={filters.sort} onPick={(v)=>set({sort:v})} label="Recommended" sub="Our pick of the trail"/>
          <RadioRow value="rating" current={filters.sort} onPick={(v)=>set({sort:v})} label="Top rated"/>
          <RadioRow value="eta" current={filters.sort} onPick={(v)=>set({sort:v})} label="Fastest delivery"/>
          <RadioRow value="fee" current={filters.sort} onPick={(v)=>set({sort:v})} label="Lowest delivery fee"/>
        </div>

        <div style={{ marginTop: 22 }}>
          <Label>Delivery fee</Label>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <RadioRow value="any" current={filters.maxFee} onPick={(v)=>set({maxFee:v})} label="Any"/>
            <RadioRow value="2" current={filters.maxFee} onPick={(v)=>set({maxFee:v})} label="Under £2"/>
            <RadioRow value="1" current={filters.maxFee} onPick={(v)=>set({maxFee:v})} label="Under £1"/>
          </div>
        </div>

        <div style={{ marginTop: 22 }}>
          <Label>Quick filters</Label>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
            <ToggleRow value={filters.openNow} onToggle={(v)=>set({openNow:v})} label="Open now" sub="Hide closed kitchens"/>
            <ToggleRow value={filters.featured} onToggle={(v)=>set({featured:v})} label="Featured only" sub="Wrangler's Picks"/>
          </div>
        </div>

        {/* Footer */}
        <div style={{ display: 'flex', gap: 10, marginTop: 26 }}>
          <Button kind="ghost" size="lg" onClick={() => setFilters({ sort: 'recommended', maxFee: 'any', openNow: false, featured: false })}>Clear</Button>
          <div style={{ flex: 1 }}>
            <Button kind="primary" size="lg" full onClick={onClose}>Show {resultCount} {resultCount === 1 ? 'place' : 'places'}</Button>
          </div>
        </div>
      </div>
    </BottomSheet>
  );
}

// ─── Offers rail (horizontal promo cards) ───────────────────────────

const OFFERS = [
  { id: 'welcome', eyebrow: 'Welcome to Lasso', title: 'Food.\nGroceries.\nEssentials.', sub: 'Local favourites, all in one app.', cta: 'Explore Lasso', bg: 'linear-gradient(125deg,#2E1A0F 0%,#171009 100%)',
    image: 'assets/onboarding/lasso-segment-1-mixed-haul-landscape-v2-candidate.png',
    creativeVersion: 'segment-1-v2-candidate',
    compactCta: true,
    a11yLabel: 'Welcome to Lasso — food, groceries, and everyday essentials',
    detail: 'Lasso brings prepared food, fresh groceries and everyday essentials together from local places.', action: 'explore' },
  { id: 'freedel',   eyebrow: 'This week',     title: 'Free delivery over £25', sub: 'On every featured kitchen.', cta: 'Browse', bg: 'linear-gradient(125deg,#2E2618 0%,#171009 100%)',
    detail: 'Any Wrangler\u2019s Pick kitchen, all week. The fee drops off automatically at checkout.', action: 'featured' },
  { id: 'tacos',     eyebrow: 'Taco Tuesday',  title: '2-for-1 tacos', sub: 'All day at Sundown & Rojo.', cta: 'Order tacos', bg: 'linear-gradient(125deg,#C13B1F 0%,#5A1E0E 100%)',
    detail: 'Every taco on the menu, doubled. Add two — the second rings up free. Today only.', action: 'category', category: 'Tacos' },
  { id: 'latenight', eyebrow: 'Open late',     title: 'Late-night eats till 2am', sub: 'Riders on the trail all night.', cta: 'See spots', bg: 'linear-gradient(125deg,#3A1F11 0%,#1B120D 100%)',
    detail: 'Kitchens cooking past midnight, filtered to what\u2019s open right now.', action: 'opennow' },
  { id: 'refer',     eyebrow: 'Lasso crew',    title: 'Refer a friend, get £10', sub: 'They save, you save.', cta: 'Invite', bg: 'linear-gradient(125deg,#5A6F2E 0%,#283417 100%)',
    detail: '£10 of wallet credit for both of you when they finish their first ride.', action: 'refer' },
];

function OffersRail({ app, onOpen }) {
  const loopLength = OFFERS.length;
  const loopedOffers = [...OFFERS, ...OFFERS, ...OFFERS];
  const [active, setActive] = React.useState(loopLength);
  const [revealIndex, setRevealIndex] = React.useState(loopLength);
  const [leavingIndex, setLeavingIndex] = React.useState(null);
  const activeOfferIndex = ((active % loopLength) + loopLength) % loopLength;
  const railRef = React.useRef(null);
  const pauseUntilRef = React.useRef(0);
  const centeredRef = React.useRef(false);
  const resetTimerRef = React.useRef(0);
  const revealTimerRef = React.useRef(0);
  const glideFrameRef = React.useRef(0);
  const initialHoldUntilRef = React.useRef(Date.now() + 9000);

  const scrollToOffer = React.useCallback((index, behavior = 'glide', outgoingIndex = active) => {
    const rail = railRef.current;
    const card = rail?.querySelector(`[data-offer-scroll-index="${index}"]`);
    if (!rail || !card) return;
    const target = card.offsetLeft - 20;
    window.cancelAnimationFrame(glideFrameRef.current);
    window.clearTimeout(revealTimerRef.current);
    if (behavior === 'auto') {
      rail.style.scrollSnapType = '';
      rail.scrollTo({ left: target, behavior: 'auto' });
      setRevealIndex(index);
      setLeavingIndex(null);
      return;
    }
    const start = rail.scrollLeft;
    const distance = target - start;
    const duration = 1450;
    const startedAt = performance.now();
    const ease = (t) => {
      if (t <= 0) return 0;
      if (t >= 1) return 1;
      return 1 - Math.pow(2, -8 * t);
    };
    rail.style.scrollSnapType = 'none';
    const outgoing = Number.isFinite(outgoingIndex)
      ? outgoingIndex
      : Number(document.querySelector('[data-offer-active="true"]')?.getAttribute('data-offer-card-index') || active);
    setLeavingIndex(outgoing);
    revealTimerRef.current = window.setTimeout(() => {
      setRevealIndex(index);
    }, Math.round(duration * 0.68));
    const glide = (now) => {
      const progress = Math.min(1, (now - startedAt) / duration);
      rail.scrollLeft = start + distance * ease(progress);
      if (progress < 1) {
        glideFrameRef.current = window.requestAnimationFrame(glide);
      } else {
        rail.scrollLeft = target;
        rail.style.scrollSnapType = '';
        setLeavingIndex(null);
      }
    };
    glideFrameRef.current = window.requestAnimationFrame(glide);
  }, []);

  React.useEffect(() => {
    const rail = railRef.current;
    if (!rail) return undefined;
    let frame = 0;
    const syncActive = () => {
      if (!centeredRef.current) return;
      window.cancelAnimationFrame(frame);
      frame = window.requestAnimationFrame(() => {
        const cards = [...rail.querySelectorAll('[data-offer-scroll-index]')];
        const center = rail.scrollLeft + rail.clientWidth / 2;
        let nextActive = 0;
        let nextDistance = Number.POSITIVE_INFINITY;
        cards.forEach((card, index) => {
          const distance = Math.abs(card.offsetLeft + card.offsetWidth / 2 - center);
          if (distance < nextDistance) {
            nextActive = index;
            nextDistance = distance;
          }
        });
        setActive(nextActive);
        window.clearTimeout(resetTimerRef.current);
        resetTimerRef.current = window.setTimeout(() => {
          const current = Number(document.querySelector('[data-lasso="offer-card"][data-offer-active="true"]')?.getAttribute('data-offer-card-index') || nextActive);
          if (current < loopLength) {
            const resetIndex = current + loopLength;
            setActive(resetIndex);
            setRevealIndex(resetIndex);
            scrollToOffer(resetIndex, 'auto');
          } else if (current >= loopLength * 2) {
            const resetIndex = current - loopLength;
            setActive(resetIndex);
            setRevealIndex(resetIndex);
            scrollToOffer(resetIndex, 'auto');
          } else {
            setRevealIndex(current);
          }
        }, 220);
      });
    };
    syncActive();
    rail.addEventListener('scroll', syncActive, { passive: true });
    return () => {
      window.cancelAnimationFrame(frame);
      window.cancelAnimationFrame(glideFrameRef.current);
      window.clearTimeout(resetTimerRef.current);
      window.clearTimeout(revealTimerRef.current);
      rail.removeEventListener('scroll', syncActive);
    };
  }, [loopLength, scrollToOffer]);

  React.useEffect(() => {
    if (centeredRef.current) return undefined;
    centeredRef.current = true;
    const frame = window.requestAnimationFrame(() => scrollToOffer(loopLength, 'auto'));
    return () => window.cancelAnimationFrame(frame);
  }, [loopLength, scrollToOffer]);

  React.useEffect(() => {
    const prefersReducedMotion = window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
    if (prefersReducedMotion) return undefined;
    const timer = window.setInterval(() => {
      if (Date.now() < initialHoldUntilRef.current) return;
      if (Date.now() < pauseUntilRef.current) return;
      const next = active + 1;
      setLeavingIndex(active);
      setActive(next);
      scrollToOffer(next, 'glide', active);
    }, 5200);
    return () => window.clearInterval(timer);
  }, [active, scrollToOffer]);

  const pauseAutoSlide = React.useCallback(() => {
    initialHoldUntilRef.current = 0;
    pauseUntilRef.current = Date.now() + 7600;
  }, []);

  const revealStyle = (selected, order) => ({
    opacity: selected ? 1 : 0,
    transform: selected ? 'translateY(0)' : 'translateY(12px)',
    filter: selected ? 'blur(0)' : 'blur(4px)',
    transition: 'opacity 620ms var(--ease-out), transform 720ms var(--ease-out), filter 620ms var(--ease-out)',
    transitionDelay: selected ? `${120 + order * 95}ms` : '0ms',
  });

  return (
    <div data-lasso="offers-carousel" style={{ position: 'relative', margin: '0 0 24px' }}>
      <style>{`
        [data-lasso="offers-carousel-track"]::-webkit-scrollbar{display:none}
        [data-lasso="offer-cta-letter"]{
          transform-origin:center;
          backface-visibility:hidden;
          will-change:transform;
        }
        @keyframes lassoCtaYSpin {
          0% { transform: rotateY(0deg); }
          100% { transform: rotateY(360deg); }
        }
      `}</style>
      <div
        ref={railRef}
        data-lasso="offers-carousel-track"
        onPointerDown={pauseAutoSlide}
        onTouchStart={pauseAutoSlide}
        onWheel={pauseAutoSlide}
        style={{
          display: 'flex',
          gap: 12,
          overflowX: 'auto',
          scrollSnapType: 'x mandatory',
          scrollPaddingInline: 12,
          scrollBehavior: 'auto',
          padding: '0 12px 2px',
          scrollbarWidth: 'none',
        }}
      >
        {loopedOffers.map((o, index) => {
          const selected = active === index;
          const contentSelected = revealIndex === index;
          const contentLeaving = leavingIndex === index && revealIndex === index;
          return (
            <div
              key={`${o.id}-${index}`}
              data-offer-scroll-index={index}
              style={{
                flex: '0 0 min(414px, calc(100vw - 24px))',
                boxSizing: 'border-box',
                padding: '10px 4px 28px',
                scrollSnapAlign: 'center',
                scrollSnapStop: 'always',
              }}
            >
              <button
                type="button"
                data-lasso="offer-card"
                data-offer-card-index={index}
                data-offer-active={selected ? 'true' : 'false'}
                data-action="open-offer"
                aria-label={o.a11yLabel || `${o.title}. ${o.sub}`}
                onFocus={() => scrollToOffer(index)}
                onClick={() => onOpen ? onOpen(o) : app.toast(`${o.title} · ${o.cta}`, 'success')}
                style={{
                  width: '100%',
                  minHeight: 176,
                  borderRadius: 24,
                  overflow: 'hidden',
                  border: 'none',
                  background: o.bg,
                  color: 'var(--dust-cream)',
                  position: 'relative',
                  padding: 0,
                  cursor: 'pointer',
                  textAlign: 'left',
                  boxShadow: selected ? '0 22px 42px rgba(27,18,13,0.20)' : '0 12px 26px rgba(27,18,13,0.12)',
                  transform: selected ? 'scale(1)' : 'scale(0.992)',
                  transition: 'transform 640ms cubic-bezier(.16,1,.3,1), box-shadow 640ms cubic-bezier(.16,1,.3,1)',
                }}
              >
                {o.image && (
                  <img
                    src={o.image}
                    alt=""
                    aria-hidden="true"
                    loading={selected ? 'eager' : 'lazy'}
                    fetchPriority={selected ? 'high' : 'auto'}
                    decoding="async"
                    style={{
                      position: 'absolute',
                      inset: 0,
                      width: '100%',
                      height: '100%',
                      objectFit: 'cover',
                      objectPosition: o.imagePosition || 'center',
                    }}
                  />
                )}
                {o.image && <div aria-hidden="true" style={{
                  position: 'absolute',
                  inset: 0,
                  background: 'linear-gradient(90deg, rgba(23,12,7,0.72) 0%, rgba(23,12,7,0.54) 36%, rgba(23,12,7,0.18) 50%, rgba(23,12,7,0.04) 66%, transparent 82%)',
                }}/>}
                <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(circle at 86% 74%, rgba(242,140,27,0.22), transparent 56%)' }}/>
                {o.bull && <div style={{ position: 'absolute', right: -10, bottom: -18, opacity: 0.92, transform: selected ? 'translateY(0) scale(1)' : 'translateY(5px) scale(0.98)', transition: 'transform 760ms var(--ease-out)' }}><Bull size={128}/></div>}
                {o.tag && <div style={{
                  position: 'absolute',
                  right: 18,
                  bottom: 18,
                  fontFamily: 'var(--font-display)',
                  fontWeight: 800,
                  fontSize: 11,
                  color: 'var(--brand-orange)',
                  border: '1.5px dashed rgba(242,140,27,0.52)',
                  padding: '6px 10px',
                  borderRadius: 10,
                  letterSpacing: '0.08em',
                }}>{o.tag}</div>}
                <div
                  data-lasso="offer-card-content"
                  style={{
                    position: 'relative',
                    zIndex: 2,
                    width: o.image ? '40%' : o.bull ? '68%' : '76%',
                    minHeight: 176,
                    padding: '22px 20px 20px',
                    display: 'flex',
                    flexDirection: 'column',
                    opacity: contentSelected ? (contentLeaving ? 0.72 : 1) : 0,
                    transform: contentSelected ? (contentLeaving ? 'translateY(3px)' : 'translateY(0)') : 'translateY(12px)',
                    filter: contentSelected ? (contentLeaving ? 'blur(1.8px)' : 'blur(0)') : 'blur(4px)',
                    transition: 'opacity 620ms var(--ease-out), transform 720ms var(--ease-out), filter 620ms var(--ease-out)',
                    transitionDelay: contentSelected ? '120ms' : '0ms',
                  }}
                >
                  <div style={revealStyle(contentSelected, 0)}><Eyebrow color="var(--brand-orange)">{o.eyebrow}</Eyebrow></div>
                  <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 25, lineHeight: 0.98, letterSpacing: '-0.01em', marginTop: 9, whiteSpace: 'pre-line', ...revealStyle(contentSelected, 1) }}>{o.title}</div>
                  <div style={{ fontFamily: 'var(--font-body)', fontSize: 12.5, color: '#D6C7BC', marginTop: 8, lineHeight: 1.38, ...revealStyle(contentSelected, 2) }}>{o.sub}</div>
                  <div style={{ marginTop: 'auto', paddingTop: o.compactCta ? 9 : 0, ...revealStyle(contentSelected, 3) }}>
                    <span data-lasso="offer-cta" style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: o.compactCta ? 11 : 12, color: '#fff', background: 'var(--brand-orange)', padding: o.compactCta ? '7px 11px' : '9px 14px', borderRadius: o.compactCta ? 10 : 12, letterSpacing: '0.02em', whiteSpace: 'nowrap', display: 'inline-flex', overflow: 'hidden', perspective: 420 }}>
                      {Array.from(`${o.cta} →`).map((char, charIndex) => (
                        <span
                          key={charIndex}
                          data-lasso="offer-cta-letter"
                          style={{
                            display: 'inline-block',
                            minWidth: char === ' ' ? 3 : 'auto',
                            transform: 'rotateY(0deg)',
                            animation: contentSelected ? 'lassoCtaYSpin 700ms cubic-bezier(.2,.8,.2,1) both' : 'none',
                            animationDelay: contentSelected ? `${430 + charIndex * 28}ms` : '0ms',
                          }}
                        >
                          {char === ' ' ? '\u00A0' : char}
                        </span>
                      ))}
                    </span>
                  </div>
                </div>
              </button>
            </div>
          );
        })}
      </div>
      <div data-lasso="offers-carousel-dots" style={{ position: 'absolute', left: 0, right: 0, bottom: 14, display: 'flex', justifyContent: 'center', gap: 6, padding: '0 20px', pointerEvents: 'none' }}>
        {OFFERS.map((o, index) => (
          <button
            key={o.id}
            type="button"
            aria-label={`Show ${o.title}`}
            onClick={() => {
              pauseAutoSlide();
              const target = loopLength + index;
              setLeavingIndex(active);
              setActive(target);
              scrollToOffer(target, 'glide', active);
            }}
            style={{
              width: activeOfferIndex === index ? 18 : 6,
              height: 6,
              borderRadius: 999,
              border: 'none',
              background: activeOfferIndex === index ? 'var(--brand-orange)' : 'rgba(27,18,13,0.22)',
              padding: 0,
              pointerEvents: 'auto',
              transition: 'width 260ms var(--ease-out), background 260ms var(--ease-out)',
            }}
          />
        ))}
      </div>
    </div>
  );
}

// ─── Offer detail sheet — every promo card resolves to a real action ─
function OfferSheet({ offer: o, onClose, app, onBrowseCategory }) {
  if (!o) return <BottomSheet open={false} onClose={onClose}/>;
  const claimed = o.promoCode && app.promo?.code === o.promoCode;
  const run = () => {
    if (o.action === 'claim') {
      const p = EARNED_PROMOS.find(x => x.code === o.promoCode) || { code: o.promoCode, label: o.title, type: 'pct', value: 0.2, cap: 8 };
      app.setPromo(p);
      app.toast(`${o.promoCode} claimed — applies at checkout`, 'success');
      onClose();
    } else if (o.action === 'category') {
      onBrowseCategory(o.category);
    } else if (o.action === 'refer') {
      onClose();
      app.replaceTo('rewards');
    } else {
      onClose();
      app.toast(o.action === 'opennow' ? 'Showing kitchens open right now' : 'Ride on — pick a kitchen below', 'info');
    }
  };
  return (
    <BottomSheet open={!!o} onClose={onClose}>
      <div style={{ padding: '0 20px 28px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        {/* Hero recap */}
        <div style={{ borderRadius: 20, padding: '20px 20px 18px', background: o.bg, color: 'var(--dust-cream)', position: 'relative', overflow: 'hidden' }}>
          {o.bull && <div style={{ position: 'absolute', right: -14, bottom: -16, opacity: 0.9 }}><Bull size={104}/></div>}
          <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(circle at 88% 82%, rgba(242,140,27,0.20), transparent 55%)' }}></div>
          <div style={{ position: 'relative', maxWidth: o.bull ? '72%' : '100%' }}>
            <Eyebrow color="var(--brand-orange)">{o.eyebrow}</Eyebrow>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 26, lineHeight: 1.0, letterSpacing: '-0.01em', marginTop: 8 }}>{o.title}</div>
            <div style={{ fontFamily: 'var(--font-body)', fontSize: 12.5, color: '#C9B7AA', marginTop: 6, lineHeight: 1.45 }}>{o.sub}</div>
          </div>
        </div>
        {/* Details */}
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 13.5, color: 'var(--fg-primary)', lineHeight: 1.55 }}>{o.detail}</div>
        {o.tag && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '12px 14px', background: 'var(--bg-warm)', border: '1.5px dashed var(--brand-orange)', borderRadius: 14 }}>
            <Ico.ticket c="var(--brand-orange)" s={18}/>
            <span style={{ flex: 1, fontFamily: 'var(--font-mono)', fontWeight: 700, fontSize: 14, letterSpacing: '0.08em', color: 'var(--fg-primary)' }}>{o.tag}</span>
            {claimed
              ? <Pill bg="rgba(46,139,78,0.14)" fg="var(--live-green)"><Ico.check c="var(--live-green)" s={12}/> Claimed</Pill>
              : <span style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--warm-stone)' }}>Tap claim below</span>}
          </div>
        )}
        <Button kind="accent" full size="lg" disabled={claimed} onClick={run}>{claimed ? 'Already in your bag' : `${o.cta}`}</Button>
      </div>
    </BottomSheet>
  );
}

// ─── Hero variants ──────────────────────────────────────────────────

function HeroClassic({ app }) {
  return (
    <div style={{
      margin: '0 20px 18px', padding: '22px 22px 20px', borderRadius: 28,
      background: 'linear-gradient(125deg,#1B120D 0%,#3A1F11 100%)',
      color: 'var(--dust-cream)', position: 'relative', overflow: 'hidden',
    }}>
      <div style={{ position: 'absolute', right: -10, bottom: -18, opacity: 0.95 }}>
        <Bull size={130}/>
      </div>
      <div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(circle at 85% 80%, rgba(242,140,27,0.22), transparent 55%)' }}/>
      <div style={{ position: 'relative', maxWidth: '68%' }}>
        <Eyebrow color="var(--brand-orange)">Fast delivery. No bull.</Eyebrow>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 28, lineHeight: 0.98, letterSpacing: '-0.01em', marginTop: 8 }}>Catch your craving.</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 12.5, color: '#C9B7AA', marginTop: 6, lineHeight: 1.45 }}>Local restaurants, picked up the moment you order.</div>
        <div style={{ display: 'flex', gap: 8, marginTop: 14 }}>
          <Button kind="accent" size="sm" onClick={() => {}}>Order Now</Button>
          <Button kind="ghost" size="sm" onClick={() => app.activeOrderId ? app.nav('tracking') : app.toast('No active order', 'info')} style={{ color: 'var(--dust-cream)', borderColor: 'var(--warm-stone)' }}>Track Order</Button>
        </div>
      </div>
    </div>
  );
}

function HeroIllustration({ app }) {
  return (
    <div style={{
      margin: '0 20px 18px', borderRadius: 28, overflow: 'hidden',
      background: 'var(--dark-chocolate)', position: 'relative', minHeight: 220,
    }}>
      <img src={(window.__resources && window.__resources.lassoHero) || "assets/lasso-hero-illustration.png"} alt=""
        style={{ position: 'absolute', right: -20, top: -20, height: '108%', width: 'auto', objectFit: 'cover', opacity: 0.95, mixBlendMode: 'screen' }}/>
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(110deg, rgba(27,18,13,0.85) 0%, rgba(27,18,13,0.55) 50%, transparent 100%)' }}/>
      <div style={{ position: 'relative', padding: '24px 22px 22px', color: 'var(--dust-cream)' }}>
        <Eyebrow color="var(--brand-orange)">Lasso Pass</Eyebrow>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 32, lineHeight: 0.96, letterSpacing: '-0.01em', marginTop: 8, maxWidth: '70%' }}>Catch your craving.</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 12.5, color: '#C9B7AA', marginTop: 6, maxWidth: '60%' }}>30% off your first two rides this week.</div>
        <div style={{ marginTop: 16 }}>
          <Button kind="accent" size="sm">Round up an order</Button>
        </div>
      </div>
    </div>
  );
}

function HeroTrust({ app }) {
  return (
    <div style={{ margin: '0 20px 18px' }}>
      <div style={{
        padding: '20px 20px 16px', borderRadius: 28,
        background: 'var(--dark-chocolate)', color: 'var(--dust-cream)', position: 'relative', overflow: 'hidden',
      }}>
        <div style={{ position: 'absolute', right: -12, top: -12, opacity: 0.85 }}>
          <Bull size={90}/>
        </div>
        <Eyebrow color="var(--brand-orange)">Tonight</Eyebrow>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 26, lineHeight: 1.0, letterSpacing: '-0.01em', marginTop: 6, maxWidth: '70%' }}>Ride hungry.</div>
        <div style={{ display: 'flex', gap: 8, marginTop: 14 }}>
          <div style={{ flex: 1, background: 'rgba(255,255,255,0.06)', border: '1.5px solid rgba(255,255,255,0.1)', borderRadius: 14, padding: '10px 12px' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 18, color: 'var(--brand-orange)' }}>18 min</div>
            <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, color: '#9F8979', textTransform: 'uppercase', letterSpacing: '0.08em', marginTop: 2 }}>Avg arrival</div>
          </div>
          <div style={{ flex: 1, background: 'rgba(255,255,255,0.06)', border: '1.5px solid rgba(255,255,255,0.1)', borderRadius: 14, padding: '10px 12px' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 18, color: '#fff' }}>34</div>
            <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, color: '#9F8979', textTransform: 'uppercase', letterSpacing: '0.08em', marginTop: 2 }}>Riders live</div>
          </div>
          <div style={{ flex: 1, background: 'rgba(255,255,255,0.06)', border: '1.5px solid rgba(255,255,255,0.1)', borderRadius: 14, padding: '10px 12px' }}>
            <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 18, color: '#fff' }}>1.2k</div>
            <div style={{ fontFamily: 'var(--font-body)', fontSize: 10.5, color: '#9F8979', textTransform: 'uppercase', letterSpacing: '0.08em', marginTop: 2 }}>Orders today</div>
          </div>
        </div>
      </div>
    </div>
  );
}

// ─── Discovery rails (themed horizontal restaurant rails) ───────────

function railDist(r) {
  return parseFloat(String(r.distance || '9').replace(/[^0-9.]/g, '')) || 9;
}
function railDropBy(r) {
  const m = parseInt(String(r.etaShort || r.eta || '25').replace(/[^0-9]/g, ''), 10) || 25;
  const d = new Date(Date.now() + m * 60000);
  let h = d.getHours(); const mm = d.getMinutes();
  const ap = h >= 12 ? 'PM' : 'AM'; h = h % 12 || 12;
  return `${h}:${String(mm).padStart(2, '0')} ${ap}`;
}
function railBadge(r, v) {
  if (v === 'featured') return { text: '★ Featured', bg: 'var(--brand-orange)', fg: '#fff' };
  if (v === 'free')     return { text: 'Free delivery', bg: 'var(--brand-orange)', fg: '#fff' };
  if (v === 'value')    return { text: 'Great value', bg: '#5A6F2E', fg: '#fff' };
  if (v === 'pickup')   return { text: 'Pickup ready', bg: 'var(--dark-chocolate)', fg: 'var(--brand-orange)' };
  if (v === 'vegan')    return { text: 'Vegan · Eco', bg: '#5A6F2E', fg: '#fff' };
  if (v === 'nearby')   return { text: r.distance || '—', bg: 'var(--dark-chocolate)', fg: 'var(--dust-cream)' };
  return null;
}
function railMeta(r, v) {
  const txt = { fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 11, color: 'var(--horse-brown)' };
  const dot = { fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--warm-stone)' };
  if (v === 'nearby') return (
    <>
      <Ico.pin c="var(--horse-brown)" s={11}/>
      <span style={txt}>{r.distance}</span>
      <span style={dot}>·</span>
      <span style={txt}>by {railDropBy(r)}</span>
    </>
  );
  if (v === 'pickup') return (
    <>
      <Ico.bag c="var(--horse-brown)" s={11}/>
      <span style={txt}>Ready in {r.etaShort}</span>
    </>
  );
  if (v === 'value') return (
    <>
      <Ico.star c="var(--sun-orange)" s={11}/>
      <span style={{ ...txt, color: 'var(--fg-primary)' }}>{r.rating}</span>
      <span style={dot}>·</span>
      <span style={txt}>Min £{r.minOrder}</span>
    </>
  );
  return (
    <>
      <Ico.clock c="var(--horse-brown)" s={11}/>
      <span style={txt}>{r.etaShort}</span>
      <span style={dot}>·</span>
      <Ico.star c="var(--sun-orange)" s={11}/>
      <span style={{ ...txt, color: 'var(--fg-primary)' }}>{r.rating}</span>
    </>
  );
}

function RailCard({ r, onOpen, variant, rank }) {
  const badge = railBadge(r, variant);
  const [saved, setSaved] = React.useState(false);
  return (
    <div onClick={onOpen} data-lasso="rail-card-template" data-restaurant-id={r.id} style={{
      width: 198, flexShrink: 0, background: 'var(--bg-surface)', borderRadius: 20, overflow: 'hidden',
      boxShadow: 'var(--shadow-md)', cursor: 'pointer',
      scrollSnapAlign: 'start', scrollSnapStop: 'always',
    }}>
      <div style={{ position: 'relative' }}>
        {r.heroArt ? <FoodArt dish={r.heroArt} height={106} radius={0}/> : <FoodPh height={106} hue={r.hue} radius={0}/>}
        <button onClick={(e) => { e.stopPropagation(); setSaved(!saved); }} style={{
          position: 'absolute', top: 8, right: 8, width: 30, height: 30, borderRadius: '50%',
          background: saved ? 'rgba(216,90,20,0.54)' : 'rgba(245,239,231,0.20)',
          border: saved ? '1px solid rgba(255,255,255,0.42)' : '1px solid rgba(255,255,255,0.30)',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.32), 0 8px 24px rgba(27,18,13,0.20)',
          backdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          WebkitBackdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
        }}><Ico.heart c="#fff" s={15} filled={saved}/></button>
        {rank != null ? (
          <div style={{
            position: 'absolute', top: 8, left: 8, width: 26, height: 26, borderRadius: '50%',
            background: 'var(--dark-chocolate)', color: 'var(--brand-orange)', display: 'flex',
            alignItems: 'center', justifyContent: 'center', fontFamily: 'var(--font-display)',
            fontWeight: 900, fontSize: 13, border: '1.5px solid rgba(242,140,27,0.5)',
          }}>{rank}</div>
        ) : badge && (
          <div style={{ position: 'absolute', top: 8, left: 8 }}>
            <Pill bg={badge.bg} fg={badge.fg}>{badge.text}</Pill>
          </div>
        )}
      </div>
      <div style={{ padding: '10px 12px 12px' }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 14.5, lineHeight: 1.15, color: 'var(--fg-primary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.name}</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--warm-stone)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.tagline}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 8 }}>
          {railMeta(r, variant)}
        </div>
      </div>
    </div>
  );
}

// ─── Top 10 — cinematic marketing hero with cards that slide over it ─
function TopTenCard({ r, rank, onOpen, h = 248 }) {
  const [saved, setSaved] = React.useState(false);
  const move = rankMoveFor(r, rank);
  return (
    <div onClick={onOpen} data-lasso="rank-card-template" data-restaurant-id={r.id} style={{
      width: 250, height: h, flexShrink: 0, borderRadius: 22, overflow: 'hidden',
      position: 'relative', cursor: 'pointer', boxShadow: 'var(--shadow-lg)',
      scrollSnapAlign: 'start', scrollSnapStop: 'always',
    }}>
      {r.heroArt ? <FoodArt dish={r.heroArt} height={h} radius={0}/> : <FoodPh height={h} hue={r.hue} radius={0} glyph="bowl"/>}
      <div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(20,14,10,0.25) 0%, transparent 28%, rgba(20,14,10,0.88) 100%)' }}/>
      {/* dramatic rank numeral */}
      <div style={{
        position: 'absolute', top: -4, left: 12, fontFamily: 'var(--font-display)', fontWeight: 900,
        fontSize: 80, lineHeight: 1, color: '#fff', textShadow: '0 2px 16px rgba(0,0,0,0.55)',
      }}>{rank}</div>
      <button onClick={(e) => { e.stopPropagation(); setSaved(!saved); }} style={{
        position: 'absolute', top: 12, right: 12, width: 34, height: 34, borderRadius: '50%',
        background: saved ? 'rgba(216,90,20,0.54)' : 'rgba(245,239,231,0.20)',
        border: saved ? '1px solid rgba(255,255,255,0.42)' : '1px solid rgba(255,255,255,0.30)',
        boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.32), 0 8px 24px rgba(27,18,13,0.20)',
        backdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
        WebkitBackdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
        display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', padding: 0,
      }}><Ico.heart c="#fff" s={17} filled={saved}/></button>
      <div style={{ position: 'absolute', left: 14, right: 14, bottom: 14 }}>
        {RANK_TITLES[rank] && <div style={{ marginBottom: 7 }}><RankTitleChip rank={rank}/></div>}
        <div style={{
          fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 19, lineHeight: 1.05, color: '#fff',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{r.name}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 5, marginTop: 6 }}>
          <Ico.star c="var(--sun-orange)" s={13}/>
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 13, color: '#fff' }}>{r.rating}</span>
          <span style={{ fontFamily: 'var(--font-body)', fontSize: 12, color: 'rgba(245,239,231,0.7)' }}>· {r.reviews} orders</span>
          <span style={{ marginLeft: 'auto' }}><RankMoveChip move={move}/></span>
        </div>
      </div>
    </div>
  );
}

function TopTenRail({ app, items }) {
  if (!items || items.length === 0) return null;
  const [first, ...rest] = items;
  const PANEL = 290;
  const youRideWithClimber = rankClimbers(items).some(id => PAST_ORDERS.some(o => o.restaurantId === id));
  return (
    <div style={{ position: 'relative', paddingBottom: 22 }}>
      <style>{`
        [data-lasso="topten-rail"]::-webkit-scrollbar{display:none}
        @keyframes bullCharge{
          0%   {transform:translateX(0) rotate(0deg)}
          7%   {transform:translateX(-7px) rotate(-0.6deg)}
          15%  {transform:translateX(15px) rotate(1.4deg)}
          21%  {transform:translateX(-4px) rotate(-0.4deg)}
          29%  {transform:translateX(11px) rotate(1deg)}
          36%  {transform:translateX(-2px) rotate(-0.2deg)}
          44%  {transform:translateX(6px) rotate(0.5deg)}
          52%  {transform:translateX(0) rotate(0deg)}
          100% {transform:translateX(0) rotate(0deg)}
        }
        [data-lasso="bull-card"]{transform-origin:left center;animation:bullCharge 2.6s cubic-bezier(.36,.07,.19,.97) infinite}
        @media (prefers-reduced-motion: reduce){[data-lasso="bull-card"]{animation:none}}
      `}</style>

      {/* Fixed textured cage holding the leaderboard text — the cards float OVER this */}
      <div aria-hidden="true" style={{
        position: 'absolute', left: 0, right: 0, top: 0, bottom: 22, zIndex: 0, overflow: 'hidden',
        display: 'flex', alignItems: 'center',
        background: `repeating-linear-gradient(45deg, rgba(0,0,0,0.16) 0 2px, transparent 2px 9px), repeating-linear-gradient(-45deg, rgba(245,239,231,0.045) 0 1px, transparent 1px 7px), linear-gradient(135deg, #4A2310 0%, #2A160C 55%, #160E08 100%)`,
      }}>
        <div style={{ position: 'relative', width: PANEL, flexShrink: 0, padding: '0 20px' }}>
          <div style={{
            fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 11, letterSpacing: '0.14em',
            color: 'var(--brand-orange)', textTransform: 'uppercase', marginBottom: 10,
          }}>★ Wrangler Rankings</div>
          <div style={{
            fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 34, lineHeight: 0.95,
            letterSpacing: '-0.02em', color: '#fff',
          }}>This week's<br/>best</div>
          <div style={{
            fontFamily: 'var(--font-body)', fontSize: 12.5, lineHeight: 1.4, color: 'rgba(245,239,231,0.78)',
            marginTop: 12, maxWidth: 200,
          }}>Kitchens fight for these spots. Ranked fresh every Monday.</div>
          {youRideWithClimber && (
            <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginTop: 10, padding: '5px 10px', borderRadius: 999, background: 'rgba(242,140,27,0.14)', border: '1px solid rgba(242,140,27,0.35)' }}>
              <Ico.flame c="var(--sun-orange)" s={11}/>
              <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 10, color: 'var(--sun-orange)' }}>A kitchen you ride with climbed</span>
            </div>
          )}
          <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginTop: 18 }}>
            <span style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 12, color: 'var(--brand-orange)' }}>Drag to wrangle</span>
            <Ico.chev c="var(--brand-orange)" s={16}/>
          </div>
        </div>
        {/* Cage bars — the bull pushes through these as it floats out */}
        <div style={{
          position: 'absolute', top: 0, bottom: 0, left: PANEL - 26, width: 26, zIndex: 1,
          background: 'repeating-linear-gradient(90deg, rgba(245,239,231,0.28) 0 3px, transparent 3px 13px)',
          boxShadow: 'inset -8px 0 14px rgba(0,0,0,0.45)',
        }}/>
      </div>

      {/* Cards scroller floating over the fixed cage */}
      <div data-lasso="topten-rail" style={{ position: 'relative', zIndex: 1, display: 'flex', alignItems: 'stretch', gap: 14, overflowX: 'auto', scrollbarWidth: 'none', padding: '16px 0 18px', scrollSnapType: 'x mandatory', scrollPaddingLeft: PANEL }}>
        {/* transparent spacer lets the fixed text show through; card #1 peeks past it */}
        <div style={{ width: PANEL, flexShrink: 0 }}/>
        <div data-lasso="bull-card" style={{ flexShrink: 0, position: 'relative', zIndex: 1 }}>
          <TopTenCard r={first} rank={1} onOpen={() => app.openRestaurant(first.id)}/>
        </div>
        {rest.map((r, i) => (
          <TopTenCard key={r.id} r={r} rank={i + 2} onOpen={() => app.openRestaurant(r.id)}/>
        ))}
        <div style={{ width: 8, flexShrink: 0 }}/>
      </div>
    </div>
  );
}

// ─── Value grid — 2-row horizontal scroller of compact deal tiles ───
function ValueTile({ r, onOpen }) {
  const [saved, setSaved] = React.useState(false);
  return (
    <div onClick={onOpen} data-lasso="value-tile-template" data-restaurant-id={r.id} style={{
      width: 101, flexShrink: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 8, cursor: 'pointer',
      scrollSnapAlign: 'start', scrollSnapStop: 'always',
    }}>
      <div style={{ position: 'relative', width: '100%' }}>
        {r.heroArt ? <FoodArt dish={r.heroArt} height={104} radius={18}/> : <FoodPh height={104} hue={r.hue} radius={18} glyph="bowl"/>}
        <button onClick={(e) => { e.stopPropagation(); setSaved(!saved); }} style={{
          position: 'absolute', top: 7, right: 7, width: 28, height: 28, borderRadius: '50%',
          background: saved ? 'rgba(216,90,20,0.54)' : 'rgba(245,239,231,0.20)',
          border: saved ? '1px solid rgba(255,255,255,0.42)' : '1px solid rgba(255,255,255,0.30)',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.32), 0 8px 24px rgba(27,18,13,0.20)',
          backdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          WebkitBackdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', padding: 0,
        }}><Ico.heart c="#fff" s={15} filled={saved}/></button>
        {/* Value hook — delivery fee chip */}
        <div style={{
          position: 'absolute', bottom: 7, left: 7,
          background: 'var(--dark-chocolate)', color: 'var(--brand-orange)',
          fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 12, letterSpacing: '0.01em',
          padding: '3px 7px', borderRadius: 9,
        }}>£{r.deliveryFee.toFixed(2)}</div>
      </div>
      <div style={{ width: '100%', textAlign: 'center', minWidth: 0 }}>
        <div style={{
          fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 13, lineHeight: 1.15, color: 'var(--fg-primary)',
          whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        }}>{r.name}</div>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 4, marginTop: 3 }}>
          <Ico.star c="var(--sun-orange)" s={11}/>
          <span style={{ fontFamily: 'var(--font-body)', fontSize: 11.5, color: 'var(--warm-stone)' }}>{r.rating}</span>
        </div>
      </div>
    </div>
  );
}

function ValueGrid({ app, eyebrow, title, items }) {
  if (!items || items.length === 0) return null;
  return (
    <>
      <SectionTitle eyebrow={eyebrow} title={title} action="See all" onAction={() => app.toast(`${title} · see all`, 'info')}/>
      <div data-lasso="value-grid" style={{
        display: 'grid', gridAutoFlow: 'column', gridTemplateRows: 'repeat(2, auto)',
        gap: '14px 10px', overflowX: 'auto', scrollbarWidth: 'none',
        padding: '0 20px 22px',
        scrollSnapType: 'x mandatory',
        scrollPaddingInline: 20,
      }}>
        <style>{`[data-lasso="value-grid"]::-webkit-scrollbar{display:none}`}</style>
        {Array.from({ length: 24 }, (_, i) => items[i % items.length]).map((r, i) => (
          <ValueTile key={`${r.id}-${i}`} r={r} onOpen={() => app.openRestaurant(r.id)}/>
        ))}
      </div>
    </>
  );
}

function DiscoveryRail({ app, eyebrow, title, items, variant }) {
  if (!items || items.length === 0) return null;
  return (
    <>
      <SectionTitle eyebrow={eyebrow} title={title} action="See all" onAction={() => app.toast(`${title} · see all`, 'info')}/>
      <div data-lasso={`rail-${variant}`} style={{ display: 'flex', gap: 12, padding: '0 20px 22px', overflowX: 'auto', scrollbarWidth: 'none', scrollSnapType: 'x mandatory', scrollPaddingInline: 20 }}>
        {items.map((r, i) => (
          <RailCard key={r.id} r={r} variant={variant} rank={variant === 'rank' ? i + 1 : null} onOpen={() => app.openRestaurant(r.id)}/>
        ))}
      </div>
    </>
  );
}

// ─── Cards ──────────────────────────────────────────────────────────

function RestaurantCard({ restaurant: r, onOpen }) {
  const [saved, setSaved] = React.useState(false);
  return (
    <div onClick={onOpen} data-lasso="restaurant-card-template" data-restaurant-id={r.id} style={{
      background: 'var(--bg-surface)', borderRadius: 24, padding: 12,
      display: 'flex', flexDirection: 'column', gap: 12,
      boxShadow: 'var(--shadow-md)', cursor: 'pointer',
      transition: 'transform 220ms var(--ease-out)',
    }}
    onMouseDown={e=>e.currentTarget.style.transform='scale(0.98)'}
    onMouseUp={e=>e.currentTarget.style.transform=''}
    onMouseLeave={e=>e.currentTarget.style.transform=''}>
      <div style={{ position: 'relative' }}>
        {r.heroArt ? <FoodArt dish={r.heroArt} height={140} radius={18}/> : <FoodPh height={140} hue={r.hue} radius={18} glyph={r.hue==='bun'?'bowl':'bowl'}/>}
        <button onClick={(e) => { e.stopPropagation(); setSaved(!saved); }} style={{
          position: 'absolute', top: 10, right: 10, width: 36, height: 36, borderRadius: '50%',
          background: saved ? 'rgba(216,90,20,0.54)' : 'rgba(245,239,231,0.20)',
          border: saved ? '1px solid rgba(255,255,255,0.42)' : '1px solid rgba(255,255,255,0.30)',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.32), 0 8px 24px rgba(27,18,13,0.20)',
          backdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          WebkitBackdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
        }}><Ico.heart c="#fff" s={18} filled={saved}/></button>
        <div style={{ position: 'absolute', top: 10, left: 10 }}>
          <StatusPill kind={r.status}>{r.statusLabel}</StatusPill>
        </div>
        {r.featuredBadge && (
          <div data-field="featured-badge" style={{ position: 'absolute', bottom: 10, left: 10 }}>
            <Pill bg="var(--dark-chocolate)" fg="var(--brand-orange)">★ {r.featuredBadge}</Pill>
          </div>
        )}
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 10 }}>
        <div style={{ minWidth: 0 }}>
          <div data-field="name" style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 17, lineHeight: 1.15, color: 'var(--fg-primary)' }}>{r.name}</div>
          <div data-field="cuisine" style={{ fontFamily: 'var(--font-body)', fontSize: 12.5, color: 'var(--warm-stone)', marginTop: 2, display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>{r.cuisine}<ActivityLabel id={r.id}/></div>
        </div>
        <div style={{ display: 'flex', gap: 6, flexShrink: 0 }}>
          <Pill bg="var(--bg-warm)" fg="var(--horse-brown)" style={{ '--field': 'prep-time' }}><Ico.clock c="var(--horse-brown)" s={11}/><span data-field="prep-time">{r.etaShort}</span></Pill>
          <Pill bg="var(--dust-cream)" fg="var(--dark-chocolate)"><Ico.star c="var(--sun-orange)" s={11}/><span data-field="rating">{r.rating}</span></Pill>
        </div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', borderTop: '1px solid var(--soft-line)', paddingTop: 10 }}>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 11.5, color: 'var(--warm-stone)' }}>
          <span data-field="delivery-fee">£{r.deliveryFee.toFixed(2)} delivery</span> · <span data-field="reviews">{r.reviews} reviews</span>
        </div>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 800, fontSize: 11, color: 'var(--brand-orange)' }}>View menu →</div>
      </div>
    </div>
  );
}

function FeaturedCard({ restaurant: r, onOpen }) {
  const [saved, setSaved] = React.useState(false);
  return (
    <div onClick={onOpen} data-lasso="featured-card-template" data-restaurant-id={r.id} style={{
      width: 220, flexShrink: 0, background: 'var(--bg-surface)', borderRadius: 22, overflow: 'hidden',
      boxShadow: 'var(--shadow-md)', cursor: 'pointer',
      scrollSnapAlign: 'start', scrollSnapStop: 'always',
    }}>
      <div style={{ position: 'relative' }}>
        {r.heroArt ? <FoodArt dish={r.heroArt} height={120} radius={0}/> : <FoodPh height={120} hue={r.hue} radius={0}/>}
        <button onClick={(e) => { e.stopPropagation(); setSaved(!saved); }} style={{
          position: 'absolute', top: 10, right: 10, width: 32, height: 32, borderRadius: '50%',
          background: saved ? 'rgba(216,90,20,0.54)' : 'rgba(245,239,231,0.20)',
          border: saved ? '1px solid rgba(255,255,255,0.42)' : '1px solid rgba(255,255,255,0.30)',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.32), 0 8px 24px rgba(27,18,13,0.20)',
          backdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          WebkitBackdropFilter: 'blur(10px) saturate(1.45) brightness(1.08)',
          display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
        }}><Ico.heart c="#fff" s={16} filled={saved}/></button>
        <div style={{ position: 'absolute', top: 10, left: 10 }}>
          <Pill bg="var(--brand-orange)" fg="#fff">★ Featured</Pill>
        </div>
      </div>
      <div style={{ padding: 12 }}>
        <div data-field="name" style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 15, lineHeight: 1.2 }}>{r.name}</div>
        <div data-field="tagline" style={{ fontFamily: 'var(--font-body)', fontSize: 11.5, color: 'var(--warm-stone)', marginTop: 2 }}>{r.tagline}</div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
          <Ico.clock c="var(--horse-brown)" s={11}/>
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 11, color: 'var(--horse-brown)' }}>{r.etaShort}</span>
          <span style={{ fontFamily: 'var(--font-body)', fontSize: 11, color: 'var(--warm-stone)' }}>·</span>
          <Ico.star c="var(--sun-orange)" s={11}/>
          <span style={{ fontFamily: 'var(--font-display)', fontWeight: 700, fontSize: 11 }}>{r.rating}</span>
        </div>
      </div>
    </div>
  );
}

function TrustChip({ icon, value, label }) {
  return (
    <div style={{
      flex: 1, background: 'var(--bg-surface)', borderRadius: 14, padding: '8px 10px',
      display: 'flex', alignItems: 'center', gap: 8, border: '1.5px solid var(--soft-line)',
    }}>
      {icon}
      <div style={{ minWidth: 0 }}>
        <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 12, color: 'var(--fg-primary)', lineHeight: 1 }}>{value}</div>
        <div style={{ fontFamily: 'var(--font-body)', fontSize: 9.5, color: 'var(--warm-stone)', marginTop: 2, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{label}</div>
      </div>
    </div>
  );
}

// ─── Skeletons ──────────────────────────────────────────────────────

function RestaurantSkeleton() {
  return (
    <div data-lasso="loading-skeleton" style={{ background: 'var(--bg-surface)', borderRadius: 24, padding: 12, display: 'flex', flexDirection: 'column', gap: 12 }}>
      <Skeleton h={140} r={18}/>
      <div style={{ display: 'flex', justifyContent: 'space-between', gap: 10 }}>
        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6 }}>
          <Skeleton w="70%" h={14}/>
          <Skeleton w="50%" h={11}/>
        </div>
        <Skeleton w={60} h={22} r={999}/>
      </div>
    </div>
  );
}

function FeaturedSkeleton() {
  return (
    <div style={{ width: 220, flexShrink: 0, background: 'var(--bg-surface)', borderRadius: 22, overflow: 'hidden' }}>
      <Skeleton h={120} r={0}/>
      <div style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 6 }}>
        <Skeleton w="80%" h={14}/>
        <Skeleton w="60%" h={11}/>
      </div>
    </div>
  );
}

// ─── Empty state ────────────────────────────────────────────────────

function EmptyState({ title, body, cta, onAction, icon }) {
  return (
    <div data-lasso="no-results" style={{ padding: '40px 20px', display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 12, textAlign: 'center' }}>
      <div style={{ width: 72, height: 72, borderRadius: '50%', background: 'var(--bg-warm)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        {icon || <Bull size={48} variant="orange"/>}
      </div>
      <div style={{ fontFamily: 'var(--font-display)', fontWeight: 900, fontSize: 18, color: 'var(--fg-primary)' }}>{title}</div>
      <div style={{ fontFamily: 'var(--font-body)', fontSize: 13, color: 'var(--warm-stone)', maxWidth: 240, lineHeight: 1.5 }}>{body}</div>
      {cta && <div style={{ marginTop: 4 }}><Button kind="primary" size="sm" onClick={onAction}>{cta}</Button></div>}
    </div>
  );
}

Object.assign(window, {
  HomeScreen, RestaurantCard, FeaturedCard, EmptyState, OfferSheet,
  // shared with desktop
  CategorySlider, OffersRail, OFFERS, CATEGORIES, WHEN_OPTIONS, POPULAR_SEARCHES,
  SearchDrawer, FilterDrawer, DeliverySheet, SavedDishRail, sortRestaurants,
  TopTenCard, RailCard, ValueTile, railBadge, railMeta, railDist, railDropBy,
});
