fix(site): цена «сумма + ₸ + /мес» не разрывается по строкам (320–402px)
Some checks are pending
Auto-tag / Create date-tag (push) Waiting to run
CI / Backend (.NET 8) (push) Waiting to run
CI / Web (React + Vite) (push) Waiting to run
CI / POS (WPF, Windows) (push) Waiting to run
Docker Public / Build + push Public (test + latest) (push) Waiting to run
Docker Public / Deploy :test → test.food-market.kz (push) Blocked by required conditions

Круг 3 закрыл только /pricing. Прогон в Safari на iPhone 17 (ширина 402)
нашёл ещё два разрыва: главная (hero: «5 / 000 ₸/мес») и /signup после
конфигуратора «Бизнес» (итог «14 000 ₸/ | мес» рвётся на слэше, плитки
тарифов тоже).

Системно, а не точечно:
- fmtKzt: разряды и пробел перед «₸» — неразрывные (U+00A0);
- новый fmtKztMonth: сумма + «₸» + «/мес» одним куском (word-joiner U+2060
  вокруг «/»), используется в SignupForm (плитки + итог «Бизнес»);
- index.astro: hero-строка и карточки «Старт»/«Бизнес» — whitespace-nowrap +  ;
- BusinessTariffBuilder: подпись «База 10 000 ₸» — nowrap;
- контент kb/billing.md, kb/faq.md, blog/launch.md, FAQ.tsx — nbsp + word-joiner
  в суммах, чтобы цена не рвалась и в тексте.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nns 2026-10-12 00:32:37 +05:00
parent 93e26dc8df
commit a0f9e0b889
8 changed files with 20 additions and 14 deletions

View file

@ -51,7 +51,7 @@ export default function BusinessTariffBuilder() {
<div className="flex items-baseline justify-between mb-6">
<div>
<h3 className="text-lg font-bold">Конструктор «Бизнес»</h3>
<p className="text-xs text-slate-500 mt-0.5">База 10 000 ₸ + ваши параметры</p>
<p className="text-xs text-slate-500 mt-0.5">База <span className="whitespace-nowrap">10&nbsp;000&nbsp;₸</span> + ваши параметры</p>
</div>
<div className="text-right">
<div className="text-3xl font-extrabold whitespace-nowrap">{fmt.format(total)}&nbsp;₸</div>

View file

@ -7,7 +7,7 @@ const ITEMS = [
},
{
q: 'Сколько стоит подписка?',
a: 'Тариф «Старт» — 5 000 ₸/мес (1 магазин, 1 касса, 1 склад). Тариф «Бизнес» — от 10 000 ₸, цена настраивается через конструктор (до 3 магазинов, 5 касс, 5 складов). Тариф «Сеть» — индивидуально для крупных сетей. Все тарифы — 90 дней бесплатно.',
a: 'Тариф «Старт» — 5 000 ₸⁠/⁠мес (1 магазин, 1 касса, 1 склад). Тариф «Бизнес» — от 10 000 ₸, цена настраивается через конструктор (до 3 магазинов, 5 касс, 5 складов). Тариф «Сеть» — индивидуально для крупных сетей. Все тарифы — 90 дней бесплатно.',
},
{
q: 'Нужно покупать какое-то железо?',

View file

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'
import { validateEmail, validatePassword, validatePhone } from '@/lib/validation'
import { PhoneInput } from '@/components/PhoneInput'
import { PRICING, businessPrice, clamp, fmtKzt, type BusinessConfig } from '@/lib/pricing'
import { PRICING, businessPrice, clamp, fmtKztMonth, type BusinessConfig } from '@/lib/pricing'
const VALID_PLANS = ['start', 'business', 'network'] as const
type PlanId = (typeof VALID_PLANS)[number]
@ -223,8 +223,8 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) {
<div className="text-sm font-medium mb-2">Тариф</div>
<div className="grid grid-cols-3 gap-2">
{[
{ id: 'start', name: 'Старт', price: `${fmtKzt(PRICING.startPrice)}/мес` },
{ id: 'business', name: 'Бизнес', price: `${fmtKzt(businessTotal)}/мес` },
{ id: 'start', name: 'Старт', price: fmtKztMonth(PRICING.startPrice) },
{ id: 'business', name: 'Бизнес', price: fmtKztMonth(businessTotal) },
{ id: 'network', name: 'Сеть', price: 'по запросу' },
].map((p) => (
<label key={p.id} className={`p-2.5 rounded-md border cursor-pointer text-center text-sm ${plan === p.id ? 'border-brand bg-brand/5' : 'border-slate-200'}`}>
@ -238,7 +238,7 @@ export default function SignupForm({ defaultPlan = 'start' }: Props) {
<div className="mt-3 rounded-md border border-brand/30 bg-brand/5 p-3 text-sm">
<div className="flex items-baseline justify-between">
<span className="font-medium">Ваша конфигурация «Бизнес»</span>
<span className="font-bold">{fmtKzt(businessTotal)}/мес</span>
<span className="font-bold whitespace-nowrap">{fmtKztMonth(businessTotal)}</span>
</div>
<div className="mt-1.5 text-xs text-slate-600 grid grid-cols-2 gap-x-4 gap-y-0.5">
<span>Магазинов: <b>{config.shops}</b></span>

View file

@ -22,7 +22,7 @@ cover_image: /photos/blog-launch.jpg
## Что вы получаете
- **5 000 ₸/мес** за тариф «Старт» — 1 магазин, 1 касса, 1 склад, без лимита товаров
- **5 000 ₸⁠/⁠мес** за тариф «Старт» — 1 магазин, 1 касса, 1 склад, без лимита товаров
- **90 дней бесплатно** — без карты и без обязательств
- **Касса для Windows и Linux** с нативной поддержкой торговых весов (приложение для скачивания — скоро)
- **Импорт каталога** из файлов Excel/CSV за один клик

View file

@ -12,8 +12,8 @@ order: 4
Кратко — первые 90 дней бесплатно, без банковской карты:
- **Старт** — 5 000 ₸/мес, 1 магазин, 1 касса, 1 склад, до 2 сотрудников
- **Бизнес** — от 10 000 ₸/мес, конструктор (до 3 магазинов / 5 касс / 5 складов)
- **Старт** — 5 000 ₸⁠/⁠мес, 1 магазин, 1 касса, 1 склад, до 2 сотрудников
- **Бизнес** — от 10 000 ₸⁠/⁠мес, конструктор (до 3 магазинов / 5 касс / 5 складов)
- **Сеть** — по запросу, без ограничений
## Способы оплаты

View file

@ -21,7 +21,7 @@ order: 5
## Тарифы и оплата
### Сколько стоит самый дешёвый тариф?
Старт — 5 000 ₸/мес. Включает 1 магазин, 1 кассу, 1 склад, до 2 сотрудников, без лимита товаров.
Старт — 5 000 ₸⁠/⁠мес. Включает 1 магазин, 1 кассу, 1 склад, до 2 сотрудников, без лимита товаров.
### Что входит в тариф Бизнес?
Конструктор: до 3 магазинов, до 5 касс, до 5 складов. Цена считается по формуле base 10 000 + 2 000 за магазин + 500 за кассу + 500 за склад.

View file

@ -32,4 +32,10 @@ export function clamp(n: number, min: number, max: number): number {
}
const KZT = new Intl.NumberFormat('ru')
export const fmtKzt = (n: number): string => `${KZT.format(n)} ₸`
// Разряды и пробел перед «₸» — неразрывные (U+00A0), чтобы сумма никогда не
// переносилась по строкам (напр. «5» на одной строке, «000 ₸» на следующей).
const nb = (s: string) => s.replace(/\s/g, ' ')
export const fmtKzt = (n: number): string => `${nb(KZT.format(n))} ₸`
// Сумма + «₸» + «/мес» одним неразрывным куском: word-joiner (U+2060) вокруг «/»
// не даёт браузеру разорвать строку на слэше.
export const fmtKztMonth = (n: number): string => `${fmtKzt(n)}⁠/⁠мес`

View file

@ -31,7 +31,7 @@ const modules = [
<a href="/signup" class="px-5 py-3 bg-brand text-white text-sm font-semibold rounded-md hover:bg-brand-hover">Начать бесплатно</a>
<a href="#screenshot" class="px-5 py-3 border border-slate-300 text-sm font-semibold rounded-md hover:bg-slate-50">Посмотреть демо</a>
</div>
<p class="mt-3 text-xs text-slate-500">Без банковской карты · 90 дней бесплатно · Подписка от 5 000 ₸/мес</p>
<p class="mt-3 text-xs text-slate-500">Без банковской карты · 90 дней бесплатно · Подписка от <span class="whitespace-nowrap">5&nbsp;000&nbsp;₸/мес</span></p>
</div>
<div class="rounded-2xl border border-emerald-100 bg-white shadow-2xl overflow-hidden">
<div class="bg-slate-100 border-b border-slate-200 px-3 py-2 flex gap-1.5">
@ -192,7 +192,7 @@ const modules = [
<div class="grid lg:grid-cols-3 gap-4">
<div class="rounded-xl border border-slate-200 p-6 bg-white">
<h3 class="font-bold text-lg">Старт</h3>
<p class="text-3xl font-bold mt-2">5 000 ₸<span class="text-sm font-normal text-slate-500">/мес</span></p>
<p class="text-3xl font-bold mt-2 whitespace-nowrap">5&nbsp;000&nbsp;₸<span class="text-sm font-normal text-slate-500">/мес</span></p>
<p class="text-xs text-slate-500 mt-1">90 дней бесплатно</p>
<ul class="text-sm space-y-1.5 mt-5">
<li>1 магазин · 1 касса · 1 склад</li>
@ -206,7 +206,7 @@ const modules = [
<div class="rounded-xl border-2 border-brand p-6 bg-white relative">
<span class="absolute -top-3 left-1/2 -translate-x-1/2 bg-brand text-white text-xs px-2 py-0.5 rounded-full">Популярный</span>
<h3 class="font-bold text-lg">Бизнес</h3>
<p class="text-3xl font-bold mt-2">от 10 000 ₸<span class="text-sm font-normal text-slate-500">/мес</span></p>
<p class="text-3xl font-bold mt-2">от <span class="whitespace-nowrap">10&nbsp;000&nbsp;₸<span class="text-sm font-normal text-slate-500">/мес</span></span></p>
<p class="text-xs text-slate-500 mt-1">Под объём бизнеса</p>
<ul class="text-sm space-y-1.5 mt-5">
<li>До 3 магазинов</li>