@gemboss/gem-framework 0.9.8 → 0.10.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.
package/README.md CHANGED
@@ -25,6 +25,26 @@ khi CÀI, nên build trên Railway không kéo được, và GemFactory phải c
25
25
  thì mọi repo cài theo version, và bị tụt thì thấy ngay ở số version. Mã trong gói là CSS và runtime vốn đã tới
26
26
  trình duyệt của người mua, cộng bộ kiểm; `license: UNLICENSED` giữ nguyên (xem được, không có quyền dùng lại).
27
27
 
28
+ ## Kênh `next` (thử trước khi phát hành)
29
+
30
+ Mỗi commit lên main của gemtheme mà đổi gói thì có ngay một bản trên GitHub Packages:
31
+ `@chriskoh26/gem-framework@next`, số phiên bản `<x.y.z của main>-next.<10 ký tự băm của bản dựng>`. Workflow:
32
+ `.github/workflows/gem-framework-next.yml`. Kênh `latest` trên npm vẫn chỉ đi qua passkey (mục dưới).
33
+
34
+ Thử ở một repo dùng gói, KHÔNG commit hai thay đổi này:
35
+
36
+ ```
37
+ # .npmrc của repo (hoặc ~/.npmrc), token chỉ cần read:packages
38
+ @chriskoh26:registry=https://npm.pkg.github.com
39
+ //npm.pkg.github.com/:_authToken=${GITHUB_PACKAGES_TOKEN}
40
+
41
+ npm install --no-save "@gemboss/gem-framework@npm:@chriskoh26/gem-framework@next"
42
+ ```
43
+
44
+ Bí danh giữ nguyên tên `@gemboss/gem-framework`, nên mã và `import` của bên dùng không đổi. Bản dựng ghi
45
+ `gemtheme_sha` trong `dist/SOURCE.json`. Luôn ghi `@next` (hoặc đúng số): tag `latest` của gói trên GitHub Packages là
46
+ 0.4.0 từ 29/09/2026, không phải bản mới nhất.
47
+
28
48
  ## Phát hành
29
49
 
30
50
  Một lệnh, chạy trên máy, không dùng GitHub Actions (Chris chốt 30/09/2026: không trả tiền runner):
@@ -73,6 +93,60 @@ Từ 0.8.2 mỗi module có `.d.ts` cạnh nó (`mount(root?: ParentNode | null,
73
93
 
74
94
  ## Nhật ký thay đổi
75
95
 
96
+ - **0.10.1** (09/10/2026): màu `color.warning` mặc định `#b7791f` → `#9a661a`. Màu cũ chỉ đạt 3,36:1 trên nền mặc định và
97
+ 3,64:1 trên surface (dưới AA 4,5:1), trong khi section GemFactory dùng nó làm màu chữ và badge vẽ `accentText` trên nó. Màu
98
+ mới đạt 4,52 / 4,90, giữ tông. Phiên gemfactory phát hiện khi thêm cảnh báo tương phản cho preset. Test mới
99
+ `default-colours-meet-aa`: mọi màu mặc định dùng làm chữ ≥ 4,5:1 trên cả hai nền. Plan khai `color.warning` riêng (mọi
100
+ plan sinh từ style pack) thì không đổi: bảng màu pack sửa ở theme-presets (#722). Preview harness của gem-landing-blocks
101
+ theo màu mới. Vòng tham chiếu chéo nhóm của khoá điện thoại vẫn CHƯA chặn (ghi ở `readPhoneTokens`).
102
+ - **0.10.0** (08/10/2026, gộp 0.9.10 và 0.9.11 chưa phát hành): phần còn lại của yêu cầu #4 GemFactory (Figma CRO, issue
103
+ gemfactory #95). Plan không khai khoá nào dưới đây thì CSS tính ra như cũ (mỗi `var()` lùi về đúng giá trị cũ).
104
+ - Line-height tiêu đề: opt-in `data-gem-heading="section"` (1.1), `.gem-section__heading` nền (1.04) và của Dawn parity
105
+ (1.08) đọc `--gem-font-heading-line-height`, tức `typography.headingLineHeight`. Khoá này ĐÃ CÓ trong hai style pack
106
+ (`graphic-editorial` 0.95, `warm-mineral-spf` 1.2) mà trước giờ không tới tiêu đề section: store dùng hai pack đó đổi
107
+ line-height tiêu đề section và hero theo đúng khoá đã khai. Giá trị không phải line-height bị bỏ (biến khai sai làm
108
+ `var()` bỏ qua fallback và rơi về 1,5 của body), `validateGemTokens` báo `invalid_line_height_token`. Luật riêng của
109
+ biến thể Dawn có cỡ px cố định (hero split-proof 46px/1.08, compact-collection…) giữ line-height riêng.
110
+ - Cỡ tiêu đề theo thang chữ, CHỈ khi plan khai `typography.base` hoặc `scale`: phát `--gem-section-heading-size`
111
+ (= `--gem-h2` × núm heading-scale), `--gem-hero-heading-size` (`--gem-h1`, chỉ luật hero h1/h2 của Dawn đọc) và bản
112
+ `-mobile` (không lớn hơn `--gem-heading-phone-cap`, trần điện thoại của PROFILE: Dawn khai `clamp(2rem, 11vw, 3.6rem)`
113
+ trên `:root`, profile nền lùi về `clamp(1.75rem, 9vw, 2.75rem)` như #587). Mọi luật tiêu đề section đọc chúng.
114
+ - Khoá điện thoại: `spacing.<bậc>Mobile` (cả 10 bậc) và `layout.sectionMobile` (so khoá bỏ qua hoa thường và dấu nối)
115
+ khai lại biến gốc trong khối `@media (max-width: 749px)` của `baseMobile`. Spacing nhân núm mật độ; giá trị kiểm hình
116
+ dạng như `md` (0 trần thành `0px`); giá trị không đơn vị, hoặc đọc biến của chính nhóm nó (khai lại trên `:root` là
117
+ vòng lặp, padding về 0), bị bỏ và `validateGemTokens` báo `invalid_phone_token`. `sectionMobile` còn đặt
118
+ `--gem-layout-section-mobile` trên `:root` desktop: padding ≤760px của profile nền dùng nguyên giá trị thay vì
119
+ desktop × 0,72. Không có khoá điện thoại cho `page`/`gutter` (bề rộng điện thoại của profile nền không đọc chúng).
120
+ Ở lane một stylesheet (`createGemThemeCss`), `:root` của Dawn đứng sau token và khoá `layout.*` như trước.
121
+ - Opt-in eyebrow có `font-size: var(--gem-text-sm)` (mặc định 13,1px, luật nền 13px).
122
+ - Xuất `optionalGemTokenKeys` (manifest `token_groups[].optional_keys` đọc từ đây, gồm khoá nút của 0.9.10),
123
+ `gemMobileTokenCssVars`, `gemPhoneTokenTarget`, `GEM_HEADING_SIZE_CSS_VARS`, `GEM_LINE_HEIGHT_TOKEN_KEYS`: bản chép ở
124
+ gemsite (`theme-plan-style.ts`) và cổng preset của GemFactory đối chiếu với chúng thay vì chép tay.
125
+ - **0.9.11** (08/10/2026): marquee còn MỘT định nghĩa. Trước đó primitives.css có hai: bản của gemtheme #124 (một track
126
+ nhân đôi con, dịch -50%) nằm TRÊN bản #79 (`__viewport` + hai track, dịch -100%) nên thua mọi thuộc tính cả hai cùng đặt
127
+ (`min-width`, `animation`), và hợp đồng của #124 chưa từng chạy. Giữ hợp đồng #79, đúng thứ clone kit của gemcapture và
128
+ GemFactory dựng, cộng những gì của #124 vẫn đang có hiệu lực: viền `border-block`, `will-change`, dừng khi rê lên cả
129
+ `.gem-marquee`, đảo chiều bằng `data-gem-marquee-reverse` (bên cạnh `.gem-marquee--reverse`). Bỏ dòng `animation-duration`
130
+ của marquee trong khối reduced-motion của ambient: ở đó track đã `animation: none`. Đo Chromium (6 kiểu dựng marquee, 390 và
131
+ 1280, có và không reduced-motion, rê chuột 3 chỗ, đồng hồ animation ghim ở 1234 ms): 516.832 giá trị tính ra, 56 lệch, cả 56
132
+ là `animation-duration` dưới reduced-motion trên track có `animation-name: none` ở cả hai bản, tức không có gì chạy; bản cố ý
133
+ hỏng (bỏ `will-change` và hook đảo chiều bằng thuộc tính) lệch 328. Test mới `one-owner-per-selector`: selector viết hai lần
134
+ trong cùng file, cùng @media, có thuộc tính đè nhau thì chỉ được giảm (còn 28 ca, liệt kê trong test).
135
+ - **0.9.10** (08/10/2026): token nút tuỳ chọn (`GEM_BUTTON_TOKEN_KEYS`, hình dạng ở `GEM_BUTTON_TOKEN_SHAPES`), cả bốn tên
136
+ đã có mà chưa ai vẽ: `radius.button` (`--gem-radius-button`, scale theo `--gem-style-radius`; validators và generation-jobs
137
+ đã dùng tên này), `typography.buttonWeight`, `typography.buttonTransform` và `typography.buttonLetterSpacing` (schema style
138
+ pack; `luxury-editorial` khai nút in hoa 0.18em từ trước, giờ mới hiện). Không khai thì nút vẽ y như trước (đo Chromium:
139
+ 90/90 giá trị tính ra trùng bản cũ ở dawn-parity và ở morph). Bo góc là của riêng nút, nên nút pill không kéo thẻ thành
140
+ pill; dawn-parity hết ép nút vuông / đậm 400 khi có token. In hoa và giãn chữ không nằm trong `.gem-button` (UA cho
141
+ `<button>` `none`, còn `<a>` kế thừa): `createGemTokenCss` thêm luật `:where(...)` độ ưu tiên 0 chỉ khi có token
142
+ (`gemButtonCaseCss`, export cho storefront tự viết token CSS), nên luật CTA của section vẫn thắng. Nút dạng link không nhận
143
+ độ đậm / chữ hoa của token. Giá trị sai hình dạng (`"pill"`, rỗng, `"semibold"`) không được phát, `validateGemTokens` cảnh
144
+ báo `invalid_button_token`; khoá snake_case của editor đi qua cùng cổng. Manifest có `optional_keys`. Nguồn: GemFactory
145
+ Global Style 08/10/2026 (339 trang store hàng đầu: CTA pill 39%, in hoa 47%, giãn chữ 18%).
146
+ - **0.9.9** (08/10/2026): `gemFrameworkVersion` (và `framework_version` / `gem_framework_version` trong manifest) bằng
147
+ đúng phiên bản của gói. Từ 0.4.0 tới 0.9.8 nó đứng yên ở `"0.1.0"`, nên trang Framework của gemboss hiện sai phiên bản
148
+ và GemFactory phải tự đọc phiên bản npm. Test trong `index.test.ts` đỏ khi hai giá trị lệch nhau. Không đổi CSS hay
149
+ runtime.
76
150
  - **0.9.8** (06/10/2026): biến chỉ được tính là "khai trên :root" khi MỌI vế của selector đúng là gốc (`:root`, `html`,
77
151
  `:host`, có thể kèm điều kiện như `:root[data-theme="dark"]`), không bao giờ là con của nó. Bản 0.9.7 chỉ xét đầu
78
152
  selector, nên một luật `:root .gem-row { --gem-row-auto-sm: … }` sẽ biến biến nội bộ thành công khai. Stylesheet hiện
package/dist/SOURCE.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "package": "@gemboss/gem-framework",
3
- "version": "0.9.8",
4
- "gemtheme_sha": "fea3af50909be36e9fc8968ae407409e324a1c6b",
3
+ "version": "0.10.1",
4
+ "gemtheme_sha": "cc172eb630a30b0bacf02648deaa097c9d149883",
5
5
  "gemtheme_dirty": false,
6
- "framework_version": "0.1.0"
6
+ "framework_version": "0.10.1"
7
7
  }
@@ -7,7 +7,7 @@
7
7
  --gem-color-accent-text: #ffffff;
8
8
  --gem-color-border: #d2d5d8;
9
9
  --gem-color-success: #008060;
10
- --gem-color-warning: #b7791f;
10
+ --gem-color-warning: #9a661a;
11
11
  --gem-color-danger: #d72c0d;
12
12
  --gem-font-body: Inter, system-ui, sans-serif;
13
13
  --gem-font-heading: Inter, system-ui, sans-serif;
@@ -160,9 +160,9 @@ a {
160
160
  justify-content: center;
161
161
  min-height: var(--gem-component-input-height, 44px);
162
162
  border: var(--gem-button-border-width, 1px) solid var(--gem-color-border);
163
- border-radius: var(--gem-radius-md);
163
+ border-radius: var(--gem-radius-button, var(--gem-radius-md));
164
164
  padding: var(--gem-component-button-padding-block, 12px) var(--gem-component-button-padding-inline, 18px);
165
- font-weight: 700;
165
+ font-weight: var(--gem-font-button-weight, 700);
166
166
  text-decoration: none;
167
167
  transition: background var(--gem-motion-duration, 160ms) var(--gem-motion-easing, ease), color var(--gem-motion-duration, 160ms) var(--gem-motion-easing, ease);
168
168
  }
@@ -440,23 +440,6 @@ a {
440
440
  border-radius: var(--gem-radius-md);
441
441
  background: color-mix(in srgb, var(--gem-color-text) 8%, var(--gem-color-background));
442
442
  }
443
- .gem-marquee {
444
- overflow: hidden;
445
- border-block: var(--gem-border-subtle, 1px solid var(--gem-color-border));
446
- }
447
- .gem-marquee__track {
448
- display: flex;
449
- gap: var(--gem-space-lg);
450
- min-width: max-content;
451
- animation: gem-marquee-scroll var(--gem-marquee-duration, 40s) linear infinite;
452
- will-change: transform;
453
- }
454
- .gem-marquee:hover .gem-marquee__track {
455
- animation-play-state: paused;
456
- }
457
- .gem-marquee[data-gem-marquee-reverse] .gem-marquee__track {
458
- animation-direction: reverse;
459
- }
460
443
  .gem-carousel {
461
444
  display: grid;
462
445
  gap: var(--gem-space-md);
@@ -582,7 +565,10 @@ a {
582
565
  overflow: hidden;
583
566
  color: var(--gem-color-accent);
584
567
  }
585
- .gem-marquee { overflow: hidden; }
568
+ .gem-marquee {
569
+ overflow: hidden;
570
+ border-block: var(--gem-border-subtle, 1px solid var(--gem-color-border));
571
+ }
586
572
  .gem-marquee__viewport { display: flex; overflow: hidden; }
587
573
  .gem-marquee__track {
588
574
  flex: 0 0 auto;
@@ -593,9 +579,10 @@ a {
593
579
  gap: var(--gem-space-lg);
594
580
  white-space: nowrap;
595
581
  animation: gem-marquee var(--gem-marquee-duration, 24s) linear infinite;
582
+ will-change: transform;
596
583
  }
597
- .gem-marquee__viewport:hover .gem-marquee__track { animation-play-state: paused; }
598
- .gem-marquee--reverse .gem-marquee__track { animation-direction: reverse; }
584
+ .gem-marquee:hover .gem-marquee__track, .gem-marquee__viewport:hover .gem-marquee__track { animation-play-state: paused; }
585
+ .gem-marquee--reverse .gem-marquee__track, .gem-marquee[data-gem-marquee-reverse] .gem-marquee__track { animation-direction: reverse; }
599
586
  @keyframes gem-marquee { from { transform: translateX(0); } to { transform: translateX(-100%); } }
600
587
  @media (prefers-reduced-motion: reduce) {
601
588
  .gem-marquee__track { animation: none; }
@@ -1109,7 +1096,6 @@ a {
1109
1096
  @media (prefers-reduced-motion: reduce) {
1110
1097
  [data-gem-ambient] { --gem-ambient-amp: 2px; animation-duration: 12s; }
1111
1098
  [data-gem-reveal] { transition: none; opacity: 1; transform: none; }
1112
- .gem-marquee__track { animation-duration: calc(var(--gem-marquee-duration, 40s) * 2.5); }
1113
1099
  }
1114
1100
  :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top], :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom] { position: relative; overflow: clip; overflow-clip-margin: var(--gem-divider-height, 48px); }
1115
1101
  :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom='wave']::after, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom='slope']::after, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom='spikes']::after, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom='arch']::after, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-bottom='blob']::after, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top='wave']::before, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top='slope']::before, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top='spikes']::before, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top='arch']::before, :is(.gem-section, .gem-hero, .gem-hero-split, .gem-hero-collage, .gem-hero-triptych, .gem-slideshow)[data-gem-divider-top='blob']::before { content: ''; position: absolute; left: 0; right: 0; z-index: 1; pointer-events: none; height: var(--gem-divider-height, 48px); background: var(--gem-section-bg, var(--gem-color-background)); -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; }
@@ -2231,8 +2217,8 @@ sticky-header.header-wrapper {
2231
2217
  .gem-section__heading {
2232
2218
  margin: 0;
2233
2219
  font-family: var(--gem-font-heading);
2234
- font-size: clamp(calc(2rem * var(--gem-typography-heading-scale, 1)), calc(4vw * var(--gem-typography-heading-scale, 1)), calc(4rem * var(--gem-typography-heading-scale, 1)));
2235
- line-height: 1.04;
2220
+ font-size: var(--gem-section-heading-size, clamp(calc(2rem * var(--gem-typography-heading-scale, 1)), calc(4vw * var(--gem-typography-heading-scale, 1)), calc(4rem * var(--gem-typography-heading-scale, 1))));
2221
+ line-height: var(--gem-font-heading-line-height, 1.04);
2236
2222
  }
2237
2223
  .gem-section__body {
2238
2224
  margin: 0;
@@ -2349,20 +2335,20 @@ sticky-header.header-wrapper {
2349
2335
  }
2350
2336
  }
2351
2337
  @media (max-width: 760px) {
2352
- .gem-section { padding-block: calc(var(--gem-layout-section, 64px) * 0.72); }
2338
+ .gem-section { padding-block: var(--gem-layout-section-mobile, calc(var(--gem-layout-section, 64px) * 0.72)); }
2353
2339
  .gem-section__inner, .gem-container { width: min(100% - 32px, var(--gem-layout-page, 1180px)); }
2354
- .gem-section__heading { font-size: clamp(1.75rem, 9vw, 2.75rem); }
2340
+ .gem-section__heading { font-size: var(--gem-section-heading-size-mobile, clamp(1.75rem, 9vw, 2.75rem)); }
2355
2341
  .gem-section__grid { grid-template-columns: minmax(0, 1fr); }
2356
2342
  .gem-section:is([data-gem-composition-mode='split-collage'], [data-gem-editorial-band-mode='media-story']) .gem-section__grid { grid-template-columns: minmax(0, 1fr); }
2357
2343
  .gem-product-main[data-gem-product-main-layout='dawn-main'] .gem-product-main__inner { grid-template-columns: minmax(0, 1fr); }
2358
2344
  .gem-product-main[data-gem-product-main-layout='dawn-main'] .gem-product-main__summary { position: static; }
2359
2345
  .gem-section:is([data-gem-composition-mode='slideshow'], [data-gem-composition-mode='video-led']) .gem-section__inner { min-block-size: min(62vh, 560px); }
2360
2346
  }
2361
- :where(.gem-section[data-gem-eyebrow='show']) .gem-section__eyebrow { display: block; letter-spacing: 0.12em; }
2362
- :where(.gem-section[data-gem-heading='section']) .gem-section__heading { margin: 0; font-family: var(--gem-font-heading); font-size: var(--gem-h2); line-height: 1.1; letter-spacing: -0.01em; text-wrap: balance; }
2347
+ :where(.gem-section[data-gem-eyebrow='show']) .gem-section__eyebrow { display: block; font-size: var(--gem-text-sm); letter-spacing: 0.12em; }
2348
+ :where(.gem-section[data-gem-heading='section']) .gem-section__heading { margin: 0; font-family: var(--gem-font-heading); font-size: var(--gem-section-heading-size, var(--gem-h2)); line-height: var(--gem-font-heading-line-height, 1.1); letter-spacing: -0.01em; text-wrap: balance; }
2363
2349
  :where(.gem-section[data-gem-body='section']) .gem-section__body { margin: 0; color: var(--gem-color-muted); font-size: var(--gem-body); line-height: var(--gem-font-body-line-height, 1.5); }
2364
2350
  @media (max-width: 760px) {
2365
- :where(.gem-section[data-gem-heading='section']) .gem-section__heading { font-size: min(var(--gem-h2), clamp(1.75rem, 9vw, 2.75rem)); }
2351
+ :where(.gem-section[data-gem-heading='section']) .gem-section__heading { font-size: var(--gem-section-heading-size-mobile, min(var(--gem-h2), clamp(1.75rem, 9vw, 2.75rem))); }
2366
2352
  }
2367
2353
  .gem-row:where(:not([hidden])) { --gem-row-auto-sm: repeat(var(--gem-row-cols-sm), minmax(0, 1fr)); --gem-row-auto-md: repeat(var(--gem-row-cols-md), minmax(0, 1fr)); --gem-row-auto-lg: repeat(var(--gem-row-cols), minmax(0, 1fr)); display: grid; grid-template-columns: var(--gem-row-tracks-sm, var(--gem-row-auto-sm, minmax(0, 1fr))); }
2368
2354
  :where(.gem-row > *) { min-width: 0; --gem-row-cols: initial; --gem-row-cols-md: initial; --gem-row-cols-sm: initial; --gem-row-tracks: initial; --gem-row-tracks-md: initial; --gem-row-tracks-sm: initial; --gem-row-gap: initial; --gem-row-gap-y: initial; }
@@ -2370,7 +2356,7 @@ sticky-header.header-wrapper {
2370
2356
  @media (min-width: 990px) { .gem-row { grid-template-columns: var(--gem-row-tracks, var(--gem-row-auto-lg, repeat(2, minmax(0, 1fr)))); } }
2371
2357
 
2372
2358
  /* Dawn parity profile: locked default baseline; morph presets must opt out before applying Gem visual identity. */
2373
- :root { --gem-profile-mode: dawn-parity; --gem-layout-page: 1100px; --gem-layout-gutter: 32px; --gem-layout-section: 52px; --gem-radius-md: 0px; --gem-card-radius: 0px; --gem-media-radius: 0px; --gem-grid-column-gap: 8px; --gem-grid-row-gap: 8px; }
2359
+ :root { --gem-profile-mode: dawn-parity; --gem-layout-page: 1100px; --gem-layout-gutter: 32px; --gem-layout-section: 52px; --gem-radius-md: 0px; --gem-card-radius: 0px; --gem-media-radius: 0px; --gem-grid-column-gap: 8px; --gem-grid-row-gap: 8px; --gem-heading-phone-cap: clamp(2rem, 11vw, 3.6rem); }
2374
2360
  .utility-bar, .announcement-bar, .utility-bar__grid { min-height: 38px; }
2375
2361
  .announcement-bar__message { margin: 0; min-height: 38px; padding-block: 10px; font-size: 0.75rem; font-weight: 400; letter-spacing: 0.04em; }
2376
2362
  .announcement-bar__message span { font-size: 0.75rem; }
@@ -2423,7 +2409,9 @@ main > .gem-section:first-child { padding-top: 0; }
2423
2409
  .gem-hero-banner--split-proof .gem-hero-banner__media, .gem-hero-banner--editorial .gem-hero-banner__media { position: relative; inset: auto; min-height: 26rem; height: 100%; overflow: hidden; border-radius: 6px; background: rgb(244 244 244); }
2424
2410
  .gem-hero-banner--split-proof .gem-hero-banner__image, .gem-hero-banner--split-proof .gem-hero-banner__placeholder, .gem-hero-banner--editorial .gem-hero-banner__image, .gem-hero-banner--editorial .gem-hero-banner__placeholder { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
2425
2411
  .gem-hero-banner .gem-section__content { max-width: 78rem; margin-inline: auto; }
2426
- .gem-hero-banner h1, .gem-hero-banner h2, .gem-section__heading { margin: 0 0 1.5rem; font-size: clamp(calc(2.5rem * var(--gem-typography-heading-scale, 1)), calc(4vw * var(--gem-typography-heading-scale, 1)), calc(5.2rem * var(--gem-typography-heading-scale, 1))); font-weight: 400; line-height: 1.08; letter-spacing: 0; }
2412
+ .gem-hero-banner h1, .gem-hero-banner h2, .gem-section__heading { margin: 0 0 1.5rem; font-weight: 400; line-height: var(--gem-font-heading-line-height, 1.08); letter-spacing: 0; }
2413
+ .gem-section__heading { font-size: var(--gem-section-heading-size, clamp(calc(2.5rem * var(--gem-typography-heading-scale, 1)), calc(4vw * var(--gem-typography-heading-scale, 1)), calc(5.2rem * var(--gem-typography-heading-scale, 1)))); }
2414
+ .gem-hero-banner h1, .gem-hero-banner h2 { font-size: var(--gem-hero-heading-size, clamp(calc(2.5rem * var(--gem-typography-heading-scale, 1)), calc(4vw * var(--gem-typography-heading-scale, 1)), calc(5.2rem * var(--gem-typography-heading-scale, 1)))); }
2427
2415
  .gem-section__eyebrow { display: none; }
2428
2416
  .gem-hero-banner--compact-collection { min-height: auto; display: block; text-align: left; padding-block: 25px; }
2429
2417
  .gem-hero-banner--compact-collection .gem-section__content, .gem-product-card-grid--quick-view-grid > .gem-section__inner { width: min(var(--gem-layout-page, 1200px), calc(100% - var(--gem-layout-gutter, 32px))); margin-inline: auto; }
@@ -2438,7 +2426,8 @@ main > .gem-section:first-child { padding-top: 0; }
2438
2426
  .gem-rich-text--dawn-intro .gem-section__body { width: 100%; max-width: none; margin: 20px 0 0; font-size: 16px; line-height: 28.8px; }
2439
2427
  .gem-rich-text--dawn-intro .gem-section__body p { margin: 0; }
2440
2428
  .gem-rich-text--dawn-intro .gem-rich-text__blocks { display: block; width: 100%; }
2441
- .gem-button { min-width: 12rem; border-radius: 0; font-weight: 400; box-shadow: none; }
2429
+ .gem-button { min-width: 12rem; border-radius: var(--gem-radius-button, 0); font-weight: 400; box-shadow: none; }
2430
+ .gem-button:where(:not(.gem-button--link)) { font-weight: var(--gem-font-button-weight, 400); }
2442
2431
  .gem-product-card-wrapper { display: block; min-width: 0; }
2443
2432
  .gem-card, .gem-product-card { border: 0; border-radius: 0; box-shadow: none; background: transparent; padding: 0; }
2444
2433
  .gem-product-card__media, .gem-media { border: 0; border-radius: 0; box-shadow: none; background: rgb(244 244 244); }
@@ -2577,8 +2566,8 @@ main > .gem-section:first-child { padding-top: 0; }
2577
2566
  .gem-shell-footer__payment-fallback { display: inline-grid; place-items: center; min-width: 38px; height: 24px; border: 1px solid rgb(18 18 18 / 12%); border-radius: 3px; background: white; color: rgb(18 18 18 / 72%); font-size: 8px; line-height: 1; }
2578
2567
  .gem-shell-footer__localization { order: -1; flex: 0 0 100%; width: 100%; }
2579
2568
  .gem-shell-footer__localization .gem-select, .gem-shell-footer__localization > .gem-select { display: inline-flex; align-items: center; min-width: 210px; min-height: 45px; border: 1px solid rgb(18 18 18 / 55%); border-radius: 0; background: transparent; padding: 10px 36px 10px 12px; color: rgb(18 18 18); font-size: 13px; line-height: 18px; }
2580
- :where(.gem-section[data-gem-eyebrow='show']) .gem-section__eyebrow { display: block; letter-spacing: 0.12em; }
2581
- :where(.gem-section[data-gem-heading='section']) .gem-section__heading { margin: 0; font-family: var(--gem-font-heading); font-size: var(--gem-h2); line-height: 1.1; letter-spacing: -0.01em; text-wrap: balance; }
2569
+ :where(.gem-section[data-gem-eyebrow='show']) .gem-section__eyebrow { display: block; font-size: var(--gem-text-sm); letter-spacing: 0.12em; }
2570
+ :where(.gem-section[data-gem-heading='section']) .gem-section__heading { margin: 0; font-family: var(--gem-font-heading); font-size: var(--gem-section-heading-size, var(--gem-h2)); line-height: var(--gem-font-heading-line-height, 1.1); letter-spacing: -0.01em; text-wrap: balance; }
2582
2571
  :where(.gem-section[data-gem-body='section']) .gem-section__body { margin: 0; color: var(--gem-color-muted); font-size: var(--gem-body); line-height: var(--gem-font-body-line-height, 1.5); }
2583
2572
  @media (max-width: 760px) {
2584
2573
  :root { --gem-layout-gutter: 24px; --gem-layout-section: 36px; }
@@ -2615,7 +2604,8 @@ main > .gem-section:first-child { padding-top: 0; }
2615
2604
  .gem-product-card__content { padding-top: 8px; gap: 4px; }
2616
2605
  .gem-product-card__title { font-size: 15px; line-height: 24px; }
2617
2606
  .gem-product-card__price, .gem-product-card__price .gem-price { font-size: 16px; line-height: 24px; }
2618
- .gem-hero-banner h1, .gem-hero-banner h2, .gem-section__heading { font-size: clamp(2rem, 11vw, 3.6rem); }
2607
+ .gem-section__heading { font-size: var(--gem-section-heading-size-mobile, var(--gem-heading-phone-cap)); }
2608
+ .gem-hero-banner h1, .gem-hero-banner h2 { font-size: var(--gem-hero-heading-size-mobile, var(--gem-heading-phone-cap)); }
2619
2609
  .gem-featured-collection--editorial-grid .gem-section__grid, .gem-product-card-grid--quick-view-grid .gem-section__grid, .gem-main-search .gem-section__grid { grid-template-columns: repeat(var(--gem-product-card-grid-mobile-columns, 2), minmax(0, 1fr)); }
2620
2610
  .gem-product-main[data-gem-product-main-layout='dawn-main'] > .gem-section__inner { width: calc(100% - 30px); }
2621
2611
  .gem-product-main[data-gem-product-main-layout='dawn-main'] .gem-product-media-gallery__frame { display: flex; gap: 0; overflow-x: auto; scroll-snap-type: x mandatory; }
@@ -2696,7 +2686,7 @@ main > .gem-section:first-child { padding-top: 0; }
2696
2686
  .gem-hero-video--dawn-video > .gem-section__inner, .gem-hero-video--dawn-video .gem-hero-video__inner, .gem-hero-video--dawn-video .gem-hero-video__media { height: 212px; min-height: 212px; }
2697
2687
  .gem-advanced-list--feature-grid { height: 420px; min-height: 420px; padding-block: 36px; }
2698
2688
  .gem-advanced-list--feature-grid .gem-section__grid { grid-template-columns: 1fr; }
2699
- :where(.gem-section[data-gem-heading='section']) .gem-section__heading { font-size: min(var(--gem-h2), clamp(1.75rem, 9vw, 2.75rem)); }
2689
+ :where(.gem-section[data-gem-heading='section']) .gem-section__heading { font-size: var(--gem-section-heading-size-mobile, min(var(--gem-h2), clamp(1.75rem, 9vw, 2.75rem))); }
2700
2690
  }
2701
2691
  .gem-row:where(:not([hidden])) { --gem-row-auto-sm: repeat(var(--gem-row-cols-sm), minmax(0, 1fr)); --gem-row-auto-md: repeat(var(--gem-row-cols-md), minmax(0, 1fr)); --gem-row-auto-lg: repeat(var(--gem-row-cols), minmax(0, 1fr)); display: grid; grid-template-columns: var(--gem-row-tracks-sm, var(--gem-row-auto-sm, minmax(0, 1fr))); }
2702
2692
  :where(.gem-row > *) { min-width: 0; --gem-row-cols: initial; --gem-row-cols-md: initial; --gem-row-cols-sm: initial; --gem-row-tracks: initial; --gem-row-tracks-md: initial; --gem-row-tracks-sm: initial; --gem-row-gap: initial; --gem-row-gap-y: initial; }