v0.2.0:26 — Buy-page + entitlement-picker visual polish
Cluster of small visual fixes: - Tier-card feature list seam. Zeroed margin-top between adjacent marketing-bullet + entitlement lists in either order so the gap between lists matches the within-list gap. Reads as one column. - MOST POPULAR clip. When a tier was both highlighted AND had a launch ribbon, overflow:hidden (for the ribbon overhang) was also clipping the popular pill that floats above the card. Pill drops to top:8px (inside the card) only for the highlighted + has-launch combination. - Price card width. :23 stretched the price card to 1040px alongside the headline; that overpowered everything below the tier picker. Constrained back to 560px (centered); headline stays full-width. - Entitlement bubble picker theme. Selected chips switch from gold- filled to navy-filled with cream text (matches "Selected" tier- select-btn + Featured-ON toggle). Hidden-on-buy state drops the strikethrough — opacity:0.5 on the whole pill is the signal. - Discount-code policy multi-pickers follow the same navy theme on Create + Edit (re-aligned from the brief gold pass in :25). - Admin Policies grid also drops strikethrough on hidden chips; opacity-only, italic "(hidden on buy)" hint stays. CSS + inline-style only; no data, schema, or API change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -205,10 +205,11 @@ body {{
|
||||
centered text within the full width; only the form below is
|
||||
constrained narrower for focused interaction. */
|
||||
.wrap {{ max-width:1040px; margin:48px auto; padding:0 24px; }}
|
||||
/* Form stays narrow so input fields aren't oddly stretched. */
|
||||
.wrap > form {{ max-width:560px; margin-left:auto; margin-right:auto; }}
|
||||
/* Headline elements + price card text-align center at the wider width
|
||||
so the page visually stays centered as readers scan top-to-bottom. */
|
||||
/* Form + price card stay narrow so inputs aren't oddly stretched and
|
||||
the price card doesn't visually swallow the tier picker above it.
|
||||
Headline + description span the full container with centered text. */
|
||||
.wrap > form,
|
||||
.wrap > .cert {{ max-width:560px; margin-left:auto; margin-right:auto; }}
|
||||
.wrap > h1, .wrap > .product-slug, .wrap > .description {{ text-align:center; }}
|
||||
.cert {{ text-align:center; }}
|
||||
.eyebrow {{
|
||||
@@ -334,6 +335,15 @@ h1 {{
|
||||
letter-spacing:0.16em; text-transform:uppercase;
|
||||
padding:4px 10px; border-radius:999px;
|
||||
white-space:nowrap;
|
||||
z-index:3;
|
||||
}}
|
||||
/* When a tier carries BOTH the "most popular" pill and the launch
|
||||
ribbon, the ribbon's `overflow:hidden` (which clips the ribbon's
|
||||
off-card overhang) was also clipping the popular pill that sits 10px
|
||||
above the card. Move the pill INSIDE the card top edge in that
|
||||
specific combination so the pill stays visible. */
|
||||
.tier.has-launch .tier-popular {{
|
||||
top:8px;
|
||||
}}
|
||||
.tier-name {{
|
||||
font-family:var(--font-display); font-weight:600; font-size:18px;
|
||||
@@ -392,10 +402,13 @@ h1 {{
|
||||
content:'✓'; position:absolute; left:0; top:3px;
|
||||
color:var(--gold-700); font-weight:700;
|
||||
}}
|
||||
/* Marketing bullets and entitlements should read as one coherent
|
||||
feature list regardless of which one renders first. */
|
||||
.tier-bullets + .tier-entitlements {{ margin-top:2px; }}
|
||||
.tier-entitlements + .tier-bullets {{ margin-top:2px; }}
|
||||
/* Marketing bullets and entitlements should read as ONE coherent
|
||||
feature list regardless of which one renders first. Zero margin-top
|
||||
here so the gap between the two lists matches the within-list gap
|
||||
(each li already contributes 3px of top + bottom padding, so 6px
|
||||
total between consecutive lines either way). */
|
||||
.tier-bullets + .tier-entitlements {{ margin-top:0; }}
|
||||
.tier-entitlements + .tier-bullets {{ margin-top:0; }}
|
||||
.tier-select-btn {{
|
||||
margin-top:auto;
|
||||
padding:8px 12px;
|
||||
|
||||
@@ -2358,7 +2358,7 @@ hr.div { border:none; border-top:1px solid var(--border-1); margin:18px 0; }
|
||||
? desc + ' — Hidden from the buy page tier card (license still grants it).'
|
||||
: desc,
|
||||
style: 'padding:2px 0 2px 16px; position:relative' +
|
||||
(isHidden ? '; opacity:0.55; text-decoration:line-through' : ''),
|
||||
(isHidden ? '; opacity:0.5' : ''),
|
||||
}, [
|
||||
el('span', {
|
||||
style: 'position:absolute; left:0; top:2px; color:var(--gold-700); font-weight:700',
|
||||
@@ -3831,15 +3831,15 @@ hr.div { border:none; border-top:1px solid var(--border-1); margin:18px 0; }
|
||||
}
|
||||
policyMultiHost._available.forEach((p) => {
|
||||
const on = policyMultiHost._selected.has(p.slug)
|
||||
// Match the gold-filled pill convention used by
|
||||
// entitlementBubblePicker so the admin UI looks consistent.
|
||||
// Navy-filled when selected (matches the admin UI's
|
||||
// "selected" / "on" convention). Cream-outlined otherwise.
|
||||
const pill = el('button', {
|
||||
type: 'button',
|
||||
'data-policy-slug': p.slug,
|
||||
style: 'font-size:13px; padding:6px 12px; border-radius:999px; cursor:pointer; ' +
|
||||
'font-family:var(--font-body); font-weight:500; transition:all 100ms; ' +
|
||||
(on
|
||||
? 'background:var(--gold-500); color:var(--navy-950); border:1px solid var(--gold-500);'
|
||||
? 'background:var(--navy-800); color:var(--cream-50); border:1px solid var(--navy-800);'
|
||||
: 'background:transparent; color:var(--ink-700); border:1px solid var(--border-2);'),
|
||||
}, p.name)
|
||||
pill.addEventListener('click', () => {
|
||||
@@ -4222,15 +4222,15 @@ hr.div { border:none; border-top:1px solid var(--border-1); margin:18px 0; }
|
||||
}
|
||||
editPolicies.forEach((p) => {
|
||||
const on = editPolicyHost._selected.has(p.id)
|
||||
// Match the gold-filled pill convention used by
|
||||
// entitlementBubblePicker so the admin UI looks consistent.
|
||||
// Navy-filled when selected (matches the admin UI's
|
||||
// "selected" / "on" convention). Cream-outlined otherwise.
|
||||
const pill = el('button', {
|
||||
type: 'button',
|
||||
'data-policy-id': p.id,
|
||||
style: 'font-size:13px; padding:6px 12px; border-radius:999px; cursor:pointer; ' +
|
||||
'font-family:var(--font-body); font-weight:500; transition:all 100ms; ' +
|
||||
(on
|
||||
? 'background:var(--gold-500); color:var(--navy-950); border:1px solid var(--gold-500);'
|
||||
? 'background:var(--navy-800); color:var(--cream-50); border:1px solid var(--navy-800);'
|
||||
: 'background:transparent; color:var(--ink-700); border:1px solid var(--border-2);'),
|
||||
}, p.name)
|
||||
pill.addEventListener('click', () => {
|
||||
@@ -6005,10 +6005,22 @@ hr.div { border:none; border-top:1px solid var(--border-1); margin:18px 0; }
|
||||
function paint(pill, slug) {
|
||||
const isSel = selected.has(slug)
|
||||
const isHid = hidden.has(slug)
|
||||
// Pill container styling tracks selection state.
|
||||
pill.style.background = isSel ? 'var(--gold-500)' : 'transparent'
|
||||
pill.style.color = isSel ? 'var(--navy-950)' : 'var(--ink-700)'
|
||||
pill.style.borderColor = isSel ? 'var(--gold-500)' : 'var(--border-2)'
|
||||
// Navy-filled pill when selected (matches "Selected" tier-select-
|
||||
// btn + Featured-ON toggle conventions in the admin UI). Cream
|
||||
// outline when not selected. Hidden state = reduced opacity on
|
||||
// the whole pill — no strikethrough since the chip is still
|
||||
// granted by the license.
|
||||
if (isSel) {
|
||||
pill.style.background = 'var(--navy-800)'
|
||||
pill.style.color = 'var(--cream-50)'
|
||||
pill.style.borderColor = 'var(--navy-800)'
|
||||
pill.style.opacity = isHid ? '0.5' : '1'
|
||||
} else {
|
||||
pill.style.background = 'transparent'
|
||||
pill.style.color = 'var(--ink-700)'
|
||||
pill.style.borderColor = 'var(--border-2)'
|
||||
pill.style.opacity = '1'
|
||||
}
|
||||
// Eye toggle: only visible/clickable when entitlement is selected.
|
||||
const eye = pill.querySelector('[data-eye]')
|
||||
const nameEl = pill.querySelector('[data-name]')
|
||||
@@ -6016,14 +6028,16 @@ hr.div { border:none; border-top:1px solid var(--border-1); margin:18px 0; }
|
||||
eye.style.display = isSel ? 'inline-flex' : 'none'
|
||||
// "Open eye" = visible on buy; "closed eye" = hidden on buy.
|
||||
eye.textContent = isHid ? '\u{1F441}\u{200D}\u{1F5E8}' : '\u{1F441}'
|
||||
eye.style.opacity = isHid ? '0.5' : '1'
|
||||
eye.title = isHid
|
||||
? 'Hidden from the buy page tier card. Click to show.'
|
||||
: 'Shown on the buy page tier card. Click to hide (license still grants it).'
|
||||
}
|
||||
// No text strikethrough — opacity on the whole pill is enough to
|
||||
// signal "muted / hidden on buy" without the misleading "deleted"
|
||||
// affordance of a strike.
|
||||
if (nameEl) {
|
||||
nameEl.style.textDecoration = isSel && isHid ? 'line-through' : 'none'
|
||||
nameEl.style.opacity = isSel && isHid ? '0.6' : '1'
|
||||
nameEl.style.textDecoration = 'none'
|
||||
nameEl.style.opacity = '1'
|
||||
}
|
||||
}
|
||||
function renderPill(entry) {
|
||||
|
||||
Reference in New Issue
Block a user