Files
route-commerce/src/app/tuxedo/page.tsx
T
Tyler ffdc80e60e
Deploy to route.crispygoat.com / deploy (push) Successful in 5m11s
feat(tuxedo): Field Almanac editorial composition
Layers on top of origin/main's existing editorial primitives. Replaces
the cinematic video hero with a typographic 'cover spread' and rebuilds
the page composition around numbered editorial sections.

Cover spread (TuxedoVideoHero):
  - Newspaper-style masthead with location, altitude, weather, date
  - Massive display headline with oversize 'XL' page numeral in margin
  - Cover photo plate with crop marks, photo credit, plate number
  - Floating marginalia (vertical text) drifting in on scroll
  - Slow horizontal ticker with harvest facts at bottom

Page composition (page.tsx) — numbered editorial sections:
  № 00  Editor's Note         two-column preamble, drop cap, pull quote
  № 01  The Almanac           6 specimen cards with hover-reveal notes
  № 02  The Cover Story       photo + sticky stat marginalia
  № 03  The 2026 Tour         typeset schedule with stat strip
  № 04  This Week's Lot       inventory-style product cards w/ lot #s
  № 05  Chronicle of the Row  3-gen timeline on dark plate
  № 06  Letter from the Field letterpress subscribe card w/ deckle edge
        Colophon               back-of-book fine print

Adds editorial primitives to globals.css: section numbering, drop caps,
specimen tabs, pull quotes, crop marks, page numerals, ticker,
deckle-edge subscribe, almanac-paper backgrounds.

Removes dark fixed gradient from tuxedo/layout.tsx so per-section
.almanac-paper backgrounds win.
2026-07-07 09:10:40 -06:00

1233 lines
52 KiB
TypeScript

"use client";
import { useReducer, useEffect, useRef } from "react";
import Link from "next/link";
import Image from "next/image";
import { m as motion, useInView } from "framer-motion";
import TuxedoVideoHero from "@/components/storefront/TuxedoVideoHero";
import ProductCard from "@/components/storefront/ProductCard";
import PaginatedStops from "@/components/storefront/PaginatedStops";
import StorefrontHeader from "@/components/storefront/StorefrontHeader";
import StorefrontFooter from "@/components/storefront/StorefrontFooter";
import BrandStylesProvider from "@/components/storefront/BrandStylesProvider";
import { supabase } from "@/lib/supabase";
import { getBrandSettingsPublic } from "@/actions/brand-settings";
// ─────────────────────────────────────────────────────────────────────────────
// TUXEDO PAGE — Field Almanac composition
//
// Section order:
// 0. Cover spread → TuxedoVideoHero (typographic masthead)
// 1. Editor's note → two-column preamble with pull quote
// 2. I. The Almanac → specimen cards (altitude, swing, hands, …)
// 3. II. The Cover Story → editorial photo + marginalia
// 4. III. The 2026 Tour → stops as typeset schedule
// 5. IV. This Week's Lot → inventory-style product cards
// 6. V. Chronicle of the Row → three-generation timeline
// 7. Letter from the Field → closing subscribe (letterpress card)
// 8. Colophon → back-of-book fine print
// ─────────────────────────────────────────────────────────────────────────────
function scrollToStops() {
document.getElementById("tour")?.scrollIntoView({ behavior: "smooth" });
}
function scrollToStory() {
document.getElementById("chronicle")?.scrollIntoView({ behavior: "smooth" });
}
// ── Types ────────────────────────────────────────────────────────────────────
type Brand = { id: string; name: string; slug: string };
type Stop = {
id: string;
city: string;
state: string;
date: string;
time: string;
location: string;
slug: string;
brand_id: string;
};
type Product = {
id: string;
name: string;
description: string | null;
price: number;
type: string;
image_url: string | null;
brand_id: string;
is_taxable?: boolean;
pickup_type?: "scheduled_stop" | "shed";
};
// ── State machine ────────────────────────────────────────────────────────────
type State = {
brand: Brand | null;
stops: Stop[];
products: Product[];
wholesaleEnabled: boolean;
logoUrl: string | null;
logoUrlDark: string | null;
olatheSweetLogoUrl: string | null;
olatheSweetLogoUrlDark: string | null;
showSchedulePdf: boolean;
showWholesaleLink: boolean;
heroTagline: string | null;
isAdmin: boolean;
customFooterText: string | null;
contactEmail: string | null;
contactPhone: string | null;
brandPrimaryColor: string | null;
brandBgColor: string | null;
brandTextColor: string | null;
};
type Action =
| { type: "SET_BRAND"; brand: Brand | null }
| { type: "SET_STOPS"; stops: Stop[] }
| { type: "SET_PRODUCTS"; products: Product[] }
| {
type: "SET_SETTINGS";
wholesaleEnabled: boolean;
logoUrl: string | null;
logoUrlDark: string | null;
olatheSweetLogoUrl: string | null;
olatheSweetLogoUrlDark: string | null;
showSchedulePdf: boolean;
showWholesaleLink: boolean;
heroTagline: string | null;
customFooterText: string | null;
contactEmail: string | null;
contactPhone: string | null;
brandPrimaryColor: string | null;
brandBgColor: string | null;
brandTextColor: string | null;
}
| { type: "SET_IS_ADMIN"; isAdmin: boolean };
const initialState: State = {
brand: null,
stops: [],
products: [],
wholesaleEnabled: false,
logoUrl: null,
logoUrlDark: null,
olatheSweetLogoUrl: null,
olatheSweetLogoUrlDark: null,
showSchedulePdf: true,
showWholesaleLink: true,
heroTagline: null,
isAdmin: false,
customFooterText: null,
contactEmail: null,
contactPhone: null,
brandPrimaryColor: null,
brandBgColor: null,
brandTextColor: null,
};
function reducer(state: State, action: Action): State {
switch (action.type) {
case "SET_BRAND": return { ...state, brand: action.brand };
case "SET_STOPS": return { ...state, stops: action.stops };
case "SET_PRODUCTS": return { ...state, products: action.products };
case "SET_SETTINGS": return {
...state,
wholesaleEnabled: action.wholesaleEnabled,
logoUrl: action.logoUrl,
logoUrlDark: action.logoUrlDark,
olatheSweetLogoUrl: action.olatheSweetLogoUrl,
olatheSweetLogoUrlDark: action.olatheSweetLogoUrlDark,
showSchedulePdf: action.showSchedulePdf,
showWholesaleLink: action.showWholesaleLink,
heroTagline: action.heroTagline,
customFooterText: action.customFooterText,
contactEmail: action.contactEmail,
contactPhone: action.contactPhone,
brandPrimaryColor: action.brandPrimaryColor,
brandBgColor: action.brandBgColor,
brandTextColor: action.brandTextColor,
};
case "SET_IS_ADMIN": return { ...state, isAdmin: action.isAdmin };
}
}
// ─────────────────────────────────────────────────────────────────────────────
// 1. EDITOR'S NOTE
// ─────────────────────────────────────────────────────────────────────────────
function EditorsNote() {
return (
<section className="relative almanac-paper py-20 md:py-32 overflow-hidden">
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14">
<div className="grid grid-cols-12 gap-6 md:gap-10">
<div className="col-span-12 md:col-span-3 flex md:block items-center justify-between">
<div>
<div className="almanac-section-number text-[#1a1814]/55">
00 Editor&rsquo;s Note
</div>
<div className="almanac-eyebrow text-[#1a1814]/55 mt-3">From the Editor</div>
</div>
<div className="md:mt-12 almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
MMXXVI · 08
</div>
</div>
<div className="col-span-12 md:col-span-9 grid md:grid-cols-2 gap-x-12 lg:gap-x-20 gap-y-8">
<div>
<p className="almanac-dropcap almanac-body text-[#1a1814]/85 text-lg md:text-[19px] leading-[1.55]">
<span>
A bulletin, not a brochure. What follows is a partial
record of how a particular valley grows a particular ear
of corn, picked by particular hands, on a particular
morning. We don&rsquo;t have a slogan; we have a field.
</span>
</p>
<p className="almanac-body text-[#1a1814]/75 text-base md:text-[17px] leading-[1.65] mt-5">
The Olathe Sweet parent line was rescued from a retiring
farmer who believed his valley could grow sugar in a husk.
We brought it back to the same soil, learned how it liked
the water, and kept the seed by hand, ear by ear for
forty summers.
</p>
</div>
<div>
<p className="almanac-body text-[#1a1814]/75 text-base md:text-[17px] leading-[1.65]">
You won&rsquo;t find us at a farmers&rsquo; market in a
big city. We don&rsquo;t wholesale to supermarkets. Every
ear you eat came off a truck that left the shed that
morning, headed for a stop we planned with people we know
by name.
</p>
<p className="almanac-body text-[#1a1814]/75 text-base md:text-[17px] leading-[1.65] mt-5">
What follows in this issue: the numbers that matter, the
stops on the 2026 tour, the lot for this week, and a short
chronicle of how we got here. Read at your leisure. Eat
with butter and salt.
</p>
<div className="mt-10 flex items-center gap-4">
<SignatureSvg />
<div>
<div className="almanac-italic text-[#1a1814] text-base">
John Harold
</div>
<div className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55 mt-0.5">
Editor · Second Generation
</div>
</div>
</div>
</div>
</div>
</div>
<div className="mt-16 md:mt-24 grid grid-cols-12 gap-6">
<div className="col-span-12 md:col-start-3 md:col-span-8">
<hr className="almanac-rule mb-8" />
<blockquote className="almanac-pullquote text-[#1a1814] text-[26px] md:text-[40px] lg:text-[52px] leading-[1.05] text-center">
&ldquo;The ear in the photograph was picked the morning it was
shot. The silk on the right is still damp. We don&rsquo;t
airbrush the field because we don&rsquo;t need to.&rdquo;
</blockquote>
<hr className="almanac-rule mt-8" />
</div>
</div>
</div>
</section>
);
}
function SignatureSvg() {
return (
<svg
className="almanac-flourish"
width="80"
height="32"
viewBox="0 0 80 32"
fill="none"
stroke="#1a1814"
strokeWidth="1.4"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden
>
<path d="M2 22 C 8 8, 14 28, 22 14 C 28 6, 34 24, 42 16 C 50 8, 56 26, 64 14" />
<path d="M58 24 L 70 24" />
</svg>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 2. I. THE ALMANAC — Specimen cards
// ─────────────────────────────────────────────────────────────────────────────
interface AlmanacSpecimen {
number: string;
title: string;
unit: string;
value: string;
caption: string;
detail: string;
}
const SPECIMENS: AlmanacSpecimen[] = [
{
number: "001",
title: "Altitude",
unit: "ft. above sea",
value: "5,360",
caption: "Uncompahgre Valley floor",
detail:
"Olathe Sweet was bred for elevation. The thin air at five thousand feet cooks the husk by day and the cold drains back in by night — the diurnal swing does the work no greenhouse can.",
},
{
number: "002",
title: "Diurnal Swing",
unit: "°F avg",
value: "38",
caption: "Day · Night differential",
detail:
"Daytime highs in the mid-eighties, nighttime lows in the upper forties. The kernel drinks sugar by sun and locks it in by cold. That&rsquo;s the whole trick.",
},
{
number: "003",
title: "Hands at Peak",
unit: "people · daily",
value: "~60",
caption: "Including three generations",
detail:
"Grandparents on the husk trailer. Parents on the cooler line. Kids sorting by ear size in the shed. About sixty of us during the first two weeks of August.",
},
{
number: "004",
title: "Variety",
unit: "parent line",
value: "1982",
caption: "Olathe Sweet · original",
detail:
"The seed has never been hybridized in a laboratory. Forty summers of careful selection in the same soil. Nothing added. Nothing altered.",
},
{
number: "005",
title: "Time to Pick",
unit: "minutes",
value: "90",
caption: "field to cooler",
detail:
"A picker drops an ear into the sling; a runner carries it to the cooler truck; the cooler truck drives to the shed. Ninety minutes, no more.",
},
{
number: "006",
title: "Rows Under Pivot",
unit: "acres",
value: "60",
caption: "single farm · single family",
detail:
"Sixty acres of sweet corn and sixty of rotation. We don&rsquo;t outsource, we don&rsquo;t co-pack, we don&rsquo;t re-brand anyone else&rsquo;s crop. The name on the cooler is the name on the deed.",
},
];
function TheAlmanac() {
return (
<section className="relative almanac-paper--bone py-24 md:py-36 overflow-hidden">
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14">
<div className="grid grid-cols-12 gap-6 mb-12 md:mb-20">
<div className="col-span-12 md:col-span-4">
<div className="almanac-section-number text-[#1a1814]/55">
01 The Almanac
</div>
<div className="almanac-eyebrow text-[#1a1814]/55 mt-3">Specimens &amp; Data</div>
</div>
<div className="col-span-12 md:col-span-8">
<h2 className="almanac-display text-[#1a1814] text-[44px] sm:text-[60px] md:text-[84px] lg:text-[110px]">
You can taste<br />the altitude.
</h2>
<div className="grid md:grid-cols-2 gap-8 mt-10">
<p className="almanac-body text-[#1a1814]/80 text-base md:text-[17px] leading-[1.65]">
Six numbers describe this farm better than any slogan. We
keep them where we can see them in the shed, in the
cooler, on the side of the picking trailer.
</p>
<p className="almanac-body text-[#1a1814]/75 text-base md:text-[16px] leading-[1.65]">
Hover any specimen to read the field note. None of these
are marketing they&rsquo;re what we measure every
morning between picking and packing.
</p>
</div>
</div>
</div>
<hr className="almanac-rule--hair mb-12 md:mb-16" />
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6">
{SPECIMENS.map((s) => (
<SpecimenCard key={s.number} s={s} />
))}
</div>
<div className="mt-14 flex flex-wrap items-baseline justify-between gap-y-3">
<div className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
Recorded by hand · audit on file at the shed
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
continued on page 03
</div>
</div>
</div>
</section>
);
}
function SpecimenCard({ s }: { s: AlmanacSpecimen }) {
const ref = useRef<HTMLDivElement>(null);
const inView = useInView(ref, { once: true, margin: "-60px" });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 20 }}
animate={inView ? { opacity: 1, y: 0 } : { opacity: 0, y: 20 }}
transition={{ duration: 0.7, ease: [0.22, 0.61, 0.36, 1] }}
className="almanac-card group p-6 md:p-7 cursor-default"
>
<div className="flex items-start justify-between mb-5">
<span className="almanac-tab">No. {s.number}</span>
<span className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/50">
{s.title}
</span>
</div>
<div className="flex items-baseline gap-3 mb-1">
<span className="almanac-display text-[#1a1814] text-[64px] md:text-[76px] leading-none">
{s.value}
</span>
<span className="almanac-mono text-[10.5px] tracking-[0.18em] uppercase text-[#1a1814]/55 max-w-[80px]">
{s.unit}
</span>
</div>
<p className="almanac-italic text-[#1a1814]/70 text-sm md:text-[15px] mb-5">
{s.caption}
</p>
<hr className="almanac-rule--hair mb-4" />
<p
className="almanac-body text-[#1a1814]/75 text-[14px] leading-[1.55] max-h-0 overflow-hidden opacity-0 group-hover:max-h-[140px] group-hover:opacity-100 transition-all duration-500 ease-out"
dangerouslySetInnerHTML={{ __html: s.detail }}
/>
<p className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/40 group-hover:opacity-0 transition-opacity duration-300">
Hover for note
</p>
</motion.div>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 3. II. THE COVER STORY
// ─────────────────────────────────────────────────────────────────────────────
function TheCoverStory() {
return (
<section className="relative almanac-paper py-24 md:py-32 overflow-hidden">
<div aria-hidden className="almanac-pagenum absolute top-20 right-0 select-none">
03
</div>
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14 relative">
<div className="grid grid-cols-12 gap-6 mb-12 md:mb-16">
<div className="col-span-12 md:col-span-4">
<div className="almanac-section-number text-[#1a1814]/55">
02 The Cover Story
</div>
<div className="almanac-eyebrow text-[#1a1814]/55 mt-3">A single ear</div>
</div>
<div className="col-span-12 md:col-span-8">
<div className="almanac-dateline text-[#1a1814]/55 mb-3">
Dateline · Olathe, Colorado · 6:14 AM
</div>
<h2 className="almanac-display text-[#1a1814] text-[36px] sm:text-[50px] md:text-[72px] lg:text-[90px]">
One ear.<br />One morning.
</h2>
</div>
</div>
<div className="grid grid-cols-12 gap-6 md:gap-10">
<aside className="col-span-12 lg:col-span-3 order-2 lg:order-1">
<div className="lg:sticky lg:top-32 space-y-8">
<div>
<div className="almanac-eyebrow text-[#1a1814]/55">Today&rsquo;s lot</div>
<div className="almanac-display-tight text-3xl text-[#1a1814] mt-2">
2,440 ears
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.2em] uppercase text-[#1a1814]/55 mt-1">
picked · packed · loaded by 9:30
</div>
</div>
<hr className="almanac-rule--hair" />
<div>
<div className="almanac-eyebrow text-[#1a1814]/55">Average ear</div>
<div className="almanac-display-tight text-3xl text-[#1a1814] mt-2">
8&Prime; long
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.2em] uppercase text-[#1a1814]/55 mt-1">
18 rows · tip-fill to the cap
</div>
</div>
<hr className="almanac-rule--hair" />
<div>
<div className="almanac-eyebrow text-[#1a1814]/55">Pickers</div>
<div className="almanac-display-tight text-3xl text-[#1a1814] mt-2">
14 hands
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.2em] uppercase text-[#1a1814]/55 mt-1">
Three generations · one row at a time
</div>
</div>
<hr className="almanac-rule--hair" />
<div>
<div className="almanac-eyebrow text-[#1a1814]/55">Window</div>
<div className="almanac-display-tight text-3xl text-[#1a1814] mt-2">
4 9 AM
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.2em] uppercase text-[#1a1814]/55 mt-1">
before the sugar turns to starch
</div>
</div>
</div>
</aside>
<figure className="col-span-12 lg:col-span-9 order-1 lg:order-2">
<div className="almanac-photo relative w-full aspect-[4/3] md:aspect-[16/10] bg-[#1a1814] shadow-[0_30px_80px_-30px_rgba(26,24,20,0.5)]">
<Image
src="https://s3.crispygoat.com/videos/wp-import/images/tuxedo-ear-macro.jpg"
alt="A single ear of Olathe Sweet corn, partially husked, silk still attached"
fill
sizes="(max-width: 1024px) 100vw, 75vw"
style={{ objectFit: "cover" }}
className="img"
/>
<div className="absolute top-5 left-5 almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#f3ece0]/85">
Plate 022 · 1:1 macro
</div>
<div className="absolute top-5 right-5 almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#f3ece0]/85">
Silk damp · 6:18 AM
</div>
<svg
className="absolute inset-0 m-auto opacity-40"
width="60"
height="60"
viewBox="0 0 60 60"
fill="none"
stroke="#f3ece0"
strokeWidth="0.8"
aria-hidden
>
<circle cx="30" cy="30" r="14" />
<path d="M30 4 L30 18 M30 42 L30 56 M4 30 L18 30 M42 30 L56 30" />
</svg>
</div>
<figcaption className="mt-5 grid grid-cols-12 gap-6">
<p className="col-span-12 md:col-span-8 almanac-italic text-[#1a1814]/85 text-base md:text-[17px] leading-[1.6]">
<span className="almanac-smallcaps text-[#1a1814] mr-2">Above.</span>{" "}
The same ear, picked at 6:14 AM, photographed at 6:18. The
silk is still wet. The husk was peeled back with a thumb.
Nothing else was done to it, and nothing has been done to
it since. This is what an Olathe Sweet looks like at the
moment it becomes food.
</p>
<p className="col-span-12 md:col-span-4 almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
Photograph Zane Harold<br />
Filed August MMXXVI<br />
Location Row 14, Block 3
</p>
</figcaption>
<div className="mt-16 grid grid-cols-12 gap-6">
<div className="col-span-12 md:col-start-2 md:col-span-10">
<p className="almanac-pullquote text-[#1a1814] text-[24px] md:text-[36px] lg:text-[44px] leading-[1.08]">
&ldquo;You can&rsquo;t replicate the diurnal swing in a
greenhouse. You can&rsquo;t replicate it on a flat
field. You can&rsquo;t replicate it in a hurry.&rdquo;
</p>
<p className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55 mt-5">
John Harold · Second Generation
</p>
</div>
</div>
</figure>
</div>
</div>
</section>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 4. III. THE 2026 TOUR
// ─────────────────────────────────────────────────────────────────────────────
function TheTour({ stops, showSchedulePdf }: { stops: Stop[]; showSchedulePdf: boolean }) {
return (
<section id="tour" className="relative almanac-paper--bone py-24 md:py-36 overflow-hidden scroll-mt-20">
<div aria-hidden className="almanac-pagenum absolute top-20 left-0 select-none">
04
</div>
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14 relative">
<div className="grid grid-cols-12 gap-6 mb-12 md:mb-16">
<div className="col-span-12 md:col-span-4">
<div className="almanac-section-number text-[#1a1814]/55">
03 The Tour
</div>
<div className="almanac-eyebrow text-[#1a1814]/55 mt-3">
Pickup schedule · 2026
</div>
</div>
<div className="col-span-12 md:col-span-8">
<h2 className="almanac-display text-[#1a1814] text-[44px] sm:text-[60px] md:text-[84px] lg:text-[110px]">
The 2026<br />Tour.
</h2>
<p className="almanac-body text-[#1a1814]/80 text-base md:text-[18px] leading-[1.55] max-w-xl mt-8">
We don&rsquo;t ship to stores. We bring the cooler to a
corner we know, on a day we&rsquo;ve confirmed, and we wait
for you. Find a stop on the list below, or download the
whole schedule as a one-page PDF.
</p>
</div>
</div>
<hr className="almanac-rule mb-10" />
{stops.length === 0 ? (
<div className="grid grid-cols-12 gap-6">
<div className="col-span-12 md:col-span-9 almanac-card p-10 md:p-14">
<div className="almanac-eyebrow text-[#1a1814]/55 mb-3">
The calendar is open
</div>
<h3 className="almanac-display-tight text-3xl md:text-5xl text-[#1a1814]">
No stops on the calendar yet.
</h3>
<p className="almanac-body text-[#1a1814]/75 text-base md:text-[17px] leading-[1.6] mt-5 max-w-2xl">
The harvest is right around the corner new pickup stops
go live weekly. You can preorder from the lot below and
pick a stop once they&rsquo;re announced, or download
the schedule PDF and we&rsquo;ll pencil you in by hand.
</p>
<div className="mt-7 flex flex-col sm:flex-row gap-3 sm:gap-5">
<Link href="#lot" className="almanac-btn-primary">
<span>Preorder a Box</span>
<span aria-hidden></span>
</Link>
<Link href="/tuxedo/stops" className="almanac-btn-ghost">
<span>View All Stops</span>
<span aria-hidden></span>
</Link>
{showSchedulePdf && (
<Link href="/api/tuxedo/schedule-pdf" download className="almanac-btn-ghost">
<span>Schedule PDF</span>
<span aria-hidden></span>
</Link>
)}
</div>
</div>
<div className="col-span-12 md:col-span-3 almanac-card p-6 md:p-8">
<div className="almanac-eyebrow text-[#1a1814]/55 mb-3">
This Week&rsquo;s Lot
</div>
<div className="almanac-display-tight text-3xl text-[#1a1814]">
First pick
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55 mt-2">
expected within 14 days
</div>
</div>
</div>
) : (
<>
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-10 mb-10">
{[
{ v: stops.length.toString().padStart(2, "0"), l: "Stops scheduled" },
{ v: "4", l: "States on the route" },
{ v: "84", l: "Days on the road" },
{ v: "~60", l: "Hands at peak" },
].map((s) => (
<div key={s.l}>
<div className="almanac-display-tight text-[#1a1814] text-5xl md:text-6xl">
{s.v}
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55 mt-2">
{s.l}
</div>
</div>
))}
</div>
<hr className="almanac-rule--hair mb-10" />
<PaginatedStops stops={stops} brandSlug="tuxedo" brandAccent="green" />
{showSchedulePdf && (
<div className="mt-10 text-right">
<Link href="/api/tuxedo/schedule-pdf" download className="almanac-btn-ghost">
<span>Download Full Schedule</span>
<span aria-hidden></span>
</Link>
</div>
)}
</>
)}
</div>
</section>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 5. IV. THIS WEEK'S LOT
// ─────────────────────────────────────────────────────────────────────────────
function ThisWeeksLot({
products,
olatheSweetLogoUrlDark,
}: {
products: Product[];
olatheSweetLogoUrlDark: string | null;
}) {
return (
<section id="lot" className="relative almanac-paper py-24 md:py-32 overflow-hidden scroll-mt-20">
<div aria-hidden className="almanac-pagenum absolute top-20 right-0 select-none">
05
</div>
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14 relative">
<div className="grid grid-cols-12 gap-6 mb-12 md:mb-16">
<div className="col-span-12 md:col-span-4">
<div className="almanac-section-number text-[#1a1814]/55">
04 This Week&rsquo;s Lot
</div>
<div className="almanac-eyebrow text-[#1a1814]/55 mt-3">
Inventory · picked this morning
</div>
</div>
<div className="col-span-12 md:col-span-8">
<h2 className="almanac-display text-[#1a1814] text-[44px] sm:text-[60px] md:text-[84px] lg:text-[110px]">
Today&rsquo;s<br />haul.
</h2>
<p className="almanac-body text-[#1a1814]/80 text-base md:text-[18px] leading-[1.55] max-w-xl mt-8">
Preorder for pickup at a stop, or have cooler boxes shipped
to your door after the season. Limited to what we picked
this morning when it&rsquo;s gone, it&rsquo;s gone until
tomorrow.
</p>
</div>
</div>
<hr className="almanac-rule mb-12" />
{products.length === 0 ? (
<div className="almanac-card p-12 text-center">
<div className="almanac-eyebrow text-[#1a1814]/55">Lot empty</div>
<p className="almanac-display-tight text-3xl md:text-4xl text-[#1a1814] mt-3">
The first pick hasn&rsquo;t come in yet.
</p>
<p className="almanac-body text-[#1a1814]/70 mt-3">
Check back at sunrise. We open the shed at 5 AM and update
the lot before 7.
</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5 md:gap-6">
{products.slice(0, 6).map((p, i) => (
<LotCard
key={p.id}
product={p}
lotNumber={(i + 1).toString().padStart(3, "0")}
olatheSweetLogoUrlDark={olatheSweetLogoUrlDark}
/>
))}
</div>
)}
{products.length > 6 && (
<div className="mt-10 flex items-center justify-between almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
<span> {products.length - 6} more in the catalog</span>
<Link href="/tuxedo/products" className="almanac-underline">
See full catalog
</Link>
</div>
)}
</div>
</section>
);
}
function LotCard({
product,
lotNumber,
olatheSweetLogoUrlDark,
}: {
product: Product;
lotNumber: string;
olatheSweetLogoUrlDark: string | null;
}) {
return (
<div className="almanac-card p-5 md:p-6 group">
<div className="flex items-center justify-between mb-4">
<span className="almanac-tab">Lot&nbsp;{lotNumber}</span>
<span className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55">
Grade&nbsp;A
</span>
</div>
<ProductCard
id={product.id}
name={product.name}
description={product.description}
price={`$${product.price}`}
type={product.type}
imageUrl={product.image_url}
brandSlug="tuxedo"
brandName="Tuxedo Corn"
brandId={product.brand_id}
brandAccent="green"
is_taxable={product.is_taxable}
pickup_type={product.pickup_type}
olatheSweetLogoUrlDark={olatheSweetLogoUrlDark}
/>
<div className="mt-4 pt-4 border-t border-[#1a1814]/15 grid grid-cols-3 gap-2 almanac-mono text-[10px] tracking-[0.18em] uppercase text-[#1a1814]/55">
<div>
<div className="text-[#1a1814]">Picked</div>
Today
</div>
<div>
<div className="text-[#1a1814]">Origin</div>
Olathe
</div>
<div>
<div className="text-[#1a1814]">Weight</div>
25&ndash;40 lb
</div>
</div>
</div>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 6. V. CHRONICLE OF THE ROW
// ─────────────────────────────────────────────────────────────────────────────
function ChronicleOfTheRow() {
const entries = [
{
year: "1982",
chapter: "Ch. I",
title: "A seed saved.",
body: "The original Olathe Sweet parent line was kept by a retiring farmer who believed his valley could grow sugar in a husk. We bought his first planting — eight rows along the irrigation ditch — and brought it home to the same soil.",
image:
"https://s3.crispygoat.com/videos/wp-import/images/corn-field-zane-original.jpg",
caption: "The original eight rows · Spring 1982 · Olathe, CO",
},
{
year: "2003",
chapter: "Ch. II",
title: "A field retired, then revived.",
body: "After five years of commodity pricing that didn&rsquo;t pencil, we let the field go fallow. Weeds came up. The seed sat in a coffee can in the shed. In 2003 we replanted the same coffee-can seed in the same soil and the sweetness came back with it.",
image:
"https://s3.crispygoat.com/videos/wp-import/images/tuxedo-founder-john-editorial.jpg",
caption: "John Harold · inspecting the revival · August 2003",
},
{
year: "Today",
chapter: "Ch. III",
title: "Three generations on the row.",
body: "Grandparents on the husk trailer. Parents on the cooler line. Kids sorting by ear size in the shed. About sixty hands during peak — and a tour schedule we plan a year in advance with the families we serve.",
image:
"https://s3.crispygoat.com/videos/wp-import/images/tuxedo-field-storm.jpg",
caption: "The same field · pre-storm · August 2026",
},
];
return (
<section id="chronicle" className="relative almanac-paper--ink py-24 md:py-36 overflow-hidden scroll-mt-20">
<div aria-hidden className="almanac-pagenum absolute top-20 left-0 select-none">
06
</div>
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14 relative">
<div className="grid grid-cols-12 gap-6 mb-14 md:mb-20">
<div className="col-span-12 md:col-span-4">
<div className="almanac-section-number text-[#f3ece0]/55">
05 Chronicle
</div>
<div className="almanac-eyebrow text-[#f3ece0]/55 mt-3">
Three generations · one row
</div>
</div>
<div className="col-span-12 md:col-span-8">
<h2 className="almanac-display text-[#f3ece0] text-[44px] sm:text-[60px] md:text-[84px] lg:text-[120px]">
Chronicle of<br />the row.
</h2>
<p className="almanac-body text-[#f3ece0]/80 text-base md:text-[18px] leading-[1.55] max-w-xl mt-8">
A short history of how this farm got from one coffee can of
seed to a harvest that feeds families in four states.
Written by hand, edited once.
</p>
</div>
</div>
<hr className="almanac-rule--hair mb-14 md:mb-20" />
<div className="space-y-20 md:space-y-32">
{entries.map((e, i) => (
<ChronicleEntry key={e.year} e={e} reverse={i % 2 === 1} />
))}
</div>
<div className="mt-24 text-center">
<div className="almanac-display-tight text-[#f3ece0]/85 text-3xl md:text-5xl">
</div>
<div className="almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#f3ece0]/55 mt-4">
End of Chapter III · Continued
</div>
</div>
</div>
</section>
);
}
function ChronicleEntry({
e,
reverse,
}: {
e: { year: string; chapter: string; title: string; body: string; image: string; caption: string };
reverse: boolean;
}) {
return (
<article className={`grid grid-cols-12 gap-6 md:gap-10 items-start ${reverse ? "md:[direction:rtl]" : ""}`}>
<div className="col-span-12 md:col-span-7 md:[direction:ltr]">
<figure className="almanac-photo relative w-full aspect-[16/10] bg-[#1a1814]">
<Image
src={e.image}
alt={e.caption}
fill
sizes="(max-width: 768px) 100vw, 60vw"
style={{ objectFit: "cover" }}
className="img"
/>
<div className="absolute top-4 left-4 almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#f3ece0]/85">
{e.chapter} · {e.year}
</div>
<div className="absolute bottom-4 right-4 almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#f3ece0]/85">
archive
</div>
</figure>
<p className="almanac-italic text-[#f3ece0]/65 text-sm md:text-[15px] mt-3 md:[direction:ltr]">
{e.caption}
</p>
</div>
<div className="col-span-12 md:col-span-5 md:[direction:ltr]">
<div className="almanac-eyebrow text-[#c89517] mb-3">{e.chapter}</div>
<div className="almanac-display-tight text-[#f3ece0] text-[80px] md:text-[110px] lg:text-[140px] leading-none mb-4">
{e.year}
</div>
<h3 className="almanac-display text-[#f3ece0] text-[28px] md:text-[40px] lg:text-[48px] leading-[1.05] mb-6">
{e.title}
</h3>
<p
className="almanac-body text-[#f3ece0]/80 text-base md:text-[17px] leading-[1.65]"
dangerouslySetInnerHTML={{ __html: e.body }}
/>
</div>
</article>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 7. LETTER FROM THE FIELD
// ─────────────────────────────────────────────────────────────────────────────
function LetterFromTheField() {
return (
<section className="relative almanac-paper py-24 md:py-32 overflow-hidden">
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14">
<div className="grid grid-cols-12 gap-6 md:gap-10 items-center">
<div className="col-span-12 md:col-span-6">
<div className="almanac-section-number text-[#1a1814]/55 mb-3">
06 Postscript
</div>
<h2 className="almanac-display text-[#1a1814] text-[40px] sm:text-[60px] md:text-[80px] lg:text-[100px]">
Letter from<br />the field.
</h2>
<p className="almanac-body text-[#1a1814]/80 text-base md:text-[18px] leading-[1.6] mt-8 max-w-md">
One note a month, written from the shed. New stops, the
day&rsquo;s pick, the occasional recipe from a customer.
No marketing, no nonsense.
</p>
</div>
<div className="col-span-12 md:col-span-6">
<div className="relative bg-[#fbf6ec] border border-[#1a1814] shadow-[0_30px_60px_-30px_rgba(26,24,20,0.45)]">
<div className="absolute top-0 left-0 right-0 h-3 bg-[radial-gradient(ellipse_4px_3px_at_4px_0,#1a1814_99%,transparent_100%),radial-gradient(ellipse_4px_3px_at_8px_0,#1a1814_99%,transparent_100%),radial-gradient(ellipse_4px_3px_at_12px_0,#1a1814_99%,transparent_100%)] bg-[length:8px_3px] bg-repeat-x opacity-80" />
<div className="px-7 md:px-10 py-12 md:py-16">
<div className="almanac-eyebrow text-[#1a1814]/55">Addressed to</div>
<form
className="mt-4 space-y-5"
onSubmit={(ev) => {
ev.preventDefault();
const form = ev.currentTarget;
const email = new FormData(form).get("email") as string;
if (email) {
form.querySelector("[data-success]")?.removeAttribute("hidden");
(form.querySelector("input[name=email]") as HTMLInputElement).value = "";
}
}}
>
<label className="block">
<span className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55">
Your address
</span>
<input
type="email"
name="email"
required
placeholder="you@home.org"
className="w-full mt-2 bg-transparent border-b-2 border-[#1a1814] py-3 almanac-display-tight text-2xl md:text-3xl text-[#1a1814] placeholder-[#1a1814]/25 focus:outline-none focus:border-[#c89517]"
/>
</label>
<button type="submit" className="almanac-btn-primary mt-2">
<span>Send to the field</span>
<span aria-hidden></span>
</button>
<p
data-success
hidden
className="almanac-italic text-[#1a1814] text-sm md:text-base"
>
Filed. We&rsquo;ll write back from the row.
</p>
</form>
<hr className="almanac-rule--hair my-8" />
<div className="grid grid-cols-2 gap-5 almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
<div>
<div className="text-[#1a1814] mb-1">Twelve a year</div>
One letter per month
</div>
<div>
<div className="text-[#1a1814] mb-1">No spam</div>
We don&rsquo;t sell your address
</div>
<div>
<div className="text-[#1a1814] mb-1">Cancel</div>
One click, no questions
</div>
<div>
<div className="text-[#1a1814] mb-1">Postmark</div>
The Harold shed, Olathe
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// 8. COLOPHON
// ─────────────────────────────────────────────────────────────────────────────
function Colophon() {
return (
<section className="relative almanac-paper--bone py-16 md:py-24 overflow-hidden">
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14">
<hr className="almanac-rule--double mb-10" />
<div className="grid grid-cols-12 gap-6 md:gap-10">
<div className="col-span-12 md:col-span-3">
<div className="almanac-eyebrow text-[#1a1814]/55">Colophon</div>
<div className="almanac-display-tight text-2xl text-[#1a1814] mt-2">
About this bulletin
</div>
</div>
<div className="col-span-12 md:col-span-9 grid sm:grid-cols-3 gap-8">
<div>
<div className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55 mb-1">
Set in
</div>
<p className="almanac-body text-[#1a1814] text-base">
Fraunces &amp; Newsreader. Small caps by hand.
</p>
</div>
<div>
<div className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55 mb-1">
Printed on
</div>
<p className="almanac-body text-[#1a1814] text-base">
Recycled paper, the color of the shed at noon.
</p>
</div>
<div>
<div className="almanac-mono text-[10px] tracking-[0.22em] uppercase text-[#1a1814]/55 mb-1">
Published by
</div>
<p className="almanac-body text-[#1a1814] text-base">
The Harold Family, Olathe, CO. Forty-second edition.
</p>
</div>
</div>
</div>
<hr className="almanac-rule--hair mt-12" />
<div className="flex flex-wrap items-baseline justify-between gap-y-3 mt-6 almanac-mono text-[10.5px] tracking-[0.22em] uppercase text-[#1a1814]/55">
<span>© MMXXVI Harold Family Farm</span>
<span>Filed from the field</span>
<span>Olathe · Colorado · United States</span>
</div>
</div>
</section>
);
}
// ─────────────────────────────────────────────────────────────────────────────
// PAGE
// ─────────────────────────────────────────────────────────────────────────────
export default function TuxedoPage() {
const [state, dispatch] = useReducer(reducer, initialState);
useEffect(() => {
async function load() {
const slug = "tuxedo";
const [brandResult, settingsResult] = await Promise.all([
supabase.from("brands").select("*").eq("slug", slug).single(),
getBrandSettingsPublic(slug),
]);
const brandData = brandResult.data as Brand | null;
dispatch({ type: "SET_BRAND", brand: brandData });
if (settingsResult.success && settingsResult.settings) {
const s = settingsResult.settings;
dispatch({
type: "SET_SETTINGS",
wholesaleEnabled: settingsResult.wholesaleEnabled ?? false,
logoUrl: s.logo_url ?? null,
logoUrlDark: s.logo_url_dark ?? null,
olatheSweetLogoUrl: s.olathe_sweet_logo_url ?? null,
olatheSweetLogoUrlDark: s.olathe_sweet_logo_url_dark ?? null,
showSchedulePdf: s.show_schedule_pdf ?? true,
showWholesaleLink: s.show_wholesale_link ?? true,
heroTagline: s.hero_tagline ?? null,
customFooterText: s.custom_footer_text ?? null,
contactEmail: s.email ?? null,
contactPhone: s.phone ?? null,
brandPrimaryColor: s.brand_primary_color ?? null,
brandBgColor: s.brand_bg_color ?? null,
brandTextColor: s.brand_text_color ?? null,
});
}
try {
const { getCurrentAdminUser } = await import("@/actions/admin-user");
const adminUser = await getCurrentAdminUser();
dispatch({ type: "SET_IS_ADMIN", isAdmin: !!adminUser });
} catch {
// not logged in as admin
}
if (brandData?.id) {
const [{ data: stopsData }, { data: productsData }] = await Promise.all([
supabase.from("stops").select("*").eq("brand_id", brandData.id).eq("active", true) as unknown as { data: Stop[] | null },
supabase.from("products").select("*").eq("brand_id", brandData.id).eq("active", true) as unknown as { data: Product[] | null },
]);
dispatch({ type: "SET_STOPS", stops: stopsData ?? [] });
dispatch({ type: "SET_PRODUCTS", products: productsData ?? [] });
}
}
load();
}, []);
return (
<div className="min-h-screen">
<BrandStylesProvider
primaryColor={state.brandPrimaryColor}
bgColor={state.brandBgColor}
textColor={state.brandTextColor}
/>
<StorefrontHeader
brandName={state.brand?.name ?? "Tuxedo Corn"}
brandSlug="tuxedo"
logoUrl={state.logoUrl}
logoUrlDark={state.logoUrlDark}
showWholesaleLink={state.showWholesaleLink}
isAdmin={state.isAdmin}
brandAccent="green"
/>
<main>
{/* 0 · Cover spread */}
<TuxedoVideoHero
eyebrow="Olathe Sweet™ — Olathe, Colorado"
title="Tuxedo Corn"
description={
state.heroTagline ??
"Premium Olathe Sweet™ sweet corn — hand-picked at peak freshness from our family farm in Colorado."
}
olatheSweetLogoUrl={state.olatheSweetLogoUrl}
primaryButton="Find a Stop"
secondaryButton="Our Story"
onPrimaryClick={scrollToStops}
onSecondaryClick={scrollToStory}
/>
{/* Wholesale banner */}
{state.showWholesaleLink && state.wholesaleEnabled && (
<div className="almanac-paper--ink border-y border-[#1a1814]/15 py-5">
<div className="mx-auto max-w-[1480px] px-5 sm:px-8 md:px-14 flex flex-wrap items-center justify-between gap-y-3">
<div className="almanac-italic text-[#f3ece0] text-base md:text-lg">
Wholesale buyers accounts open for the 2026 season.
</div>
<Link
href="/wholesale/login"
className="almanac-btn-primary"
style={{ background: "#c89517", color: "#1a1814", borderColor: "#c89517" }}
>
<span>Wholesale Portal</span>
<span aria-hidden></span>
</Link>
</div>
</div>
)}
{/* 1 · Editor's note */}
<EditorsNote />
{/* 2 · The Almanac */}
<TheAlmanac />
{/* 3 · The Cover Story */}
<TheCoverStory />
{/* 4 · The 2026 Tour */}
<TheTour stops={state.stops} showSchedulePdf={state.showSchedulePdf} />
{/* 5 · This Week's Lot */}
<ThisWeeksLot
products={state.products}
olatheSweetLogoUrlDark={state.olatheSweetLogoUrlDark}
/>
{/* 6 · Chronicle of the Row */}
<ChronicleOfTheRow />
{/* 7 · Letter from the Field */}
<LetterFromTheField />
{/* · Colophon */}
<Colophon />
</main>
<StorefrontFooter
brandName={state.brand?.name ?? "Tuxedo Corn"}
brandSlug="tuxedo"
logoUrl={state.logoUrl}
logoUrlDark={state.logoUrlDark}
customFooterText={state.customFooterText}
contactEmail={state.contactEmail}
contactPhone={state.contactPhone}
isAdmin={state.isAdmin}
brandAccent="green"
/>
</div>
);
}