|
3 | 3 | import PrimaryButton from '$lib/components/PrimaryButton.svelte'; |
4 | 4 | import SecondaryButton from '$lib/components/SecondaryButton.svelte'; |
5 | 5 | import { links } from '$lib/data/links'; |
6 | | - import { logEvent, logGitHubClick } from '$lib/utils/analytics'; |
| 6 | + import { logDownload, logEvent, logGitHubClick } from '$lib/utils/analytics'; |
7 | 7 | import { Download, Github } from 'lucide-svelte'; |
8 | 8 |
|
9 | 9 | let { onDownload }: { onDownload: () => void } = $props(); |
|
18 | 18 | return () => mq.removeEventListener('change', update); |
19 | 19 | }); |
20 | 20 |
|
| 21 | + function handleStoreDownload(url: string, platform: string) { |
| 22 | + logDownload(platform, 'hero'); |
| 23 | + window.open(url, '_blank', 'noopener'); |
| 24 | + } |
| 25 | +
|
21 | 26 | function handleDownload() { |
22 | 27 | logEvent('hero_download_click'); |
23 | 28 | onDownload(); |
|
30 | 35 | </script> |
31 | 36 |
|
32 | 37 | <section id="hero" class="hero section-scroll-margin" aria-labelledby="hero-title"> |
33 | | - <ContentContainer padding={isMobile ? '64px 0 64px' : '140px 0 120px'}> |
| 38 | + <ContentContainer padding={isMobile ? '56px 0 56px' : '92px 0 96px'}> |
34 | 39 | <div class="hero-content"> |
35 | | - <div class="badge"> |
36 | | - <span class="dot"></span> |
37 | | - <span>Open Source & Privacy-First</span> |
38 | | - </div> |
| 40 | + <div class="hook"> |
| 41 | + <div class="badge"> |
| 42 | + <span class="dot"></span> |
| 43 | + <span>Private OCD journaling & ERP practice</span> |
| 44 | + </div> |
39 | 45 |
|
40 | | - <h1 id="hero-title" class="headline serif"> |
41 | | - Patterns OCD<br />tracker. |
42 | | - </h1> |
43 | | - |
44 | | - <p class="subhead"> |
45 | | - A private iPhone-first app for OCD journaling, tracking obsessions and compulsions, |
46 | | - rating distress, and supporting ERP practice — calm, local-first, and built to help |
47 | | - you see the pattern clearly. |
48 | | - </p> |
49 | | - |
50 | | - <div class="cta-row"> |
51 | | - <PrimaryButton |
52 | | - label="Download for Free" |
53 | | - icon={Download} |
54 | | - onclick={handleDownload} |
55 | | - fullWidth={isMobile} |
56 | | - /> |
57 | | - <SecondaryButton |
58 | | - label="View on GitHub" |
59 | | - icon={Github} |
60 | | - onclick={handleGitHub} |
61 | | - fullWidth={isMobile} |
62 | | - /> |
| 46 | + <h1 id="hero-title" class="headline serif"> |
| 47 | + See the loop.<br />Choose your next response. |
| 48 | + </h1> |
| 49 | + |
| 50 | + <p class="subhead"> |
| 51 | + Patterns helps you journal, track OCD events, practice ERP, and spot trends |
| 52 | + without accounts, cloud sync, or anyone else reading your notes. |
| 53 | + </p> |
| 54 | + |
| 55 | + <div class="cta-row"> |
| 56 | + <PrimaryButton |
| 57 | + label="Download for Free" |
| 58 | + icon={Download} |
| 59 | + onclick={handleDownload} |
| 60 | + fullWidth={isMobile} |
| 61 | + /> |
| 62 | + <SecondaryButton |
| 63 | + label="View on GitHub" |
| 64 | + icon={Github} |
| 65 | + onclick={handleGitHub} |
| 66 | + fullWidth={isMobile} |
| 67 | + /> |
| 68 | + </div> |
63 | 69 | </div> |
64 | 70 |
|
65 | 71 | <figure class="app-preview"> |
|
71 | 77 | height="1024" |
72 | 78 | fetchpriority="high" |
73 | 79 | /> |
| 80 | + <a |
| 81 | + class="store-link app-store-link" |
| 82 | + href={links.ios} |
| 83 | + target="_blank" |
| 84 | + rel="noopener noreferrer" |
| 85 | + aria-label="Download Patterns on the App Store" |
| 86 | + onclick={() => handleStoreDownload(links.ios, 'iOS')} |
| 87 | + ></a> |
| 88 | + <a |
| 89 | + class="store-link play-store-link" |
| 90 | + href={links.playStore} |
| 91 | + target="_blank" |
| 92 | + rel="noopener noreferrer" |
| 93 | + aria-label="Get Patterns on Google Play" |
| 94 | + onclick={() => handleStoreDownload(links.playStore, 'Android')} |
| 95 | + ></a> |
74 | 96 | <figcaption class="sr-only"> |
75 | 97 | Patterns mobile app CTA with App Store and Google Play download badges |
76 | 98 | </figcaption> |
|
94 | 116 | text-align: center; |
95 | 117 | } |
96 | 118 |
|
| 119 | + .hook { |
| 120 | + display: flex; |
| 121 | + flex-direction: column; |
| 122 | + align-items: center; |
| 123 | + max-width: 760px; |
| 124 | + } |
| 125 | +
|
97 | 126 | .badge { |
98 | 127 | display: inline-flex; |
99 | 128 | align-items: center; |
|
103 | 132 | border: 1px solid color-mix(in srgb, var(--accent) 20%, transparent); |
104 | 133 | background: color-mix(in srgb, var(--accent) 10%, transparent); |
105 | 134 | font-size: 13px; |
106 | | - font-weight: 500; |
| 135 | + font-weight: 600; |
107 | 136 | color: var(--accent); |
108 | | - letter-spacing: 0.5px; |
| 137 | + letter-spacing: 0.03em; |
109 | 138 | } |
110 | 139 |
|
111 | 140 | .dot { |
|
116 | 145 | } |
117 | 146 |
|
118 | 147 | .headline { |
119 | | - margin: 40px 0 0; |
120 | | - font-size: 80px; |
121 | | - line-height: 1.08; |
122 | | - letter-spacing: -0.03em; |
| 148 | + margin: 36px 0 0; |
| 149 | + font-size: 72px; |
| 150 | + line-height: 1.06; |
123 | 151 | font-weight: 400; |
124 | 152 | } |
125 | 153 |
|
126 | 154 | .subhead { |
127 | | - margin: 24px 0 0; |
128 | | - max-width: 560px; |
| 155 | + margin: 22px 0 0; |
| 156 | + max-width: 620px; |
129 | 157 | font-size: 19px; |
130 | | - line-height: 1.6; |
| 158 | + line-height: 1.58; |
131 | 159 | color: var(--text-secondary); |
132 | 160 | } |
133 | 161 |
|
|
136 | 164 | flex-wrap: wrap; |
137 | 165 | justify-content: center; |
138 | 166 | gap: 16px; |
139 | | - margin-top: 48px; |
| 167 | + margin-top: 40px; |
140 | 168 | } |
141 | 169 |
|
142 | 170 | .app-preview { |
| 171 | + position: relative; |
143 | 172 | width: 100%; |
144 | | - max-width: 1120px; |
145 | | - margin: 80px 0 0; |
| 173 | + max-width: 1160px; |
| 174 | + margin: 72px 0 0; |
| 175 | + border-radius: 28px; |
| 176 | + overflow: hidden; |
| 177 | + box-shadow: 0 34px 100px -42px rgba(0, 0, 0, 0.9); |
146 | 178 | } |
147 | 179 |
|
148 | 180 | .mobile-preview { |
149 | 181 | width: 100%; |
150 | 182 | height: auto; |
151 | | - border-radius: 24px; |
152 | | - box-shadow: |
153 | | - 0 24px 64px -16px rgba(0, 0, 0, 0.55), |
154 | | - 0 8px 24px color-mix(in srgb, var(--accent) 10%, transparent); |
| 183 | + display: block; |
| 184 | + } |
| 185 | +
|
| 186 | + .store-link { |
| 187 | + position: absolute; |
| 188 | + display: block; |
| 189 | + border-radius: 12px; |
| 190 | + } |
| 191 | +
|
| 192 | + .store-link:focus-visible { |
| 193 | + outline: 3px solid var(--accent); |
| 194 | + outline-offset: 3px; |
| 195 | + } |
| 196 | +
|
| 197 | + .app-store-link { |
| 198 | + left: 5.9%; |
| 199 | + top: 82.15%; |
| 200 | + width: 15.4%; |
| 201 | + height: 6.35%; |
| 202 | + } |
| 203 | +
|
| 204 | + .play-store-link { |
| 205 | + left: 22.7%; |
| 206 | + top: 82.15%; |
| 207 | + width: 14.35%; |
| 208 | + height: 6.35%; |
155 | 209 | } |
156 | 210 |
|
157 | 211 | .sr-only { |
|
168 | 222 |
|
169 | 223 | @media (max-width: 1023px) { |
170 | 224 | .headline { |
171 | | - font-size: 64px; |
| 225 | + font-size: 60px; |
172 | 226 | } |
173 | 227 |
|
174 | | - .app-preview { max-width: 960px; } |
| 228 | + .app-preview { |
| 229 | + max-width: 980px; |
| 230 | + border-radius: 24px; |
| 231 | + } |
175 | 232 | } |
176 | 233 |
|
177 | 234 | @media (max-width: 599px) { |
| 235 | + .badge { |
| 236 | + font-size: 12px; |
| 237 | + padding: 7px 12px; |
| 238 | + } |
| 239 | +
|
178 | 240 | .headline { |
179 | | - font-size: 48px; |
180 | 241 | margin-top: 28px; |
| 242 | + font-size: 44px; |
181 | 243 | } |
182 | 244 |
|
183 | 245 | .subhead { |
|
186 | 248 | } |
187 | 249 |
|
188 | 250 | .cta-row { |
189 | | - margin-top: 36px; |
| 251 | + margin-top: 32px; |
190 | 252 | } |
191 | 253 |
|
192 | 254 | .app-preview { |
| 255 | + width: calc(100% + 12px); |
193 | 256 | margin-top: 48px; |
194 | | - } |
195 | | -
|
196 | | - .mobile-preview { |
197 | 257 | border-radius: 18px; |
198 | 258 | } |
199 | 259 | } |
|
0 commit comments