@gemboss/website-ui 0.8.0 → 0.9.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.
package/AGENTS.md CHANGED
@@ -10,7 +10,7 @@ Files you will use, all inside `node_modules/@gemboss/website-ui/`:
10
10
  | file | what it is |
11
11
  |---|---|
12
12
  | `website.css` | the whole kit, one file (tokens, base, layout, components, sections) |
13
- | `js/website.js` | header, mega menu, mobile menu (and its drill-down), language switcher, theme toggle, scroll reveal, segmented control, busy buttons, an article's contents and its tables' scroll box, the footer's folds; no dependencies; `window.gwWebsite.init(root)` |
13
+ | `js/website.js` | header, mega menu, mobile menu (and its drill-down), language switcher, theme toggle, scroll reveal, segmented control, busy buttons, an article's contents and its tables' scroll box, the footer's folds, the spotlight; no dependencies; `window.gwWebsite.init(root)` |
14
14
  | `js/website.mjs` | the same script as an ES module for apps: `import { init, destroy } from '@gemboss/website-ui/website.mjs'` |
15
15
  | `system.json` | every value of the system: colours, spacing, type, radii, shadows, widths |
16
16
  | `templates/page.html` | the page skeleton: start every new page from it |
@@ -23,6 +23,7 @@ Files you will use, all inside `node_modules/@gemboss/website-ui/`:
23
23
  | `demo/forms.html` | fields in every state, choices, the footer newsletter, a short form, stars, the testimonial wall and the rating badge |
24
24
  | `demo/footer.html` | the full footer (Ask AI, columns that fold on phones, family rows, theme toggle, wordmark), and the same for a GemPages-style brand |
25
25
  | `demo/header.html` | the header of gemboss.ai: the mobile menu that drills down with its bar of actions, and the language switcher, and the same for a GemPages-style brand |
26
+ | `demo/spotlight.html` | the spotlight on plan cards, link cards, the feature card and a night band's button, and the same for a GemPages-style brand |
26
27
  | `docs/components.md` | every class, what it is for, its variants |
27
28
  | `docs/theming.md` | what a brand other than GemBoss chooses (accent, wash, logo, three named profiles), and what it must not change |
28
29
 
@@ -122,6 +123,9 @@ Files you will use, all inside `node_modules/@gemboss/website-ui/`:
122
123
  else on white or `--gw-bg-soft`.
123
124
  - **Motion**: only `transform` and `opacity`, the kit's ease, nothing moves under `prefers-reduced-motion`. Scroll
124
125
  reveal is `.gw-reveal` (the JS adds `.is-in`, and a failsafe shows it anyway).
126
+ - **Spotlight** (0.9.0): `data-gw-spot` only on a link, a button or a card with exactly one action (a plan, a link
127
+ card, the mega menu's feature card, a night band's primary button). The kit draws the light in the accent; never on a
128
+ heading or text, never in another colour, never your own script.
125
129
 
126
130
  ## 4. Don't (each of these has happened, and each one made a page look cheap or broke it)
127
131
 
@@ -272,8 +276,9 @@ with numbers and no caption, an unknown brand profile or one set off `<html>`, `
272
276
  words on the accent button below 4.5:1, breadcrumbs without their markup, a contents link to an id that is not on
273
277
  the page, a callout without its label, stars without their value in words, a rating or a testimonial without a link to
274
278
  its source, a footer fold without its heading in the summary or a wordmark a screen reader reads, a drill-down level
275
- without its Back or its title, a language switcher shown by the markup or a translation without `hreflang`, and
276
- patterns that repeat across pages (likely gaps). It does not replace looking at the page at 320px and 1366px.
279
+ without its Back or its title, a language switcher shown by the markup or a translation without `hreflang`, a
280
+ spotlight on a heading or on something that is not one action, and patterns that repeat across pages (likely gaps). It
281
+ does not replace looking at the page at 320px and 1366px.
277
282
 
278
283
  ## 11. Upgrading the kit
279
284
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,66 @@ Every release lists what changed for a site that uses the kit. A release that ad
4
4
  says so with `Closes gap #N` (the GitHub issue number), so `npx gemboss-website upgrade` can tell that site which
5
5
  stopgap (`kitgap-*`) it can now delete.
6
6
 
7
+ ## 0.9.0 (2026-10-25)
8
+
9
+ The spotlight, a named hover effect for the things you click (Chris, 25/10/2026, chosen over a heading that glows
10
+ under the pointer): a soft light in the brand's accent follows the pointer over a plan card, a link card, the mega menu's
11
+ feature card or the primary button of a night band. Opt-in per element: a page without `data-gw-spot` looks exactly
12
+ as before. The demo index (without its new link), `brand-profiles.html`, `article.html`, `blog.html`, `forms.html`,
13
+ `footer.html`, `header.html` and `theme-example.html` are pixel-identical to 0.8.0 at 390 and 1366px, light and dark.
14
+
15
+ Closes #74
16
+
17
+ - `data-gw-spot` on an `<a href>`, a `<button>`, or a card (`<div>`, `<article>`, `<li>`) that holds exactly one of
18
+ them: a `.gw-plan` with its one `.gw-btn`, a `.gw-card--link`, the `.gw-mega__feature`, the `.gw-btn` of a
19
+ `.gw-band--night`. `data-gw-spot="<selector>"` lights the first element the selector names inside it instead (the art
20
+ of a card). Never on a heading or running text: the script refuses a heading, and the kit still forbids gradient or
21
+ accent-coloured words in a heading, and lights of more than one colour.
22
+ - The light: a radial gradient on the `::before` of the element that carries it, under its words, in its own corners
23
+ (`border-radius: inherit`, so both radius profiles hold), no layout change. One colour, `--gw-accent` mixed with
24
+ `color-mix()`: 22% on a card, 38% on a button, 40% on the Stage's night ground; the `-dark` family in dark mode and in
25
+ night bands, the brand's own accent everywhere (a GemPages-style page lights blue). Its radius, a registered
26
+ `@property --gw-spot-r` that inherits, grows to 260px (120px on a button) in 0.55s with `cubic-bezier(.16,1,.3,1)`:
27
+ the one animated property that is neither transform nor opacity (`system.json` `motion` says so).
28
+ - The motion, as Chris asked after the prototype: the light starts where the pointer comes in, then trails it, going
29
+ 18% of the way that is left on each frame (one `requestAnimationFrame` loop per element, stopped within 0.3px), so a
30
+ jump of the pointer is followed over several frames, never snapped.
31
+ - Pointer devices only: the CSS is all inside `(hover: hover) and (pointer: fine)`, and `website.js` adds no listener
32
+ without a fine pointer (again on every change). Keyboard focus shows the same light, centred and as large, on the
33
+ focused host or on a card whose one action has the focus (`:has(:focus-visible)`, in a rule of its own). Reduced
34
+ motion: no listener, a still light in the middle that appears without a transition.
35
+ - Fallback: without `@property` or `color-mix()` the script wires nothing, so nothing is drawn, not half a light.
36
+ - Tokens (`system.json` `motion.spot`): `--gw-spot-radius`, `--gw-spot-radius-action`, `--gw-spot-strength`,
37
+ `--gw-spot-strength-action`, `--gw-spot-strength-stage`, `--gw-spot-ms`, `--gw-spot-ease`; the trail and the settle
38
+ distance are the script's, held equal to `motion.spot.trail` and `.settle` by the tests.
39
+ - CSS: `css/effects.css`, last in `website.css`. Every rule of it selects a wired spot (`data-gw-ready~="spot"`) or the
40
+ element carrying its light (`.gw-spot`, set by the script), so no page without the attribute can change.
41
+ - `website.js`: the `spot` part in the `init(root)`/`destroy(root)` registry (`data-gw-ready="spot"`), a
42
+ `pointerenter` and a `pointermove` listener per element, `--gw-spot-x` and `--gw-spot-y` on the element that carries
43
+ the light, nothing else and no event. `destroy` takes the listeners, the frame, the class and the place back.
44
+ - `check`: `spot-target` (an error): `data-gw-spot` on a heading, with a selector that names a heading, on running text
45
+ or a section, on an `<a>` without `href`, or on a card with no link or button or with more than one.
46
+ - Words under the light: while a thing is lit (hover, keyboard focus), its muted words read `--gw-ink-2`: the host sets
47
+ `--gw-muted: var(--gw-ink-2)`, so every part that uses the muted token follows, with no class of its own. Muted would
48
+ dip to 4.0:1 at the light's middle on a white card (5.3:1 at rest); ink-2 there is 8.2:1 measured in Chromium (7.6:1
49
+ in dark), and 4.5:1 or more on every ground by the tokens (a card, a soft card, a night band). The words turn ink-2 at
50
+ once and turn back only once the light has gone (a colour that waits 0.55s, no movement; at once under reduced
51
+ motion). Ink keeps 10:1 or more and a button's words 4.5:1 in every profile, light and dark.
52
+ - Demo: `demo/spotlight.html`, plan cards, link cards, the feature card (also in the Product menu) and the night band's
53
+ CTA, with a switch to a GemPages-style brand (`data-gw-action="accent"`, `data-gw-radius="soft"`, `#3c67ff`), linked
54
+ from `demo/index.html`. No other demo page opts in.
55
+ - Budget: `js/website.js` is 23.1 KB unminified, 7.1 KB gzipped (0.8.0: 20.8 KB, 6.4 KB); the test's budget is now
56
+ 24 KB and 8 KB. `website.min.css` is 73.8 KB, 12.7 KB gzipped (0.8.0: 71.8 KB, 12.3 KB).
57
+ - Tests: `test/run.mjs` holds the CSS (every rule opt-in, fine pointer only, the accent only, the reduced-motion and
58
+ focus rules, the tokens), the contrast under the light, the script in its fake DOM (entry point, trailing frame by
59
+ frame, settling, touch, inner element, init twice, destroy, media changes, no `@property`, no `color-mix()`) and the
60
+ check rule; `test/browser.mjs` drives `demo/spotlight.html` in Chromium (the radius read on the `::before` while
61
+ hovering, the light following a jump over several frames, no layout shift, the Stage and the night band, Tab to a
62
+ card and to a plan's button, reduced motion, a touch screen with no listener counted by DevTools and the same
63
+ pixels after a tap, no `@property` or `color-mix()`, init twice and destroy counted by DevTools, a heading refused
64
+ by the script and flagged by `check`, the GemPages-style accent and corners, light and dark, and a muted line at the
65
+ light's middle measured on the drawn pixel: 4.5:1 or more, light and dark, ink-2 while the light fades, muted after).
66
+
7
67
  ## 0.8.0 (2026-10-25)
8
68
 
9
69
  The header of gemboss.ai on a phone, so the site's header can move onto the kit: a mobile menu that drills down into
package/README.md CHANGED
@@ -12,11 +12,11 @@ it. Made so that a team working with Claude Code, even without a designer, ships
12
12
 
13
13
  | | |
14
14
  |---|---|
15
- | `website.css`, `website.min.css` | toàn bộ kit trong một tệp (khoảng 101 KB, 72 KB bản min, 12,2 KB khi nén gzip): token sáng và tối, chữ, bố cục, nút (cả trạng thái nhấn, đang chờ và nút xoá), link, nhãn, chip, segmented control, badge, card (kèm ảnh trên đầu thẻ), khung cửa sổ kèm chú thích "Illustrative data.", bảng, FAQ, chữ bài dài (`.gw-prose`), bộ khung bài blog (breadcrumb, dòng tác giả và ngày, mục lục dính, hộp lưu ý, danh sách bài, phân trang), ô nhập và trạng thái lỗi (ô chữ, danh sách chọn, ô nhiều dòng, checkbox, radio, hàng một ô một nút cho newsletter), sao đánh giá, tường review, huy hiệu điểm App Store, header + mega menu + menu điện thoại (tuỳ chọn đi sâu từng mục kèm thanh nút dính đáy, như gemboss.ai) + nút đổi ngôn ngữ, footer như gemboss.ai (cột gập lại trên điện thoại, hàng Ask AI, hàng sản phẩm cùng nhà, nút sáng/tối, wordmark; ô newsletter tuỳ chọn), 16 section, 3 hồ sơ thương hiệu |
16
- | `js/website.js`, `js/website.mjs` | mega menu, menu điện thoại (kể cả bản đi sâu từng mục: Back, giữ focus trong menu, sự kiện `gw:menu`), nút đổi ngôn ngữ (đọc `<link rel="alternate" hreflang>` của trang, ẩn khi trang chỉ có một ngôn ngữ), đổi giao diện sáng/tối, hiện dần khi cuộn, đánh dấu trang hiện tại, segmented control, chặn bấm lần hai khi nút đang chờ, đánh dấu mục đang đọc trong mục lục bài viết, khung cuộn cho bảng trong bài, cột footer gập/mở theo khổ màn; không phụ thuộc thư viện nào, khoảng 6,4 KB khi nén gzip. `website.js` là script thường (tự chạy khi trang tải xong), `website.mjs` là bản ES module cho app; cả hai có `init(root)` để gắn lại phần markup chèn sau (`docs/markup-contract.md`, mục "Script") |
15
+ | `website.css`, `website.min.css` | toàn bộ kit trong một tệp (khoảng 107 KB, 74 KB bản min, 12,7 KB khi nén gzip): token sáng và tối, chữ, bố cục, nút (cả trạng thái nhấn, đang chờ và nút xoá), link, nhãn, chip, segmented control, badge, card (kèm ảnh trên đầu thẻ), khung cửa sổ kèm chú thích "Illustrative data.", bảng, FAQ, chữ bài dài (`.gw-prose`), bộ khung bài blog (breadcrumb, dòng tác giả và ngày, mục lục dính, hộp lưu ý, danh sách bài, phân trang), ô nhập và trạng thái lỗi (ô chữ, danh sách chọn, ô nhiều dòng, checkbox, radio, hàng một ô một nút cho newsletter), sao đánh giá, tường review, huy hiệu điểm App Store, header + mega menu + menu điện thoại (tuỳ chọn đi sâu từng mục kèm thanh nút dính đáy, như gemboss.ai) + nút đổi ngôn ngữ, footer như gemboss.ai (cột gập lại trên điện thoại, hàng Ask AI, hàng sản phẩm cùng nhà, nút sáng/tối, wordmark; ô newsletter tuỳ chọn), đốm sáng theo con trỏ trên thứ bấm được (`data-gw-spot`, bật từng chỗ), 16 section, 3 hồ sơ thương hiệu |
16
+ | `js/website.js`, `js/website.mjs` | mega menu, menu điện thoại (kể cả bản đi sâu từng mục: Back, giữ focus trong menu, sự kiện `gw:menu`), nút đổi ngôn ngữ (đọc `<link rel="alternate" hreflang>` của trang, ẩn khi trang chỉ có một ngôn ngữ), đổi giao diện sáng/tối, hiện dần khi cuộn, đánh dấu trang hiện tại, segmented control, chặn bấm lần hai khi nút đang chờ, đánh dấu mục đang đọc trong mục lục bài viết, khung cuộn cho bảng trong bài, cột footer gập/mở theo khổ màn, đốm sáng theo con trỏ; không phụ thuộc thư viện nào, khoảng 7,1 KB khi nén gzip. `website.js` là script thường (tự chạy khi trang tải xong), `website.mjs` là bản ES module cho app; cả hai có `init(root)` để gắn lại phần markup chèn sau (`docs/markup-contract.md`, mục "Script") |
17
17
  | `AGENTS.md` | luật mà Claude Code (và người) phải theo: 10 luật, cách dựng trang, điều cấm, chữ, khả năng truy cập, đổi thương hiệu, khi kit thiếu |
18
18
  | `templates/` | khung một trang, header (`header.html`: hai tuỳ chọn, menu điện thoại đi sâu và nút ngôn ngữ), footer (`footer.html`: footer của gemboss.ai, mọi phần tuỳ chọn đều ghi rõ) và 16 section (hero, logos, stats, features, split, showcase, steps, quote, pricing, faq, cta, prose, article, post-list, form, testimonials) |
19
- | `demo/` | mọi thành phần trên một trang; `theme-example.html` cho thấy một thương hiệu khác đổi màu nhấn; `brand-profiles.html` là một thương hiệu kiểu GemPages bật cả 3 hồ sơ; `article.html` là một bài blog trọn vẹn, `blog.html` là trang danh sách bài; `forms.html` là mọi trạng thái của ô nhập, newsletter ở footer, tường review và huy hiệu điểm (dữ liệu mẫu); `footer.html` là footer đầy đủ như gemboss.ai, kèm nút chuyển sang bản kiểu GemPages; `header.html` là header của gemboss.ai trên điện thoại (menu đi sâu từng mục, thanh nút dính đáy, nút đổi ngôn ngữ với ba ngôn ngữ), cũng có bản kiểu GemPages |
19
+ | `demo/` | mọi thành phần trên một trang; `theme-example.html` cho thấy một thương hiệu khác đổi màu nhấn; `brand-profiles.html` là một thương hiệu kiểu GemPages bật cả 3 hồ sơ; `article.html` là một bài blog trọn vẹn, `blog.html` là trang danh sách bài; `forms.html` là mọi trạng thái của ô nhập, newsletter ở footer, tường review và huy hiệu điểm (dữ liệu mẫu); `footer.html` là footer đầy đủ như gemboss.ai, kèm nút chuyển sang bản kiểu GemPages; `header.html` là header của gemboss.ai trên điện thoại (menu đi sâu từng mục, thanh nút dính đáy, nút đổi ngôn ngữ với ba ngôn ngữ), cũng có bản kiểu GemPages; `spotlight.html` là đốm sáng trên thẻ gói giá, thẻ link, thẻ nổi bật của mega menu và nút trong dải tối, cũng có bản kiểu GemPages |
20
20
  | `docs/` | danh mục class (`components.md`), đổi thương hiệu (`theming.md`), triết lý (`philosophy.md`), hợp đồng markup |
21
21
  | `system.json` | mọi giá trị của hệ thống (màu, khoảng cách, chữ, bo góc, bóng, khung): nguồn duy nhất |
22
22
  | CLI `gemboss-website` | `init`, `check`, `gap`, `upgrade` |
@@ -40,9 +40,9 @@ nó đọc `AGENTS.md` của kit trước, dựng từ mẫu, và chạy `check`
40
40
  **Site HTML tĩnh hoặc không có bước build:** dùng CDN của npm, ghim đúng phiên bản.
41
41
 
42
42
  ```html
43
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gemboss/website-ui@0.8.0/website.min.css">
43
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@gemboss/website-ui@0.9.0/website.min.css">
44
44
  <link rel="stylesheet" href="/site.css"> <!-- CSS riêng của site, luôn SAU kit -->
45
- <script src="https://cdn.jsdelivr.net/npm/@gemboss/website-ui@0.8.0/js/website.js" defer></script>
45
+ <script src="https://cdn.jsdelivr.net/npm/@gemboss/website-ui@0.9.0/js/website.js" defer></script>
46
46
  ```
47
47
 
48
48
  Chèn markup của kit sau khi trang đã tải (block của builder, bản xem trước CMS): gọi `gwWebsite.init(phầnMới)` ngay
package/bin/lib/check.mjs CHANGED
@@ -159,6 +159,11 @@ export const RULES = {
159
159
  why: "website.js fills .gw-lang from the page's <link rel=\"alternate\" hreflang href> and shows it only when there is another language: it starts hidden, its button (.gw-lang__btn) names its list with aria-controls and says aria-expanded, and the list is empty in the markup. A switcher shown by the markup is an empty button on a page with one language; links typed into the list are replaced; a translation's <link rel=\"alternate\"> without hreflang is one the switcher (and search engines) cannot list.",
160
160
  fix: "Copy the switcher from templates/header.html: <div class=\"gw-lang\" data-gw-lang hidden><button class=\"gw-lang__btn\" type=\"button\" aria-expanded=\"false\" aria-controls=\"gw-lang-menu\">…</button><ul class=\"gw-lang__menu\" id=\"gw-lang-menu\" hidden></ul></div>, and give each translation <link rel=\"alternate\" hreflang=\"vi\" href=\"…\"> in <head>.",
161
161
  },
162
+ "spot-target": {
163
+ level: "error", title: "Spotlight on something that is not one action",
164
+ why: "data-gw-spot lights what you can click: the light says \"this goes somewhere\". On a heading or running text it promises a link that is not there (and a heading stays ink: no gradient, no accent words, no light); on a box with no action, or several, the light cannot say which one it means.",
165
+ fix: "Put data-gw-spot on the <a href> or the <button> itself, or on a card (a <div>, <article> or <li>) that holds exactly one of them, such as a .gw-plan with its one .gw-btn. Never on a heading, a paragraph or a section, and never with a selector that names a heading (docs/components.md, Spotlight).",
166
+ },
162
167
  "uppercase-text": {
163
168
  level: "warning", title: "Uppercase text",
164
169
  why: "Headings, buttons, labels and menu items are sentence case.",
@@ -509,6 +514,9 @@ function elementEnd(text, start, tag) {
509
514
  }
510
515
  return -1;
511
516
  }
517
+ // The spotlight (0.9.0): a card lit as a whole is one of these, holding exactly one link or button.
518
+ const SPOT_BOX = new Set(["div", "article", "li"]);
519
+ const HEADING_SEL = /(?:^|[\s>+~,(])h[1-6](?![\w-])/i;
512
520
  const LOADING_CLASS = /^(?:is-)?(?:loading|busy)$|--(?:loading|busy)$/;
513
521
  const SPINNER_CLASS = /\bclass\s*=\s*["'][^"']*(?:spinner|loader|loading)/i;
514
522
 
@@ -692,6 +700,17 @@ function analyzeHtml(file, text, out) {
692
700
  if (missing.length) add("lang-markup", where(`${m[0].replace(/\s+/g, " ").slice(0, 80)} missing: ${missing.join(", ")}`));
693
701
  }
694
702
  if (tag === "link" && /(?:^|\s)alternate(?:\s|$)/i.test(a.rel || "") && !a.type) alts.push({ a, where: where() });
703
+ if ("data-gw-spot" in a) {
704
+ const sel = (a["data-gw-spot"] || "").trim();
705
+ const acts = (inner().match(/<a\b[^>]*\bhref\s*=|<button\b/gi) || []).length;
706
+ const why = /^h[1-6]$/.test(tag) ? `on a heading (<${tag}>)`
707
+ : HEADING_SEL.test(sel) ? `its selector names a heading (data-gw-spot="${sel}")`
708
+ : (tag === "a" && "href" in a) || tag === "button" ? ""
709
+ : tag === "a" ? "on an <a> without href"
710
+ : !SPOT_BOX.has(tag) ? `on a <${tag}>, which is not a link, a button or a card`
711
+ : acts !== 1 ? `on a <${tag}> that holds ${acts} links or buttons, not exactly one` : "";
712
+ if (why) add("spot-target", where(`${m[0].replace(/\s+/g, " ").slice(0, 80)} ${why}`));
713
+ }
695
714
  if (cls.includes("gw-rating-badge") || cls.includes("gw-testimonial")) {
696
715
  const linked = (tag === "a" && "href" in a) || /<a\b[^>]*\bhref\s*=/i.test(inner());
697
716
  if (!linked) add("rating-source", where(`${m[0].replace(/\s+/g, " ").slice(0, 80)} ${decode(visible.slice(m.index, elementEnd(scanText, m.index, tag))).replace(/\s+/g, " ").trim().slice(0, 60)}`));
@@ -0,0 +1,71 @@
1
+ /* effects: the spotlight (0.9.0, Chris 25/10/2026, issue #74), a named hover effect for the things you click.
2
+ data-gw-spot on an <a href>, a <button>, or a card that holds exactly one of them (a plan card, the mega menu's
3
+ feature card, the primary button of a night band, a .gw-card--link): a soft light in the brand's accent follows the
4
+ pointer over it. Opt-in per element: a page without the attribute does not change.
5
+ website.js wires the host (data-gw-ready="spot") and puts .gw-spot on the element that carries the light: the host,
6
+ or the one its data-gw-spot="<selector>" names inside it. It writes the pointer's place there (--gw-spot-x,
7
+ --gw-spot-y, trailing it a little every frame); this file draws the light on its ::before, under its words, in its
8
+ own corners (border-radius: inherit, so the radius profile holds), and grows the light's radius on hover.
9
+ - Pointer devices only: nothing here applies without (hover: hover) and (pointer: fine).
10
+ - Keyboard focus (on the host, or on the one action of a card) shows the same light, centred.
11
+ - Reduced motion: the light stands still, centred, and appears without a transition.
12
+ - Words under the light: while a thing is lit, its muted words read --gw-ink-2 (the token, set as --gw-muted on the
13
+ host), so they keep 4.5:1 at the light's middle; they turn back once the light has gone. A colour, no movement.
14
+ - Without @property or color-mix(), website.js puts .gw-spot nowhere: no light at all, nothing half drawn.
15
+ - One colour, the accent family, as the page has it (the -dark family in dark mode and in night bands). Never on a
16
+ heading or running text: a heading stays ink, with no gradient and no accent words (check: spot-target).
17
+ The radius is the one animated property that is neither transform nor opacity: it repaints one layer of one
18
+ element. Registered, so it can transition; inherited, so the ::before (and an inner element) reads the host's. */
19
+ @property --gw-spot-r {
20
+ syntax: "<length>";
21
+ inherits: true;
22
+ initial-value: 0px;
23
+ }
24
+
25
+ @media (hover: hover) and (pointer: fine) {
26
+ /* the host: no light at rest (a lit card does not light a lit button inside it), the light's size on hover and focus */
27
+ [data-gw-ready~="spot"] { --gw-spot-r: 0px; }
28
+ [data-gw-ready~="spot"]:is(:hover, :focus-visible) { --gw-spot-r: var(--gw-spot-radius); --gw-muted: var(--gw-ink-2); }
29
+ /* a card whose one action has the keyboard focus; a rule of its own, so a browser without :has() drops only it */
30
+ [data-gw-ready~="spot"]:has(:focus-visible) { --gw-spot-r: var(--gw-spot-radius); --gw-muted: var(--gw-ink-2); }
31
+ .gw-btn[data-gw-ready~="spot"]:is(:hover, :focus-visible) { --gw-spot-r: var(--gw-spot-radius-action); }
32
+
33
+ /* the element that carries the light: positioned for its layer, isolated so the layer sits under its words.
34
+ :where(), so a site's own position for an inner element wins. */
35
+ :where(.gw-spot) { position: relative; isolation: isolate; }
36
+ /* the light. Not on a busy button: its ::before is the ring. */
37
+ .gw-spot:not([aria-busy="true"])::before {
38
+ --gw-spot-mix: var(--gw-spot-strength);
39
+ content: "";
40
+ position: absolute;
41
+ inset: 0;
42
+ z-index: -1;
43
+ border-radius: inherit;
44
+ background: radial-gradient(circle var(--gw-spot-r) at var(--gw-spot-x, 50%) var(--gw-spot-y, 50%), color-mix(in srgb, var(--gw-accent) var(--gw-spot-mix), transparent), transparent 70%);
45
+ pointer-events: none;
46
+ transition: --gw-spot-r var(--gw-spot-ms) var(--gw-spot-ease);
47
+ }
48
+ /* a button is small and filled: a stronger light. Its words keep their contrast: the light is the accent over the
49
+ button's fill, so under the accent action profile it lies between the accent and the accent-strong, the two fills
50
+ the build checks the words on. */
51
+ .gw-btn.gw-spot:not([aria-busy="true"])::before { --gw-spot-mix: var(--gw-spot-strength-action); }
52
+ /* the mega menu's feature card is the Stage, a night ground: a stronger light, or it would not show */
53
+ .gw-mega__feature.gw-spot:not([aria-busy="true"])::before { --gw-spot-mix: var(--gw-spot-strength-stage); }
54
+
55
+ /* words under the light: lit, muted words are ink-2 at once (rules above); back to muted only once the light has gone
56
+ out (a colour that waits the light's time, no movement). :where(), so a part's own transition wins. */
57
+ :where([data-gw-ready~="spot"] *) { transition: color 0s linear var(--gw-spot-ms); }
58
+ :where([data-gw-ready~="spot"]:is(:hover, :focus-visible) *) { transition-delay: 0s; }
59
+ :where([data-gw-ready~="spot"]:has(:focus-visible) *) { transition-delay: 0s; }
60
+
61
+ /* keyboard focus: the light in the middle, so focus is never weaker than hover */
62
+ :is([data-gw-ready~="spot"]:focus-visible, [data-gw-ready~="spot"]:focus-visible *).gw-spot::before { --gw-spot-x: 50%; --gw-spot-y: 50%; }
63
+ :is([data-gw-ready~="spot"]:has(:focus-visible), [data-gw-ready~="spot"]:has(:focus-visible) *).gw-spot::before { --gw-spot-x: 50%; --gw-spot-y: 50%; }
64
+ }
65
+
66
+ /* reduced motion: a still light in the middle, there at once and gone at once, the words with it (website.js follows
67
+ no pointer then) */
68
+ @media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
69
+ .gw-spot:not([aria-busy="true"])::before { --gw-spot-x: 50%; --gw-spot-y: 50%; transition: none; }
70
+ :where([data-gw-ready~="spot"] *) { transition: none; }
71
+ }
package/css/tokens.css CHANGED
@@ -69,6 +69,13 @@
69
69
  --gw-ease: cubic-bezier(.22,1,.36,1);
70
70
  --gw-reveal-ms: 700ms;
71
71
  --gw-spin-ms: 700ms;
72
+ --gw-spot-radius: 260px;
73
+ --gw-spot-radius-action: 120px;
74
+ --gw-spot-strength: 22%;
75
+ --gw-spot-strength-action: 38%;
76
+ --gw-spot-strength-stage: 40%;
77
+ --gw-spot-ms: 550ms;
78
+ --gw-spot-ease: cubic-bezier(.16,1,.3,1);
72
79
  --gw-wash-pink: #ff6f5e;
73
80
  --gw-wash-violet: #7c8af7;
74
81
  --gw-wash-sky: #43b0ff;
package/demo/index.html CHANGED
@@ -587,6 +587,7 @@ npx gemboss-website check --fix-list</code></pre>
587
587
  <p class="demo-mt">Fields in every state, the footer newsletter, a testimonial wall and the rating badge: <a href="forms.html">forms and ratings</a>.</p>
588
588
  <p class="demo-mt">The footer as gemboss.ai has it, with Ask AI links, columns that fold on a phone, family rows and the wordmark: <a href="footer.html">the full footer</a>.</p>
589
589
  <p class="demo-mt">The header as gemboss.ai has it on a phone, with a menu that drills down into each section and the language switch: <a href="header.html">the header</a>.</p>
590
+ <p class="demo-mt">A light in the accent that follows the pointer on plan cards, link cards, the feature card and the button of a night band: <a href="spotlight.html">the spotlight</a>.</p>
590
591
  </div>
591
592
  </section>
592
593
 
@@ -0,0 +1,291 @@
1
+ <!doctype html>
2
+ <!-- Kit demo (0.9.0): the spotlight, data-gw-spot (docs/components.md, Spotlight), on what it is for: plan cards, link
3
+ cards, the mega menu's feature card (open Product, or see it below) and the primary button of a night band. Move a
4
+ mouse over them; on a phone nothing changes. The switch shows the same page for a GemPages-style brand: soft corners,
5
+ the accent button and the GemPages accent of docs/theming.md, so the light turns blue by itself. A real site picks
6
+ its profiles once, on <html> (docs/theming.md); here a small demo script sets them when the switch moves. Words and
7
+ prices from gemboss.ai; "Example brand" is not GemPages. -->
8
+ <html lang="en">
9
+ <head>
10
+ <meta charset="utf-8">
11
+ <meta name="viewport" content="width=device-width, initial-scale=1">
12
+ <title>Spotlight · website UI kit</title>
13
+ <meta name="description" content="The kit's spotlight: a soft light in the brand's accent that follows the pointer on plan cards, link cards, the feature card and the button of a night band.">
14
+ <script>try{var t=localStorage.getItem('gw-theme');if(t==='light'||t==='dark')document.documentElement.setAttribute('data-theme',t)}catch(e){}</script>
15
+ <link rel="preconnect" href="https://fonts.googleapis.com">
16
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
17
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;650;700&display=swap">
18
+ <link rel="stylesheet" href="../website.css">
19
+ <!-- the GemPages-style brand's whole CSS (docs/theming.md, "A full example"); on only while the switch says so -->
20
+ <style id="demo-gempages" media="not all">
21
+ :root {
22
+ --gw-accent-light: #3c67ff;
23
+ --gw-accent-strong-light: #2f52d6;
24
+ --gw-accent-soft-light: #eaefff;
25
+ --gw-on-accent-light: #ffffff;
26
+ --gw-accent-dark: #7c9bff;
27
+ --gw-accent-strong-dark: #a9beff;
28
+ --gw-accent-soft-dark: #16213d;
29
+ --gw-on-accent-dark: #0d0d12;
30
+ }
31
+ </style>
32
+ </head>
33
+ <body class="gw-page">
34
+ <a class="gw-skip" href="#main">Skip to content</a>
35
+
36
+ <div class="gw-announce" role="region" aria-label="Announcement">Kit demo: the spotlight, a light that follows the pointer on what you can click. <a href="index.html">Back to the full demo</a></div>
37
+ <header class="gw-header" data-gw-header>
38
+ <div class="gw-wrap gw-header__bar">
39
+ <a class="gw-brand" href="https://gemboss.ai/" data-gw-show="brand:gemboss"><svg class="gw-brand__mark" viewBox="0 0 100 116" aria-hidden="true"><polygon points="26,14 74,14 66,50 34,50" style="fill:var(--gw-accent)"/><path d="M26,14 74,14 96,50 50,110 4,50Z" fill="none" stroke="currentColor" stroke-width="9" stroke-linejoin="round"/><path d="M4,50H96M26,14 34,50M74,14 66,50M34,50 50,110M66,50 50,110" fill="none" stroke="currentColor" stroke-width="5.6" stroke-linejoin="round"/></svg><span class="gw-brand__name">GemBoss</span></a><a class="gw-brand" href="spotlight.html#gempages" data-gw-show="brand:gempages" hidden><svg class="gw-brand__mark" viewBox="0 0 26 26" aria-hidden="true"><rect width="26" height="26" rx="7" style="fill:var(--gw-accent)"/><path d="M8 9h10M8 13h10M8 17h6" fill="none" stroke="#ffffff" stroke-width="2" stroke-linecap="round"/></svg><span class="gw-brand__name">Example brand</span></a>
40
+ <nav class="gw-nav" aria-label="Main">
41
+ <ul class="gw-nav__list">
42
+ <li class="gw-nav__item">
43
+ <button class="gw-nav__trigger" type="button" aria-expanded="false" aria-controls="gw-mega-product" data-gw-mega="gw-mega-product">Product</button>
44
+ <div class="gw-mega" id="gw-mega-product" hidden>
45
+ <div class="gw-wrap gw-mega__grid">
46
+ <div class="gw-mega__col">
47
+ <p class="gw-mega__heading">Build</p>
48
+ <a class="gw-mega__link" href="https://gemboss.ai/funnel"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 4h18l-7 8v6l-4 2v-8z"/></svg></span><span><b>Funnel builder</b><i>Angles, advertorials, offers</i></span></a>
49
+ <a class="gw-mega__link" href="https://gemboss.ai/ai-store-builder"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l1.5-5h15L21 9"/><path d="M4 9v11h16V9"/><path d="M9 20v-6h6v6"/></svg></span><span><b>Storefront builder</b><i>Headless, on your Shopify</i></span></a>
50
+ <a class="gw-mega__link" href="https://gemboss.ai/gempages"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/></svg></span><span><b>Page builder</b><i>Ask, watch, publish</i></span></a>
51
+ </div>
52
+ <div class="gw-mega__col">
53
+ <p class="gw-mega__heading">Grow</p>
54
+ <a class="gw-mega__link" href="https://gemboss.ai/gemx"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4v16M20 4v16M4 9h16M4 15h16"/></svg></span><span><b>Tests &amp; personalization</b><i>You promote the winner</i></span></a>
55
+ <a class="gw-mega__link" href="https://gemboss.ai/gemoffers"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M20 12l-8 8-9-9V3h8z"/><circle cx="7.5" cy="7.5" r="1.5"/></svg></span><span><b>Offers &amp; upsells</b><i>Bundles, gifts, bumps</i></span></a>
56
+ <a class="gw-mega__link" href="https://gemboss.ai/data"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M7 15l4-4 3 3 5-6"/></svg></span><span><b>Analytics &amp; data</b><i>First-party, yours</i></span></a>
57
+ </div>
58
+ <div class="gw-mega__col">
59
+ <p class="gw-mega__heading">Connect</p>
60
+ <a class="gw-mega__link" href="https://gemboss.ai/integrations"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M9 3v6M15 3v6M6 9h12v3a6 6 0 0 1-12 0z"/><path d="M12 18v3"/></svg></span><span><b>Integrations</b><i>Klaviyo, Meta, Google</i></span></a>
61
+ <a class="gw-mega__link" href="https://gemboss.ai/mcp"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="m8 9-4 3 4 3"/><path d="m16 9 4 3-4 3"/><path d="m14 5-4 14"/></svg></span><span><b>MCP &amp; agents</b><i>Claude and Codex</i></span></a>
62
+ <a class="gw-mega__link" href="https://gemboss.ai/agent-ready"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="8" width="14" height="11" rx="3"/><path d="M12 4v4"/><circle cx="9.5" cy="13.5" r="1"/><circle cx="14.5" cy="13.5" r="1"/></svg></span><span><b>Agent-ready storefront</b><i>Ready for AI shoppers</i></span></a>
63
+ </div>
64
+ <a class="gw-mega__feature" data-gw-spot href="https://gemboss.ai/engine"><b>How the engine works</b><i>Six stages, one loop. Nothing ships without you.</i><span class="gw-link gw-link--arrow">See the engine</span></a>
65
+ </div>
66
+ </div>
67
+ </li>
68
+ <li class="gw-nav__item">
69
+ <button class="gw-nav__trigger" type="button" aria-expanded="false" aria-controls="gw-mega-usecases" data-gw-mega="gw-mega-usecases">Use cases</button>
70
+ <div class="gw-mega" id="gw-mega-usecases" hidden>
71
+ <div class="gw-wrap gw-mega__grid">
72
+ <div class="gw-mega__col">
73
+ <p class="gw-mega__heading">Adaptive funnels</p>
74
+ <a class="gw-mega__link" href="https://gemboss.ai/meta-ads"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11v2a1 1 0 0 0 1 1h3l5 4V6L7 10H4a1 1 0 0 0-1 1z"/><path d="M16 9a4 4 0 0 1 0 6"/></svg></span><span><b>Meta, Google &amp; TikTok ads</b><i>A funnel for every ad angle</i></span></a>
75
+ <a class="gw-mega__link" href="https://gemboss.ai/klaviyo-personalized-landing-pages"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="m3 7 9 6 9-6"/></svg></span><span><b>Klaviyo email <span class="gw-badge gw-badge--warning">Early access</span></b><i>A page per recipient, after the click</i></span></a>
76
+ </div>
77
+ <div class="gw-mega__col">
78
+ <p class="gw-mega__heading">Adaptive storefront</p>
79
+ <a class="gw-mega__link" href="https://gemboss.ai/ai-store-builder"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l1.5-5h15L21 9"/><path d="M4 9v11h16V9"/><path d="M9 20v-6h6v6"/></svg></span><span><b>Headless storefront</b><i>Your whole Shopify store, on GemBoss</i></span></a>
80
+ <a class="gw-mega__link" href="https://gemboss.ai/agent-ready"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="8" width="14" height="11" rx="3"/><path d="M12 4v4"/><circle cx="9.5" cy="13.5" r="1"/><circle cx="14.5" cy="13.5" r="1"/></svg></span><span><b>AI agents &amp; AI search</b><i>Readable by AI, shoppable by agents</i></span></a>
81
+ </div>
82
+ <a class="gw-mega__feature" data-gw-spot href="https://gemboss.ai/meta-ads"><b>Every ad gets its own page</b><i>The page keeps the promise the ad made.</i><span class="gw-link gw-link--arrow">See the ad-to-page path</span></a>
83
+ </div>
84
+ </div>
85
+ </li>
86
+ <li class="gw-nav__item"><a class="gw-nav__link" href="https://gemboss.ai/pricing">Pricing</a></li>
87
+ <li class="gw-nav__item">
88
+ <button class="gw-nav__trigger" type="button" aria-expanded="false" aria-controls="gw-mega-resources" data-gw-mega="gw-mega-resources">Resources</button>
89
+ <div class="gw-mega" id="gw-mega-resources" hidden>
90
+ <div class="gw-wrap gw-mega__grid">
91
+ <div class="gw-mega__col">
92
+ <p class="gw-mega__heading">Learn</p>
93
+ <a class="gw-mega__link" href="https://gemboss.ai/blog"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4h11a2 2 0 0 1 2 2v14H6a2 2 0 0 1-2-2z"/><path d="M8 8h7M8 12h7"/></svg></span><span><b>Blog</b><i>Playbooks and guides</i></span></a>
94
+ <a class="gw-mega__link" href="https://gemboss.ai/docs"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/></svg></span><span><b>Docs</b><i>Guides and MCP tools</i></span></a>
95
+ </div>
96
+ <div class="gw-mega__col">
97
+ <p class="gw-mega__heading">Company</p>
98
+ <a class="gw-mega__link" href="https://gemboss.ai/company"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 11v5M12 8h.01"/></svg></span><span><b>About</b><i>Why we build it</i></span></a>
99
+ <a class="gw-mega__link" href="https://gemboss.ai/contact"><span class="gw-mega__icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg></span><span><b>Contact</b><i>Talk to a human</i></span></a>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </li>
104
+ </ul>
105
+ </nav>
106
+ <div class="gw-header__actions">
107
+ <button class="gw-theme-toggle" type="button" data-gw-theme-toggle aria-label="Switch to dark theme"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 4a8 8 0 0 1 0 16z" fill="currentColor"/></svg></button>
108
+ <a class="gw-nav__link gw-header__signin" href="https://admin.gemboss.ai/login">Sign in</a>
109
+ <a class="gw-btn gw-btn--secondary gw-btn--sm" href="https://gemboss.ai/contact">Book a demo</a>
110
+ <a class="gw-btn gw-btn--sm" href="https://admin.gemboss.ai/signup">Start free</a>
111
+ <button class="gw-menu-toggle" type="button" aria-expanded="false" aria-controls="gw-mobile-menu" data-gw-menu-toggle aria-label="Open menu"><svg class="gw-menu-toggle__open" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M4 7h16M4 12h16M4 17h16"/></svg><svg class="gw-menu-toggle__close" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button>
112
+ </div>
113
+ </div>
114
+ <div class="gw-mobile-menu" id="gw-mobile-menu" hidden>
115
+ <nav class="gw-wrap" aria-label="Main, mobile">
116
+ <details class="gw-mobile-menu__group">
117
+ <summary>Product</summary>
118
+ <ul class="gw-mobile-menu__list">
119
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/funnel">Funnel builder</a></li>
120
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/ai-store-builder">Storefront builder</a></li>
121
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/gemx">Tests &amp; personalization</a></li>
122
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/gemoffers">Offers &amp; upsells</a></li>
123
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/integrations">Integrations</a></li>
124
+ </ul>
125
+ </details>
126
+ <details class="gw-mobile-menu__group">
127
+ <summary>Use cases</summary>
128
+ <ul class="gw-mobile-menu__list">
129
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/meta-ads">Meta, Google &amp; TikTok ads</a></li>
130
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/ai-store-builder">Headless storefront</a></li>
131
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/agent-ready">AI agents &amp; AI search</a></li>
132
+ </ul>
133
+ </details>
134
+ <a class="gw-mobile-menu__top" href="https://gemboss.ai/pricing">Pricing</a>
135
+ <details class="gw-mobile-menu__group">
136
+ <summary>Resources</summary>
137
+ <ul class="gw-mobile-menu__list">
138
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/blog">Blog</a></li>
139
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/docs">Docs</a></li>
140
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/company">About</a></li>
141
+ <li><a class="gw-mobile-menu__link" href="https://gemboss.ai/contact">Contact</a></li>
142
+ </ul>
143
+ </details>
144
+ <div class="gw-mobile-menu__actions">
145
+ <a class="gw-btn gw-btn--block" href="https://admin.gemboss.ai/signup">Start free</a>
146
+ <a class="gw-btn gw-btn--secondary gw-btn--block" href="https://gemboss.ai/contact">Book a demo</a>
147
+ <a class="gw-mobile-menu__link" href="https://admin.gemboss.ai/login">Sign in</a>
148
+ </div>
149
+ </nav>
150
+ </div>
151
+ </header>
152
+
153
+ <main id="main">
154
+ <section class="gw-section" aria-labelledby="spot-h">
155
+ <div class="gw-wrap">
156
+ <div class="gw-section__head">
157
+ <p class="gw-label">Spotlight, 0.9.0</p>
158
+ <h1 id="spot-h"><span class="gw-dim">A light where you can click.</span> Nowhere else.</h1>
159
+ <p class="gw-lead">Move a mouse over a plan, a card or the button at the end: a soft light in the brand's accent follows it. Headings and words stay still. On a phone nothing changes, and with reduced motion the light stays in the middle.</p>
160
+ </div>
161
+ <div class="gw-segmented" role="radiogroup" aria-label="Brand" data-gw-segmented="brand">
162
+ <button type="button" role="radio" aria-checked="true" value="gemboss">GemBoss</button>
163
+ <button type="button" role="radio" aria-checked="false" value="gempages">GemPages-style</button>
164
+ </div>
165
+ <p class="gw-note" data-gw-show="brand:gemboss">GemBoss: tight corners, the ink button, the iris accent. The words and prices are gemboss.ai's.</p>
166
+ <p class="gw-note" data-gw-show="brand:gempages" hidden>A GemPages-style brand: soft corners, the accent button and GemPages' blue. The light is the brand's accent, so it turns blue by itself. Example brand, not GemPages' page.</p>
167
+ </div>
168
+ </section>
169
+
170
+ <!-- plan cards: each a card with one action, lit as a whole -->
171
+ <section class="gw-section gw-section--soft" aria-labelledby="plans-h">
172
+ <div class="gw-wrap">
173
+ <div class="gw-section__head">
174
+ <p class="gw-label">Plan cards</p>
175
+ <h2 id="plans-h"><span class="gw-dim">One plan, one action.</span> The whole card lights.</h2>
176
+ <p class="gw-lead">Each card holds exactly one button, so the light on the card points at it.</p>
177
+ </div>
178
+ <div class="gw-pricing">
179
+ <div class="gw-plan" data-gw-spot>
180
+ <h3>Starter</h3>
181
+ <p>Find your first winning angle.</p>
182
+ <p class="gw-plan__price">$49 <small>/ month</small></p>
183
+ <p>Billed monthly through Shopify</p>
184
+ <a class="gw-btn gw-btn--secondary gw-btn--block" href="https://admin.gemboss.ai/signup">Start 14-day free trial</a>
185
+ <ul class="gw-list--check">
186
+ <li>10,000 funnel sessions a month</li>
187
+ <li>3 funnels live at once</li>
188
+ <li>2 A/B tests at once</li>
189
+ <li>50 AI credits a month</li>
190
+ </ul>
191
+ </div>
192
+ <div class="gw-plan gw-plan--featured" data-gw-spot>
193
+ <span class="gw-badge gw-badge--accent">Recommended for $10k–50k</span>
194
+ <h3>Growth</h3>
195
+ <p>Test new angles every week.</p>
196
+ <p class="gw-plan__price">$149 <small>/ month</small></p>
197
+ <p>Billed monthly through Shopify</p>
198
+ <a class="gw-btn gw-btn--block" href="https://admin.gemboss.ai/signup">Start 14-day free trial</a>
199
+ <ul class="gw-list--check">
200
+ <li>50,000 funnel sessions a month</li>
201
+ <li>15 funnels live at once</li>
202
+ <li>5 A/B tests at once</li>
203
+ <li>200 AI credits a month</li>
204
+ </ul>
205
+ </div>
206
+ <div class="gw-plan" data-gw-spot>
207
+ <h3>Scale</h3>
208
+ <p>Run many angles at once.</p>
209
+ <p class="gw-plan__price">$399 <small>/ month</small></p>
210
+ <p>Billed monthly through Shopify</p>
211
+ <a class="gw-btn gw-btn--secondary gw-btn--block" href="https://admin.gemboss.ai/signup">Start 14-day free trial</a>
212
+ <ul class="gw-list--check">
213
+ <li>200,000 funnel sessions a month</li>
214
+ <li>Unlimited funnels live at once</li>
215
+ <li>20 A/B tests at once</li>
216
+ <li>500 AI credits a month</li>
217
+ </ul>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </section>
222
+
223
+ <!-- link cards and the mega menu's feature card: each one link -->
224
+ <section class="gw-section" aria-labelledby="cards-h">
225
+ <div class="gw-wrap">
226
+ <div class="gw-section__head">
227
+ <p class="gw-label">Cards</p>
228
+ <h2 id="cards-h"><span class="gw-dim">A card that is one link.</span> The light says so.</h2>
229
+ <p class="gw-lead">Two link cards, and the feature card of the Product menu: the Stage, a night ground where the light is stronger.</p>
230
+ </div>
231
+ <div class="gw-grid--3">
232
+ <a class="gw-card gw-card--link" href="https://gemboss.ai/funnel" data-gw-spot>
233
+ <h3>Funnel builder</h3>
234
+ <p>A funnel for every angle, from the ad to the second order.</p>
235
+ <span class="gw-link gw-link--arrow">See the funnel builder</span>
236
+ </a>
237
+ <a class="gw-card gw-card--link" href="https://gemboss.ai/ai-store-builder" data-gw-spot>
238
+ <h3>Storefront builder</h3>
239
+ <p>A headless storefront on the Shopify you already run.</p>
240
+ <span class="gw-link gw-link--arrow">See the storefront builder</span>
241
+ </a>
242
+ <a class="gw-mega__feature" href="https://gemboss.ai/engine" data-gw-spot><b>How the engine works</b><i>Six stages, one loop. Nothing ships without you.</i><span class="gw-link gw-link--arrow">See the engine</span></a>
243
+ </div>
244
+ </div>
245
+ </section>
246
+
247
+ <!-- night band: only the primary button lights -->
248
+ <section class="gw-section gw-band--night" aria-labelledby="cta-h">
249
+ <div class="gw-wrap gw-cta">
250
+ <h2 id="cta-h">Put the engine to work on your next launch.</h2>
251
+ <p class="gw-lead">Start with one product. GemBoss drafts the angles, the pages and the first test, and waits for your OK.</p>
252
+ <div class="gw-actions">
253
+ <a class="gw-btn" href="https://admin.gemboss.ai/signup" data-gw-spot>Start free</a>
254
+ <a class="gw-btn gw-btn--secondary" href="https://gemboss.ai/contact">Book a demo</a>
255
+ </div>
256
+ </div>
257
+ </section>
258
+ </main>
259
+
260
+ <footer class="gw-footer">
261
+ <div class="gw-wrap">
262
+ <div class="gw-footer__bottom">
263
+ <span><span data-gw-show="brand:gemboss">© 2026 GemBoss. All rights reserved.</span><span data-gw-show="brand:gempages" hidden>© 2026 Example brand. Sample text.</span></span>
264
+ <ul aria-label="Legal">
265
+ <li><a href="https://gemboss.ai/privacy">Privacy</a></li>
266
+ <li><a href="https://gemboss.ai/terms">Terms</a></li>
267
+ </ul>
268
+ </div>
269
+ </div>
270
+ </footer>
271
+
272
+ <script src="../js/website.js" defer></script>
273
+ <script>
274
+ // demo only: the switch above picks a brand. A real site sets its profiles once, in the markup of <html>, and its
275
+ // accent in site.css (docs/theming.md); this page flips them so one file can show both. #gempages opens it so.
276
+ (function () {
277
+ var root = document.documentElement, accent = document.getElementById('demo-gempages');
278
+ function brand(b) {
279
+ var gp = b === 'gempages';
280
+ if (gp) { root.setAttribute('data-gw-radius', 'soft'); root.setAttribute('data-gw-action', 'accent'); }
281
+ else { root.removeAttribute('data-gw-radius'); root.removeAttribute('data-gw-action'); }
282
+ accent.media = gp ? 'all' : 'not all';
283
+ }
284
+ document.addEventListener('gw:segmented', function (e) { if (e.detail.name === 'brand') brand(e.detail.value); });
285
+ document.addEventListener('DOMContentLoaded', function () {
286
+ if (location.hash === '#gempages') { var o = document.querySelector('[data-gw-segmented="brand"] [value="gempages"]'); if (o) o.click(); }
287
+ });
288
+ })();
289
+ </script>
290
+ </body>
291
+ </html>