diff --git a/licensing-service/web/index.html b/licensing-service/web/index.html
index 49823d0..dc99052 100644
--- a/licensing-service/web/index.html
+++ b/licensing-service/web/index.html
@@ -3831,14 +3831,16 @@ 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.
const pill = el('button', {
type: 'button',
'data-policy-slug': p.slug,
- style: 'font-size:12px; padding:4px 12px; border-radius:999px; cursor:pointer; ' +
- 'border:1.5px solid ' + (on ? 'var(--gold-700)' : 'var(--border-1)') + '; ' +
- 'background:' + (on ? 'var(--navy-950)' : 'var(--cream-100)') + '; ' +
- 'color:' + (on ? 'var(--gold-500)' : 'var(--ink-700)') + '; ' +
- 'font-weight:' + (on ? '600' : '500') + ';',
+ 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:transparent; color:var(--ink-700); border:1px solid var(--border-2);'),
}, p.name)
pill.addEventListener('click', () => {
if (policyMultiHost._selected.has(p.slug)) policyMultiHost._selected.delete(p.slug)
@@ -4220,14 +4222,16 @@ 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.
const pill = el('button', {
type: 'button',
'data-policy-id': p.id,
- style: 'font-size:12px; padding:4px 12px; border-radius:999px; cursor:pointer; ' +
- 'border:1.5px solid ' + (on ? 'var(--gold-700)' : 'var(--border-1)') + '; ' +
- 'background:' + (on ? 'var(--navy-950)' : 'var(--cream-100)') + '; ' +
- 'color:' + (on ? 'var(--gold-500)' : 'var(--ink-700)') + '; ' +
- 'font-weight:' + (on ? '600' : '500') + ';',
+ 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:transparent; color:var(--ink-700); border:1px solid var(--border-2);'),
}, p.name)
pill.addEventListener('click', () => {
if (editPolicyHost._selected.has(p.id)) editPolicyHost._selected.delete(p.id)
diff --git a/startos/versions/v0.2.0.ts b/startos/versions/v0.2.0.ts
index fdb051d..68ced45 100644
--- a/startos/versions/v0.2.0.ts
+++ b/startos/versions/v0.2.0.ts
@@ -58,6 +58,8 @@ const RELEASE_NOTES = [
// in RELEASE_NOTES above (the milestone). Subsequent revisions
// append here.
const ROUTINE_NOTES = [
+ '0.2.0:25 — **Discount-code policy pills match the admin UI palette.** Cosmetic-only fix. The "Restrict to policies" multi-pickers on both the Create and Edit forms were rendering selected pills as dark navy with gold text, which clashed with the gold-filled / cream-outlined pill convention used everywhere else in the admin (entitlements bubble picker, marketing-bullets position, etc.). Aligned both pickers to that shared style. No data or behavior change — purely a CSS swap.',
+ '',
'0.2.0:24 — **Per-entitlement "hide on buy page" toggle.** Decouples "what the license grants" (functional) from "what the buyer sees on the tier card" (marketing). The entitlement is still issued — only its display is filtered.',
'',
'**Why.** A common pattern: Pro inherits everything from Creator. On the buy page, instead of duplicating "Self-host on Start9 ✓" on every tier, the operator wants to write "Everything in Creator, plus:" as a marketing bullet and hide the duplicate entitlement from Pro\'s card. Before :24, the only way was to either drop the entitlement from the policy (breaks the license) or accept the duplication.',
@@ -400,7 +402,7 @@ const ROUTINE_NOTES = [
].join('\n\n')
export const v0_2_0 = VersionInfo.of({
- version: '0.2.0:24',
+ version: '0.2.0:25',
releaseNotes: { en_US: ROUTINE_NOTES },
// No on-disk transformation needed — v0.2.0:0 is a label change.
// SQLite-level migrations live separately under