@runwell/shopify-toolkit 0.17.4 → 0.21.0

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 (51) hide show
  1. package/bin/runwell-shopify +14 -2
  2. package/lib/list.js +133 -0
  3. package/lib/qa-bundles.js +117 -0
  4. package/lib/qa.js +147 -13
  5. package/modules/INDEX.md +65 -23
  6. package/modules/_shared/css-tokens/assets/runwell-tokens.css +24 -4
  7. package/modules/_shared/css-tokens/module.json +2 -2
  8. package/modules/_shared/css-typography/assets/runwell-typography.css +14 -6
  9. package/modules/_shared/css-typography/module.json +2 -2
  10. package/modules/bundle-builder/README.md +6 -1
  11. package/modules/bundle-builder/module.json +5 -1
  12. package/modules/care-coaching-medvi/README.md +46 -0
  13. package/modules/care-coaching-medvi/assets/runwell-care-coaching-medvi.css +241 -0
  14. package/modules/care-coaching-medvi/module.json +80 -0
  15. package/modules/care-coaching-medvi/sections/runwell-care-coaching-medvi.liquid +274 -0
  16. package/modules/care-coaching-medvi/snippets/runwell-care-coaching-medvi-circular-text.liquid +25 -0
  17. package/modules/cart-cross-sell/snippets/runwell-cart-xsell.liquid +16 -0
  18. package/modules/collection-block-medvi/README.md +50 -0
  19. package/modules/collection-block-medvi/assets/runwell-collection-block-medvi.css +242 -0
  20. package/modules/collection-block-medvi/module.json +83 -0
  21. package/modules/collection-block-medvi/sections/runwell-collection-block-medvi.liquid +355 -0
  22. package/modules/product-trio-medvi/README.md +35 -0
  23. package/modules/product-trio-medvi/assets/runwell-product-trio-medvi.css +119 -0
  24. package/modules/product-trio-medvi/module.json +48 -0
  25. package/modules/product-trio-medvi/sections/runwell-product-trio-medvi.liquid +188 -0
  26. package/modules/runwell-bundle-system/README.md +35 -0
  27. package/modules/runwell-bundle-system/admin-metafields.json +46 -0
  28. package/modules/runwell-bundle-system/assets/runwell-bundle-system.css +861 -0
  29. package/modules/runwell-bundle-system/assets/runwell-bundle-system.js +287 -0
  30. package/modules/runwell-bundle-system/module.json +126 -0
  31. package/modules/runwell-bundle-system/qa/mobile-checklist.md +105 -0
  32. package/modules/runwell-bundle-system/sections/runwell-bundle-cart-xsell.liquid +59 -0
  33. package/modules/runwell-bundle-system/sections/runwell-bundle-collection.liquid +121 -0
  34. package/modules/runwell-bundle-system/sections/runwell-bundle-home-stacks.liquid +77 -0
  35. package/modules/runwell-bundle-system/sections/runwell-bundle-pdp-banner.liquid +50 -0
  36. package/modules/runwell-bundle-system/sections/runwell-bundle-pdp-pairs-with.liquid +72 -0
  37. package/modules/runwell-bundle-system/sections/runwell-bundle-pdp.liquid +105 -0
  38. package/modules/runwell-bundle-system/settings.json +25 -0
  39. package/modules/runwell-bundle-system/snippets/runwell-bundle-card.liquid +70 -0
  40. package/modules/runwell-bundle-system/snippets/runwell-bundle-cross-supplier.liquid +18 -0
  41. package/modules/runwell-bundle-system/snippets/runwell-bundle-data.liquid +67 -0
  42. package/modules/runwell-bundle-system/snippets/runwell-bundle-fomo.liquid +32 -0
  43. package/modules/runwell-bundle-system/snippets/runwell-bundle-free-gift.liquid +34 -0
  44. package/modules/runwell-bundle-system/snippets/runwell-bundle-multi-product.liquid +86 -0
  45. package/modules/runwell-bundle-system/snippets/runwell-bundle-pricing.liquid +30 -0
  46. package/modules/runwell-bundle-system/snippets/runwell-bundle-quantity-tiers.liquid +73 -0
  47. package/modules/testimonials-medvi/README.md +44 -0
  48. package/modules/testimonials-medvi/assets/runwell-testimonials-medvi.css +239 -0
  49. package/modules/testimonials-medvi/module.json +68 -0
  50. package/modules/testimonials-medvi/sections/runwell-testimonials-medvi.liquid +355 -0
  51. package/package.json +2 -2
@@ -0,0 +1,34 @@
1
+ {%- comment -%}
2
+ runwell-bundle-system: runwell-bundle-free-gift.liquid.
3
+ Wraps the gift-with-purchase module. The bundle's free-gift handle is
4
+ passed in. Display only here; the actual auto-add to cart logic lives
5
+ in the gift-with-purchase module's runwell-gwp.js.
6
+
7
+ Inputs:
8
+ bundle_free_gift_handle (product handle of the gift)
9
+ {%- endcomment -%}
10
+
11
+ {%- if bundle_free_gift_handle != blank -%}
12
+ {%- assign gift_product = all_products[bundle_free_gift_handle] -%}
13
+ {%- if gift_product != blank -%}
14
+ <div class="runwell-bundle-system__free-gift" data-runwell-bundle-free-gift data-gift-handle="{{ bundle_free_gift_handle }}">
15
+ <p class="runwell-bundle-system__free-gift-label">Plus a free gift:</p>
16
+ <div class="runwell-bundle-system__free-gift-product">
17
+ {%- if gift_product.featured_image -%}
18
+ {{ gift_product.featured_image | image_url: width: 80 | image_tag:
19
+ width: 60,
20
+ height: 60,
21
+ loading: 'lazy',
22
+ class: 'runwell-bundle-system__free-gift-thumb',
23
+ alt: gift_product.title
24
+ }}
25
+ {%- endif -%}
26
+ <span class="runwell-bundle-system__free-gift-name">{{ gift_product.title }}</span>
27
+ <span class="runwell-bundle-system__free-gift-price">
28
+ <s>{{ gift_product.price | money }}</s>
29
+ <strong>Free</strong>
30
+ </span>
31
+ </div>
32
+ </div>
33
+ {%- endif -%}
34
+ {%- endif -%}
@@ -0,0 +1,86 @@
1
+ {%- comment -%}
2
+ runwell-bundle-system: runwell-bundle-multi-product.liquid.
3
+ Mode B render. Lists the bundle's component products (thumbnail, qty,
4
+ name, individual price), the bundle pricing block (delegated to
5
+ runwell-bundle-pricing), and the single Add to Cart button that adds
6
+ the bundle product as one line item.
7
+
8
+ Inputs:
9
+ product
10
+ bundle_pricing_model
11
+ bundle_components (array of {product_handle, qty})
12
+ bundle_subtotal (sum of component prices)
13
+ bundle_price (the bundle product's price)
14
+ bundle_savings_amount
15
+ bundle_savings_pct
16
+ {%- endcomment -%}
17
+
18
+ {% render 'runwell-bundle-pricing',
19
+ bundle_pricing_model: bundle_pricing_model,
20
+ bundle_price: bundle_price,
21
+ bundle_subtotal: bundle_subtotal,
22
+ bundle_savings_amount: bundle_savings_amount,
23
+ bundle_savings_pct: bundle_savings_pct
24
+ %}
25
+
26
+ <div class="runwell-bundle-system__components">
27
+ <p class="runwell-bundle-system__components-heading">Includes:</p>
28
+ <ul class="runwell-bundle-system__components-list" role="list">
29
+ {%- if bundle_components and bundle_components.size > 0 -%}
30
+ {%- for component in bundle_components -%}
31
+ {%- assign comp_product = all_products[component.product_handle] -%}
32
+ {%- assign comp_qty = component.qty | default: 1 -%}
33
+ <li class="runwell-bundle-system__component">
34
+ {%- if comp_product != blank and comp_product.featured_image -%}
35
+ {{ comp_product.featured_image | image_url: width: 120 | image_tag:
36
+ width: 60,
37
+ height: 60,
38
+ loading: 'lazy',
39
+ class: 'runwell-bundle-system__component-thumb',
40
+ alt: comp_product.title
41
+ }}
42
+ {%- else -%}
43
+ <span class="runwell-bundle-system__component-thumb runwell-bundle-system__component-thumb--placeholder" aria-hidden="true"></span>
44
+ {%- endif -%}
45
+
46
+ <span class="runwell-bundle-system__component-qty">{{ comp_qty }}x</span>
47
+
48
+ <span class="runwell-bundle-system__component-name">
49
+ {%- if comp_product != blank -%}
50
+ {{ comp_product.title }}
51
+ {%- else -%}
52
+ <em>(unavailable)</em>
53
+ {%- endif -%}
54
+ </span>
55
+
56
+ {%- if comp_product != blank -%}
57
+ <span class="runwell-bundle-system__component-price">{{ comp_product.price | money }}</span>
58
+ {%- endif -%}
59
+ </li>
60
+ {%- endfor -%}
61
+ {%- else -%}
62
+ <li class="runwell-bundle-system__component runwell-bundle-system__component--empty">
63
+ <em>No components configured. Set the runwell.bundle_components metafield on this bundle product.</em>
64
+ </li>
65
+ {%- endif -%}
66
+ </ul>
67
+ </div>
68
+
69
+ {%- form 'product', product, class: 'runwell-bundle-system__form', novalidate: 'novalidate' -%}
70
+ <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
71
+ <input type="hidden" name="quantity" value="1">
72
+ <button
73
+ type="submit"
74
+ name="add"
75
+ class="runwell-bundle-system__atc button button--primary button--full-width"
76
+ {% if product.available == false %}disabled{% endif %}
77
+ >
78
+ <span class="runwell-bundle-system__atc-label">
79
+ {%- if product.available -%}
80
+ Add bundle to cart
81
+ {%- else -%}
82
+ Sold out
83
+ {%- endif -%}
84
+ </span>
85
+ </button>
86
+ {%- endform -%}
@@ -0,0 +1,30 @@
1
+ {%- comment -%}
2
+ runwell-bundle-system: runwell-bundle-pricing.liquid.
3
+ Shared price + strikethrough subtotal + savings badge. Used by Mode B
4
+ (multi-product) and bundle cards on Surfaces 1 to 5.
5
+
6
+ Inputs:
7
+ bundle_price (the bundle product's selling price, in cents)
8
+ bundle_subtotal (sum of component prices, in cents; 0 if not multi-product)
9
+ bundle_savings_amount (subtotal - price, in cents; 0 if no savings)
10
+ bundle_savings_pct (savings_amount / subtotal * 100; 0 if no savings)
11
+ bundle_pricing_model (used to choose dollar vs percent badge format)
12
+ {%- endcomment -%}
13
+
14
+ <div class="runwell-bundle-system__pricing">
15
+ <span class="runwell-bundle-system__pricing-current">{{ bundle_price | money }}</span>
16
+
17
+ {%- if bundle_subtotal > bundle_price -%}
18
+ <span class="runwell-bundle-system__pricing-strikethrough">{{ bundle_subtotal | money }}</span>
19
+ {%- endif -%}
20
+
21
+ {%- if bundle_savings_amount > 0 -%}
22
+ <span class="runwell-bundle-system__pricing-badge">
23
+ {%- if bundle_pricing_model == 'percent_off_subtotal' -%}
24
+ Save {{ bundle_savings_pct }}%
25
+ {%- else -%}
26
+ Save {{ bundle_savings_amount | money_without_trailing_zeros }}
27
+ {%- endif -%}
28
+ </span>
29
+ {%- endif -%}
30
+ </div>
@@ -0,0 +1,73 @@
1
+ {%- comment -%}
2
+ runwell-bundle-system: runwell-bundle-quantity-tiers.liquid.
3
+ Mode A radio picker. Renders one row per tier in bundle_quantity_tiers
4
+ metafield. Default selection is the highest discount_pct tier (last in
5
+ the array, by convention). Display only; the discount itself is applied
6
+ by a Shopify Function (see module.json admin_steps:
7
+ configure-quantity-tier-discount-function).
8
+
9
+ Inputs (passed via render):
10
+ product
11
+ bundle_pricing_model
12
+ bundle_quantity_tiers (array of {qty, discount_pct})
13
+ section
14
+ {%- endcomment -%}
15
+
16
+ {%- if bundle_quantity_tiers and bundle_quantity_tiers.size > 0 -%}
17
+ {%- assign default_tier_index = bundle_quantity_tiers.size | minus: 1 -%}
18
+
19
+ {%- form 'product', product, class: 'runwell-bundle-system__form', novalidate: 'novalidate' -%}
20
+ <fieldset class="runwell-bundle-system__tiers" aria-label="Choose quantity">
21
+ <legend class="visually-hidden">Choose quantity</legend>
22
+ {%- for tier in bundle_quantity_tiers -%}
23
+ {%- assign discount_amount = product.price | times: tier.discount_pct | divided_by: 100 -%}
24
+ {%- assign per_unit_price = product.price | minus: discount_amount -%}
25
+ {%- assign total_price = per_unit_price | times: tier.qty -%}
26
+ {%- assign full_price = product.price | times: tier.qty -%}
27
+ {%- assign savings = full_price | minus: total_price -%}
28
+
29
+ <label class="runwell-bundle-system__tier" data-qty="{{ tier.qty }}" data-discount-pct="{{ tier.discount_pct }}">
30
+ <input
31
+ type="radio"
32
+ name="quantity"
33
+ value="{{ tier.qty }}"
34
+ class="runwell-bundle-system__tier-input"
35
+ {% if forloop.index0 == default_tier_index %}checked{% endif %}
36
+ >
37
+ <span class="runwell-bundle-system__tier-row">
38
+ <span class="runwell-bundle-system__tier-qty">{{ tier.qty }}{% if tier.qty == 1 %} unit{% else %} units{% endif %}</span>
39
+ {%- if tier.discount_pct > 0 -%}
40
+ <span class="runwell-bundle-system__tier-savings-badge">Save {{ tier.discount_pct }}%</span>
41
+ {%- endif -%}
42
+ </span>
43
+ <span class="runwell-bundle-system__tier-row runwell-bundle-system__tier-row--prices">
44
+ <span class="runwell-bundle-system__tier-total">{{ total_price | money }}</span>
45
+ {%- if tier.discount_pct > 0 -%}
46
+ <span class="runwell-bundle-system__tier-strikethrough">{{ full_price | money }}</span>
47
+ {%- endif -%}
48
+ <span class="runwell-bundle-system__tier-per-unit">{{ per_unit_price | money }} / unit</span>
49
+ </span>
50
+ </label>
51
+ {%- endfor -%}
52
+ </fieldset>
53
+
54
+ <input type="hidden" name="id" value="{{ product.selected_or_first_available_variant.id }}">
55
+
56
+ <button
57
+ type="submit"
58
+ name="add"
59
+ class="runwell-bundle-system__atc button button--primary button--full-width"
60
+ {% if product.selected_or_first_available_variant.available == false %}disabled{% endif %}
61
+ >
62
+ <span class="runwell-bundle-system__atc-label">
63
+ {%- if product.selected_or_first_available_variant.available -%}
64
+ Add to cart
65
+ {%- else -%}
66
+ Sold out
67
+ {%- endif -%}
68
+ </span>
69
+ </button>
70
+ {%- endform -%}
71
+ {%- else -%}
72
+ <p class="runwell-bundle-system__empty">No quantity tiers configured for this bundle.</p>
73
+ {%- endif -%}
@@ -0,0 +1,44 @@
1
+ # testimonials-medvi
2
+
3
+ Interleaved photo card + quote card mosaic in a CSS grid. Magazine-spread feel. Blocks of type `quote` and `photo` are user-droppable in the Theme Editor; the designer controls visual rhythm by ordering blocks.
4
+
5
+ Category: `social-proof`
6
+
7
+ ## Files
8
+
9
+ - `sections/runwell-testimonials-medvi.liquid`
10
+ - `assets/runwell-testimonials-medvi.css`
11
+
12
+ ## Layout
13
+
14
+ - Header: centered eyebrow + 2-color split headline + lede.
15
+ - Grid: responsive 1/2/4 columns at 320 / 768 / 1024 px breakpoints. Each cell has a 5:6 aspect ratio; photos and quote cards line up.
16
+ - Cells flow naturally; designer arranges blocks in the Theme Editor for the photo + quote interleave.
17
+ - Up to 12 blocks (max_blocks).
18
+
19
+ ## Tokens consumed
20
+
21
+ Brand: `--runwell-oat`, `--runwell-primary`, `--runwell-tertiary`. Design system: `--runwell-radius-lg`. Typography: `--runwell-eyebrow-size`, `--runwell-h2-size`, `--runwell-body-size`, `--runwell-meta-size`, `--runwell-cta-size`.
22
+
23
+ ## Block types
24
+
25
+ - **quote**: author, rating (range 0 to 5), quote (richtext). Renders as padded card with author + star rating row + quote body.
26
+ - **photo**: image (uploaded) with image_asset fallback, image_focal (center / top / bottom). Renders as full-bleed portrait card with `object-fit: cover` driven by focal point.
27
+
28
+ ## Lineage
29
+
30
+ Extracted from `https://home.medvi.org/` on 2026-05-06. Framer label: `What Our Clients Say Parallax Scroll Section`. Scrape path: `_clients/capital-v/lushi/research/medvi-reference/site-scrape/`.
31
+
32
+ The medvi reference uses an interleaved row pattern: row 1 = `[photo, quote, quote, quote]`, row 2 = `[quote, quote, photo, quote]`. We keep the layout flexible (CSS grid plus block order) rather than encoding the pattern in code; designers reproduce the rhythm by ordering blocks.
33
+
34
+ ## Tenant overrides
35
+
36
+ - `runwell.config.json` brand block: rewires the global tokens.
37
+ - Per-instance Theme Editor: header copy, accent color, card background, star color, bg band, plus block ordering and content.
38
+
39
+ ## Notes
40
+
41
+ - Photo focal point: medvi crops portraits tight with face at top of frame. Default is `center`; use `top` for headshots and `bottom` for full-body shots where the bottom should anchor the visual.
42
+ - Star rating: ASCII `★` glyph in the configured color. Zero rating hides the star row.
43
+ - Default preset includes 6 quote cards + 2 photo placeholders, using medvi's reference quotes (with anonymised first names). Tenants replace the preset content with their own customer testimonials post-launch.
44
+ - Mobile: grid collapses to 1 column, then 2 columns at 768px, then 4 columns at 1024px.
@@ -0,0 +1,239 @@
1
+ /* Runwell testimonials-medvi.
2
+
3
+ Two horizontal marquee rows. Each row scrolls continuously; row 2
4
+ reverses direction. Cards are landscape (~16:9), ~400px wide. Photos
5
+ fill cards with object-fit cover. Quote cards have tinted background,
6
+ padded interior with author + star row + quote body.
7
+
8
+ Loop: each row's content rendered twice in DOM. CSS animation
9
+ translates by -50% (one full set width) over the configured duration
10
+ (--rw-tm-duration). When set 1 leaves viewport, set 2 has taken its
11
+ place; loop is seamless.
12
+
13
+ Accessibility: pause on hover and on focus-within for users navigating
14
+ with keyboard. Stops entirely on prefers-reduced-motion.
15
+
16
+ Tokens: --runwell-oat, --runwell-primary, --runwell-tertiary,
17
+ --runwell-radius-lg.
18
+ */
19
+
20
+ .runwell-testimonials-medvi {
21
+ padding: clamp(3rem, 8vw, 7rem) 0;
22
+ font-family: var(--font-body-family, inherit);
23
+ color: var(--runwell-tertiary, #0B3D38);
24
+
25
+ /* css custom properties driven from Liquid; defaults below are fallbacks */
26
+ --rw-tm-duration: 60s;
27
+ --rw-tm-card-w: 400px;
28
+ --rw-tm-gap: 20px;
29
+ }
30
+
31
+ .runwell-testimonials-medvi--bg-white {
32
+ background: #ffffff;
33
+ }
34
+
35
+ .runwell-testimonials-medvi--bg-oat {
36
+ background: var(--runwell-oat, #F5F0EE);
37
+ }
38
+
39
+ .runwell-testimonials-medvi--bg-celadon-tint {
40
+ background: color-mix(in srgb, var(--runwell-celadon, #ADDDBD) 18%, white);
41
+ }
42
+
43
+ /* ----- Header ----- */
44
+
45
+ .runwell-testimonials-medvi__header {
46
+ max-width: 760px;
47
+ margin: 0 auto clamp(2rem, 5vw, 4rem);
48
+ padding: 0 clamp(1.5rem, 4vw, 3rem);
49
+ text-align: center;
50
+ }
51
+
52
+ .runwell-testimonials-medvi__eyebrow {
53
+ font-size: var(--runwell-eyebrow-size, 0.78rem);
54
+ letter-spacing: 0.16em;
55
+ text-transform: uppercase;
56
+ font-weight: 600;
57
+ margin: 0 0 1rem;
58
+ opacity: 0.85;
59
+ }
60
+
61
+ .runwell-testimonials-medvi__headline {
62
+ font-family: var(--font-heading-family, inherit);
63
+ font-size: var(--runwell-h2-size, clamp(3rem, 5vw, 4.8rem));
64
+ font-weight: 700;
65
+ line-height: 1.1;
66
+ margin: 0;
67
+ letter-spacing: -0.01em;
68
+ }
69
+
70
+ .runwell-testimonials-medvi__headline-pre,
71
+ .runwell-testimonials-medvi__headline-post {
72
+ display: inline;
73
+ }
74
+
75
+ .runwell-testimonials-medvi__lede {
76
+ font-size: var(--runwell-body-size, 1.6rem);
77
+ line-height: 1.5;
78
+ opacity: 0.85;
79
+ margin: 1rem auto 0;
80
+ max-width: 50ch;
81
+ }
82
+
83
+ /* ----- Marquee rows ----- */
84
+
85
+ .runwell-testimonials-medvi__rows {
86
+ display: flex;
87
+ flex-direction: column;
88
+ gap: clamp(0.75rem, 1.5vw, 1.25rem);
89
+ width: 100%;
90
+ }
91
+
92
+ .runwell-testimonials-medvi__row {
93
+ position: relative;
94
+ width: 100%;
95
+ overflow: hidden; /* clip the moving track at viewport edges */
96
+ -webkit-mask-image: linear-gradient(to right, transparent 0, black 4%, black 96%, transparent 100%);
97
+ mask-image: linear-gradient(to right, transparent 0, black 4%, black 96%, transparent 100%);
98
+ }
99
+
100
+ .runwell-testimonials-medvi__track {
101
+ list-style: none;
102
+ margin: 0;
103
+ padding: 0;
104
+ display: flex;
105
+ flex-wrap: nowrap;
106
+ align-items: center;
107
+ gap: var(--rw-tm-gap, 20px);
108
+ width: max-content;
109
+ animation: runwell-tm-marquee var(--rw-tm-duration, 60s) linear infinite;
110
+ will-change: transform;
111
+ }
112
+
113
+ /* Row 2 direction: same (default, medvi-faithful) keeps both rows scrolling left.
114
+ `reverse` flips row 2 to create visual rhythm. */
115
+ .runwell-testimonials-medvi__rows--row2-reverse .runwell-testimonials-medvi__row--2 .runwell-testimonials-medvi__track {
116
+ animation-direction: reverse;
117
+ }
118
+
119
+ .runwell-testimonials-medvi__row:hover .runwell-testimonials-medvi__track,
120
+ .runwell-testimonials-medvi__row:focus-within .runwell-testimonials-medvi__track {
121
+ animation-play-state: paused;
122
+ }
123
+
124
+ @keyframes runwell-tm-marquee {
125
+ from { transform: translateX(0); }
126
+ to { transform: translateX(calc(-50% - var(--rw-tm-gap, 20px) / 2)); }
127
+ }
128
+
129
+ @media (prefers-reduced-motion: reduce) {
130
+ .runwell-testimonials-medvi__track {
131
+ animation: none;
132
+ transform: none;
133
+ }
134
+ }
135
+
136
+ /* ----- Items + cards ----- */
137
+
138
+ .runwell-testimonials-medvi__item {
139
+ flex: 0 0 auto;
140
+ width: var(--rw-tm-card-w, 400px);
141
+ }
142
+
143
+ .runwell-testimonials-medvi__card {
144
+ margin: 0;
145
+ border-radius: var(--runwell-radius-lg, 20px);
146
+ overflow: hidden;
147
+ display: flex;
148
+ flex-direction: column;
149
+ }
150
+
151
+ /* Per-card aspect ratios: photos default 16:9 (medvi-faithful native landscape),
152
+ quotes default 5:3 (taller for multi-line text). Either can be overridden
153
+ per-instance via section settings. */
154
+ .runwell-testimonials-medvi__rows--photo-aspect-16-9 .runwell-testimonials-medvi__card--photo { aspect-ratio: 16 / 9; }
155
+ .runwell-testimonials-medvi__rows--photo-aspect-5-3 .runwell-testimonials-medvi__card--photo { aspect-ratio: 5 / 3; }
156
+ .runwell-testimonials-medvi__rows--photo-aspect-4-3 .runwell-testimonials-medvi__card--photo { aspect-ratio: 4 / 3; }
157
+ .runwell-testimonials-medvi__rows--photo-aspect-1-1 .runwell-testimonials-medvi__card--photo { aspect-ratio: 1 / 1; }
158
+
159
+ .runwell-testimonials-medvi__rows--quote-aspect-16-9 .runwell-testimonials-medvi__card--quote { aspect-ratio: 16 / 9; }
160
+ .runwell-testimonials-medvi__rows--quote-aspect-5-3 .runwell-testimonials-medvi__card--quote { aspect-ratio: 5 / 3; }
161
+ .runwell-testimonials-medvi__rows--quote-aspect-4-3 .runwell-testimonials-medvi__card--quote { aspect-ratio: 4 / 3; }
162
+ .runwell-testimonials-medvi__rows--quote-aspect-1-1 .runwell-testimonials-medvi__card--quote { aspect-ratio: 1 / 1; }
163
+
164
+ /* Quote card */
165
+ .runwell-testimonials-medvi__card--quote {
166
+ padding: clamp(1.5rem, 2.2vw, 2rem) clamp(1.75rem, 2.6vw, 2.25rem);
167
+ justify-content: flex-start;
168
+ gap: 0.85rem;
169
+ }
170
+
171
+ .runwell-testimonials-medvi__card-head {
172
+ display: flex;
173
+ align-items: center;
174
+ justify-content: space-between;
175
+ gap: 1rem;
176
+ }
177
+
178
+ .runwell-testimonials-medvi__card-author {
179
+ font-family: var(--font-heading-family, inherit);
180
+ font-size: var(--runwell-body-size, 1.6rem);
181
+ font-weight: 700;
182
+ margin: 0;
183
+ line-height: 1.2;
184
+ }
185
+
186
+ .runwell-testimonials-medvi__card-rating {
187
+ display: inline-flex;
188
+ gap: 0.15em;
189
+ font-size: var(--runwell-body-size, 1.6rem);
190
+ letter-spacing: 0.05em;
191
+ line-height: 1;
192
+ }
193
+
194
+ .runwell-testimonials-medvi__star {
195
+ line-height: 1;
196
+ }
197
+
198
+ .runwell-testimonials-medvi__card-quote {
199
+ font-size: var(--runwell-cta-size, 1.5rem);
200
+ line-height: 1.5;
201
+ opacity: 0.85;
202
+ flex: 1 1 auto;
203
+ display: -webkit-box;
204
+ -webkit-line-clamp: 6;
205
+ -webkit-box-orient: vertical;
206
+ overflow: hidden;
207
+ }
208
+
209
+ .runwell-testimonials-medvi__card-quote p {
210
+ margin: 0 0 0.4em;
211
+ }
212
+
213
+ .runwell-testimonials-medvi__card-quote p:last-child {
214
+ margin-bottom: 0;
215
+ }
216
+
217
+ /* Photo card */
218
+ .runwell-testimonials-medvi__card--photo {
219
+ background: var(--runwell-oat, #F5F0EE);
220
+ padding: 0;
221
+ }
222
+
223
+ .runwell-testimonials-medvi__card--photo img {
224
+ display: block;
225
+ width: 100%;
226
+ height: 100%;
227
+ object-fit: cover;
228
+ }
229
+
230
+ .runwell-testimonials-medvi__card--photo-focal-center img { object-position: center; }
231
+ .runwell-testimonials-medvi__card--photo-focal-top img { object-position: 50% 25%; }
232
+ .runwell-testimonials-medvi__card--photo-focal-bottom img { object-position: 50% 75%; }
233
+
234
+ /* Mobile: smaller card width but keep marquee */
235
+ @media (max-width: 767px) {
236
+ .runwell-testimonials-medvi {
237
+ --rw-tm-card-w: 280px;
238
+ }
239
+ }
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "testimonials-medvi",
3
+ "version": "0.3.0",
4
+ "category": "social-proof",
5
+ "source": "medvi",
6
+ "base": "testimonials",
7
+ "qualifier": null,
8
+ "description": "Two horizontal marquee rows of testimonial cards. Cards are landscape (16:9), ~400px wide. Photos and quote cards are auto-split across two rows; row 2 scrolls in reverse for visual rhythm. Pauses on hover or focus; stops on prefers-reduced-motion.",
9
+ "replaces": null,
10
+ "files": {
11
+ "sections": [
12
+ "sections/runwell-testimonials-medvi.liquid"
13
+ ],
14
+ "snippets": [],
15
+ "assets": [
16
+ "assets/runwell-testimonials-medvi.css"
17
+ ]
18
+ },
19
+ "config": {
20
+ "schema": {
21
+ "section_eyebrow": {
22
+ "type": "string",
23
+ "default": "THOSE WHO CHOSE US",
24
+ "label": "Section eyebrow"
25
+ },
26
+ "headline_pre": {
27
+ "type": "string",
28
+ "default": "There's a reason people are",
29
+ "label": "Headline first part"
30
+ },
31
+ "headline_post": {
32
+ "type": "string",
33
+ "default": "raving about us.",
34
+ "label": "Headline accent part"
35
+ },
36
+ "headline_accent_color": {
37
+ "type": "string",
38
+ "default": "{{brand.primary}}",
39
+ "label": "Accent color for headline post"
40
+ },
41
+ "lede": {
42
+ "type": "string",
43
+ "default": "Join the multitude of people who have trusted us to help change their lives.",
44
+ "label": "Centered lede paragraph"
45
+ },
46
+ "card_bg": {
47
+ "type": "string",
48
+ "default": "{{brand.oat}}",
49
+ "label": "Quote card background"
50
+ },
51
+ "star_color": {
52
+ "type": "string",
53
+ "default": "{{brand.primary}}",
54
+ "label": "Star rating color"
55
+ },
56
+ "bg_band": {
57
+ "type": "string",
58
+ "default": "white",
59
+ "label": "Background band: white, oat, celadon-tint"
60
+ }
61
+ }
62
+ },
63
+ "lineage": {
64
+ "extracted_from": "https://home.medvi.org/",
65
+ "scrape_path": "_clients/capital-v/lushi/research/medvi-reference/site-scrape/",
66
+ "framer_label": "What Our Clients Say Parallax Scroll Section"
67
+ }
68
+ }