@dloizides/marketing-astro-kit 1.5.0 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/TrustSection.astro +530 -524
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dloizides/marketing-astro-kit",
3
- "version": "1.5.0",
3
+ "version": "1.5.1",
4
4
  "description": "Reusable, themeable Astro marketing sections for product sites — a Free-vs-Pro pricing comparison table, a trust / credibility section (security cards, customer-logo + testimonial placeholders, discreet built-by), a client-side \"try it\" demo shell, and an SEO guides/content-hub card + frontmatter schema. Data-driven via a shared TypeScript schema; themed entirely through the host site's CSS custom properties.",
5
5
  "keywords": [
6
6
  "astro",
@@ -1,524 +1,530 @@
1
- ---
2
- // Reusable trust / credibility section for product marketing sites — the
3
- // markers an executive scans for: security & compliance cards, a discreet
4
- // "built by" line, plus clearly-labeled PLACEHOLDER slots for customer logos
5
- // and testimonials the site owner fills in with real ones.
6
- //
7
- // Data-driven (schema.ts → TrustSectionData); themed via host CSS vars.
8
- //
9
- // <TrustSection data={trust} id="trust" />
10
- import type { TrustSectionData } from './schema';
11
- import './a11y.css';
12
- import { ctaAttrs } from './schema';
13
- import { TRUST_ICONS } from './icons';
14
-
15
- interface Props {
16
- data: TrustSectionData;
17
- id?: string;
18
- }
19
-
20
- const { data, id = 'trust' } = Astro.props as Props;
21
-
22
- // A product can inject bespoke mock markup for the sample-deliverable showcase
23
- // via <TrustSection><Fragment slot="sample-deliverable">…</Fragment></TrustSection>
24
- // while the eyebrow/title/caption chrome stays shared.
25
- const hasSampleSlot = Astro.slots.has('sample-deliverable');
26
- ---
27
-
28
- <section class="tmk-trust" id={id} aria-labelledby={`${id}-title`}>
29
- <div class="tmk-container">
30
- <p class="tmk-eyebrow" data-reveal>{data.eyebrow}</p>
31
- <h2 class="tmk-title" id={`${id}-title`} data-reveal>{data.title}</h2>
32
- {data.subtitle && <p class="tmk-sub" data-reveal>{data.subtitle}</p>}
33
-
34
- <div class="tmk-grid">
35
- {data.items.map((item, i) => (
36
- <article class="tmk-card" data-reveal style={`--d:${(i % 4) + 1}`}>
37
- <span class="tmk-ico" set:html={TRUST_ICONS[item.icon]} />
38
- <h3 class="tmk-card-t">{item.title}</h3>
39
- <p class="tmk-card-b">{item.body}</p>
40
- </article>
41
- ))}
42
- </div>
43
-
44
- {data.sampleDeliverable && (
45
- <div class="tmk-sample" data-reveal>
46
- <div class="tmk-sample-copy">
47
- <p class="tmk-sample-eyebrow">{data.sampleDeliverable.eyebrow}</p>
48
- <h3 class="tmk-sample-t">{data.sampleDeliverable.title}</h3>
49
- <p class="tmk-sample-cap">{data.sampleDeliverable.caption}</p>
50
- </div>
51
- <div class="tmk-sample-media">
52
- {hasSampleSlot ? (
53
- <slot name="sample-deliverable" />
54
- ) : data.sampleDeliverable.image ? (
55
- <img class="tmk-sample-img" src={data.sampleDeliverable.image} alt={data.sampleDeliverable.title} loading="lazy" />
56
- ) : null}
57
- </div>
58
- </div>
59
- )}
60
-
61
- {data.logos && (
62
- <div class="tmk-logos" data-reveal>
63
- <p class="tmk-logos-label">{data.logos.label}</p>
64
- <div class="tmk-logos-row">
65
- {data.logos.items.map((logo) =>
66
- logo.placeholder ? (
67
- <span class="tmk-logo tmk-logo--ph" aria-hidden="true">Your logo</span>
68
- ) : logo.href ? (
69
- <a class="tmk-logo" href={logo.href} target="_blank" rel="noopener">{logo.name}</a>
70
- ) : (
71
- <span class="tmk-logo">{logo.name}</span>
72
- ),
73
- )}
74
- </div>
75
- {data.logos.note && <p class="tmk-ph-note">{data.logos.note}</p>}
76
- </div>
77
- )}
78
-
79
- {data.testimonials && (
80
- <div class="tmk-quotes" data-reveal>
81
- <p class="tmk-logos-label">{data.testimonials.label}</p>
82
- <div class="tmk-quotes-row">
83
- {data.testimonials.items.map((t) => (
84
- <figure class={`tmk-quote${t.placeholder ? ' tmk-quote--ph' : ''}`}>
85
- {t.placeholder && <span class="tmk-ph-tag">Placeholder</span>}
86
- <blockquote class="tmk-quote-b">{t.quote}</blockquote>
87
- <figcaption class="tmk-quote-cite">
88
- <span class="tmk-quote-name">{t.authorName}</span>
89
- {t.authorRole && <span class="tmk-quote-role">{t.authorRole}</span>}
90
- </figcaption>
91
- </figure>
92
- ))}
93
- </div>
94
- {data.testimonials.note && <p class="tmk-ph-note">{data.testimonials.note}</p>}
95
- </div>
96
- )}
97
-
98
- {data.foundingPartner && (
99
- <aside class="tmk-founding" data-reveal>
100
- <div class="tmk-founding-body">
101
- <p class="tmk-founding-eyebrow">{data.foundingPartner.eyebrow}</p>
102
- <h3 class="tmk-founding-t">{data.foundingPartner.title}</h3>
103
- <p class="tmk-founding-b">{data.foundingPartner.body}</p>
104
- <ul class="tmk-founding-perks">
105
- {data.foundingPartner.perks.map((perk) => <li>{perk}</li>)}
106
- </ul>
107
- </div>
108
- {data.foundingPartner.cta && (
109
- <a class="tmk-founding-cta" {...ctaAttrs(data.foundingPartner.cta)}>
110
- {data.foundingPartner.cta.label}
111
- </a>
112
- )}
113
- </aside>
114
- )}
115
-
116
- {data.founder && (
117
- <figure class="tmk-founder" data-reveal>
118
- {data.founder.avatar ? (
119
- <img class="tmk-founder-av" src={data.founder.avatar} alt={data.founder.name ?? 'Founder'} loading="lazy" />
120
- ) : (
121
- <span class="tmk-founder-av tmk-founder-av--ph" aria-hidden="true">
122
- {(data.founder.name ?? '·').trim().charAt(0)}
123
- </span>
124
- )}
125
- <figcaption class="tmk-founder-cap">
126
- <p class="tmk-founder-line">{data.founder.line}</p>
127
- {(data.founder.name || data.founder.role) && (
128
- <p class="tmk-founder-cite">
129
- {data.founder.name && <span class="tmk-founder-name">{data.founder.name}</span>}
130
- {data.founder.role && <span class="tmk-founder-role">{data.founder.role}</span>}
131
- </p>
132
- )}
133
- </figcaption>
134
- </figure>
135
- )}
136
-
137
- {data.builtBy && (
138
- <p class="tmk-builtby" data-reveal>
139
- {data.builtBy.label}
140
- <a href={data.builtBy.href} target="_blank" rel="noopener">{data.builtBy.name}</a>
141
- </p>
142
- )}
143
- </div>
144
- </section>
145
-
146
- <style>
147
- .tmk-trust {
148
- --_brand: var(--brand, #d8332a);
149
- --_accent: var(--accent, #f5a300);
150
- --_ink: var(--ink, #0a0a1f);
151
- --_paper: var(--paper, #fff);
152
- --_bg: var(--bg, #fbf9f4);
153
- --_bg-tint: var(--bg-tint, #f1ede1);
154
- --_muted: var(--muted, rgba(10, 10, 31, 0.62));
155
- --_hairline: var(--hairline, rgba(10, 10, 31, 0.1));
156
- --_r-md: var(--radius-md, 14px);
157
- --_r-lg: var(--radius-lg, 24px);
158
- --_shadow: var(--shadow, 0 30px 60px -30px rgba(10, 10, 31, 0.4));
159
- padding: 96px 0;
160
- background: var(--_paper);
161
- border-top: 1px solid var(--_hairline);
162
- border-bottom: 1px solid var(--_hairline);
163
- }
164
- .tmk-container {
165
- width: min(1200px, 100% - 48px);
166
- margin-inline: auto;
167
- }
168
- .tmk-eyebrow {
169
- font-family: 'Oswald', sans-serif;
170
- font-size: 12px;
171
- font-weight: 600;
172
- letter-spacing: 0.22em;
173
- text-transform: uppercase;
174
- color: var(--_brand);
175
- margin: 0;
176
- }
177
- .tmk-title {
178
- font-family: 'Oswald', 'Inter', sans-serif;
179
- font-size: clamp(30px, 3.8vw, 46px);
180
- font-weight: 700;
181
- line-height: 1.08;
182
- letter-spacing: -0.01em;
183
- margin: 12px 0 14px;
184
- max-width: 22ch;
185
- color: var(--_ink);
186
- }
187
- .tmk-sub {
188
- color: var(--_muted);
189
- max-width: 60ch;
190
- margin: 0 0 44px;
191
- }
192
-
193
- /* Cards --------------------------------------------------------------- */
194
- .tmk-grid {
195
- display: grid;
196
- grid-template-columns: repeat(4, 1fr);
197
- gap: 18px;
198
- }
199
- @media (max-width: 1024px) { .tmk-grid { grid-template-columns: repeat(2, 1fr); } }
200
- @media (max-width: 560px) { .tmk-grid { grid-template-columns: 1fr; } }
201
- .tmk-card {
202
- position: relative;
203
- padding: 26px 22px;
204
- border-radius: var(--_r-md);
205
- background: var(--_bg);
206
- border: 1px solid var(--_hairline);
207
- transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
208
- }
209
- .tmk-card:hover {
210
- transform: translateY(-4px);
211
- box-shadow: var(--_shadow);
212
- border-color: transparent;
213
- }
214
- .tmk-ico {
215
- display: inline-grid;
216
- place-items: center;
217
- width: 44px;
218
- height: 44px;
219
- border-radius: 12px;
220
- background: color-mix(in srgb, var(--_brand) 10%, transparent);
221
- color: var(--_brand);
222
- margin-bottom: 16px;
223
- }
224
- .tmk-ico :global(svg) { width: 22px; height: 22px; }
225
- .tmk-card-t {
226
- font-family: 'Oswald', 'Inter', sans-serif;
227
- font-size: 18px;
228
- font-weight: 600;
229
- margin: 0 0 8px;
230
- color: var(--_ink);
231
- }
232
- .tmk-card-b { color: var(--_muted); margin: 0; font-size: 14.5px; }
233
-
234
- /* Logos --------------------------------------------------------------- */
235
- .tmk-logos { margin-top: 52px; }
236
- .tmk-logos-label {
237
- font-family: 'Oswald', sans-serif;
238
- font-size: 12px;
239
- letter-spacing: 0.2em;
240
- text-transform: uppercase;
241
- color: var(--_muted);
242
- margin: 0 0 18px;
243
- text-align: center;
244
- }
245
- .tmk-logos-row {
246
- display: flex;
247
- align-items: center;
248
- justify-content: center;
249
- flex-wrap: wrap;
250
- gap: 14px 18px;
251
- }
252
- .tmk-logo {
253
- font-family: 'Oswald', sans-serif;
254
- font-weight: 600;
255
- font-size: 17px;
256
- letter-spacing: 0.02em;
257
- color: var(--_ink);
258
- padding: 12px 22px;
259
- border-radius: var(--_r-md);
260
- background: var(--_bg-tint);
261
- border: 1px solid var(--_hairline);
262
- transition: color 0.14s ease, border-color 0.14s ease;
263
- }
264
- a.tmk-logo:hover { color: var(--_brand); border-color: var(--_brand); }
265
- .tmk-logo--ph {
266
- color: var(--_muted);
267
- background: transparent;
268
- border: 1px dashed var(--_hairline);
269
- opacity: 0.75;
270
- }
271
-
272
- /* Testimonials -------------------------------------------------------- */
273
- .tmk-quotes { margin-top: 52px; }
274
- .tmk-quotes-row {
275
- display: grid;
276
- grid-template-columns: repeat(3, 1fr);
277
- gap: 18px;
278
- }
279
- @media (max-width: 900px) { .tmk-quotes-row { grid-template-columns: 1fr; } }
280
- .tmk-quote {
281
- position: relative;
282
- padding: 26px 24px 22px;
283
- border-radius: var(--_r-md);
284
- background: var(--_bg);
285
- border: 1px solid var(--_hairline);
286
- }
287
- .tmk-quote--ph { border-style: dashed; background: transparent; }
288
- .tmk-ph-tag {
289
- position: absolute;
290
- top: 14px;
291
- right: 14px;
292
- font-family: 'Oswald', sans-serif;
293
- font-size: 9.5px;
294
- font-weight: 600;
295
- letter-spacing: 0.14em;
296
- text-transform: uppercase;
297
- color: var(--_muted);
298
- background: var(--_bg-tint);
299
- padding: 3px 8px;
300
- border-radius: 999px;
301
- }
302
- .tmk-quote-b {
303
- margin: 0 0 16px;
304
- font-size: 16px;
305
- line-height: 1.5;
306
- color: var(--_ink);
307
- }
308
- .tmk-quote--ph .tmk-quote-b { color: var(--_muted); }
309
- .tmk-quote-b::before { content: '“'; color: var(--_brand); font-weight: 700; }
310
- .tmk-quote-cite { font-style: normal; }
311
- .tmk-quote-name {
312
- display: block;
313
- font-family: 'Oswald', sans-serif;
314
- font-weight: 600;
315
- font-size: 14.5px;
316
- color: var(--_ink);
317
- }
318
- .tmk-quote-role { display: block; font-size: 12.5px; color: var(--_muted); }
319
-
320
- .tmk-ph-note {
321
- text-align: center;
322
- color: var(--_muted);
323
- font-size: 12.5px;
324
- margin: 16px 0 0;
325
- font-style: italic;
326
- }
327
-
328
- /* Sample deliverable -------------------------------------------------- */
329
- .tmk-sample {
330
- margin-top: 52px;
331
- display: grid;
332
- grid-template-columns: 0.92fr 1.08fr;
333
- gap: 34px;
334
- align-items: center;
335
- padding: 30px;
336
- border-radius: var(--_r-lg);
337
- background: var(--_bg);
338
- border: 1px solid var(--_hairline);
339
- }
340
- @media (max-width: 860px) {
341
- .tmk-sample { grid-template-columns: 1fr; gap: 24px; padding: 24px; }
342
- }
343
- .tmk-sample-eyebrow {
344
- font-family: 'Oswald', sans-serif;
345
- font-size: 11px;
346
- font-weight: 600;
347
- letter-spacing: 0.2em;
348
- text-transform: uppercase;
349
- color: var(--_brand);
350
- margin: 0 0 8px;
351
- }
352
- .tmk-sample-t {
353
- font-family: 'Oswald', 'Inter', sans-serif;
354
- font-size: clamp(20px, 2.4vw, 26px);
355
- font-weight: 700;
356
- line-height: 1.14;
357
- margin: 0 0 10px;
358
- color: var(--_ink);
359
- }
360
- .tmk-sample-cap { color: var(--_muted); margin: 0; font-size: 14.5px; }
361
- .tmk-sample-media { min-width: 0; }
362
- .tmk-sample-img {
363
- display: block;
364
- width: 100%;
365
- height: auto;
366
- border-radius: var(--_r-md);
367
- border: 1px solid var(--_hairline);
368
- }
369
-
370
- /* Founding-partner panel ---------------------------------------------- */
371
- .tmk-founding {
372
- margin-top: 52px;
373
- display: flex;
374
- align-items: center;
375
- justify-content: space-between;
376
- gap: 30px;
377
- flex-wrap: wrap;
378
- padding: 30px 32px;
379
- border-radius: var(--_r-lg);
380
- background: var(--_ink);
381
- color: var(--_paper);
382
- border: 1px solid var(--_ink);
383
- }
384
- .tmk-founding-body { flex: 1 1 420px; }
385
- .tmk-founding-eyebrow {
386
- font-family: 'Oswald', sans-serif;
387
- font-size: 11px;
388
- font-weight: 600;
389
- letter-spacing: 0.2em;
390
- text-transform: uppercase;
391
- color: var(--_accent);
392
- margin: 0 0 8px;
393
- }
394
- .tmk-founding-t {
395
- font-family: 'Oswald', 'Inter', sans-serif;
396
- font-size: clamp(22px, 2.6vw, 28px);
397
- font-weight: 700;
398
- line-height: 1.12;
399
- margin: 0 0 10px;
400
- color: var(--_paper);
401
- }
402
- .tmk-founding-b {
403
- color: rgba(255, 255, 255, 0.78);
404
- margin: 0 0 16px;
405
- font-size: 14.5px;
406
- max-width: 60ch;
407
- }
408
- .tmk-founding-perks {
409
- list-style: none;
410
- padding: 0;
411
- margin: 0;
412
- display: grid;
413
- grid-template-columns: repeat(2, minmax(0, 1fr));
414
- gap: 8px 22px;
415
- }
416
- @media (max-width: 560px) { .tmk-founding-perks { grid-template-columns: 1fr; } }
417
- .tmk-founding-perks li {
418
- position: relative;
419
- padding-left: 22px;
420
- color: rgba(255, 255, 255, 0.9);
421
- font-size: 13.8px;
422
- }
423
- .tmk-founding-perks li::before {
424
- content: '';
425
- position: absolute;
426
- left: 0;
427
- top: 7px;
428
- width: 8px;
429
- height: 8px;
430
- border-radius: 50%;
431
- background: var(--_accent);
432
- }
433
- .tmk-founding-cta {
434
- box-sizing: border-box;
435
- min-height: var(--tmk-min-target);
436
- min-width: var(--tmk-min-target);
437
- display: inline-flex;
438
- align-items: center;
439
- justify-content: center;
440
- flex-shrink: 0;
441
- padding: 14px 24px;
442
- border-radius: 999px;
443
- background: var(--_accent);
444
- color: var(--_ink);
445
- font-family: 'Inter', system-ui, sans-serif;
446
- font-weight: 700;
447
- font-size: 14.5px;
448
- letter-spacing: 0.01em;
449
- text-decoration: none;
450
- border: 1px solid transparent;
451
- transition: transform 0.12s ease, box-shadow 0.12s ease;
452
- cursor: pointer;
453
- }
454
- .tmk-founding-cta:hover {
455
- transform: translateY(-1px);
456
- box-shadow: 0 12px 26px -12px rgba(0, 0, 0, 0.6);
457
- }
458
- .tmk-founding-cta:focus-visible {
459
- outline: 2px solid var(--_accent);
460
- outline-offset: 3px;
461
- }
462
-
463
- /* Founder line -------------------------------------------------------- */
464
- .tmk-founder {
465
- margin: 40px 0 0;
466
- display: flex;
467
- align-items: center;
468
- gap: 16px;
469
- max-width: 78ch;
470
- }
471
- .tmk-founder-av {
472
- flex-shrink: 0;
473
- width: 52px;
474
- height: 52px;
475
- border-radius: 50%;
476
- object-fit: cover;
477
- border: 1px solid var(--_hairline);
478
- }
479
- .tmk-founder-av--ph {
480
- display: inline-grid;
481
- place-items: center;
482
- background: color-mix(in srgb, var(--_brand) 12%, transparent);
483
- color: var(--_brand);
484
- font-family: 'Oswald', sans-serif;
485
- font-weight: 700;
486
- font-size: 20px;
487
- text-transform: uppercase;
488
- }
489
- .tmk-founder-cap { margin: 0; }
490
- .tmk-founder-line {
491
- margin: 0;
492
- color: var(--_ink);
493
- font-size: 15.5px;
494
- line-height: 1.5;
495
- }
496
- .tmk-founder-cite { margin: 4px 0 0; }
497
- .tmk-founder-name {
498
- font-family: 'Oswald', sans-serif;
499
- font-weight: 600;
500
- font-size: 13.5px;
501
- color: var(--_ink);
502
- }
503
- .tmk-founder-role {
504
- font-size: 12.5px;
505
- color: var(--_muted);
506
- margin-left: 8px;
507
- }
508
-
509
- /* Built by ------------------------------------------------------------ */
510
- .tmk-builtby {
511
- margin: 48px 0 0;
512
- text-align: center;
513
- color: var(--_muted);
514
- font-size: 13px;
515
- }
516
- .tmk-builtby a {
517
- color: var(--_ink);
518
- border-bottom: 1px dotted var(--_muted);
519
- margin-left: 5px;
520
- }
521
- .tmk-builtby a:hover { color: var(--_brand); }
522
-
523
- @media (max-width: 560px) { .tmk-trust { padding: 64px 0; } }
524
- </style>
1
+ ---
2
+ // Reusable trust / credibility section for product marketing sites — the
3
+ // markers an executive scans for: security & compliance cards, a discreet
4
+ // "built by" line, plus clearly-labeled PLACEHOLDER slots for customer logos
5
+ // and testimonials the site owner fills in with real ones.
6
+ //
7
+ // Data-driven (schema.ts → TrustSectionData); themed via host CSS vars.
8
+ //
9
+ // <TrustSection data={trust} id="trust" />
10
+ import type { TrustSectionData } from './schema';
11
+ import './a11y.css';
12
+ import { ctaAttrs } from './schema';
13
+ import { TRUST_ICONS } from './icons';
14
+
15
+ interface Props {
16
+ data: TrustSectionData;
17
+ id?: string;
18
+ }
19
+
20
+ const { data, id = 'trust' } = Astro.props as Props;
21
+
22
+ // A product can inject bespoke mock markup for the sample-deliverable showcase
23
+ // via <TrustSection><Fragment slot="sample-deliverable">…</Fragment></TrustSection>
24
+ // while the eyebrow/title/caption chrome stays shared.
25
+ const hasSampleSlot = Astro.slots.has('sample-deliverable');
26
+ ---
27
+
28
+ <section class="tmk-trust" id={id} aria-labelledby={`${id}-title`}>
29
+ <div class="tmk-container">
30
+ <p class="tmk-eyebrow" data-reveal>{data.eyebrow}</p>
31
+ <h2 class="tmk-title" id={`${id}-title`} data-reveal>{data.title}</h2>
32
+ {data.subtitle && <p class="tmk-sub" data-reveal>{data.subtitle}</p>}
33
+
34
+ <div class="tmk-grid">
35
+ {data.items.map((item, i) => (
36
+ <article class="tmk-card" data-reveal style={`--d:${(i % 4) + 1}`}>
37
+ <span class="tmk-ico" set:html={TRUST_ICONS[item.icon]} />
38
+ <h3 class="tmk-card-t">{item.title}</h3>
39
+ <p class="tmk-card-b">{item.body}</p>
40
+ </article>
41
+ ))}
42
+ </div>
43
+
44
+ {data.sampleDeliverable && (
45
+ <div class="tmk-sample" data-reveal>
46
+ <div class="tmk-sample-copy">
47
+ <p class="tmk-sample-eyebrow">{data.sampleDeliverable.eyebrow}</p>
48
+ <h3 class="tmk-sample-t">{data.sampleDeliverable.title}</h3>
49
+ <p class="tmk-sample-cap">{data.sampleDeliverable.caption}</p>
50
+ </div>
51
+ <div class="tmk-sample-media">
52
+ {hasSampleSlot ? (
53
+ <slot name="sample-deliverable" />
54
+ ) : data.sampleDeliverable.image ? (
55
+ <img class="tmk-sample-img" src={data.sampleDeliverable.image} alt={data.sampleDeliverable.title} loading="lazy" />
56
+ ) : null}
57
+ </div>
58
+ </div>
59
+ )}
60
+
61
+ {data.logos && (
62
+ <div class="tmk-logos" data-reveal>
63
+ <p class="tmk-logos-label">{data.logos.label}</p>
64
+ <div class="tmk-logos-row">
65
+ {data.logos.items.map((logo) =>
66
+ logo.placeholder ? (
67
+ <span class="tmk-logo tmk-logo--ph" aria-hidden="true">Your logo</span>
68
+ ) : logo.href ? (
69
+ <a class="tmk-logo" href={logo.href} target="_blank" rel="noopener">{logo.name}</a>
70
+ ) : (
71
+ <span class="tmk-logo">{logo.name}</span>
72
+ ),
73
+ )}
74
+ </div>
75
+ {data.logos.note && <p class="tmk-ph-note">{data.logos.note}</p>}
76
+ </div>
77
+ )}
78
+
79
+ {data.testimonials && (
80
+ <div class="tmk-quotes" data-reveal>
81
+ <p class="tmk-logos-label">{data.testimonials.label}</p>
82
+ <div class="tmk-quotes-row">
83
+ {data.testimonials.items.map((t) => (
84
+ <figure class={`tmk-quote${t.placeholder ? ' tmk-quote--ph' : ''}`}>
85
+ {t.placeholder && <span class="tmk-ph-tag">Placeholder</span>}
86
+ <blockquote class="tmk-quote-b">{t.quote}</blockquote>
87
+ <figcaption class="tmk-quote-cite">
88
+ <span class="tmk-quote-name">{t.authorName}</span>
89
+ {t.authorRole && <span class="tmk-quote-role">{t.authorRole}</span>}
90
+ </figcaption>
91
+ </figure>
92
+ ))}
93
+ </div>
94
+ {data.testimonials.note && <p class="tmk-ph-note">{data.testimonials.note}</p>}
95
+ </div>
96
+ )}
97
+
98
+ {data.foundingPartner && (
99
+ <aside class="tmk-founding" data-reveal>
100
+ <div class="tmk-founding-body">
101
+ <p class="tmk-founding-eyebrow">{data.foundingPartner.eyebrow}</p>
102
+ <h3 class="tmk-founding-t">{data.foundingPartner.title}</h3>
103
+ <p class="tmk-founding-b">{data.foundingPartner.body}</p>
104
+ <ul class="tmk-founding-perks">
105
+ {data.foundingPartner.perks.map((perk) => <li>{perk}</li>)}
106
+ </ul>
107
+ </div>
108
+ {data.foundingPartner.cta && (
109
+ <a class="tmk-founding-cta" {...ctaAttrs(data.foundingPartner.cta)}>
110
+ {data.foundingPartner.cta.label}
111
+ </a>
112
+ )}
113
+ </aside>
114
+ )}
115
+
116
+ {data.founder && (
117
+ <figure class="tmk-founder" data-reveal>
118
+ {data.founder.avatar ? (
119
+ <img class="tmk-founder-av" src={data.founder.avatar} alt={data.founder.name ?? 'Founder'} loading="lazy" />
120
+ ) : (
121
+ <span class="tmk-founder-av tmk-founder-av--ph" aria-hidden="true">
122
+ {(data.founder.name ?? '·').trim().charAt(0)}
123
+ </span>
124
+ )}
125
+ <figcaption class="tmk-founder-cap">
126
+ <p class="tmk-founder-line">{data.founder.line}</p>
127
+ {(data.founder.name || data.founder.role) && (
128
+ <p class="tmk-founder-cite">
129
+ {data.founder.name && <span class="tmk-founder-name">{data.founder.name}</span>}
130
+ {data.founder.role && <span class="tmk-founder-role">{data.founder.role}</span>}
131
+ </p>
132
+ )}
133
+ </figcaption>
134
+ </figure>
135
+ )}
136
+
137
+ {data.builtBy && (
138
+ <p class="tmk-builtby" data-reveal>
139
+ {data.builtBy.label}
140
+ <a href={data.builtBy.href} target="_blank" rel="noopener">{data.builtBy.name}</a>
141
+ </p>
142
+ )}
143
+ </div>
144
+ </section>
145
+
146
+ <style>
147
+ .tmk-trust {
148
+ --_brand: var(--brand, #d8332a);
149
+ --_accent: var(--accent, #f5a300);
150
+ --_ink: var(--ink, #0a0a1f);
151
+ --_paper: var(--paper, #fff);
152
+ --_bg: var(--bg, #fbf9f4);
153
+ --_bg-tint: var(--bg-tint, #f1ede1);
154
+ --_muted: var(--muted, rgba(10, 10, 31, 0.62));
155
+ --_hairline: var(--hairline, rgba(10, 10, 31, 0.1));
156
+ --_r-md: var(--radius-md, 14px);
157
+ --_r-lg: var(--radius-lg, 24px);
158
+ --_shadow: var(--shadow, 0 30px 60px -30px rgba(10, 10, 31, 0.4));
159
+ padding: 96px 0;
160
+ background: var(--_paper);
161
+ border-top: 1px solid var(--_hairline);
162
+ border-bottom: 1px solid var(--_hairline);
163
+ }
164
+ .tmk-container {
165
+ width: min(1200px, 100% - 48px);
166
+ margin-inline: auto;
167
+ }
168
+ .tmk-eyebrow {
169
+ font-family: 'Oswald', sans-serif;
170
+ font-size: 12px;
171
+ font-weight: 600;
172
+ letter-spacing: 0.22em;
173
+ text-transform: uppercase;
174
+ color: var(--_brand);
175
+ margin: 0;
176
+ }
177
+ .tmk-title {
178
+ font-family: 'Oswald', 'Inter', sans-serif;
179
+ font-size: clamp(30px, 3.8vw, 46px);
180
+ font-weight: 700;
181
+ line-height: 1.08;
182
+ letter-spacing: -0.01em;
183
+ margin: 12px 0 14px;
184
+ max-width: 22ch;
185
+ color: var(--_ink);
186
+ }
187
+ .tmk-sub {
188
+ color: var(--_muted);
189
+ max-width: 60ch;
190
+ margin: 0 0 44px;
191
+ }
192
+
193
+ /* Cards --------------------------------------------------------------- */
194
+ .tmk-grid {
195
+ display: grid;
196
+ grid-template-columns: repeat(4, 1fr);
197
+ gap: 18px;
198
+ }
199
+ @media (max-width: 1024px) { .tmk-grid { grid-template-columns: repeat(2, 1fr); } }
200
+ @media (max-width: 560px) { .tmk-grid { grid-template-columns: 1fr; } }
201
+ .tmk-card {
202
+ position: relative;
203
+ padding: 26px 22px;
204
+ border-radius: var(--_r-md);
205
+ background: var(--_bg);
206
+ border: 1px solid var(--_hairline);
207
+ transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
208
+ }
209
+ .tmk-card:hover {
210
+ transform: translateY(-4px);
211
+ box-shadow: var(--_shadow);
212
+ border-color: transparent;
213
+ }
214
+ .tmk-ico {
215
+ display: inline-grid;
216
+ place-items: center;
217
+ width: 44px;
218
+ height: 44px;
219
+ border-radius: 12px;
220
+ background: color-mix(in srgb, var(--_brand) 10%, transparent);
221
+ color: var(--_brand);
222
+ margin-bottom: 16px;
223
+ }
224
+ .tmk-ico :global(svg) { width: 22px; height: 22px; }
225
+ .tmk-card-t {
226
+ font-family: 'Oswald', 'Inter', sans-serif;
227
+ font-size: 18px;
228
+ font-weight: 600;
229
+ margin: 0 0 8px;
230
+ color: var(--_ink);
231
+ }
232
+ .tmk-card-b { color: var(--_muted); margin: 0; font-size: 14.5px; }
233
+
234
+ /* Logos --------------------------------------------------------------- */
235
+ .tmk-logos { margin-top: 52px; }
236
+ .tmk-logos-label {
237
+ font-family: 'Oswald', sans-serif;
238
+ font-size: 12px;
239
+ letter-spacing: 0.2em;
240
+ text-transform: uppercase;
241
+ color: var(--_muted);
242
+ margin: 0 0 18px;
243
+ text-align: center;
244
+ }
245
+ .tmk-logos-row {
246
+ display: flex;
247
+ align-items: center;
248
+ justify-content: center;
249
+ flex-wrap: wrap;
250
+ gap: 14px 18px;
251
+ }
252
+ .tmk-logo {
253
+ font-family: 'Oswald', sans-serif;
254
+ font-weight: 600;
255
+ font-size: 17px;
256
+ letter-spacing: 0.02em;
257
+ color: var(--_ink);
258
+ padding: 12px 22px;
259
+ border-radius: var(--_r-md);
260
+ background: var(--_bg-tint);
261
+ border: 1px solid var(--_hairline);
262
+ transition: color 0.14s ease, border-color 0.14s ease;
263
+ }
264
+ a.tmk-logo:hover { color: var(--_brand); border-color: var(--_brand); }
265
+ .tmk-logo--ph {
266
+ color: var(--_muted);
267
+ background: transparent;
268
+ border: 1px dashed var(--_hairline);
269
+ opacity: 0.75;
270
+ }
271
+
272
+ /* Testimonials -------------------------------------------------------- */
273
+ .tmk-quotes { margin-top: 52px; }
274
+ .tmk-quotes-row {
275
+ display: grid;
276
+ grid-template-columns: repeat(3, 1fr);
277
+ gap: 18px;
278
+ }
279
+ @media (max-width: 900px) { .tmk-quotes-row { grid-template-columns: 1fr; } }
280
+ .tmk-quote {
281
+ position: relative;
282
+ padding: 26px 24px 22px;
283
+ border-radius: var(--_r-md);
284
+ background: var(--_bg);
285
+ border: 1px solid var(--_hairline);
286
+ }
287
+ .tmk-quote--ph { border-style: dashed; background: transparent; }
288
+ .tmk-ph-tag {
289
+ position: absolute;
290
+ top: 14px;
291
+ right: 14px;
292
+ font-family: 'Oswald', sans-serif;
293
+ font-size: 9.5px;
294
+ font-weight: 600;
295
+ letter-spacing: 0.14em;
296
+ text-transform: uppercase;
297
+ color: var(--_muted);
298
+ background: var(--_bg-tint);
299
+ padding: 3px 8px;
300
+ border-radius: 999px;
301
+ }
302
+ .tmk-quote-b {
303
+ margin: 0 0 16px;
304
+ font-size: 16px;
305
+ line-height: 1.5;
306
+ color: var(--_ink);
307
+ }
308
+ .tmk-quote--ph .tmk-quote-b { color: var(--_muted); }
309
+ .tmk-quote-b::before { content: '“'; color: var(--_brand); font-weight: 700; }
310
+ .tmk-quote-cite { font-style: normal; }
311
+ .tmk-quote-name {
312
+ display: block;
313
+ font-family: 'Oswald', sans-serif;
314
+ font-weight: 600;
315
+ font-size: 14.5px;
316
+ color: var(--_ink);
317
+ }
318
+ .tmk-quote-role { display: block; font-size: 12.5px; color: var(--_muted); }
319
+
320
+ .tmk-ph-note {
321
+ text-align: center;
322
+ color: var(--_muted);
323
+ font-size: 12.5px;
324
+ margin: 16px 0 0;
325
+ font-style: italic;
326
+ }
327
+
328
+ /* Sample deliverable -------------------------------------------------- */
329
+ .tmk-sample {
330
+ margin-top: 52px;
331
+ display: grid;
332
+ grid-template-columns: 0.92fr 1.08fr;
333
+ gap: 34px;
334
+ align-items: center;
335
+ padding: 30px;
336
+ border-radius: var(--_r-lg);
337
+ background: var(--_bg);
338
+ border: 1px solid var(--_hairline);
339
+ }
340
+ @media (max-width: 860px) {
341
+ .tmk-sample { grid-template-columns: 1fr; gap: 24px; padding: 24px; }
342
+ }
343
+ .tmk-sample-eyebrow {
344
+ font-family: 'Oswald', sans-serif;
345
+ font-size: 11px;
346
+ font-weight: 600;
347
+ letter-spacing: 0.2em;
348
+ text-transform: uppercase;
349
+ color: var(--_brand);
350
+ margin: 0 0 8px;
351
+ }
352
+ .tmk-sample-t {
353
+ font-family: 'Oswald', 'Inter', sans-serif;
354
+ font-size: clamp(20px, 2.4vw, 26px);
355
+ font-weight: 700;
356
+ line-height: 1.14;
357
+ margin: 0 0 10px;
358
+ color: var(--_ink);
359
+ }
360
+ .tmk-sample-cap { color: var(--_muted); margin: 0; font-size: 14.5px; }
361
+ .tmk-sample-media { min-width: 0; }
362
+ .tmk-sample-img {
363
+ display: block;
364
+ width: 100%;
365
+ height: auto;
366
+ border-radius: var(--_r-md);
367
+ border: 1px solid var(--_hairline);
368
+ }
369
+
370
+ /* Founding-partner panel ---------------------------------------------- */
371
+ .tmk-founding {
372
+ margin-top: 52px;
373
+ display: flex;
374
+ align-items: center;
375
+ justify-content: space-between;
376
+ gap: 30px;
377
+ flex-wrap: wrap;
378
+ padding: 30px 32px;
379
+ border-radius: var(--_r-lg);
380
+ background: var(--_ink);
381
+ color: var(--_paper);
382
+ border: 1px solid var(--_ink);
383
+ }
384
+ .tmk-founding-body { flex: 1 1 420px; }
385
+ .tmk-founding-eyebrow {
386
+ font-family: 'Oswald', sans-serif;
387
+ font-size: 11px;
388
+ font-weight: 600;
389
+ letter-spacing: 0.2em;
390
+ text-transform: uppercase;
391
+ color: var(--_accent);
392
+ margin: 0 0 8px;
393
+ }
394
+ .tmk-founding-t {
395
+ font-family: 'Oswald', 'Inter', sans-serif;
396
+ font-size: clamp(22px, 2.6vw, 28px);
397
+ font-weight: 700;
398
+ line-height: 1.12;
399
+ margin: 0 0 10px;
400
+ color: var(--_paper);
401
+ }
402
+ .tmk-founding-b {
403
+ color: rgba(255, 255, 255, 0.78);
404
+ margin: 0 0 16px;
405
+ font-size: 14.5px;
406
+ max-width: 60ch;
407
+ }
408
+ .tmk-founding-perks {
409
+ list-style: none;
410
+ padding: 0;
411
+ margin: 0;
412
+ display: grid;
413
+ grid-template-columns: repeat(2, minmax(0, 1fr));
414
+ gap: 8px 22px;
415
+ }
416
+ @media (max-width: 560px) { .tmk-founding-perks { grid-template-columns: 1fr; } }
417
+ .tmk-founding-perks li {
418
+ position: relative;
419
+ padding-left: 22px;
420
+ color: rgba(255, 255, 255, 0.9);
421
+ font-size: 13.8px;
422
+ }
423
+ .tmk-founding-perks li::before {
424
+ content: '';
425
+ position: absolute;
426
+ left: 0;
427
+ top: 7px;
428
+ width: 8px;
429
+ height: 8px;
430
+ border-radius: 50%;
431
+ background: var(--_accent);
432
+ }
433
+ .tmk-founding-cta {
434
+ box-sizing: border-box;
435
+ min-height: var(--tmk-min-target);
436
+ min-width: var(--tmk-min-target);
437
+ display: inline-flex;
438
+ align-items: center;
439
+ justify-content: center;
440
+ flex-shrink: 0;
441
+ padding: 14px 24px;
442
+ border-radius: 999px;
443
+ background: var(--_accent);
444
+ color: var(--_ink);
445
+ font-family: 'Inter', system-ui, sans-serif;
446
+ font-weight: 700;
447
+ font-size: 14.5px;
448
+ letter-spacing: 0.01em;
449
+ text-decoration: none;
450
+ border: 1px solid transparent;
451
+ transition: transform 0.12s ease, box-shadow 0.12s ease;
452
+ cursor: pointer;
453
+ }
454
+ .tmk-founding-cta:hover {
455
+ transform: translateY(-1px);
456
+ box-shadow: 0 12px 26px -12px rgba(0, 0, 0, 0.6);
457
+ }
458
+ .tmk-founding-cta:focus-visible {
459
+ outline: 2px solid var(--_accent);
460
+ outline-offset: 3px;
461
+ }
462
+
463
+ /* Founder line -------------------------------------------------------- */
464
+ .tmk-founder {
465
+ margin: 40px 0 0;
466
+ display: flex;
467
+ align-items: center;
468
+ gap: 16px;
469
+ max-width: 78ch;
470
+ }
471
+ .tmk-founder-av {
472
+ flex-shrink: 0;
473
+ width: 52px;
474
+ height: 52px;
475
+ border-radius: 50%;
476
+ object-fit: cover;
477
+ border: 1px solid var(--_hairline);
478
+ }
479
+ .tmk-founder-av--ph {
480
+ display: inline-grid;
481
+ place-items: center;
482
+ background: color-mix(in srgb, var(--_brand) 12%, transparent);
483
+ color: var(--_brand);
484
+ font-family: 'Oswald', sans-serif;
485
+ font-weight: 700;
486
+ font-size: 20px;
487
+ text-transform: uppercase;
488
+ }
489
+ .tmk-founder-cap { margin: 0; }
490
+ .tmk-founder-line {
491
+ margin: 0;
492
+ color: var(--_ink);
493
+ font-size: 15.5px;
494
+ line-height: 1.5;
495
+ }
496
+ .tmk-founder-cite { margin: 4px 0 0; }
497
+ .tmk-founder-name {
498
+ font-family: 'Oswald', sans-serif;
499
+ font-weight: 600;
500
+ font-size: 13.5px;
501
+ color: var(--_ink);
502
+ }
503
+ .tmk-founder-role {
504
+ font-size: 12.5px;
505
+ color: var(--_muted);
506
+ margin-left: 8px;
507
+ }
508
+
509
+ /* Built by ------------------------------------------------------------ */
510
+ .tmk-builtby {
511
+ margin: 48px 0 0;
512
+ text-align: center;
513
+ color: var(--_muted);
514
+ font-size: 13px;
515
+ }
516
+ /* 44px hit box (a11y.css --tmk-min-target) without moving the line: the
517
+ vertical padding is cancelled by an equal negative margin. */
518
+ .tmk-builtby a {
519
+ display: inline-block;
520
+ padding-block: 14px;
521
+ margin-block: -14px;
522
+ color: var(--_ink);
523
+ text-decoration: underline dotted var(--_muted);
524
+ text-underline-offset: 3px;
525
+ margin-left: 5px;
526
+ }
527
+ .tmk-builtby a:hover { color: var(--_brand); }
528
+
529
+ @media (max-width: 560px) { .tmk-trust { padding: 64px 0; } }
530
+ </style>