diff --git a/CLAUDE.md b/CLAUDE.md index 78f71f4..d3056c7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -105,9 +105,9 @@ Anything describing the mock interview has to match the client (`../client`), wh - Setup is seniority (junior/mid/senior/staff), difficulty (easy/standard/hard) and 3/5/8/12 questions. The role is **not** collected: it comes from the account's job context, and the same profile drives the questions and the scoring - The interviewer speaks its questions, gates the microphone while it does, and can follow up **twice** on one question. A language with no voice makes it write them instead - The session ends in a scored report: overall score, strengths, gaps, then per-question score, justification and a stronger answer, exportable as DOCX or Markdown -- Pricing is per unit of work, not per minute: 20 credits a question, 10 a follow-up, 40 for the report, and its transcription is unmetered (`app/cfg/payment.py` in `../backend`). Quote both halves whenever the site talks about credits, or the copy implies a mock session is metered by the clock +- Pricing is per minute, the same as a live session: 10 credits a minute from the first question to the report (`CREDITS_PER_MINUTE` in `../backend/app/cfg/payment.py`). Any session needs a minute of credit to start and stops when the credits run out; a mock still delivers its report. It used to be priced per question, follow-up and report - copy that says so is stale -`CREDIT_RATES` in `src/lib/plans.ts` mirrors those rates for the components (the pricing section's metering note reads it, alongside the credit packs it already mirrored from the same backend file). Markdown can't import it, so `src/content/docs/mock-interview.md` restates the numbers and `src/config/faq.ts` restates them again for the JSON-LD; a rate change is those three places plus the backend. +`CREDIT_RATES` in `src/lib/plans.ts` mirrors that rate for the components (the pricing section's metering note reads it, alongside the credit packs it already mirrored from the same backend file), and `mockSessionMinutes` mirrors the desktop app's length estimate. Markdown can't import either, so `src/content/docs/mock-interview.md` restates the numbers in its cost table and the FAQ answers restate them as text (`src/config/faq.ts` reads those for the JSON-LD); a rate change is those places plus the backend. ### The language list is data, not copy diff --git a/src/components/sections/PricingSection.tsx b/src/components/sections/PricingSection.tsx index ea53c39..71fe0a5 100644 --- a/src/components/sections/PricingSection.tsx +++ b/src/components/sections/PricingSection.tsx @@ -10,7 +10,7 @@ import { LocalizedLink } from '@/i18n/LocalizedLink'; import type { Locale } from '@/i18n/config'; import { format, pluralize } from '@/i18n/format'; import { getMessages } from '@/i18n/messages'; -import { CREDIT_RATES, mockSessionPrice } from '@/lib/plans'; +import { CREDIT_RATES, mockSessionMinutes } from '@/lib/plans'; import { PricingCards } from './PricingCards'; @@ -28,19 +28,21 @@ const TierValueCell: React.FC<{ value: TierValue; included: string }> = ({ value ); /** - * How the two kinds of session are metered. + * What each kind of session spends. * - * The heading above quotes the per-minute rate, which is the whole story for a - * live interview and none of it for a mock one: that is charged per question, - * per follow-up and once for the report, and its transcription isn't metered - * at all. Rates come from CREDIT_RATES so this and the mock interview page - * can't quote different numbers at each other. + * Both are metered by the minute at the same rate, but a reader comparing + * them wants to see what a typical session of each comes to, and that a + * session stops when the credits run out. Rates come from CREDIT_RATES so + * this and the mock interview page can't quote different numbers at each + * other. */ const MeteringNote: React.FC<{ locale: Locale }> = ({ locale }) => { const { pricing } = getMessages(locale); const t = pricing.metering; // Each amount carries its own noun: "1 кредит", "10 кредитов", "40 кредитов". const credits = (n: number) => `${n} ${pluralize(locale, n, pricing.creditNoun)}`; + const minutes = (n: number) => `${n} ${pluralize(locale, n, pricing.minuteNoun)}`; + const mockMinutes = mockSessionMinutes(8); return ( @@ -51,8 +53,8 @@ const MeteringNote: React.FC<{ locale: Locale }> = ({ locale }) => {
{t.live}
{format(t.liveText, { - rate: credits(CREDIT_RATES.livePerMinute), - thirty: credits(CREDIT_RATES.livePerMinute * 30), + rate: credits(CREDIT_RATES.perMinute), + thirty: credits(CREDIT_RATES.perMinute * 30), })}
@@ -68,10 +70,9 @@ const MeteringNote: React.FC<{ locale: Locale }> = ({ locale }) => {
{format(t.mockText, { - question: credits(CREDIT_RATES.mockPerQuestion), - followUp: credits(CREDIT_RATES.mockPerFollowUp), - report: credits(CREDIT_RATES.mockPerReport), - session: credits(mockSessionPrice(8)), + rate: credits(CREDIT_RATES.perMinute), + minutes: minutes(mockMinutes), + session: credits(CREDIT_RATES.perMinute * mockMinutes), })}
@@ -117,7 +118,7 @@ export const PricingSection = ({ locale, standalone = false }: PricingSectionPro { label: tier.rateLimit.label, trial: tier.rateLimit.trial, paid: tier.rateLimit.paid }, ]; - const rate = CREDIT_RATES.livePerMinute; + const rate = CREDIT_RATES.perMinute; return (
diff --git a/src/content/docs/how-it-works.md b/src/content/docs/how-it-works.md index 9e415fa..3e35448 100644 --- a/src/content/docs/how-it-works.md +++ b/src/content/docs/how-it-works.md @@ -103,4 +103,4 @@ Session transcript/suggestion content is retained in memory for the active app s Credits are consumed while AI-assisted features are active (including transcription and suggestion generation). Credit balance is refreshed periodically during a running session and updates after successful payments. -A mock interview is metered differently, because its wall clock is a poor measure of the work delivered: it is charged per question, per follow-up and once for the report, and its transcription is not metered at all. The client quotes the price before the session begins and the balance is checked against it up front, so a session that starts can reach its report. +A mock interview is metered the same way, at the same rate, from its first question to its report. Any session needs at least a minute of credit to start, the app warns at five minutes and at one minute left, and a session stops when the balance reaches zero - a mock interview still delivers its report. diff --git a/src/content/docs/introduction.md b/src/content/docs/introduction.md index 643a1db..51e8d1c 100644 --- a/src/content/docs/introduction.md +++ b/src/content/docs/introduction.md @@ -80,7 +80,7 @@ The model is provided for you - there is nothing to connect or configure. New ac ### Credits & Payments - Credits are consumed while the assistant is running (AI suggestions and transcription) -- A mock interview is priced per question, follow-up and report instead of by the minute, so think-time is free +- A mock interview is billed by the minute at the same rate, and any session stops when your credits run out - Credit balance is displayed live inside the app - Buy credits directly from the **Buy Credits** page within the app, with payment history and status tracking diff --git a/src/content/docs/live-interview.md b/src/content/docs/live-interview.md index 5fe6156..2cc6c01 100644 --- a/src/content/docs/live-interview.md +++ b/src/content/docs/live-interview.md @@ -235,7 +235,7 @@ The **Clear** button removes all transcripts, reply suggestions, and code sugges Credits are consumed while the assistant is running - covering AI reply suggestions and transcription. Your remaining credit balance is shown live in the titlebar, and in the status bar while in stealth mode. -A [mock interview](/docs/mock-interview) is charged differently: per question, follow-up and report rather than by the minute, with its transcription unmetered. The price of a session is quoted in its setup dialog before it starts. +A session needs at least a minute of credit to start. The app warns you when about five minutes and one minute are left, and when your credits run out the session stops and offers to save the transcript. A [mock interview](/docs/mock-interview) is billed the same way, at the same rate. To buy credits: diff --git a/src/content/docs/mock-interview.md b/src/content/docs/mock-interview.md index 82f0ec0..d8ac1ca 100644 --- a/src/content/docs/mock-interview.md +++ b/src/content/docs/mock-interview.md @@ -16,7 +16,7 @@ This is the half of the app to reach for first: you can run a session tonight, w | Profile / CV | **Account** | Questions and scoring are both grounded in it. | | Job context | **Account** | Stands in for the role, which the setup dialog no longer asks for. | | Microphone | **Configuration** | Your answers are transcribed from it. | -| Credits | Bought in the app | A session is priced per question - see [What a Session Costs](#what-a-session-costs). | +| Credits | Bought in the app | Billed by the minute - see [What a Session Costs](#what-a-session-costs). | If your name or profile is missing, the app says so and sends you to **Account** rather than starting a session you would have to abandon. @@ -32,7 +32,7 @@ Choose **Mock interview** on the home screen. The setup dialog asks for three th | -------------- | -------------------------------- | -------------------------------------------------------------------------------------- | | **Seniority** | Junior, Mid, Senior, Staff | Sets how deep the questions go and how much the answers are expected to carry. | | **Difficulty** | Easy, Standard, Hard | Independent of seniority - a hard junior interview is a real thing. | -| **Questions** | 3, 5, 8 or 12 | The price of each length is shown beside it; a length your balance cannot cover is disabled. | +| **Questions** | 3, 5, 8 or 12 | Each length shows roughly how long it runs; the dialog says how far your balance goes. | The interview language is the same setting the live assistant uses, edited in the same place - change it here and the live assistant follows, and the other way round. @@ -82,18 +82,18 @@ From the report you can **practice again** with the same setup, or finish and re ## What a Session Costs -A live interview is metered by the minute. A mock interview is priced by the work delivered instead, because a large part of its wall clock is the interviewer generating a question, speaking it, scoring the turn and writing the report - none of which you can act during - and most of the rest is your think-time, which is the behaviour the feature exists to train. +A mock interview is billed like a live one: **10 credits a minute**, from the first question to the report. -| Unit | Credits | -| --------------------------- | ------- | -| Question | 20 | -| Follow-up | 10 | -| Report | 40 | -| Transcription, for the whole session | 0 | +| Length | Usually runs | Roughly costs | +| ------------ | ------------ | ------------- | +| 3 questions | 8 minutes | 80 credits | +| 5 questions | 13 minutes | 130 credits | +| 8 questions | 20 minutes | 200 credits | +| 12 questions | 30 minutes | 300 credits | -So a 5-question session costs **140 credits** before follow-ups and at most 240 if every question draws both of them; an 8-question session is **200** and at most 360. +These are estimates: the session is billed for the time it actually takes, so long answers and follow-ups make it longer. The setup dialog shows the estimate and how many minutes your balance covers. -The app quotes the guaranteed price in the setup dialog and checks your balance against it before the first question, so a session that starts is a session that reaches its report. Follow-ups are charged as they are delivered, and are quietly declined rather than allowed to eat into what the report needs. +You need at least a minute of credit to start. The app warns you when about five minutes and one minute are left, and if your credits run out mid-interview the session ends there, keeps the answer you were giving, and still produces your report. Your 1-hour free trial is 600 credits, which covers a couple of full sessions. diff --git a/src/i18n/messages/en/faq.ts b/src/i18n/messages/en/faq.ts index a5c3037..0ead352 100644 --- a/src/i18n/messages/en/faq.ts +++ b/src/i18n/messages/en/faq.ts @@ -88,13 +88,13 @@ export const faq = { question: 'What does a mock interview cost?', category: 'billing', answer: - 'A mock interview is priced per question rather than per minute, so thinking about your answer is free. Each question costs 20 credits, each follow-up 10, and the report at the end 40; the transcription that runs throughout is not metered at all. A 5-question session is therefore 140 credits before follow-ups and an 8-question session 200, and the app quotes the price and checks your balance before the interview starts. Your 1-hour free trial is 600 credits, which covers a couple of full sessions.', + 'The same as the live assistant: 10 credits a minute, from the first question to the report. A 5-question session usually runs about 13 minutes (about 130 credits) and an 8-question one about 20 (about 200), and the app shows that estimate and how far your balance goes before you start. You need at least a minute of credit to begin, and if your credits run out mid-interview it ends there and you still get your report. Your 1-hour free trial is 600 credits, which covers a couple of full sessions.', }, { question: 'How do credits work?', category: 'billing', answer: - 'A live session is metered by the clock: 10 credits per minute of reply suggestions, code analysis and transcription, so a 30-minute interview costs roughly 300 credits. A mock interview is priced by the work delivered instead - see the question above. You can purchase credit packs starting from 600 credits ($5 for ~60 minutes) up to 30,000 credits ($175 for ~3,000 minutes).', + 'Every session is metered by the clock at 10 credits per minute, live or mock, so a 30-minute interview costs roughly 300 credits. A session needs at least a minute of credit to start, the app warns you at five minutes and at one minute left, and it stops when your credits run out. You can purchase credit packs starting from 600 credits ($5 for ~60 minutes) up to 30,000 credits ($175 for ~3,000 minutes).', }, { question: 'What payment methods do you accept?', diff --git a/src/i18n/messages/en/mockInterview.ts b/src/i18n/messages/en/mockInterview.ts index efb4321..9d11a49 100644 --- a/src/i18n/messages/en/mockInterview.ts +++ b/src/i18n/messages/en/mockInterview.ts @@ -36,8 +36,8 @@ export const mockInterview = { body: 'One setting covers the live assistant and the mock alike. {voices} of them the interviewer speaks aloud; where a language has no voice available it writes its questions instead, and the follow-ups and the scoring are unchanged.', }, pricing: { - title: 'Priced by the question, not the clock', - body: 'Think-time is free. You pay for each question, each follow-up and the final report; the transcription that runs the whole way through is not metered at all.', + title: 'Priced by the minute, like the live assistant', + body: 'One rate for both: 10 credits a minute from the first question to the report. If your credits run out, the interview ends there and you still get your report.', }, report: { title: 'The report leaves with you', diff --git a/src/i18n/messages/en/pricing.ts b/src/i18n/messages/en/pricing.ts index 92b9d9b..135a46f 100644 --- a/src/i18n/messages/en/pricing.ts +++ b/src/i18n/messages/en/pricing.ts @@ -31,10 +31,10 @@ export const pricing = { title: 'What a session spends', live: 'Live interview', liveText: - '{rate} a minute while the assistant is running, so a 30-minute call is about {thirty}.', + '{rate} a minute while the assistant is running, so a 30-minute call is about {thirty}. It stops when your credits run out.', mock: 'Mock interview', mockText: - '{question} a question, {followUp} a follow-up and {report} for the report, with the transcription unmetered - so an 8-question session is {session} before follow-ups, and thinking time is free.', + '{rate} a minute from the first question to the report, so an 8-question session of about {minutes} costs about {session}. If your credits run out, it ends and you still get your report.', }, plans: { diff --git a/src/i18n/messages/ru/faq.ts b/src/i18n/messages/ru/faq.ts index 4c9e39d..b1e6bc0 100644 --- a/src/i18n/messages/ru/faq.ts +++ b/src/i18n/messages/ru/faq.ts @@ -76,13 +76,13 @@ export const faq: Faq = { question: 'Сколько стоит пробное собеседование?', category: 'billing', answer: - 'Пробное собеседование оплачивается за вопрос, а не за минуту, поэтому время на обдумывание ответа бесплатно. Каждый вопрос стоит 20 кредитов, каждый уточняющий вопрос - 10, а итоговый отчёт - 40; расшифровка, которая идёт всё время, вообще не тарифицируется. Таким образом, сессия из 5 вопросов - это 140 кредитов без учёта уточняющих вопросов, а из 8 вопросов - 200, причём приложение называет цену и проверяет баланс до начала собеседования. Ваш бесплатный пробный час - это 600 кредитов, чего хватает на пару полных сессий.', + 'Так же, как живой помощник: 10 кредитов в минуту от первого вопроса до отчёта. Сессия из 5 вопросов обычно длится около 13 минут (примерно 130 кредитов), а из 8 вопросов - около 20 (примерно 200), и приложение показывает эту оценку и то, на сколько хватит баланса, ещё до начала. Чтобы начать, нужна хотя бы минута кредитов, а если кредиты закончатся посреди собеседования, оно на этом завершится, и отчёт вы всё равно получите. Ваш бесплатный пробный час - это 600 кредитов, чего хватает на пару полных сессий.', }, { question: 'Как работают кредиты?', category: 'billing', answer: - 'Живая сессия тарифицируется по времени: 10 кредитов в минуту за подсказки для ответов, анализ кода и расшифровку, так что 30-минутное собеседование стоит примерно 300 кредитов. Пробное собеседование оплачивается по объёму выполненной работы - см. вопрос выше. Можно купить пакеты кредитов: от 600 кредитов (5 $ примерно за 60 минут) до 30 000 кредитов (175 $ примерно за 3000 минут).', + 'Любая сессия, живая или пробная, тарифицируется по времени - 10 кредитов в минуту, так что 30-минутное собеседование стоит примерно 300 кредитов. Чтобы начать, нужна хотя бы минута кредитов, приложение предупреждает, когда остаётся пять минут и одна минута, а когда кредиты заканчиваются, сессия останавливается. Можно купить пакеты кредитов: от 600 кредитов (5 $ примерно за 60 минут) до 30 000 кредитов (175 $ примерно за 3000 минут).', }, { question: 'Какие способы оплаты вы принимаете?', diff --git a/src/i18n/messages/ru/mockInterview.ts b/src/i18n/messages/ru/mockInterview.ts index af85107..c7405db 100644 --- a/src/i18n/messages/ru/mockInterview.ts +++ b/src/i18n/messages/ru/mockInterview.ts @@ -37,8 +37,8 @@ export const mockInterview: MockInterview = { body: 'Одна настройка действует и для живого помощника, и для пробного собеседования. На {voices} из них интервьюер говорит вслух; если для языка нет голоса, он пишет вопросы текстом, а уточняющие вопросы и оценка остаются прежними.', }, pricing: { - title: 'Цена за вопрос, а не за время', - body: 'Время на размышление бесплатно. Вы платите за каждый вопрос, каждый уточняющий вопрос и итоговый отчёт; расшифровка, которая идёт всё время, не тарифицируется вообще.', + title: 'Поминутная оплата, как у живого помощника', + body: 'Одна ставка для обоих: 10 кредитов в минуту от первого вопроса до отчёта. Если кредиты закончатся, собеседование на этом завершится, а отчёт вы всё равно получите.', }, report: { title: 'Отчёт остаётся у вас', diff --git a/src/i18n/messages/ru/pricing.ts b/src/i18n/messages/ru/pricing.ts index e42ee06..e9bc281 100644 --- a/src/i18n/messages/ru/pricing.ts +++ b/src/i18n/messages/ru/pricing.ts @@ -35,10 +35,10 @@ export const pricing: Pricing = { title: 'Сколько тратит одна сессия', live: 'Живое собеседование', liveText: - '{rate} в минуту, пока работает помощник, поэтому 30-минутный звонок обойдётся примерно в {thirty}.', + '{rate} в минуту, пока работает помощник, поэтому 30-минутный звонок обойдётся примерно в {thirty}. Когда кредиты закончатся, сессия остановится.', mock: 'Пробное собеседование', mockText: - '{question} за вопрос, {followUp} за уточняющий вопрос и {report} за отчёт, а расшифровка не тарифицируется - поэтому сессия из 8 вопросов стоит {session} без учёта уточняющих вопросов, а время на размышление бесплатно.', + '{rate} в минуту от первого вопроса до отчёта, поэтому сессия из 8 вопросов - это примерно {minutes} и {session}. Если кредиты закончатся, собеседование завершится, а отчёт вы всё равно получите.', }, plans: { diff --git a/src/lib/plans.ts b/src/lib/plans.ts index 69d5ef4..a3b6323 100644 --- a/src/lib/plans.ts +++ b/src/lib/plans.ts @@ -23,23 +23,21 @@ export function getPlans(): Plan[] { /** * What credits are spent on, mirrored from the same backend config as the - * packs above (CREDITS_PER_MINUTE and the three MOCK_CREDITS_PER_* values in - * backend/app/cfg/payment.py). + * packs above (CREDITS_PER_MINUTE in backend/app/cfg/payment.py). * - * The two halves are metered differently and the site has to say so: a live - * session is charged by the minute, a mock interview by the question, the - * follow-up and the report, with its transcription unmetered. Quoting only - * the per-minute rate - which the FAQ did for as long as mock practice was a - * ChatGPT workaround - now describes half the product. + * One rate for everything: a live session and a mock interview are both + * metered by the minute, and both stop when the credits run out. Mock used to + * be priced per question, follow-up and report; it no longer is. */ export const CREDIT_RATES = { - livePerMinute: 10, - mockPerQuestion: 20, - mockPerFollowUp: 10, - mockPerReport: 40, + perMinute: 10, } as const; -/** What a mock interview of `questions` costs before any follow-ups. */ -export function mockSessionPrice(questions: number): number { - return CREDIT_RATES.mockPerQuestion * questions + CREDIT_RATES.mockPerReport; +/** + * How long a mock interview of `questions` usually runs, in minutes. Mirrors + * the desktop app's own estimate (`mockSessionMinutes`), so the site and the + * setup screen quote the same figure. + */ +export function mockSessionMinutes(questions: number): number { + return Math.round(questions * 2.5); }