Pulls the saturated-yellow WP-import JPGs down a half-step in the brightness hierarchy so the white type stays dominant and the photos read as editorial, not product photography.
- globals.css: new filter utilities (.filter-editorial, .filter-editorial-shell with cool/amber split-tone, .filter-editorial-strong, .filter-editorial-vignette) + reduced-motion reset
- TuxedoVideoHero: grade the Ken Burns backdrop image
- tuxedo/page.tsx: apply grades to PullQuoteBand, TheCorn ear macro, FounderStrip (also swap to the retouched founder image from b90f0fa), and HarvestEditorial packshot; bump founder quality 88 -> 92
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
Three issues caused the 'images don't work, video doesn't play' report on
https://water.tuxedocorn.com/tuxedo:
1. (ROOT CAUSE) Next.js standalone build was missing .next/standalone/public/.
The deploy workflow copied .next/static to .next/standalone/.next/static
but never mirrored public/. Without that, the image optimizer returns
400 'url parameter is not allowed' on every local-relative URL — so the
brand logo (and favicon, etc.) went blank. Symptom: HTTP 400 on
/_next/image?url=%2Fbrand-logos%2F64294306-...%2Flogo.png.
Fixed by:
- cp -r public/. .next/standalone/public/ on the running server (live)
- updating .gitea/workflows/deploy.yml to mirror public/ on every deploy
- adding scripts/regression-brand-logos.mjs so this can't regress silently
2. Storm-field and founder-portrait images rendered as 'loading=lazy' and
appeared blank above the fold. Marked priority so they preload.
3. Pre-existing build error: Next.js 16.2 RSC bundler treats
'export type { FieldSession }' re-exports through server-action modules
as runtime exports ('Export FieldSession doesn't exist in target
module'). Removed the re-exports in water-log/auth.ts and field.ts;
field.ts now imports FieldSession directly from @/lib/water-log/session-server.
Verified live (post-fix) on 2026-07-06:
- regression-brand-logos.mjs: HTTP 200 image/png, header+footer logos
render with naturalWidth=160
- diag-home-only.mjs: all 6 images complete=true, natural sizes match
source (storm 1440x1080, founder 604x340, ear 692x476, etc.)
- verify-hero-video.mjs: readyState=4, paused=false, currentTime
advancing 2.86s -> 6.87s over 4s (=30fps; the prior 'video doesn't
play' was likely user perception — the poster+video look near-
identical, and 'net::ERR_ABORTED' on the MP4 is normal Chrome
behavior for parallel byte-range requests)
Swap the templated 8-tile Why-grid for three image-driven editorial
moments using photos pulled from the tuxedocorn.com WordPress library:
- PullQuoteBand: full-bleed pre-storm field with single Fraunces quote
- TheCorn: single paragraph + ear macro, replaces feature grid
- FounderStrip: John Harold portrait + family narrative
Hero poster switched from the tight cob macro to the wide irrigation
row with Colorado sky and cumulus clouds. The video underneath stays.
Editorial copy tightened. Display type moved to Fraunces for headlines
and timeline markers to give the page a real voice instead of
default sans throughout. Tone: "you can taste the altitude."
Hero video at /videos/tuxedo-hero.mp4 was hardcoded and 404ing, leaving
the page with no background.
* Add brand_settings.hero_video_url (migration 0099) so each brand
can point its hero at any reachable mp4/webm without code changes.
Saves via a separate UPDATE so the existing 32-param
upsert_brand_settings RPC signature stays untouched.
* TuxedoVideoHero now accepts videoUrl + posterUrl. Poster renders
underneath the <video> element via next/image so the hero is
complete even without a video. <video> flips a data-failed attribute
on error/stall so the poster takes over without DOM churn.
Ambient float animations slowed 950ms → 7s/8s.
* Why section: dropped emerald/amber kaleidoscope, unified accent.
Footer is now a provenance strip (Family-Grown · 5,360 ft · Est 1982).
* Story section: removed giant decorative 'SINCE' parallax text and
the duplicate stats counter (already in hero). Replaced with a
1982/2003/Today timeline.
The legacy lib/supabase.ts shim returns { data: null } for every query,
which silently broke the tuxedo and indian-river-direct storefronts:
they displayed 'No stops on the calendar just yet' regardless of the
real data in Postgres.
Added src/actions/storefront.ts as a new "use server" module with
typed actions that hit the shared pg pool directly:
- getStorefrontData(slug) — brand + stops + products in one call
- getStorefrontStopBySlug(slug) — single stop + its products
- getStorefrontWholesaleSettings(slug) — public invoice/business info
Updated to use these actions instead of the shim:
- src/app/tuxedo/page.tsx
- src/app/tuxedo/stops/[slug]/page.tsx
- src/app/tuxedo/contact/ContactClientPage.tsx
- src/app/indian-river-direct/page.tsx
- src/app/indian-river-direct/stops/[slug]/page.tsx
The shim file itself is still imported by other pages and API routes
that will be migrated in follow-up steps.
- Add MinIO/S3-compatible storage client (src/lib/storage.ts) with uploadObject,
deleteObject, presigned URL helpers, and BUCKETS constant
- Wire product images, brand logos, and water log photos to MinIO via the
new storage client
- Migrate forgot-password to Neon Auth (remove Supabase /auth/v1/recover call)
- Migrate send-scheduled cron to direct Postgres + Resend (remove Supabase Edge
Function proxy)
- Add logoUrl to email types (OrderReceipt, Welcome, PasswordReset) and pass
brand_settings.logo_url from all call sites
- Update email templates to use dynamic logoUrl instead of hardcoded Supabase
bucket URLs
- Remove hardcoded Supabase URLs from TuxedoVideoHero, TuxedoAboutPage,
TimeTrackingFieldClient; use brand_settings props + local public/ fallback
- Download brand logos (3) and tuxedo-hero.mp4 (36MB) from Supabase bucket to
public/ for local development
- Add MinIO env vars to .env.example (endpoint, access key, secret, buckets)
- Fix TimeTrackingFieldClient to destructure logoUrl and brandAccent props
- Fix admin/users.ts logoUrl type (null → undefined for optional string)
- Remove stale sb- cookie from wholesale-auth
- Migrate tuxedo/about page to remove supabase import and use pool query for
wholesale_settings lookup