From c71354d2ae35a3662cf52324b1f856c843e3ac62 Mon Sep 17 00:00:00 2001 From: Nora Date: Mon, 6 Jul 2026 14:40:18 -0600 Subject: [PATCH] tuxedo: align storefront subpages with Field Almanac editorial voice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reskin /tuxedo/contact, /tuxedo/faq, /tuxedo/stops, and /tuxedo/stops/[id] so they match the homepage's editorial magazine language (Fraunces display serif, stone-50/stone-950 alternation, amber rims, hairline rules, tabular numerals). - About page rewritten as inlined editorial sections sharing one WP_IMAGES library with the homepage; the four lazy-loaded Tuxedo about components are removed (Indian River has its own variants). - Contact page now uses a dark hero with heroField half-bleed, a hairline-ruled info strip (address/phone/email), a two-column form section with bottom-border editorial inputs, and a closing Fraunces italic quote. /tuxedo/contact was 404ing in prod because page.tsx was missing — created and removed the duplicated render from layout.tsx. - FAQ page uses a cream hero with Fraunces headline, an editorial bottom-border search, hairline-ruled accordion rows (no card wrappers), and a dark closing CTA with Contact pill. - Stops list uses a dark hero with heroField bleed and a cream hairline-ruled sidebar list: tabular date numerals on the left, city/location middle, mono time + cutoff right, chevron for upcoming rows. - Stop detail uses a dark hero pairing the city/state headline with a giant tabular day stamp, a cream hairline-ruled 4-col info strip (Date/Time/Location/Order by), and the editorial products header. Removed the dual-brand isBlue branching now that Indian River has its own page. - Extracted TUXEDO_IMAGES to src/components/storefront/tuxedo-images.ts so the homepage, about, contact, and stops list share one brand-specific asset library. - /tuxedo/faq/layout.tsx no longer double-renders the page component (was rendering alongside children). Verified: tsc --noEmit clean, eslint clean (only pre-existing homepage warnings), npm run build produces all Tuxedo routes including the now-existing /tuxedo/contact. --- src/app/tuxedo/about/AboutClient.tsx | 649 +++++++++++++--- src/app/tuxedo/contact/ContactClientPage.tsx | 694 +++++++++++++----- src/app/tuxedo/contact/layout.tsx | 3 +- src/app/tuxedo/contact/page.tsx | 5 + src/app/tuxedo/faq/FAQClientPage.tsx | 374 +++++++--- src/app/tuxedo/faq/layout.tsx | 3 +- src/app/tuxedo/page.tsx | 30 +- src/app/tuxedo/stops/TuxedoStopsList.tsx | 422 ++++++++--- src/app/tuxedo/stops/[id]/page.tsx | 365 ++++++--- src/components/about/CTASection.tsx | 52 -- src/components/about/ContactSection.tsx | 89 --- .../about/FamilyTimelineSection.tsx | 115 --- src/components/about/MissionSection.tsx | 76 -- src/components/storefront/tuxedo-images.ts | 35 + 14 files changed, 1959 insertions(+), 953 deletions(-) create mode 100644 src/app/tuxedo/contact/page.tsx delete mode 100644 src/components/about/CTASection.tsx delete mode 100644 src/components/about/ContactSection.tsx delete mode 100644 src/components/about/FamilyTimelineSection.tsx delete mode 100644 src/components/about/MissionSection.tsx create mode 100644 src/components/storefront/tuxedo-images.ts diff --git a/src/app/tuxedo/about/AboutClient.tsx b/src/app/tuxedo/about/AboutClient.tsx index 3f5f4be..6db40fb 100644 --- a/src/app/tuxedo/about/AboutClient.tsx +++ b/src/app/tuxedo/about/AboutClient.tsx @@ -1,15 +1,12 @@ "use client"; -import { Suspense, lazy, useState } from "react"; import Image from "next/image"; -import { m as motion } from "framer-motion"; +import Link from "next/link"; +import LayoutContainer from "@/components/layout/LayoutContainer"; import StorefrontHeader from "@/components/storefront/StorefrontHeader"; import StorefrontFooter from "@/components/storefront/StorefrontFooter"; - -const MissionSection = lazy(() => import("@/components/about/MissionSection")); -const FamilyTimelineSection = lazy(() => import("@/components/about/FamilyTimelineSection")); -const ContactSection = lazy(() => import("@/components/about/ContactSection")); -const CTASection = lazy(() => import("@/components/about/CTASection")); +import { TUXEDO_IMAGES as WP_IMAGES } from "@/components/storefront/tuxedo-images"; +import { FadeOnScroll } from "@/components/ui/ScrollAnimations"; interface AboutClientProps { logoUrl: string | null; @@ -28,7 +25,6 @@ interface AboutClientProps { export default function AboutClient({ logoUrl, logoUrlDark, - olatheSweetLogoUrlDark, aboutHeadline, aboutSubheadline, showWholesaleLink, @@ -51,101 +47,25 @@ export default function AboutClient({ />
- {/* Hero Section */} -
- {/* Decorative elements */} -
-
-
-
+ - {/* Pattern overlay */} -
+ -
-
- {/* Text content */} -
- - Our Heritage - + - - {aboutHeadline ?? "Three Generations of Sweet Corn Excellence"} - + - - {aboutSubheadline ?? "Over 40 years of growing and shipping Olathe Sweet Sweet Corn from our family to yours."} - + - -
+ - {/* Logo */} - -
-
- Olathe Sweet -
- -
-
- - {/* Bottom transition */} -
-
- - {/* Mission Section */} - }> - - - - {/* Family Timeline */} - }> - - - - {/* Contact Section */} - }> - - - - {/* CTA Section */} - }> - - +
; +// ── HERO — dark editorial opener ──────────────────────────────────── +// Same architectural language as the homepage FounderStrip, but with +// the portrait as a full anchor instead of a supporting figure, and +// the headline sitting in the same Fraunces display register as the +// homepage "Three generations of sweet corn excellence" copy. +function HeroSection({ + headline, + subheadline, +}: { + headline: string; + subheadline: string; +}) { + return ( +
+ {/* A single amber rim along the bottom — same hinge used + throughout the homepage between cream and dark sections. */} +
+ ); } + +// ── THE CORN — split editorial moment ─────────────────────────────── +// Mirrors the homepage's "The Corn" section: copy left (5/12), +// single macro photograph right (7/12). One paragraph. One image. +// The feature-card grid read as a templated list of icons; a single +// honest photograph communicates everything those tiles couldn't. +function TheCornSection() { + 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 */} +
+ +
+ A single ear of Olathe Sweet corn, partially husked, silk still attached, against a field of dark green tassels + + +
+
+ +
+ ); +} + +// ── PULL-QUOTE BAND — full-bleed editorial interlude ─────────────── +// Same composition the homepage uses: pre-storm field behind, single +// Fraunces italic quote floating in the lower third. No chrome. The +// image does the talking. +function PullQuoteBand() { + return ( +
+ Pre-storm corn rows receding to the mountains at dusk +
+
+ ); +} + +// ── FOUNDER STRIP — narrative anchor for "The People" section ────── +// Cream background, magazine spread. Eyebrow + Fraunces headline on +// the left, two paragraphs of founder story on the right. Below it +// the FamilyTimeline component renders the four family-member entries +// in the same hairline-ruled editorial register. +function FounderStrip() { + return ( +
+ +
+
+ +

+ The People Behind the Corn +

+
+ +

+ The Harold family, +
{" "} + on the same row. +

+
+
+
+ +

+ John Harold began selling Olathe’s sweet corn directly + from pickups in the 1970s. The Olathe Sweet parent line was + rescued from a retiring farmer who believed his valley could + grow sugar in a husk. Three generations later, the name on + the cooler is still the name on the deed. +

+
+
+
+
+
+ ); +} + +// ── FAMILY TIMELINE — magazine "sidebar" register ────────────────── +// Hairline-ruled four-column grid. No cards, no avatars in gradient +// boxes. The homepage's HarvestEditorial data strip is the structural +// model; the era + name + role + bio columns are the editorial content. +// Order matters: founder first, then by generation. +const FAMILY = [ + { + initials: "JH", + name: "John Harold", + role: "Founder", + era: "1970s", + bio: "Started selling Olathe's sweet corn directly from pickups. Built Tuxedo Corn into a family operation rooted in the Uncompahgre Valley.", + }, + { + initials: "DH", + name: "David Harold", + role: "Second Generation", + era: "Present", + bio: "Leads sustainability across Harold farms — drip irrigation, Fair Food Program, reduced chemical inputs. Keeps the soil honest.", + }, + { + initials: "JW", + name: "John William Harold", + role: "Mexico Operations", + era: "1990s–Present", + bio: "Three decades in Guaymas, Sonora, growing sweet corn, onions, wheat, and vegetables. Extends the Harold tradition across borders.", + }, + { + initials: "JH", + name: "Joseph Harold", + role: "Third Generation", + era: "Present", + bio: "Works the corn shed in summer, handles photography and beverages. Carrying the family tradition into the next generation.", + }, +]; + +function FamilyTimeline() { + return ( +
+ + {/* Hairline above the strip — the magazine sidebar opening. */} +
+ ); +} + +// ── VISIT THE FARM — dark editorial address block ────────────────── +// Replaces the three icon-card contact layout. Stone-950 panel with a +// magazine "by the numbers" data strip + a single hairline-ruled +// address line. No SVG icons in colored boxes; the typography carries +// the section. +function VisitTheFarm({ address }: { address: string }) { + return ( +
+ {/* Single amber rim at the top — same hinge used on the dark + homepage sections. */} +
+ ); +} + +// One column of the Visit-the-Farm strip. Same hairline rules and +// label style as HarvestEditorial's DataPoint on the homepage, with +// `detail` either a string or an array of lines (for the Hours column). +function FarmPoint({ + label, + detail, + mono = false, +}: { + label: string; + detail: string | string[]; + mono?: boolean; +}) { + const lines = Array.isArray(detail) ? detail : [detail]; + return ( +
  • +

    + {label} +

    + {lines.map((line, i) => ( +

    + {line} +

    + ))} +
  • + ); +} + +// ── CTA — closing editorial pair ─────────────────────────────────── +// Stone-50 section with the dark gradient emerald→amber pill button +// used on the homepage StorySection "Read Our Story" CTA, plus a +// secondary "Shop Products" outline. Same voice as the homepage's +// closing beat: "Find a stop / Shop products". +function CTASection() { + return ( +
    + +
    + +

    + This Week +

    +
    + + +

    + Find a stop near you, +
    {" "} + or have it shipped. +

    +
    + + +
    + + + +

    + Preorder for pickup at a stop on the summer route, or have + cooler boxes shipped directly to your door after the season. +

    +
    + + +
    + + Find a Stop + + + + Shop Products + + +
    +
    +
    + +
    + ); +} \ No newline at end of file diff --git a/src/app/tuxedo/contact/ContactClientPage.tsx b/src/app/tuxedo/contact/ContactClientPage.tsx index addb4c0..08c2c4b 100644 --- a/src/app/tuxedo/contact/ContactClientPage.tsx +++ b/src/app/tuxedo/contact/ContactClientPage.tsx @@ -1,11 +1,13 @@ "use client"; import { useEffect, useState } from "react"; +import Image from "next/image"; import Link from "next/link"; +import LayoutContainer from "@/components/layout/LayoutContainer"; import StorefrontHeader from "@/components/storefront/StorefrontHeader"; import StorefrontFooter from "@/components/storefront/StorefrontFooter"; -import LayoutContainer from "@/components/layout/LayoutContainer"; -import { supabase } from "@/lib/supabase"; +import { TUXEDO_IMAGES } from "@/components/storefront/tuxedo-images"; +import { FadeOnScroll } from "@/components/ui/ScrollAnimations"; type BrandSettings = { invoice_business_name: string | null; @@ -22,6 +24,11 @@ type FormState = { message: string; }; +const FALLBACK_ADDRESS = "59751 David Road, Olathe, CO 81425"; +const FALLBACK_EMAIL = "orders@tuxedocorn.com"; +const SHIPPING_PHONE = "970-323-5631"; +const OFFICE_PHONE = "970-323-6874"; + export default function TuxedoContactPage() { const [brandSettings, setBrandSettings] = useState(null); const [submitted, setSubmitted] = useState(false); @@ -30,12 +37,16 @@ export default function TuxedoContactPage() { useEffect(() => { const TUXEDO_BRAND_ID = "64294306-5f42-463d-a5e8-2ad6c81a96de"; - supabase - .from("wholesale_settings") - .select("invoice_business_name, invoice_business_address, invoice_business_phone, invoice_business_email, invoice_business_website") - .eq("brand_id", TUXEDO_BRAND_ID) - .single() - .then(({ data }) => setBrandSettings((data as unknown as BrandSettings) ?? null)); + import("@/lib/supabase").then(({ supabase }) => { + supabase + .from("wholesale_settings") + .select( + "invoice_business_name, invoice_business_address, invoice_business_phone, invoice_business_email, invoice_business_website" + ) + .eq("brand_id", TUXEDO_BRAND_ID) + .single() + .then(({ data }) => setBrandSettings((data as unknown as BrandSettings) ?? null)); + }); }, []); function handleSubmit(e: React.FormEvent) { @@ -47,182 +58,531 @@ export default function TuxedoContactPage() { }, 600); } + const address = brandSettings?.invoice_business_address ?? FALLBACK_ADDRESS; + const email = brandSettings?.invoice_business_email ?? FALLBACK_EMAIL; + return (
    -
    - -
    +
    + - {/* Page header */} -
    -

    Get in Touch

    -

    - Contact Us -

    -

    - Questions about corn, stops, or wholesale accounts — we are happy to help. -

    -
    + {/* Contact info strip — hairline-ruled four-column data grid, + same magazine-sidebar register as the About page's + "Visit the Farm" section. */} + - {/* Contact info grid */} -
    -
    -
    - - - - -
    -

    Farm Address

    -

    - {brandSettings?.invoice_business_address ?? "59751 David Road, Olathe, CO 81425"} -

    -
    - - -
    - - {/* Contact form */} -
    -
    -

    Send a Message

    -

    - Get in Touch -

    -
    -
    - {submitted ? ( -
    -
    - - - -
    -

    Message received.

    -

    We will be in touch within 1-2 business days.

    - -
    - ) : ( -
    -
    + Tell us what you’re after, +
    {" "} + we’ll write back. + + + +
    + + +

    + Most messages get a reply within one business day. During + harvest weeks the office is open early — call if your + order is time-sensitive and we’ll get you a real + answer, not a form letter. +

    +
    + + {/* Hours caption, hairline-ruled inline. */} + +
    - - setForm({ ...form, name: e.target.value })} - className="w-full rounded-xl border border-stone-200 bg-white px-5 py-4 text-sm text-stone-900 placeholder-stone-400 outline-none focus:border-stone-900 focus:ring-1 focus:ring-stone-900 transition-colors" - placeholder="Jane Smith" - /> +
    + Weekdays +
    +
    + 7A – 6P +
    - - setForm({ ...form, email: e.target.value })} - className="w-full rounded-xl border border-stone-200 bg-white px-5 py-4 text-sm text-stone-900 placeholder-stone-400 outline-none focus:border-stone-900 focus:ring-1 focus:ring-stone-900 transition-colors" - placeholder="jane@example.com" - /> +
    + Weekends +
    +
    + 8A – 2P +
    -
    -
    - - -
    -
    - -