"use client"; import { useEffect, useRef, useState } from "react"; import Image from "next/image"; import Link from "next/link"; import { gsap } from "gsap"; import LayoutContainer from "@/components/layout/LayoutContainer"; import StorefrontHeader from "@/components/storefront/StorefrontHeader"; import StorefrontFooter from "@/components/storefront/StorefrontFooter"; import { TUXEDO_IMAGES } from "@/components/storefront/tuxedo-images"; import type { PublicStop } from "@/actions/stops"; type Props = { stops: PublicStop[]; brandName: string; brandSlug: string; }; export default function TuxedoStopsList({ stops, brandName, brandSlug }: Props) { const containerRef = useRef(null); const [now] = useState(() => new Date()); // Subtle scroll-in stagger for the stops list. The Motion policy in // ScrollAnimations.tsx caps translation to 16px / 320ms, which matches // what we want here — the list reveals as a quiet beat, not a show. useEffect(() => { if (!containerRef.current) return; const ctx = gsap.context(() => { gsap.from(".stop-row", { y: 16, opacity: 0, duration: 0.32, stagger: 0.04, ease: "power1.out", scrollTrigger: { trigger: containerRef.current, start: "top 90%", toggleActions: "play none none none", }, }); }, containerRef); return () => ctx.revert(); }, []); const upcomingStops = stops.filter((s) => new Date(s.date) >= now); const pastStops = stops.filter((s) => new Date(s.date) < now); return (
{/* Hero — dark editorial opener with the field photograph as a soft half-bleed at the bottom, same register as the Contact page hero. */}
{/* Soft field image, bottom-anchored, partially visible behind the headline column. */}
{/* Stops list — hairline-ruled magazine sidebar style. Each row is a single horizontal hairline + date numerals + city + location + time + chevron. No cards, no gradients. */}
{upcomingStops.length === 0 ? ( ) : (
{/* Eyebrow + hairline-ruled list header */}

Upcoming

{upcomingStops.length} stops

    {upcomingStops.map((stop) => ( ))}
{pastStops.length > 0 && (

Past Stops

Last {Math.min(5, pastStops.length)}

    {pastStops.slice(0, 5).map((stop) => ( ))}
)}
)}
); } // ── STOP ROW — hairline-ruled magazine-sidebar cell ──────────────── // Date numerals on the left (large tabular day, small month above), // city/state + location + time in the middle, chevron on the right. // All hairline-divided rows; the list reads as a sidebar, not a feed. function StopRow({ stop, brandSlug, past = false, }: { stop: PublicStop; brandSlug: string; past?: boolean; }) { const date = new Date(stop.date); const day = date.getDate(); const month = date.toLocaleDateString("en-US", { month: "short" }); const weekday = date.toLocaleDateString("en-US", { weekday: "short" }); const content = (
{/* Date column — big tabular day, small month/weekday above */}

{weekday} · {month}

{day}

{/* City + location */}

{stop.city}, {stop.state}

{stop.location}

{stop.address && (

{stop.address}

)}
{/* Time + cutoff */}

{stop.time}

{!past && stop.cutoff_time && (

Order by{" "} {new Date(stop.cutoff_time).toLocaleTimeString("en-US", { hour: "numeric", minute: "2-digit", })}

)} {past && (

Completed

)}
{/* Chevron — only on upcoming rows */}
{!past && ( )}
); const inner = (
  • {content}
  • ); if (past) { return inner; } return ( {content} ); } // ── EMPTY STATE — editorial Fraunces italic copy ────────────────── // Same "no stops yet" message as before, but rendered as a quiet // editorial panel instead of an icon-in-rounded-box illustration. function EmptyStops() { return (

    Coming Soon

    The corn is still ripening
    {" "} in the field.

    New pickup stops are added every week. Check back soon, or browse the full season schedule below.

    Back to Tuxedo Corn Download Schedule
    ); }