feat(tuxedo): magazine editorial — altitude ribbon, harvest spread, data strip
Deploy to route.crispygoat.com / deploy (push) Successful in 4m29s
Deploy to route.crispygoat.com / deploy (push) Successful in 4m29s
Adds a 'Cereal-magazine-did-a-feature' editorial moment to the Tuxedo
homepage instead of a templated farm-stand layout. Three pieces:
1. Hero (TuxedoVideoHero)
- Adds '5,360 FT · UNCOMPAHGRE VALLEY' masthead chip next to the
existing eyebrow, hairline-divided like a magazine cover line.
Reinforces the geographic / altitude voice of the brand.
- Adds slow Ken Burns drift on the poster image (26s alternate,
scale 1.02 → 1.10 + tiny translate). Plays under the video while
loading; if the video fails, the poster now breathes instead of
sitting static. Honors prefers-reduced-motion.
- Bumps grain overlay opacity 0.04 → 0.085 for editorial texture.
2. New HarvestEditorial section (page.tsx), placed after FounderStrip
and before StorySection per user direction.
- Full-bleed packing-shed packshot (corn-field-zane-original.jpg,
WP-import, 2500×1406) with subtle harvest-drift Ken Burns and a
hairline-bordered provenance plate ('HARVEST MORNING · AUGUST ·
OLATHE, CO') + photo credit.
- Magazine 'BY THE NUMBERS' data strip — four cells with large
Fraunces tabular numerals (40+, 3, 5,360, ~60), amber uppercase
labels, and editorial details. Hairline amber rules top + bottom.
Count-up animation reuses the existing .counter-animate GSAP
setup so no new infra. No icons, no boxes, no color highlights —
reads as a magazine sidebar.
- Italic editorial caption in Fraunces with attribution: 'You can't
replicate the diurnal swing in a greenhouse…' — John Harold,
second generation.
3. globals.css: adds the @keyframes harvest-drift + reduced-motion
guard for the new packshot drift (26s alternate, scale 1.04 → 1.10).
Verified on prod 2026-07-06:
- No failed network requests
- Hero altitude ribbon text present
- HarvestEditorial section renders at expected scroll position
- Packshot: complete=true, natural 1280×720, rendered 1232×587
- All 4 data points present with correct labels & details
- Counter values animate 0 → target when section enters viewport
- Mobile (390px) renders cleanly with correct wrap
This commit is contained in:
@@ -152,6 +152,11 @@ const WP_IMAGES = {
|
||||
/** Single yellow ear with silk and dark green bokeh — the product
|
||||
* moment in the editorial "Why" replacement. */
|
||||
earMacro: "https://s3.crispygoat.com/videos/wp-import/images/tuxedo-ear-macro.jpg",
|
||||
/** Packing-shed packshot — dozens of husked cobs piled on the table
|
||||
* the morning of pick. Documentary, not styled; reads like a
|
||||
* magazine harvest spread rather than a styled product photo. Used
|
||||
* as the full-bleed anchor of the "Harvest, by the numbers" section. */
|
||||
piledCobs: "https://s3.crispygoat.com/videos/wp-import/images/corn-field-zane-original.jpg",
|
||||
} as const;
|
||||
|
||||
function reducer(state: State, action: Action): State {
|
||||
@@ -398,6 +403,172 @@ function FounderStrip() {
|
||||
);
|
||||
}
|
||||
|
||||
// ── 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() {
|
||||
return (
|
||||
<section
|
||||
className="relative bg-stone-950 py-24 sm:py-32 overflow-hidden"
|
||||
aria-labelledby="harvest-heading"
|
||||
>
|
||||
{/* Hairline top rule — single amber line so the section reads as
|
||||
a magazine spread opener. */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-x-0 top-0 h-px bg-amber-300/15"
|
||||
/>
|
||||
|
||||
<LayoutContainer>
|
||||
{/* Section header */}
|
||||
<header className="mb-14 sm:mb-20 grid grid-cols-1 lg:grid-cols-12 gap-x-10 gap-y-6 items-end">
|
||||
<div className="lg:col-span-7">
|
||||
<p className="text-[11px] font-semibold uppercase tracking-[0.32em] text-amber-300/85 mb-5">
|
||||
Harvest · 2026
|
||||
</p>
|
||||
<h2
|
||||
id="harvest-heading"
|
||||
className="font-display text-stone-100 text-[clamp(2rem,4.4vw,3.5rem)] leading-[1.04] tracking-[-0.02em]"
|
||||
style={{ textWrap: "balance" }}
|
||||
>
|
||||
The shape of an<br className="hidden sm:block" />
|
||||
<span className="italic text-amber-200/85">Olathe summer.</span>
|
||||
</h2>
|
||||
</div>
|
||||
<div className="lg:col-span-5">
|
||||
<p className="text-stone-400 text-base leading-[1.7] lg:max-w-md lg:ml-auto">
|
||||
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.
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Full-bleed packshot */}
|
||||
<figure className="relative aspect-[16/9] sm:aspect-[21/10] overflow-hidden ring-1 ring-white/[0.06] shadow-[0_40px_120px_-40px_rgba(0,0,0,0.7)]">
|
||||
<Image
|
||||
src={WP_IMAGES.piledCobs}
|
||||
alt="Husked Olathe Sweet cobs piled on the packing shed table the morning of pick"
|
||||
fill
|
||||
sizes="(max-width: 1024px) 100vw, 1280px"
|
||||
quality={92}
|
||||
priority
|
||||
className="object-cover animate-harvest-drift"
|
||||
/>
|
||||
{/* 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. */}
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="absolute inset-x-0 bottom-0 h-1/4 bg-gradient-to-t from-amber-200/10 to-transparent pointer-events-none"
|
||||
/>
|
||||
{/* Provenance plate, lower-left, hairline-bordered, matching
|
||||
the editorial chrome used elsewhere on the page. */}
|
||||
<figcaption className="absolute bottom-4 left-4 sm:bottom-6 sm:left-6 px-3.5 py-2 rounded-full bg-stone-950/65 backdrop-blur-sm text-[10px] font-semibold uppercase tracking-[0.28em] text-amber-100">
|
||||
Harvest morning · August · Olathe, CO
|
||||
</figcaption>
|
||||
{/* Subtle photo credit on the right — like a magazine spread */}
|
||||
<div className="absolute bottom-4 right-4 sm:bottom-6 sm:right-6 hidden sm:block text-[10px] uppercase tracking-[0.28em] text-amber-100/65 font-mono">
|
||||
Frame 014 · zane
|
||||
</div>
|
||||
</figure>
|
||||
|
||||
{/* 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. */}
|
||||
<ol className="mt-16 sm:mt-20 grid grid-cols-2 lg:grid-cols-4 gap-y-12 lg:gap-y-0 lg:gap-x-10 border-t border-b border-amber-300/15">
|
||||
<DataPoint
|
||||
value={40}
|
||||
suffix="+"
|
||||
label="Years growing"
|
||||
detail="Olathe Sweet since 1982"
|
||||
/>
|
||||
<DataPoint
|
||||
value={3}
|
||||
label="Generations"
|
||||
detail="The Harold family"
|
||||
/>
|
||||
<DataPoint
|
||||
value={5360}
|
||||
label="Feet above sea"
|
||||
detail="Uncompahgre Valley"
|
||||
/>
|
||||
<DataPoint
|
||||
value={60}
|
||||
prefix="~"
|
||||
label="Hands at peak"
|
||||
detail="Summer harvest"
|
||||
/>
|
||||
</ol>
|
||||
|
||||
{/* Editor's caption — italic display serif, the editorial
|
||||
footnote that makes the section feel like a feature story. */}
|
||||
<p className="mt-16 sm:mt-20 max-w-2xl mx-auto text-center font-display italic text-stone-300 text-[clamp(1.1rem,1.8vw,1.4rem)] leading-[1.45]">
|
||||
“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.”
|
||||
</p>
|
||||
<p className="mt-5 text-center text-[10px] uppercase tracking-[0.32em] text-stone-500">
|
||||
— John Harold, second generation
|
||||
</p>
|
||||
</LayoutContainer>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// One column of the "by the numbers" strip. The number is a
|
||||
// <span className="counter-animate" data-target="N">0</span> 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 (
|
||||
<li className="relative pt-8 lg:pt-12">
|
||||
{/* Top hairline per column so the strip reads as four magazine
|
||||
sidebar cells, not four feature cards. */}
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="absolute inset-x-0 top-0 h-px bg-amber-300/20 lg:hidden"
|
||||
/>
|
||||
<p className="font-display text-stone-100 text-[clamp(2.75rem,5.6vw,4.25rem)] leading-none tracking-[-0.03em] tabular-nums">
|
||||
{prefix ? (
|
||||
<span className="text-stone-500 mr-0.5">{prefix}</span>
|
||||
) : null}
|
||||
<span className="counter-animate" data-target={value}>
|
||||
0
|
||||
</span>
|
||||
{suffix ? (
|
||||
<span className="text-amber-300 ml-0.5">{suffix}</span>
|
||||
) : null}
|
||||
</p>
|
||||
<p className="mt-4 text-[10px] font-bold uppercase tracking-[0.3em] text-amber-300/85">
|
||||
{label}
|
||||
</p>
|
||||
<p className="mt-2 text-stone-400 text-sm leading-snug max-w-[22ch]">
|
||||
{detail}
|
||||
</p>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
// ── Wholesale link banner ───────────────────────────────────────────
|
||||
function WholesaleBar({ visible }: { visible: boolean }) {
|
||||
if (!visible) return null;
|
||||
@@ -921,6 +1092,8 @@ export default function TuxedoPage() {
|
||||
|
||||
<FounderStrip />
|
||||
|
||||
<HarvestEditorial />
|
||||
|
||||
<StorySection />
|
||||
</main>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user