v0.2.0:37 — "Limited" → "Limited discount" on launch-special meta

Adds the word "discount" so buyers don't misread the limit as a
license count. "Limited: 10 remaining" was ambiguous; "Limited
discount: 10 remaining" is unmistakable.

Landing-page dynamic tier-card JS matches in a separate commit on
the keysat-xyz-landing repo.

Cosmetic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Grant
2026-05-11 18:32:20 -05:00
parent e05d357a5a
commit aaf8bddfe4
2 changed files with 10 additions and 6 deletions
+7 -5
View File
@@ -1166,7 +1166,7 @@ fn render_tier_picker(
// the launch-meta div as an in-flow element — that coupling // the launch-meta div as an in-flow element — that coupling
// made cross-card row alignment impossible). // made cross-card row alignment impossible).
// - featured_ribbon: absolutely-positioned diagonal banner // - featured_ribbon: absolutely-positioned diagonal banner
// - launch_meta_html: in-flow "Limited: N remaining" // - launch_meta_html: in-flow "Limited discount: N remaining"
// - original_price_html: in-flow struck-through original // - original_price_html: in-flow struck-through original
let (featured_ribbon, launch_meta_html, original_price_html) = if let Some(code) = featured { let (featured_ribbon, launch_meta_html, original_price_html) = if let Some(code) = featured {
let tagline = if code.kind == "percent" { let tagline = if code.kind == "percent" {
@@ -1179,12 +1179,14 @@ fn render_tier_picker(
"LAUNCH SPECIAL".to_string() "LAUNCH SPECIAL".to_string()
}; };
let remaining = code.max_uses.map(|m| (m - code.used_count).max(0)).unwrap_or(-1); let remaining = code.max_uses.map(|m| (m - code.used_count).max(0)).unwrap_or(-1);
// Display: "Limited: N remaining" (not "N of M remaining"). // Display: "Limited discount: N remaining". "discount"
// The total cap is operator-private — buyers don't need to // makes clear what's limited (otherwise buyers might read
// infer launch volume from the M. // it as the number of LICENSES left, not the discount-code
// uses left). N alone — no "of M" — keeps initial launch
// volume operator-private.
let launch_meta = if remaining > 0 { let launch_meta = if remaining > 0 {
format!( format!(
"<div class=\"tier-launch-meta\">Limited: {} remaining</div>", "<div class=\"tier-launch-meta\">Limited discount: {} remaining</div>",
remaining, remaining,
) )
} else { } else {
+3 -1
View File
@@ -58,6 +58,8 @@ const RELEASE_NOTES = [
// in RELEASE_NOTES above (the milestone). Subsequent revisions // in RELEASE_NOTES above (the milestone). Subsequent revisions
// append here. // append here.
const ROUTINE_NOTES = [ const ROUTINE_NOTES = [
'0.2.0:37 — **"Limited" → "Limited discount".** Adds the word "discount" to the launch-special remaining-count label so it\'s unambiguous what\'s limited. Without it, a buyer scanning a tier card with a launch ribbon might read "Limited: 10 remaining" as "only 10 licenses left at this tier" rather than "only 10 uses of the discount code left." Both surfaces (buy page tier card + landing-page dynamic card) now render "Limited discount: N remaining". Cosmetic.',
'',
'0.2.0:36 — **Launch-special remaining count drops the total.** The buy-page tier card\'s "Limited: N of M remaining" line now reads just "Limited: N remaining". The total cap (M) is operator-private — there\'s no upside to exposing initial volume to buyers, and it can make a tier look smaller than the operator wants to signal. Symmetric change in the landing-page dynamic tier-card render. Cosmetic; no API or schema change.', '0.2.0:36 — **Launch-special remaining count drops the total.** The buy-page tier card\'s "Limited: N of M remaining" line now reads just "Limited: N remaining". The total cap (M) is operator-private — there\'s no upside to exposing initial volume to buyers, and it can make a tier look smaller than the operator wants to signal. Symmetric change in the landing-page dynamic tier-card render. Cosmetic; no API or schema change.',
'', '',
'0.2.0:35 — **Free tiers render as "Free" on the buy page tier card.** Previously the server rendered a 0-priced tier as `0 sats` (or `0.00 USD`) in the tier-card price headline, even though the price card BELOW the picker already swapped to `FREE` via the JS path. Inconsistency fixed at the server-render layer: when post-discount price is 0, the tier card renders the headline as `Free` with no unit suffix and no cadence (`Free /yr` would be incoherent). Recurring-meta line ("Renews annually") still surfaces beneath for recurring tiers that happen to be free, so the cadence is still visible — just not stuffed into the headline. Cosmetic; no API or schema change.', '0.2.0:35 — **Free tiers render as "Free" on the buy page tier card.** Previously the server rendered a 0-priced tier as `0 sats` (or `0.00 USD`) in the tier-card price headline, even though the price card BELOW the picker already swapped to `FREE` via the JS path. Inconsistency fixed at the server-render layer: when post-discount price is 0, the tier card renders the headline as `Free` with no unit suffix and no cadence (`Free /yr` would be incoherent). Recurring-meta line ("Renews annually") still surfaces beneath for recurring tiers that happen to be free, so the cadence is still visible — just not stuffed into the headline. Cosmetic; no API or schema change.',
@@ -497,7 +499,7 @@ const ROUTINE_NOTES = [
].join('\n\n') ].join('\n\n')
export const v0_2_0 = VersionInfo.of({ export const v0_2_0 = VersionInfo.of({
version: '0.2.0:36', version: '0.2.0:37',
releaseNotes: { en_US: ROUTINE_NOTES }, releaseNotes: { en_US: ROUTINE_NOTES },
// No on-disk transformation needed — v0.2.0:0 is a label change. // No on-disk transformation needed — v0.2.0:0 is a label change.
// SQLite-level migrations live separately under // SQLite-level migrations live separately under