- {/* The storefront backdrop and ambient orbs are persistent. The
- page body itself crossfades between routes via the View
- Transitions API. */}
+ {/* No fixed dark gradient — the Field Almanac composition sets its
+ own per-section backgrounds (cream / bone / ink) via the
+ .almanac-paper utility classes on each section. */}
{children}
diff --git a/src/app/tuxedo/page.tsx b/src/app/tuxedo/page.tsx
index b8ab9e7..b472261 100644
--- a/src/app/tuxedo/page.tsx
+++ b/src/app/tuxedo/page.tsx
@@ -5,41 +5,38 @@ 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 CinematicShowcase from "@/components/storefront/CinematicShowcase";
-import LayoutContainer from "@/components/layout/LayoutContainer";
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 { TUXEDO_IMAGES as WP_IMAGES } from "@/components/storefront/tuxedo-images";
-import { ScrollReveal, ParallaxLayer, FadeOnScroll } from "@/components/ui/ScrollAnimations";
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("stops")?.scrollIntoView({ behavior: "smooth" });
+ document.getElementById("tour")?.scrollIntoView({ behavior: "smooth" });
}
function scrollToStory() {
- document.getElementById("story")?.scrollIntoView({ behavior: "smooth" });
-}
-function scrollToProducts() {
- document.getElementById("products")?.scrollIntoView({ behavior: "smooth" });
-}
-import { getBrandSettingsPublic } from "@/actions/brand-settings";
-import { gsap } from "gsap";
-import { ScrollTrigger } from "gsap/ScrollTrigger";
-
-// Register GSAP plugins
-if (typeof window !== "undefined") {
- gsap.registerPlugin(ScrollTrigger);
+ document.getElementById("chronicle")?.scrollIntoView({ behavior: "smooth" });
}
-type Brand = {
- id: string;
- name: string;
- slug: string;
-};
-
+// ── Types ────────────────────────────────────────────────────────────────────
+type Brand = { id: string; name: string; slug: string };
type Stop = {
id: string;
city: string;
@@ -50,7 +47,6 @@ type Stop = {
slug: string;
brand_id: string;
};
-
type Product = {
id: string;
name: string;
@@ -63,6 +59,7 @@ type Product = {
pickup_type?: "scheduled_stop" | "shed";
};
+// ── State machine ────────────────────────────────────────────────────────────
type State = {
brand: Brand | null;
stops: Stop[];
@@ -75,8 +72,6 @@ type State = {
showSchedulePdf: boolean;
showWholesaleLink: boolean;
heroTagline: string | null;
- heroImageUrl: string | null;
- heroVideoUrl: string | null;
isAdmin: boolean;
customFooterText: string | null;
contactEmail: string | null;
@@ -85,7 +80,6 @@ type State = {
brandBgColor: string | null;
brandTextColor: string | null;
};
-
type Action =
| { type: "SET_BRAND"; brand: Brand | null }
| { type: "SET_STOPS"; stops: Stop[] }
@@ -100,8 +94,6 @@ type Action =
showSchedulePdf: boolean;
showWholesaleLink: boolean;
heroTagline: string | null;
- heroImageUrl: string | null;
- heroVideoUrl: string | null;
customFooterText: string | null;
contactEmail: string | null;
contactPhone: string | null;
@@ -123,8 +115,6 @@ const initialState: State = {
showSchedulePdf: true,
showWholesaleLink: true,
heroTagline: null,
- heroImageUrl: null,
- heroVideoUrl: null,
isAdmin: false,
customFooterText: null,
contactEmail: null,
@@ -136,718 +126,964 @@ const initialState: State = {
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,
- heroImageUrl: action.heroImageUrl,
- heroVideoUrl: action.heroVideoUrl,
- 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 };
+ 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 };
}
}
-// ── Pull-quote band — full-bleed editorial interlude ────────────────
-// A single Fraunces italic quote floating over the pre-storm field.
-// No chrome. The image does the talking.
-function PullQuoteBand() {
+// ─────────────────────────────────────────────────────────────────────────────
+// 1. EDITOR'S NOTE
+// ─────────────────────────────────────────────────────────────────────────────
+function EditorsNote() {
return (
-
-
- {/* Layered tints: top darkens the sky, bottom grounds the type. */}
-
- {/* One warm amber rim along the lower edge to echo the corn. */}
-
+
+
+
+
+
+
+ № 00 — Editor’s Note
+
+
From the Editor
+
+
+ MMXXVI · 08
+
+
-
-
-
-
- “The best sweet corn doesn’t travel.
-
- It shouldn’t have to.”
-
-
-
-
-
-
-
- — Tuxedo Corn · Olathe, Colorado
-
-
+
+
+
+
+ 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’t have a slogan; we have a field.
+
+
+
+ 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.
+
+
+
+
+ You won’t find us at a farmers’ market in a
+ big city. We don’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.
+
+
+ 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.
+
+
+
+
+
+
+ John Harold
+
+
+ Editor · Second Generation
+
+
+
+
+
+
+
+
+
+
+
+ “The ear in the photograph was picked the morning it was
+ shot. The silk on the right is still damp. We don’t
+ airbrush the field because we don’t need to.”
+
+
+
);
}
-// ── The Corn — single editorial moment replacing the feature grid ───
-// One paragraph. One image. The feature-card grid read as a templated
-// list of icons; an honest photograph of a single ear communicates
-// everything those eight tiles couldn't.
-function TheCorn() {
+function SignatureSvg() {
return (
-
-
-
- {/* Left: copy column, 5/12 */}
-
-
-
- The Corn
-
-
-
-
- You can taste
- the altitude.
-
-
-
-
-
-
-
- Olathe Sweet was developed for the Uncompahgre Valley — five thousand,
- three hundred and sixty feet above the sea, where the daytime sun hits
- the husk and the nights cool it back down before sunrise. That diurnal
- swing is what loads every kernel with sugar; nothing we do in the
- kitchen can replicate it, and nothing in a greenhouse can grow it.
-
-
-
-
- The ear in the photograph was picked the morning it was shot. The
- silk on the right is still damp. We don’t airbrush the field
- because we don’t need to.
-
-
-
-
- {/* Right: macro photo, 7/12 — the photograph carries the section. */}
-
-
-
-
- {/* Subtle amber rim light along the bottom — picks up the corn. */}
-
- {/* Provenance plate, lower-left, hairline border. */}
-
- Olathe Sweet · Day of pick
-
-
-
-
-
-
-
+
+
+
);
}
-// ── Founder strip — Our Story anchor ─────────────────────────────────
-// Image + narrative. The previous "Our Story" was a centered text block;
-// here the founder's portrait grounds the timeline that follows.
-function FounderStrip() {
- return (
-
- {/* A single amber rim along the top so this section reads as the
- hinge between The Corn (warm cream) and the dark Story panel. */}
-
+// ─────────────────────────────────────────────────────────────────────────────
+// 2. I. THE ALMANAC — Specimen cards
+// ─────────────────────────────────────────────────────────────────────────────
+interface AlmanacSpecimen {
+ number: string;
+ title: string;
+ unit: string;
+ value: string;
+ caption: string;
+ detail: string;
+}
-
-
-
-
-
- {/* Soft amber wash on the bottom — matches The Corn plate. */}
-
-
- John Harold · Olathe, CO
-
+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’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’t outsource, we don’t co-pack, we don’t re-brand anyone else’s crop. The name on the cooler is the name on the deed.",
+ },
+];
+
+function TheAlmanac() {
+ return (
+
+
+
+
+
+ № 01 — The Almanac
-
-
-
-
-
- The family behind the row
+
Specimens & Data
+
+
+
+ You can taste the altitude.
+
+
+
+ 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.
-
-
-
- John Harold’s great-grandchildren still walk
- the same rows he cleared in 1982.
-
-
-
-
- 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. The
- name on the cooler is the name on the deed.
+
+ Hover any specimen to read the field note. None of these
+ are marketing — they’re what we measure every
+ morning between picking and packing.
-
-
-
- Est. 1982
- ·
- 3 generations on the row
- ·
- ~60 hands at peak
-
-
+
-
+
+
+
+
+ {SPECIMENS.map((s) => (
+
+ ))}
+
+
+
+
+ ↑ Recorded by hand · audit on file at the shed
+
+
+ ↘ continued on page 03
+
+
+
);
}
-// ── Harvest editorial — magazine "by the numbers" spread ─────────
-//
-// One packshot + one data strip. The strip is the brand voice — it
-// treats the farm's actual numbers (years, generations, altitude, hands)
-// as magazine sidebar content, not as a feature-card grid. Hairline
-// rules, large tabular numerals, and a single editorial caption beneath.
-// No icons, no boxes, no color highlights. Reads as Cereal magazine
-// did a feature on this farm, not as a farm-stand template.
-function HarvestEditorial() {
+function SpecimenCard({ s }: { s: AlmanacSpecimen }) {
+ const ref = useRef(null);
+ const inView = useInView(ref, { once: true, margin: "-60px" });
return (
-
- {/* Hairline top rule — single amber line so the section reads as
- a magazine spread opener. */}
-
+
+ No. {s.number}
+
+ {s.title}
+
+
-
- {/* Section header */}
-
-
-
- Harvest · 2026
-
-
- The shape of an
- Olathe summer.
+
+
+ {s.value}
+
+
+ {s.unit}
+
+
+
+ {s.caption}
+
+
+
+
+
+ Hover for note
+
+
+ );
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// 3. II. THE COVER STORY
+// ─────────────────────────────────────────────────────────────────────────────
+function TheCoverStory() {
+ return (
+
+
+ 03
+
+
+
+
+
+
+ № 02 — The Cover Story
+
+
A single ear
+
+
+
+ Dateline · Olathe, Colorado · 6:14 AM
+
+
+ One ear. One morning.
-
-
- Forty summers of the same seed, the same soil, and the same
- row. The diurnal swing that loads the kernel with sugar is
- a property of this valley — not a recipe we can take with us.
-
-
-
+
- {/* Full-bleed packshot */}
-
-
- {/* A single warm tint along the bottom to echo the existing
- amber-wash treatment used in The Corn and FounderStrip so
- the three sections feel like one editorial system. */}
-
- {/* Provenance plate, lower-left, hairline-bordered, matching
- the editorial chrome used elsewhere on the page. */}
-
- Harvest morning · August · Olathe, CO
-
- {/* Subtle photo credit on the right — like a magazine spread */}
-
- Frame 014 · zane
-
-
-
- {/* Magazine data strip — four columns, hairline-ruled, tabular
- numerals count up on scroll-in (uses the existing .counter-animate
- GSAP setup). Hairline above + below gives it the magazine-
- sidebar treatment without any colored boxes or icons. */}
-
-
-
-
-
-
-
- {/* Editor's caption — italic display serif, the editorial
- footnote that makes the section feel like a feature story. */}
-
- “You can’t replicate the diurnal swing in a
- greenhouse. You can’t replicate it on a flat field.
- You can’t replicate it in a hurry.”
-
-
- — John Harold, second generation
-
-
-
- );
-}
-
-// One column of the "by the numbers" strip. The number is a
-// 0 so the
-// existing GSAP setup in this page's useEffect picks it up and counts
-// up from 0 to N when the column scrolls into view. Prefix/suffix
-// are rendered as siblings so they survive the count-up animation.
-function DataPoint({
- value,
- prefix,
- suffix,
- label,
- detail,
-}: {
- value: number;
- prefix?: string;
- suffix?: string;
- label: string;
- detail: string;
-}) {
- return (
-
- {/* Top hairline per column so the strip reads as four magazine
- sidebar cells, not four feature cards. */}
-
-
+ We don’t ship to stores. We bring the cooler to a
+ corner we know, on a day we’ve confirmed, and we wait
+ for you. Find a stop on the list below, or download the
+ whole schedule as a one-page PDF.
+
+
+
+
+
+
+ {stops.length === 0 ? (
+
+
+
+ The calendar is open
+
+
+ No stops on the calendar yet.
+
+
+ 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’re announced, or download
+ the schedule PDF and we’ll pencil you in by hand.
+
+
+
+ Preorder a Box
+ →
+
+
+ View All Stops
+ ↗
+
+ {showSchedulePdf && (
+
+ Schedule PDF
+ ↓
+
+ )}
+
+
+
+
+ This Week’s Lot
+
+
+ First pick
+
+
+ expected within 14 days
+
+
+
+ ) : (
+ <>
+
+ {[
+ { 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) => (
+
+ 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’s gone, it’s gone until
+ tomorrow.
+
+
+
- {/* Mobile-friendly fallback grid for smaller screens */}
-
{products.length - 3} more products available in the full catalog.
-
- )}
-
- );
-}
-
-// ── Story section — slim timeline (founder portrait now lives above) ─
-function StorySection() {
- return (
-
- {/* Single soft golden-hour glow behind the headline. */}
-
-
-
-
-
- Our Story
-
-
-
-
-
- Three generations of sweet corn excellence.
-
-
-
-
-
-
-
-
-
- Tuxedo Corn is the exclusive grower and shipper of Olathe Sweet Sweet Corn — developed for Colorado's high-altitude mountain climate and grown by the same family for over 40 years.
-
-
-
- {/* Timeline — kept as the trust signal. */}
-
-
- {[
- {
- year: "1982",
- head: "A seed saved",
- body: "The original Olathe Sweet parent line was kept by a farmer who believed his valley could grow sugar in a husk.",
- },
- {
- year: "2003",
- head: "A field retired, then revived",
- body: "After a five-year pause the family brought the same seed back to the same soil — and the sweetness came back with it.",
- },
- {
- year: "Today",
- head: "Three generations on the row",
- body: "Grandparents, parents, and kids all work the summer pick — a workforce of about sixty hands during peak season.",
- },
- ].map((item) => (
-
-
-
- {item.year}
-
-
- {item.head}
-
-
- {item.body}
-
-
- ))}
-
-
-
-
-
-
- Read Our Story
-
-
-
-
+
+
+
Picked
+ Today
-
+
+
Origin
+ Olathe
+
+
+
Weight
+ 25–40 lb
+
+
+
+ );
+}
+
+// ─────────────────────────────────────────────────────────────────────────────
+// 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’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 (
+
+
+ 06
+
+
+
+
+
+
+ № 05 — Chronicle
+
+
+ Three generations · one row
+
+
+
+
+ Chronicle of the row.
+
+
+ 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.
+
+ Wholesale buyers — accounts open for the 2026 season.
+
+
+ Wholesale Portal
+ →
+
+
+
+ )}
-
+ {/* 1 · Editor's note */}
+
-
+ {/* 2 · The Almanac */}
+
-
+ {/* 3 · The Cover Story */}
+
-
+
+ {/* 5 · This Week's Lot */}
+
-
+ {/* 6 · Chronicle of the Row */}
+
-
+ {/* 7 · Letter from the Field */}
+
-
+ {/* · Colophon */}
+
);
-}
+}
\ No newline at end of file
diff --git a/src/components/storefront/TuxedoVideoHero.tsx b/src/components/storefront/TuxedoVideoHero.tsx
index 9ae76ab..4661a45 100644
--- a/src/components/storefront/TuxedoVideoHero.tsx
+++ b/src/components/storefront/TuxedoVideoHero.tsx
@@ -1,724 +1,332 @@
"use client";
import Image from "next/image";
-import { useEffect, useReducer, useRef, useState } from "react";
-import { m as motion } from "framer-motion";
-import { gsap } from "gsap";
-import { ScrollTrigger } from "gsap/ScrollTrigger";
+import { useEffect, useRef } from "react";
-if (typeof window !== "undefined") {
- gsap.registerPlugin(ScrollTrigger);
-}
+// ─────────────────────────────────────────────────────────────────────────────
+// TUXEDO COVER — Field Almanac opening spread
+//
+// Replaces the cinematic video hero with a typographic editorial cover:
+// · Masthead with date / volume / coordinates / temperature strip
+// · Massive two-line display headline with oversize numerals in the margin
+// · Hand-set cover image with crop marks, photo credit, plate number
+// · Floating marginalia that drift in on scroll
+// · Three-axis GSAP entrance — opacity, Y translate, and a subtle letterpress
+// scale on the headline. No parallax on the cover photo (vestibular safe).
+// ─────────────────────────────────────────────────────────────────────────────
type TuxedoVideoHeroProps = {
eyebrow?: string;
title: string;
description: string;
olatheSweetLogoUrl?: string | null;
- /** URL of a video (mp4/webm) to play muted & looped as the hero backdrop. */
- videoUrl?: string | null;
- /** Image shown as the video poster and as the static fallback if the
- * video fails to load, is blocked by autoplay policies, or the user
- * prefers reduced motion. Without this, the gradient stack stands on
- * its own. */
- posterUrl?: string | null;
primaryButton?: string;
secondaryButton?: string;
onPrimaryClick?: () => void;
onSecondaryClick?: () => void;
};
-const HERO_STATS = [
- { stat: "40+", label: "Years Growing" },
- { stat: "3", label: "Generations" },
- { stat: "100%", label: "Hand-Picked" },
-] as const;
+const COVER_IMAGE =
+ "https://s3.crispygoat.com/videos/wp-import/images/tuxedo-field-storm.jpg";
-type State = {
- scrollProgress: number;
-};
-
-type Action = { type: "SET_SCROLL_PROGRESS"; value: number };
-
-const initialState: State = {
- scrollProgress: 0,
-};
-
-function reducer(state: State, action: Action): State {
- switch (action.type) {
- case "SET_SCROLL_PROGRESS":
- return { ...state, scrollProgress: action.value };
- }
-}
-
-/**
- * The big thesis panel for the storefront. Composes, in z-order:
- * - poster image (Next/Image, full-bleed, always present)
- * - optional muted looping video (renders only when its URL loads)
- * - gradient overlays (warm gold -> deep night)
- * - foreground editorial content (logo / eyebrow / title / copy / CTAs / stats)
- *
- * The hero is designed to feel complete as a STILL even without the video.
- * If `videoUrl` is missing, errors, or autoplay is blocked, the poster
- * image stays as the background and the page is not left with an empty
- * dark hole.
- */
export default function TuxedoVideoHero({
eyebrow,
title,
description,
olatheSweetLogoUrl,
- videoUrl,
- posterUrl,
primaryButton,
secondaryButton,
onPrimaryClick,
onSecondaryClick,
}: TuxedoVideoHeroProps) {
- const logoSrc = olatheSweetLogoUrl;
const sectionRef = useRef(null);
- const contentRef = useRef(null);
- const videoRef = useRef(null);
+ const headlineRef = useRef(null);
+ const marginaliaRef = useRef(null);
- const [state, dispatch] = useReducer(reducer, initialState);
-
- // `videoPlaying` reflects whether the