The offer reaches the hero and the pricing strip

Featurekamo-marketing
Shipped
August 31, 2026 at 2:54 AM UTC
Author
Kamo
Commit
bdda657

The hero had a countdown ribbon already, and it had been rendering NOTHING since the catalog moved to a percentage: `pickHeroOffer` filtered on FLAT_PER_SEAT because the banner's copy was "$18/seat every month for 12 months". A second, narrower definition of "what offer is running" was the whole problem, so it is gone — the hero, the pricing page, the offer page, the calculator and now the homepage strip all read the one `PagePromotion` resolvePromotion returns. The ribbon is re-keyed to the percentage, links to /pricing/offer, and counts down to the END of the last day (midnight ran it out a day early). It carries the ember accent the offer wears on every other surface, so it is recognisable as the promotion before a word of it is read; its CTA ink is measured against that gradient rather than left white, which is under 4.5:1 at both ends. The homepage strip now renders the same PlanPrice component /pricing does — was, now, and what you save — instead of its own hand-rolled ladder over annualPrice. Both open on HOME_TERM, named once in plan-pricing.ts because three surfaces were defaulting independently, and the strip's CTAs carry the code on the plans the offer covers. Plan ids on a resolved promotion now come back in CATALOG order. The API returns them in query order — Business before Starter — and the hero writes them into a sentence, so it read "on every seat of Business and Starter", as though the tiers ran downhill. check-i18n-keys could not see Hero.tsx AT ALL: it binds `const [t, slideshow] = await **************** ...])` and the guard only understood `const t = ...`. Three missing keys in the site's most-read component, invisible to the guard whose entire purpose is catching a raw key path rendering as visible text. It now understands the destructured form, and — more importantly — a file that clearly translates but yields no namespace is REPORTED rather than skipped, so the next new binding shape costs a guard update instead of shipping "home.heroOfferCta" into the hero. Coverage went from 1044 keys to 1073. check-plan-pricing grows to 55 checks, covering how an offer's plans are written out and resolvePromotion against the shape the API actually returns — including that a promotion whose plans fail to resolve is refused rather than widened. Verified against the live catalog rather than the fallback: homepage and /pricing both render $0 / $26.13 / $66.33 / Custom.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing