Skip to content

Commit f46366e

Browse files
Ship Process flip cards, hourglass video, ambient cursor, and layout polish.
Scroll-scrub What-you-need tiles finish flip at mid-viewport; hourglass loops on the homepage Problem section; Resonant-style trailing cursor in sand glow; Process Big Picture centered without hero rule; compliance source zip stays under Cloudflare Pages size limits.
1 parent bc7b42b commit f46366e

5 files changed

Lines changed: 541 additions & 25 deletions

File tree

app/globals.css

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1939,3 +1939,208 @@ body { background: var(--bg); color: var(--text); }
19391939
display: none;
19401940
}
19411941
}
1942+
1943+
/* ── Ambient trailing cursor (Resonant pattern)
1944+
Sand glow: hourglass bottom — yellow-orange, not flat copper ── */
1945+
.ambient-cursor {
1946+
--cursor-accent: #ffc14a;
1947+
--cursor-glow: #ffb020;
1948+
position: fixed;
1949+
top: 0;
1950+
left: 0;
1951+
z-index: 9999;
1952+
display: none;
1953+
width: 6px;
1954+
height: 6px;
1955+
border-radius: 50%;
1956+
background: var(--cursor-accent);
1957+
box-shadow:
1958+
0 0 6px 1px rgba(255, 193, 74, 0.95),
1959+
0 0 14px 4px rgba(255, 176, 32, 0.55),
1960+
0 0 28px 8px rgba(255, 160, 40, 0.28);
1961+
opacity: 0;
1962+
pointer-events: none;
1963+
will-change: transform;
1964+
transition: opacity 180ms ease;
1965+
}
1966+
1967+
.ambient-cursor::after {
1968+
position: absolute;
1969+
top: 50%;
1970+
left: 50%;
1971+
width: 22px;
1972+
height: 22px;
1973+
border-radius: 50%;
1974+
background: radial-gradient(
1975+
circle,
1976+
rgba(255, 193, 74, 0.55) 0%,
1977+
rgba(255, 176, 32, 0.18) 45%,
1978+
transparent 70%
1979+
);
1980+
content: "";
1981+
opacity: 0.85;
1982+
transform: translate(-50%, -50%);
1983+
transition: width 160ms ease, height 160ms ease, opacity 160ms ease;
1984+
}
1985+
1986+
.ambient-cursor svg {
1987+
position: absolute;
1988+
top: 50%;
1989+
left: 50%;
1990+
width: 36px;
1991+
height: 36px;
1992+
overflow: visible;
1993+
transform: translate(-50%, -50%) rotate(-90deg);
1994+
}
1995+
1996+
.ambient-cursor circle {
1997+
fill: none;
1998+
stroke: var(--cursor-accent);
1999+
stroke-width: 1;
2000+
stroke-dasharray: 100;
2001+
stroke-dashoffset: 100;
2002+
opacity: 0.9;
2003+
filter: drop-shadow(0 0 3px rgba(255, 193, 74, 0.85));
2004+
transition: stroke-dashoffset 120ms linear;
2005+
}
2006+
2007+
.ambient-cursor.is-visible {
2008+
opacity: 1;
2009+
}
2010+
2011+
.ambient-cursor.is-interactive::after {
2012+
width: 34px;
2013+
height: 34px;
2014+
opacity: 0.95;
2015+
}
2016+
2017+
.ambient-cursor.is-interactive circle {
2018+
opacity: 1;
2019+
}
2020+
2021+
@media (hover: hover) and (pointer: fine) and (min-width: 992px) {
2022+
.ambient-cursor {
2023+
display: block;
2024+
}
2025+
}
2026+
2027+
@media (prefers-reduced-motion: reduce) {
2028+
.ambient-cursor {
2029+
display: none !important;
2030+
}
2031+
}
2032+
2033+
/* ── Process: What you need — scroll-scrub flip cards (rotateX) ── */
2034+
.need-flip-list {
2035+
list-style: none;
2036+
margin: 0;
2037+
padding: 0;
2038+
}
2039+
2040+
.need-flip {
2041+
position: relative;
2042+
}
2043+
2044+
/* Grid stack = height is tallest face (no clip on long backs) */
2045+
.need-flip-inner {
2046+
display: grid;
2047+
width: 100%;
2048+
transform-style: preserve-3d;
2049+
transition: none;
2050+
transform: rotateX(0deg);
2051+
will-change: transform;
2052+
}
2053+
2054+
.need-flip-face {
2055+
grid-area: 1 / 1;
2056+
box-sizing: border-box;
2057+
backface-visibility: hidden;
2058+
-webkit-backface-visibility: hidden;
2059+
border: 1px solid var(--gear-border);
2060+
background: var(--bg-panel);
2061+
min-height: 7.25rem;
2062+
}
2063+
2064+
/* Front = big keywords, centered */
2065+
.need-flip-face--front {
2066+
transform: rotateX(0deg);
2067+
display: flex;
2068+
align-items: center;
2069+
justify-content: center;
2070+
padding: 1.5rem 1.5rem;
2071+
}
2072+
2073+
.need-flip-keywords {
2074+
display: flex;
2075+
flex-direction: column;
2076+
align-items: center;
2077+
justify-content: center;
2078+
text-align: center;
2079+
gap: 0.2rem;
2080+
width: 100%;
2081+
}
2082+
2083+
.need-kw {
2084+
font-family: var(--font-playfair), Georgia, serif;
2085+
color: var(--cream);
2086+
line-height: 1.1;
2087+
letter-spacing: 0.02em;
2088+
}
2089+
2090+
.need-kw--base {
2091+
font-size: clamp(1.05rem, 2.2vw, 1.35rem);
2092+
font-weight: 600;
2093+
color: var(--text-muted);
2094+
}
2095+
2096+
/* ~2× the base line — “Google Chrome”, “Voice notes”, etc. */
2097+
.need-kw--lg {
2098+
font-size: clamp(1.9rem, 4.1vw, 2.65rem);
2099+
font-weight: 700;
2100+
color: var(--cream);
2101+
}
2102+
2103+
/* Card 6 only — single phrase, still larger */
2104+
.need-kw--xl {
2105+
font-size: clamp(2.15rem, 4.6vw, 3rem);
2106+
font-weight: 700;
2107+
color: var(--cream);
2108+
}
2109+
2110+
/* Back = number + title + detail (original card look) */
2111+
.need-flip-face--back {
2112+
transform: rotateX(180deg);
2113+
display: flex;
2114+
align-items: stretch;
2115+
padding: 1.25rem 1.35rem;
2116+
}
2117+
2118+
.need-flip-back-row {
2119+
display: flex;
2120+
align-items: flex-start;
2121+
gap: 1.25rem;
2122+
width: 100%;
2123+
}
2124+
2125+
.need-flip-num {
2126+
width: 2rem;
2127+
height: 2rem;
2128+
border-radius: 999px;
2129+
border: 1px solid rgba(168, 120, 79, 0.4);
2130+
display: flex;
2131+
align-items: center;
2132+
justify-content: center;
2133+
flex-shrink: 0;
2134+
margin-top: 0.125rem;
2135+
}
2136+
2137+
.need-flip-back-copy {
2138+
min-width: 0;
2139+
flex: 1;
2140+
}
2141+
2142+
@media (prefers-reduced-motion: reduce) {
2143+
.need-flip-inner {
2144+
transform: rotateX(180deg) !important;
2145+
}
2146+
}

app/the-process/page.tsx

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import Link from "next/link";
22
import { Gear, GearDivider, PipeLine, PipeNode } from "@/components/SteampunkElements";
3+
import WhatYouNeedFlipCards, {
4+
type NeedCard,
5+
} from "@/components/WhatYouNeedFlipCards";
36
import { pageMetadata } from "@/lib/seo";
47

58
export const metadata = pageMetadata({
@@ -165,13 +168,59 @@ const optionalAi = {
165168
],
166169
};
167170

168-
const whatYouNeed = [
169-
{ item: "A laptop and Google Chrome", detail: "The program installs into Chrome and lives on one bookmarked page you always return to. It runs right there — offline, no account, no cloud." },
170-
{ item: "A phone for voice notes", detail: "Walk each bar and speak it in order — English or Spanish — bar/barra, well/pozo, row/fila, cooler, wine — naming the bottles as you go." },
171-
{ item: "A first bar walk", detail: "That one saved voice note is the seed the built-in parser reads to build your whole bar. Upload or paste via Employee communications." },
172-
{ item: "Your invoices and POS report", detail: "Type or paste the invoice numbers and pull the POS export once the first real cycle is ready." },
173-
{ item: "A careful review", detail: "The first map gets checked and approved before the program becomes your home base." },
174-
{ item: "Optional: your own AI key", detail: "Not required for anything. Add it only if you want invoice photos read from your phone instead of typing." },
171+
const whatYouNeed: NeedCard[] = [
172+
{
173+
item: "A laptop and Google Chrome",
174+
detail:
175+
"The program installs into Chrome and lives on one bookmarked page you always return to. It runs right there — offline, no account, no cloud.",
176+
front: [
177+
{ text: "Laptop", size: "base" },
178+
{ text: "Google Chrome", size: "lg" },
179+
],
180+
},
181+
{
182+
item: "A phone for voice notes",
183+
detail:
184+
"Walk each bar and speak it in order — English or Spanish — bar/barra, well/pozo, row/fila, cooler, wine — naming the bottles as you go.",
185+
front: [
186+
{ text: "Phone", size: "base" },
187+
{ text: "Voice notes", size: "lg" },
188+
],
189+
},
190+
{
191+
item: "A first bar walk",
192+
detail:
193+
"That one saved voice note is the seed the built-in parser reads to build your whole bar. Upload or paste via Employee communications.",
194+
front: [
195+
{ text: "First", size: "base" },
196+
{ text: "Bar walk", size: "lg" },
197+
],
198+
},
199+
{
200+
item: "Your invoices and POS report",
201+
detail:
202+
"Type or paste the invoice numbers and pull the POS export once the first real cycle is ready.",
203+
front: [
204+
{ text: "Invoices", size: "base" },
205+
{ text: "POS report", size: "lg" },
206+
],
207+
},
208+
{
209+
item: "A careful review",
210+
detail:
211+
"The first map gets checked and approved before the program becomes your home base.",
212+
front: [
213+
{ text: "Careful", size: "base" },
214+
{ text: "Review", size: "lg" },
215+
],
216+
},
217+
{
218+
item: "Optional: your own AI key",
219+
detail:
220+
"Not required for anything. Add it only if you want invoice photos read from your phone instead of typing.",
221+
// Front: only the big phrase — no description on this face
222+
front: [{ text: "Your own AI", size: "xl" }],
223+
},
175224
];
176225

177226
const weeklyRhythm = [
@@ -202,7 +251,7 @@ export default function TheProcess() {
202251
<main className="min-h-screen">
203252

204253
{/* HERO */}
205-
<section className="relative overflow-hidden border-b border-gear-border">
254+
<section className="relative overflow-hidden">
206255
<div className="absolute right-[-60px] top-[-40px] text-copper pointer-events-none">
207256
<Gear size={240} className="gear-spin opacity-20" />
208257
</div>
@@ -248,21 +297,21 @@ export default function TheProcess() {
248297
{/* PRODUCT VIEW */}
249298
<section id="system-report" className="border-b border-gear-border bg-bg-warm">
250299
<div className="max-w-6xl mx-auto px-6 py-16 md:py-24">
251-
<div className="max-w-3xl mb-12">
300+
<div className="max-w-3xl mx-auto mb-12 text-center">
252301
<p className="text-[11px] tracking-[0.3em] uppercase text-text-light mb-4">
253302
The Big Picture
254303
</p>
255304
<h2 className="font-serif text-4xl md:text-5xl text-cream mb-5">
256305
What you are actually using
257306
</h2>
258-
<p className="text-text-muted leading-relaxed text-lg">
307+
<p className="text-text-muted leading-relaxed text-lg mx-auto">
259308
One calm control panel that runs on your laptop. Starts simple for
260309
the average bartender — walk or talk your inventory in, easy and
261310
fast. Grows into the most encompassing industrial-strength bar
262-
inventory system available — far better than the others, and we're
311+
inventory system available — far better than the others, and we&rsquo;re
263312
getting better every day. Built for the average bartender but it
264313
has all the bells and whistles, including built-in scales and
265-
pinpoint measurements. Scalable — it's all there for you.
314+
pinpoint measurements. Scalable — it&rsquo;s all there for you.
266315
</p>
267316
</div>
268317

@@ -344,19 +393,7 @@ export default function TheProcess() {
344393
gathered into one guided flow.
345394
</p>
346395
</div>
347-
<ul className="flex flex-col gap-5">
348-
{whatYouNeed.map((item, i) => (
349-
<li key={item.item} className="panel p-5 flex items-start gap-5">
350-
<div className="w-8 h-8 rounded-full border border-copper/40 flex items-center justify-center flex-shrink-0 mt-0.5">
351-
<span className="font-serif text-copper text-sm">{i + 1}</span>
352-
</div>
353-
<div>
354-
<p className="text-cream text-sm font-semibold mb-1">{item.item}</p>
355-
<p className="text-text-muted text-sm leading-relaxed">{item.detail}</p>
356-
</div>
357-
</li>
358-
))}
359-
</ul>
396+
<WhatYouNeedFlipCards items={whatYouNeed} />
360397
</div>
361398
</section>
362399

0 commit comments

Comments
 (0)