| 1 | import Link from "next/link"; |
| 2 | import { serverCaller } from "@/lib/trpc/server"; |
| 3 | import { Marquee } from "@/components/marquee"; |
| 4 | import { SteppingCouple } from "@/components/stepping-couple"; |
| 5 | import { MobileNav } from "@/components/mobile-nav"; |
| 6 | import { Wordmark } from "@/components/wordmark"; |
| 7 | |
| 8 | export const dynamic = "force-dynamic"; |
| 9 | |
| 10 | const COUNT = ["6", "·", "COUNT", "·", "8"]; |
| 11 | |
| 12 | const COMING = [ |
| 13 | "Little Rock", |
| 14 | "St. Louis", |
| 15 | "Huntsville", |
| 16 | "Montgomery", |
| 17 | "New Orleans", |
| 18 | "Las Vegas", |
| 19 | ]; |
| 20 | |
| 21 | const YARD = [ |
| 22 | { k: "Stepping groups", d: "The groups that keep each city stepping." }, |
| 23 | { k: "DJs", d: "Who is spinning the set, and where." }, |
| 24 | { k: "Promoters", d: "The people who host the sets and weekenders." }, |
| 25 | { k: "Classes", d: "Lessons and instructors, beginner on up." }, |
| 26 | ]; |
| 27 | |
| 28 | function fmt(d: Date | null) { |
| 29 | if (!d) return "DATE TBD"; |
| 30 | return new Date(d) |
| 31 | .toLocaleString("en-US", { |
| 32 | month: "short", |
| 33 | day: "numeric", |
| 34 | hour: "numeric", |
| 35 | minute: "2-digit", |
| 36 | }) |
| 37 | .toUpperCase(); |
| 38 | } |
| 39 | |
| 40 | export default async function Home() { |
| 41 | const caller = serverCaller(); |
| 42 | const cities = await caller.city.listLaunched(); |
| 43 | const byCity = new Map(cities.map((c) => [c.id, c])); |
| 44 | const lists = await Promise.all( |
| 45 | cities.map((c) => caller.event.list({ cityId: c.id })), |
| 46 | ); |
| 47 | const lineup = lists |
| 48 | .flat() |
| 49 | .sort( |
| 50 | (a, b) => |
| 51 | (a.startsAt ? +new Date(a.startsAt) : Infinity) - |
| 52 | (b.startsAt ? +new Date(b.startsAt) : Infinity), |
| 53 | ) |
| 54 | .slice(0, 6); |
| 55 | |
| 56 | return ( |
| 57 | <div className="relative"> |
| 58 | <header className="fixed top-0 z-50 flex w-full items-center justify-between px-5 py-4 sm:px-8"> |
| 59 | <Link href="/" className="text-lg"> |
| 60 | <Wordmark /> |
| 61 | </Link> |
| 62 | <nav className="hidden gap-7 sm:flex"> |
| 63 | <a href="#sets" className="kicker hover:text-bone"> |
| 64 | The Sets |
| 65 | </a> |
| 66 | <a href="#floor" className="kicker hover:text-bone"> |
| 67 | The Floor |
| 68 | </a> |
| 69 | <a href="#road" className="kicker hover:text-bone"> |
| 70 | The Road |
| 71 | </a> |
| 72 | </nav> |
| 73 | <MobileNav /> |
| 74 | </header> |
| 75 | |
| 76 | {} |
| 77 | <section className="relative flex min-h-[100svh] flex-col justify-center px-5 sm:px-10"> |
| 78 | <div className="pointer-events-none relative order-1 mx-auto mt-24 h-[34vh] w-[84%] max-w-[300px] sm:absolute sm:inset-y-0 sm:right-[2%] sm:order-none sm:mx-0 sm:mt-0 sm:h-auto sm:w-[38%] sm:max-w-none"> |
| 79 | <div |
| 80 | className="absolute left-1/2 top-1/2 h-[78%] w-[78%] -translate-x-1/2 -translate-y-1/2 rounded-full blur-3xl" |
| 81 | style={{ background: "radial-gradient(circle, rgba(61,90,254,0.14) 0%, transparent 62%)" }} |
| 82 | /> |
| 83 | <SteppingCouple className="absolute inset-0 flex items-center justify-center py-[4%]" /> |
| 84 | </div> |
| 85 | |
| 86 | <p className="kicker beat-in b1 order-2 mt-6 sm:absolute sm:left-10 sm:top-24 sm:mt-0"> |
| 87 | The partner dance <span className="text-gold">/</span> Born in Chicago |
| 88 | </p> |
| 89 | |
| 90 | <div className="relative z-10 order-3 mt-3 select-none sm:mt-0 sm:-ml-[2vw]"> |
| 91 | <h1 className="display" style={{ lineHeight: 0.82, letterSpacing: "-0.04em", fontSize: "clamp(3rem, 13.5vw, 17rem)" }}> |
| 92 | <span className="block beat-in b1">STEP.</span> |
| 93 | <span className="block text-gold beat-in b2">GLIDE.</span> |
| 94 | <span className="serif block italic font-normal beat-in b3">spin.</span> |
| 95 | </h1> |
| 96 | </div> |
| 97 | |
| 98 | <div className="beat-in b4 relative z-10 order-4 mt-6 max-w-md sm:mt-8"> |
| 99 | <p className="serif text-2xl leading-snug text-bone sm:text-3xl"> |
| 100 | Chicago Stepping. Your City. |
| 101 | </p> |
| 102 | <p className="serif mt-3 text-lg leading-snug text-bone/75 sm:text-xl"> |
| 103 | The sets, the people, and the road between cities, in one place. |
| 104 | </p> |
| 105 | </div> |
| 106 | |
| 107 | <div className="beat-in b5 order-5 mt-8 flex flex-wrap items-center gap-x-6 gap-y-2"> |
| 108 | {cities.map((c) => ( |
| 109 | <Link key={c.id} href={`/city/${c.slug}`} className="kicker text-bone hover:text-gold"> |
| 110 | {c.name} |
| 111 | </Link> |
| 112 | ))} |
| 113 | </div> |
| 114 | <p className="beat-in b5 order-6 mt-4 max-w-2xl text-sm leading-relaxed text-ash"> |
| 115 | <span className="kicker">More cities coming</span> |
| 116 | <span className="mx-2 text-gold">/</span> |
| 117 | {COMING.map((name, i) => ( |
| 118 | <span key={name}> |
| 119 | <span className="whitespace-nowrap">{name}</span> |
| 120 | {i < COMING.length - 1 && " · "} |
| 121 | </span> |
| 122 | ))} |
| 123 | </p> |
| 124 | </section> |
| 125 | |
| 126 | <Marquee /> |
| 127 | |
| 128 | {} |
| 129 | <section className="flex flex-wrap items-baseline justify-center gap-6 px-5 py-10 sm:px-8"> |
| 130 | {COUNT.map((g, i) => ( |
| 131 | <span |
| 132 | key={i} |
| 133 | className="display text-3xl text-bone/70 sm:text-4xl" |
| 134 | > |
| 135 | {g} |
| 136 | </span> |
| 137 | ))} |
| 138 | </section> |
| 139 | |
| 140 | {} |
| 141 | <section |
| 142 | id="sets" |
| 143 | className="border-t border-[var(--line)] px-5 py-16 sm:px-8" |
| 144 | > |
| 145 | <div className="flex items-end justify-between"> |
| 146 | <h2 className="display text-5xl sm:text-7xl">The Sets</h2> |
| 147 | <span className="kicker">01 / Sets</span> |
| 148 | </div> |
| 149 | <ul className="mt-10"> |
| 150 | {lineup.length === 0 && ( |
| 151 | <li className="kicker text-ash">No sets posted yet.</li> |
| 152 | )} |
| 153 | {lineup.map((e, i) => ( |
| 154 | <li key={e.id}> |
| 155 | <Link |
| 156 | href={`/city/${byCity.get(e.cityId)?.slug ?? ""}`} |
| 157 | className="group flex items-center gap-5 border-b border-[var(--line)] py-6" |
| 158 | > |
| 159 | <span className="kicker w-10 text-gold"> |
| 160 | {String(i + 1).padStart(2, "0")} |
| 161 | </span> |
| 162 | <span className="display flex-1 text-2xl text-bone group-hover:text-gold sm:text-4xl"> |
| 163 | {e.title} |
| 164 | </span> |
| 165 | <span className="kicker hidden sm:block"> |
| 166 | {byCity.get(e.cityId)?.name} |
| 167 | </span> |
| 168 | <span className="kicker w-28 text-right text-ash"> |
| 169 | {fmt(e.startsAt)} |
| 170 | </span> |
| 171 | </Link> |
| 172 | </li> |
| 173 | ))} |
| 174 | </ul> |
| 175 | </section> |
| 176 | |
| 177 | {} |
| 178 | <section |
| 179 | id="floor" |
| 180 | className="border-t border-[var(--line)] px-5 py-16 sm:px-8" |
| 181 | > |
| 182 | <div className="flex items-end justify-between"> |
| 183 | <h2 className="display text-5xl sm:text-7xl">The Floor</h2> |
| 184 | <span className="kicker">02 / Community</span> |
| 185 | </div> |
| 186 | <div className="mt-10 grid gap-px sm:grid-cols-2 lg:grid-cols-3"> |
| 187 | {YARD.map((y) => ( |
| 188 | <div key={y.k} className="py-6 pr-6"> |
| 189 | <h3 className="display text-3xl">{y.k}</h3> |
| 190 | <p className="serif mt-2 text-lg text-bone/70">{y.d}</p> |
| 191 | </div> |
| 192 | ))} |
| 193 | </div> |
| 194 | </section> |
| 195 | |
| 196 | {} |
| 197 | <section |
| 198 | id="road" |
| 199 | className="border-t border-[var(--line)] px-5 py-16 sm:px-8" |
| 200 | > |
| 201 | <div className="flex items-end justify-between"> |
| 202 | <h2 className="display text-5xl sm:text-7xl">The Road</h2> |
| 203 | <span className="kicker">03 / Cities</span> |
| 204 | </div> |
| 205 | <div className="mt-10 grid gap-px sm:grid-cols-2"> |
| 206 | {cities.map((c) => ( |
| 207 | <Link |
| 208 | key={c.id} |
| 209 | href={`/city/${c.slug}`} |
| 210 | className="group flex items-end justify-between border-t border-[var(--line)] py-10" |
| 211 | > |
| 212 | <div> |
| 213 | <span className="kicker text-ash">{c.metroLabel}</span> |
| 214 | <h3 className="display mt-2 text-5xl group-hover:text-gold sm:text-6xl"> |
| 215 | {c.name} |
| 216 | </h3> |
| 217 | </div> |
| 218 | <span className="kicker text-gold">Enter →</span> |
| 219 | </Link> |
| 220 | ))} |
| 221 | </div> |
| 222 | </section> |
| 223 | |
| 224 | <footer className="flex flex-wrap items-center justify-between gap-3 border-t border-[var(--line)] px-5 py-8 sm:px-8"> |
| 225 | <Link href="/" className="text-xl"> |
| 226 | <Wordmark /> |
| 227 | </Link> |
| 228 | <div className="flex items-center gap-6"> |
| 229 | <Link href="/about" className="kicker text-bone hover:text-gold"> |
| 230 | About |
| 231 | </Link> |
| 232 | <span className="kicker text-ash">Chicago Stepping. Your City.</span> |
| 233 | </div> |
| 234 | </footer> |
| 235 | </div> |
| 236 | ); |
| 237 | } |