moonlantern1 commited on
Commit
e3df0d9
·
1 Parent(s): 56617fd

Polish classic matcha landing art

Browse files
src/app/c/[slug]/LandingClient.tsx CHANGED
@@ -31,7 +31,7 @@ export function LandingClient({ slug, tableId, campaign }: Props) {
31
 
32
  return (
33
  <MobileShell>
34
- <main className="relative flex min-h-dvh flex-col overflow-hidden bg-[#EDE5D2] text-[#1B1A14]">
35
  <div
36
  className="pointer-events-none absolute inset-0"
37
  style={{
@@ -40,8 +40,8 @@ export function LandingClient({ slug, tableId, campaign }: Props) {
40
  }}
41
  />
42
 
43
- <section className="relative flex flex-1 flex-col items-center px-7 pt-20 text-center">
44
- <div className="mb-8 flex items-center justify-center gap-3 font-serif text-[15px] italic text-[#5A523F]">
45
  <span className="h-px w-6 bg-[#5A523F]/55" aria-hidden />
46
  {campaign.restaurantName}
47
  <span className="h-px w-6 bg-[#5A523F]/55" aria-hidden />
@@ -49,17 +49,17 @@ export function LandingClient({ slug, tableId, campaign }: Props) {
49
 
50
  <MatchaCircle />
51
 
52
- <h1 className="text-display mt-9 text-[38px] text-[#1B1A14]">
53
  Free matcha,
54
  <br />
55
  <em className="text-[#4A5C32]">on the house</em>
56
  </h1>
57
 
58
- <p className="mx-auto mt-5 max-w-[294px] font-serif text-[14.5px] leading-[1.55] text-[#5A6E3F]">
59
  Give us a quick guided video food review, get a free matcha.
60
  </p>
61
 
62
- <label className="mx-auto mt-6 flex w-full max-w-[324px] cursor-pointer items-start gap-3 rounded-[14px] border border-[#78694B]/20 bg-[#F5EDD9] px-4 py-3.5 text-left shadow-[inset_0_1px_0_rgba(255,255,255,0.6)]">
63
  <input
64
  type="checkbox"
65
  checked={consentAccepted}
@@ -72,7 +72,7 @@ export function LandingClient({ slug, tableId, campaign }: Props) {
72
  </label>
73
  </section>
74
 
75
- <footer className="relative px-7 pb-[30px] pt-3">
76
  <Button
77
  onClick={handleStart}
78
  disabled={!consentAccepted}
 
31
 
32
  return (
33
  <MobileShell>
34
+ <main className="relative flex h-dvh max-h-dvh flex-col overflow-hidden bg-[#EDE5D2] text-[#1B1A14]">
35
  <div
36
  className="pointer-events-none absolute inset-0"
37
  style={{
 
40
  }}
41
  />
42
 
43
+ <section className="relative flex min-h-0 flex-1 flex-col items-center px-7 pt-10 text-center [@media(max-height:720px)]:pt-6">
44
+ <div className="mb-5 flex items-center justify-center gap-3 font-serif text-[15px] italic text-[#5A523F] [@media(max-height:720px)]:mb-3">
45
  <span className="h-px w-6 bg-[#5A523F]/55" aria-hidden />
46
  {campaign.restaurantName}
47
  <span className="h-px w-6 bg-[#5A523F]/55" aria-hidden />
 
49
 
50
  <MatchaCircle />
51
 
52
+ <h1 className="text-display mt-5 text-[36px] text-[#1B1A14] [@media(max-height:720px)]:mt-3 [@media(max-height:720px)]:text-[32px]">
53
  Free matcha,
54
  <br />
55
  <em className="text-[#4A5C32]">on the house</em>
56
  </h1>
57
 
58
+ <p className="mx-auto mt-3 max-w-[294px] font-serif text-[14.5px] leading-[1.55] text-[#5A6E3F] [@media(max-height:720px)]:mt-2">
59
  Give us a quick guided video food review, get a free matcha.
60
  </p>
61
 
62
+ <label className="mx-auto mt-4 flex w-full max-w-[324px] cursor-pointer items-start gap-3 rounded-[14px] border border-[#78694B]/20 bg-[#F5EDD9] px-4 py-3.5 text-left shadow-[inset_0_1px_0_rgba(255,255,255,0.6)] [@media(max-height:720px)]:mt-3 [@media(max-height:720px)]:py-3">
63
  <input
64
  type="checkbox"
65
  checked={consentAccepted}
 
72
  </label>
73
  </section>
74
 
75
+ <footer className="relative px-7 pb-5 pt-2 [@media(max-height:720px)]:pb-4">
76
  <Button
77
  onClick={handleStart}
78
  disabled={!consentAccepted}
src/components/MatchaCircle.tsx CHANGED
@@ -24,115 +24,213 @@ export function MatchaCircle() {
24
  return (
25
  <div
26
  aria-hidden
27
- className="mx-auto my-4"
28
  style={{
29
  width: 210,
30
- height: 199.5,
31
- filter: 'drop-shadow(0 8px 14px rgba(50,60,25,0.18))',
 
 
 
32
  }}
33
  >
34
- <svg width="210" height="199.5" viewBox="0 0 200 190" className="block">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  <defs>
36
- <linearGradient id="matchaSurface" x1="0%" y1="0%" x2="0%" y2="100%">
37
- <stop offset="0%" stopColor="#7A9242" />
38
- <stop offset="40%" stopColor="#9BB75A" />
39
- <stop offset="100%" stopColor="#82A047" />
40
- </linearGradient>
41
- <radialGradient id="surfaceGlow" cx="42%" cy="35%" r="65%">
42
- <stop offset="0%" stopColor="rgba(190,215,130,0.55)" />
43
- <stop offset="100%" stopColor="rgba(190,215,130,0)" />
44
- </radialGradient>
45
- <linearGradient id="ceramicBody" x1="0%" y1="0%" x2="100%" y2="0%">
46
- <stop offset="0%" stopColor="#F0E5C8" />
47
- <stop offset="20%" stopColor="#EADDB8" />
48
- <stop offset="55%" stopColor="#D9C89C" />
49
- <stop offset="85%" stopColor="#B89F75" />
50
- <stop offset="100%" stopColor="#9C8559" />
51
  </linearGradient>
52
- <filter id="grain">
53
- <feTurbulence type="fractalNoise" baseFrequency="2.4" numOctaves="2" seed="7" />
54
- <feColorMatrix
55
- values="0 0 0 0 0.45
56
- 0 0 0 0 0.55
57
- 0 0 0 0 0.25
58
- 0 0 0 0.18 0"
59
- />
60
- <feComposite in2="SourceGraphic" operator="in" />
61
  </filter>
62
- <clipPath id="liquidClip">
63
- <ellipse cx="100" cy="62" rx="64" ry="14" />
64
- </clipPath>
65
  </defs>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- <ellipse cx="100" cy="178" rx="62" ry="4" fill="rgba(50,55,25,0.18)" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
- <path
70
- d="M 158 72 C 196 76, 198 132, 148 132 L 148 124 C 184 124, 184 84, 158 80 Z"
71
- fill="url(#ceramicBody)"
72
- stroke="rgba(80,55,30,0.45)"
73
- strokeLinejoin="round"
74
- strokeWidth="0.8"
75
- />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
77
- <path
78
- d="M 36 60 C 30 90, 38 130, 70 158 L 130 158 C 162 130, 170 90, 164 60 A 64 14 0 0 1 36 60 Z"
79
- fill="url(#ceramicBody)"
80
- />
81
 
82
- <g opacity="0.18" stroke="rgba(80,55,30,1)" strokeWidth="0.6" fill="none">
83
- <path d="M 50 70 Q 46 110, 64 152" />
84
- <path d="M 70 64 Q 66 110, 78 156" />
85
- <path d="M 130 64 Q 134 110, 122 156" />
86
- <path d="M 150 70 Q 154 110, 136 152" />
87
- </g>
 
88
 
89
- <path
90
- d="M 38 64 C 32 92, 39 130, 66 156 L 76 156 C 54 130, 47 95, 50 66 Z"
91
- fill="rgba(255,250,225,0.35)"
92
- />
93
 
94
- <ellipse
95
- cx="100"
96
- cy="60"
97
- rx="64"
98
- ry="14"
99
- fill="#E8DBB6"
100
- stroke="rgba(80,55,30,0.5)"
101
- strokeWidth="0.9"
102
- />
103
- <ellipse cx="100" cy="61" rx="60" ry="12" fill="#3F5226" />
104
- <ellipse cx="100" cy="62" rx="60" ry="12" fill="url(#matchaSurface)" />
105
- <ellipse
106
- cx="100"
107
- cy="62"
108
- rx="60"
109
- ry="12"
110
- fill="url(#matchaSurface)"
111
- filter="url(#grain)"
112
- opacity="0.55"
113
- clipPath="url(#liquidClip)"
114
- />
115
- <ellipse cx="100" cy="62" rx="60" ry="12" fill="url(#surfaceGlow)" />
116
 
117
- <path
118
- d="M 40 60 A 60 12 0 0 1 160 60 L 160 64 A 60 9 0 0 0 40 64 Z"
119
- fill="rgba(40,55,20,0.35)"
120
- />
121
 
122
- <g clipPath="url(#liquidClip)">
123
- {foamBubbles.map(([x, y, r], index) => (
124
- <circle key={index} cx={x} cy={y} r={r} fill="rgba(240,245,210,0.9)" />
125
- ))}
126
- </g>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
- <path
129
- d="M 78 62 Q 88 56 100 62 T 122 62"
130
- fill="none"
131
- stroke="rgba(245,250,215,0.95)"
132
- strokeLinecap="round"
133
- strokeWidth="2.4"
134
- />
135
- </svg>
 
 
 
 
 
 
 
 
 
 
 
 
136
  </div>
137
  );
138
  }
 
24
  return (
25
  <div
26
  aria-hidden
27
+ className="mx-auto my-2"
28
  style={{
29
  width: 210,
30
+ height: 283.5,
31
+ position: 'relative',
32
+ display: 'flex',
33
+ alignItems: 'flex-end',
34
+ justifyContent: 'center',
35
  }}
36
  >
37
+ <div
38
+ style={{
39
+ position: 'absolute',
40
+ left: '50%',
41
+ top: '40%',
42
+ width: 231,
43
+ height: 189,
44
+ transform: 'translate(-50%, -50%)',
45
+ background:
46
+ 'radial-gradient(ellipse at center, rgba(232,180,110,0.32) 0%, rgba(232,180,110,0.12) 35%, rgba(232,180,110,0) 70%)',
47
+ filter: 'blur(2px)',
48
+ pointerEvents: 'none',
49
+ animation: 'warmthPulse 6s ease-in-out infinite',
50
+ }}
51
+ />
52
+
53
+ <svg
54
+ width="147"
55
+ height="136.5"
56
+ viewBox="0 0 120 110"
57
+ style={{
58
+ position: 'absolute',
59
+ left: '50%',
60
+ top: -10,
61
+ transform: 'translateX(-50%)',
62
+ pointerEvents: 'none',
63
+ opacity: 1,
64
+ }}
65
+ >
66
  <defs>
67
+ <linearGradient id="steamFade" x1="0%" y1="0%" x2="0%" y2="100%">
68
+ <stop offset="0%" stopColor="rgba(120,100,70,0)" />
69
+ <stop offset="35%" stopColor="rgba(120,100,70,0.85)" />
70
+ <stop offset="100%" stopColor="rgba(120,100,70,0.0)" />
 
 
 
 
 
 
 
 
 
 
 
71
  </linearGradient>
72
+ <filter id="steamBlur">
73
+ <feGaussianBlur stdDeviation="0.8" />
 
 
 
 
 
 
 
74
  </filter>
 
 
 
75
  </defs>
76
+ <g filter="url(#steamBlur)">
77
+ <path
78
+ className="steam-wisp s1"
79
+ d="M 50 105 C 44 88, 56 76, 50 60 C 44 44, 56 30, 52 14"
80
+ fill="none"
81
+ stroke="url(#steamFade)"
82
+ strokeLinecap="round"
83
+ strokeWidth="4"
84
+ />
85
+ <path
86
+ className="steam-wisp s2"
87
+ d="M 64 105 C 70 86, 58 72, 66 56 C 74 40, 62 24, 70 8"
88
+ fill="none"
89
+ stroke="url(#steamFade)"
90
+ strokeLinecap="round"
91
+ strokeWidth="3.4"
92
+ />
93
+ <path
94
+ className="steam-wisp s3"
95
+ d="M 76 105 C 80 90, 70 78, 78 62 C 84 50, 76 36, 82 22"
96
+ fill="none"
97
+ stroke="url(#steamFade)"
98
+ strokeLinecap="round"
99
+ strokeWidth="3"
100
+ />
101
+ </g>
102
+ </svg>
103
 
104
+ <style>{`
105
+ @keyframes warmthPulse {
106
+ 0%, 100% { opacity: 0.7; transform: translate(-50%, -50%) scale(1); }
107
+ 50% { opacity: 1; transform: translate(-50%, -50%) scale(1.06); }
108
+ }
109
+ @keyframes steamRise {
110
+ 0% { transform: translateY(8px) translateX(0px); opacity: 0; }
111
+ 15% { opacity: 0.9; }
112
+ 70% { opacity: 0.6; }
113
+ 100% { transform: translateY(-14px) translateX(2px); opacity: 0; }
114
+ }
115
+ .steam-wisp {
116
+ transform-origin: 50% 100%;
117
+ animation: steamRise 4.2s ease-in-out infinite;
118
+ }
119
+ .steam-wisp.s1 { animation-delay: 0s; }
120
+ .steam-wisp.s2 { animation-delay: 1.2s; animation-duration: 4.6s; }
121
+ .steam-wisp.s3 { animation-delay: 2.3s; animation-duration: 3.8s; }
122
+ `}</style>
123
 
124
+ <div
125
+ style={{
126
+ width: 210,
127
+ height: 199.5,
128
+ filter: 'drop-shadow(0 8px 14px rgba(50,60,25,0.18))',
129
+ }}
130
+ >
131
+ <svg width="210" height="199.5" viewBox="0 0 200 190" className="block">
132
+ <defs>
133
+ <linearGradient id="matchaSurface" x1="0%" y1="0%" x2="0%" y2="100%">
134
+ <stop offset="0%" stopColor="#7A9242" />
135
+ <stop offset="40%" stopColor="#9BB75A" />
136
+ <stop offset="100%" stopColor="#82A047" />
137
+ </linearGradient>
138
+ <radialGradient id="surfaceGlow" cx="42%" cy="35%" r="65%">
139
+ <stop offset="0%" stopColor="rgba(190,215,130,0.55)" />
140
+ <stop offset="100%" stopColor="rgba(190,215,130,0)" />
141
+ </radialGradient>
142
+ <linearGradient id="ceramicBody" x1="0%" y1="0%" x2="100%" y2="0%">
143
+ <stop offset="0%" stopColor="#F0E5C8" />
144
+ <stop offset="20%" stopColor="#EADDB8" />
145
+ <stop offset="55%" stopColor="#D9C89C" />
146
+ <stop offset="85%" stopColor="#B89F75" />
147
+ <stop offset="100%" stopColor="#9C8559" />
148
+ </linearGradient>
149
+ <filter id="grain">
150
+ <feTurbulence type="fractalNoise" baseFrequency="2.4" numOctaves="2" seed="7" />
151
+ <feColorMatrix
152
+ values="0 0 0 0 0.45
153
+ 0 0 0 0 0.55
154
+ 0 0 0 0 0.25
155
+ 0 0 0 0.18 0"
156
+ />
157
+ <feComposite in2="SourceGraphic" operator="in" />
158
+ </filter>
159
+ <clipPath id="liquidClip">
160
+ <ellipse cx="100" cy="62" rx="64" ry="14" />
161
+ </clipPath>
162
+ </defs>
163
 
164
+ <ellipse cx="100" cy="178" rx="62" ry="4" fill="rgba(50,55,25,0.18)" />
 
 
 
165
 
166
+ <path
167
+ d="M 158 72 C 196 76, 198 132, 148 132 L 148 124 C 184 124, 184 84, 158 80 Z"
168
+ fill="url(#ceramicBody)"
169
+ stroke="rgba(80,55,30,0.45)"
170
+ strokeLinejoin="round"
171
+ strokeWidth="0.8"
172
+ />
173
 
174
+ <path
175
+ d="M 36 60 C 30 90, 38 130, 70 158 L 130 158 C 162 130, 170 90, 164 60 A 64 14 0 0 1 36 60 Z"
176
+ fill="url(#ceramicBody)"
177
+ />
178
 
179
+ <g opacity="0.18" stroke="rgba(80,55,30,1)" strokeWidth="0.6" fill="none">
180
+ <path d="M 50 70 Q 46 110, 64 152" />
181
+ <path d="M 70 64 Q 66 110, 78 156" />
182
+ <path d="M 130 64 Q 134 110, 122 156" />
183
+ <path d="M 150 70 Q 154 110, 136 152" />
184
+ </g>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
 
186
+ <path
187
+ d="M 38 64 C 32 92, 39 130, 66 156 L 76 156 C 54 130, 47 95, 50 66 Z"
188
+ fill="rgba(255,250,225,0.35)"
189
+ />
190
 
191
+ <ellipse
192
+ cx="100"
193
+ cy="60"
194
+ rx="64"
195
+ ry="14"
196
+ fill="#E8DBB6"
197
+ stroke="rgba(80,55,30,0.5)"
198
+ strokeWidth="0.9"
199
+ />
200
+ <ellipse cx="100" cy="61" rx="60" ry="12" fill="#3F5226" />
201
+ <ellipse cx="100" cy="62" rx="60" ry="12" fill="url(#matchaSurface)" />
202
+ <ellipse
203
+ cx="100"
204
+ cy="62"
205
+ rx="60"
206
+ ry="12"
207
+ fill="url(#matchaSurface)"
208
+ filter="url(#grain)"
209
+ opacity="0.55"
210
+ clipPath="url(#liquidClip)"
211
+ />
212
+ <ellipse cx="100" cy="62" rx="60" ry="12" fill="url(#surfaceGlow)" />
213
 
214
+ <path
215
+ d="M 40 60 A 60 12 0 0 1 160 60 L 160 64 A 60 9 0 0 0 40 64 Z"
216
+ fill="rgba(40,55,20,0.35)"
217
+ />
218
+
219
+ <g clipPath="url(#liquidClip)">
220
+ {foamBubbles.map(([x, y, r], index) => (
221
+ <circle key={index} cx={x} cy={y} r={r} fill="rgba(240,245,210,0.9)" />
222
+ ))}
223
+ </g>
224
+
225
+ <path
226
+ d="M 78 62 Q 88 56 100 62 T 122 62"
227
+ fill="none"
228
+ stroke="rgba(245,250,215,0.95)"
229
+ strokeLinecap="round"
230
+ strokeWidth="2.4"
231
+ />
232
+ </svg>
233
+ </div>
234
  </div>
235
  );
236
  }