Click "Block Editor" to enter the edit mode. Use layers, shapes and customize adaptability. Everything is in your hands.
Tilda Publishing
create your own block from scratch
/
Claude finished the response
The user noticed that when the results showed "12 months to goal" in the forecast, the "copy result" button showed "72 months". This is a data sync issue - the share card shows `sh-months` which is populated from `monthsToGoal`, but maybe it was populated incorrectly.
Let me look at the code to find where sh-months is set and where monthsToGoal is computed.
The issue is likely in how monthsToGoal is calculated and stored vs when it's displayed in the share card.
Let me trace through the code:
In calculate(), monthsToGoal is computed inside the forecast section
Then it's used to populate sh-months
But monthsToGoal is a const inside calculate() - it's a local variable. If there's any issue with the order of operations or if monthsToGoal is computed differently in the forecast vs the share card sections, they could diverge.
Let me look at the actual code to understand what happened.
Looking at the code flow:
monthsToGoal is computed as: Math.abs(totalDiff) * KCAL_KG_FC / (Math.abs(defDay) * 30.5)
But wait - defDay could be 0 (for maintenance goal), and totalDiff could be close to 0 (if current weight ≈ desired weight).
Also, monthsToGoal could be null in some cases.
In the forecast section:
javascript
const monthsToGoal = (Math.abs(totalDiff) > 0.05 && defDay !== 0 && movingRight)
? Math.abs(totalDiff) * KCAL_KG_FC / (Math.abs(defDay) * 30.5)
: (Math.abs(totalDiff) <= 0.05 ? 0 : null);
For the share card:
javascript
const shMonths = document.getElementById('sh-months');
if (shMonths) shMonths.textContent = monthsToGoal !== null ? rnd1(monthsToGoal) : '—';
If the user ran the calculation, changed some values (goal, BF, etc.) that changed defDay, and then ran again - the sh-months would get the new value.
But the user said they got 72 months once and 12 months in the results. Let me think about what could cause "72" specifically...
72 months = 6 years. That's a very different number from 12 months.
If defDay was very small (like -50 instead of -350), monthsToGoal would be much larger:
monthsToGoal = totalDiff * 7700 / (defDay * 30.5)
If totalDiff = 6 kg, defDay = -350: months = 6 * 7700 / (350 * 30.5) = 46200 / 10675 = 4.3 months
If totalDiff = 6 kg, defDay = -50: months = 6 * 7700 / (50 * 30.5) = 46200 / 1525 = 30.3 months
If totalDiff = 6 kg, defDay = -17: months = 6 * 7700 / (17 * 30.5) = 46200 / 518.5 = 89 months ≈ 72?
Or maybe it's a different formula path.
Actually, let me think about this differently. The forecast section computes monthsToGoal locally. The share card uses this same local variable in the same function call. So they SHOULD be consistent within a single calculate() call.
Unless the share card shows a value from a PREVIOUS calculate() call while the forecast shows the new value. But they're set in the same function, so that's not possible.
Wait... let me re-read the code more carefully. The forecast section and share card update are in the same calculate() function. They use the same monthsToGoal variable. So within one call, they should be consistent.
UNLESS... monthsToGoal is computed one way in the forecast section and then changes due to some side effect.
Actually, wait. Let me look at the exact code again. In the forecast section:
javascript
const monthsToGoal = (Math.abs(totalDiff) > 0.05 && defDay !== 0 && movingRight)
? Math.abs(totalDiff) * KCAL_KG_FC / (Math.abs(defDay) * 30.5)
: ...
And defDay is:
javascript
const defDay = gCal - adjTdee;
If defDay is very small (like -2 kcal), then monthsToGoal would be huge.
But the forecast section shows "12 months" - meaning the forecast correctly computed something reasonable. The share card shows "72" months.
Oh wait! I think I see it. The forecast section uses defDay in both:
Computing monthsToGoal for the forecast timeline
Populating the share card via monthsToGoal
If they use the same monthsToGoal variable, they should be consistent.
Unless... maybe the issue is that monthsToGoal computed in the forecast might be different due to some rounding. Let me check rnd1:
javascript
function rnd1(x) { return Math.round(x * 10) / 10; }
So rnd1(monthsToGoal) would round to 1 decimal. For "12 months", rnd1(12.x) = 12.x → displayed as "12 months". For "72 months", there would need to be a very different value.
Hmm, 72 months = 6 years. That's 6× more than 12 months. Could there be a unit error somewhere?
Actually wait, let me think about this. What if sh-months is showing monthsToGoal without rnd1? If monthsToGoal is very large before rounding... no, that doesn't explain 72 vs 12.
OR: What if the share card is somehow showing the wrong variable? Let me look at the share card code:
javascript
const shMonths = document.getElementById('sh-months');
if (shMonths) shMonths.textContent = monthsToGoal !== null ? rnd1(monthsToGoal) : '—';
And monthsToGoal is in the forecast section... wait, but is it accessible at this point in the code? Let me trace the code structure.
Actually, monthsToGoal is declared with const inside the calculate() function. The share card update happens later in the same function, so it should have access to monthsToGoal.
But wait - const in JavaScript is block-scoped. If monthsToGoal is declared inside an inner block (like inside an if or inside another section's code block), it might not be accessible in the share card section.
Let me check... in the forecast section, I have:
javascript
// Card 6 — forecast (recomputed fresh)
const KCAL_KG_FC = 7700;
const totalDiff = wt - dw;
const movingRight = ...;
const monthsToGoal = ...;
These are declared at the top level of the calculate() function (no inner blocks), so they should be accessible throughout.
Unless... hmm. Actually wait. Let me re-read the user's report: "В результатах выводилось 12 месяцев до достижения цели, а по кнопке скопировать результат 72 месяца выдало"
The RESULTS show 12 months, but COPY shows 72 months. If both use the same monthsToGoal, this discrepancy shouldn't happen.
UNLESS: the forecast section computes monthsToGoal but the share card uses a DIFFERENT value.
Wait, let me re-read the code more carefully. In the forecast section, after computing fcComputed, I see:
javascript
const shMonths = document.getElementById('sh-months');
if (shMonths) shMonths.textContent = monthsToGoal !== null ? rnd1(monthsToGoal) : '—';
And in the copyResult function:
javascript
var months = document.getElementById('sh-months') ? document.getElementById('sh-months').textContent : '—';
The copy function reads the TEXT CONTENT of sh-months. If sh-months was set correctly to "12" during the most recent calculate(), then copy should show "12".
UNLESS: the sh-months element is set to something during one calculation run, and then the user triggers copyResult without recalculating. But if the user re-ran the calculation (which they said they did to test again), the sh-months should have been updated.
Wait, the user said: "Пробовала тестировать еще раз, больше такого не было" - they tried again and it didn't happen. This suggests it was a one-time anomaly.
Could it be a race condition? In async code, but this is all synchronous...
OR: Could it be that the user ran the calculation with one set of parameters (giving 12 months), then changed something (like BF settings or goal), and the forecast updated to show different months... then copied the old sh-months value?
Actually, the copyResult function reads from the DOM element sh-months. The sh-months is only updated when calculate() is called. So if the user:
Ran calculation → sh-months = 12, forecast shows 12
Changed some parameter (goal, BF, etc.)
Did NOT recalculate
Pressed copy → sh-months still says 12, copy shows 12
The forecast in results should still show the OLD result (12 months) since results don't auto-update.
Hmm, but how would you get 72 in the copy if 12 is showing in the results?
OH WAIT! I think I understand now. Looking at the copyResult function:
javascript
var months = document.getElementById('sh-months') ? document.getElementById('sh-months').textContent : '—';
And sh-months is the display element for months on the share card. But monthsToGoal is computed in calculate() and stored in sh-months.textContent.
Let me look at what value would give 72...
Actually, could the issue be that monthsToGoal is computed as a large number (like 72) because of some edge case, but then the FORECAST caps it? The forecast function uses:
javascript
const goalCrossed = movingRight && (defDay < 0 ? rawNewWt <= dw : rawNewWt >= dw);
And for the forecast display, if monthsToGoal = 72 but the forecast period only goes to 12 months, the 12-month column would show something related to 12 months, not 72.
But the isMilestone check:
javascript
const isMilestone = goalCrossed && !goalMilestoneFound;
If monthsToGoal = 72, then at 12 months: rawNewWt = wt + (defDay × 12 × 30.5)/7700. If defDay is small, this might not reach dw yet, so goalCrossed = false. The display would show "X кг от старта" not "🎯 Цель (~72 мес.)".
So the forecast would show "progress at 12 months" and the share card would show "72 мес. до цели". But the user says "results show 12 months" which I interpret as the results section displaying something about 12 months.
Actually, maybe I'm misinterpreting. Maybe the forecast section had a milestone card that showed "🎯 Цель (~12 мес.)" for the 12-month column, meaning the goal is reached BY 12 months. And the share card showed 72 because monthsToGoal was actually 72, but the forecast milestone appeared in the 12-month column because monthsToGoal (72) >= p.m (12) is false... wait, that's backwards.
The milestone check in forecast:
javascript
goalCrossed = movingRight && (defDay < 0 ? rawNewWt <= dw : rawNewWt >= dw)
At p.m = 12:
rawNewWt = wt + (defDay × 12 × 30.5)/7700
If defDay = -350 and wt=58, dw=52:
rawNewWt = 58 + (-350 × 12 × 30.5)/7700 = 58 - 128100/7700 = 58 - 16.6 = 41.4
goalCrossed = 41.4 <= 52 = true
So for reasonable parameters, 12 months would show the milestone.
But monthsToGoal = (58-52) × 7700 / (350 × 30.5) = 46200/10675 = 4.3 months. Share card would show "4.3" not "72".
Hmm. What scenario gives monthsToGoal = 72?
72 = totalDiff × 7700 / (|defDay| × 30.5)
= totalDiff × 252.459 / |defDay|
If totalDiff = 6, |defDay| = 72×30.5/7700 × 7700 / 6 = ... hmm let me solve differently:
|defDay| = totalDiff × 7700 / (72 × 30.5) = 6 × 7700 / 2196 = 46200/2196 = 21 kcal/day
So a defDay of just -21 kcal/day would give 72 months to goal. That's a tiny deficit.
When would defDay be -21?
defDay = gCal - adjTdee
gCal = adjTdee × (1 + goalPct/100)
defDay = adjTdee × goalPct/100
For loss goal: goalPct = -10
defDay = adjTdee × (-0.10)
For defDay = -21: adjTdee = 210 kcal!
No one has an adjTdee of 210 kcal. That would be extreme.
Alternatively, maybe there's a calculation error somewhere specific. Let me think...
Actually, wait. What about the minCal floor?
javascript
const minCal = Math.round(bmr * 1.1);
const rawGoal = Math.round(adjTdee * (1 + goalPct / 100));
const gCal = Math.max(minCal, rawGoal);
If rawGoal is very low (less than minCal), then gCal = minCal. In this case:
defDay = gCal - adjTdee = minCal - adjTdee
If minCal < adjTdee (which is always true when minCal floor kicks in), defDay is negative.
But the magnitude: defDay = minCal - adjTdee = bmr × 1.1 - adjTdee
Normally adjTdee > bmr × 1.1, so defDay would be a normal deficit. This shouldn't cause extreme values.
Unless adjTdee is very close to minCal... like if adjTdee = 1400 and minCal = 1370, then gCal = 1400 × 0.9 = 1260, but minCal = 1370, so gCal = 1370. defDay = 1370 - 1400 = -30 kcal/day.
With defDay = -30 and totalDiff = 6 kg:
monthsToGoal = 6 × 7700 / (30 × 30.5) = 46200 / 915 = 50.5 months
Still not 72.
OK I think the specific value of 72 might not be reproducible and it was probably a user input scenario that's hard to recreate. The user said it only happened once and couldn't reproduce it.
Let me think about what the REAL fix should be to make the system more robust:
The key data flow: monthsToGoal is computed once in calculate() and used for both the forecast section and the share card.
The copyResult function reads from the DOM element sh-months, which was set during the last calculate() call.
A potential issue: if copyResult is called when the share section's sh-months still has a value from a PREVIOUS calculation with different parameters. But since both are set in the same calculate() call, this shouldn't happen within a single call.
Could there be a case where the forecast section is rendered with one value but sh-months is set to a different value? Let me look at the code more carefully.
In calculate():
javascript
// Forecast section
const monthsToGoal = ...;
// ... render forecast ...
// Share card
const shMonths = document.getElementById('sh-months');
if (shMonths) shMonths.textContent = monthsToGoal !== null ? rnd1(monthsToGoal) : '—';
And in copyResult:
javascript
var months = document.getElementById('sh-months') ? document.getElementById('sh-months').textContent : '—';
These should be consistent. The only way they'd differ is if:
calculate() was called and set sh-months to 72
Then some OTHER code changed sh-months (but nothing else does)
OR the forecast section somehow shows a different value than monthsToGoal
Wait, I just realized: the forecast section doesn't DISPLAY monthsToGoal directly. It uses monthsToGoal in the logic to determine when the goal is crossed, and then shows the formatted date in the milestone label. Let me look at the milestone text:
javascript
if (p.isMilestone) {
const mLabel = monthsToGoal != null && monthsToGoal < p.m
? `~${rnd1(monthsToGoal)} мес.`
: `~${p.m} мес.`;
noteHtml = `
🎯 Цель (~${mLabel})
`;
So the forecast shows either rnd1(monthsToGoal) OR p.m (the period in months).
If monthsToGoal = 72 but p.m = 12 (12-month column):
Is goalCrossed? With defDay = -21 kcal/day and 12 months: rawNewWt = wt - 21×12×30.5/7700 ≈ wt - 1 kg. If totalDiff = 6 kg, dw = wt-6, rawNewWt = wt-1 > wt-6 → goalCrossed = FALSE
So no milestone would be shown for ANY of the 4 periods (1, 3, 6, 12 months) if monthsToGoal = 72.
The user says "results show 12 months" - maybe they mean the PROGRESS BAR or some other element shows something related to 12 months.
OR maybe they misread the results - the "Прогресс за 12 месяцев" title (which is always there regardless of the goal) made them think "12 months".
In any case, the bug seems to be an edge case that couldn't be reproduced. But to make the code more robust, I could:
Add a sanity check: if monthsToGoal > 120 (10 years), show '—' or cap it with a message
Also sync the value: when the forecast section computes the mLabel, use the same displayed value in sh-months
Actually, looking at the code, I think the issue might be more subtle. The forecast section shows the milestone with a different format:
javascript
const mLabel = monthsToGoal != null && monthsToGoal < p.m
? `~${rnd1(monthsToGoal)} мес.`
: `~${p.m} мес.`;
This shows rnd1(monthsToGoal) if monthsToGoal < p.m, or p.m if monthsToGoal >= p.m.
So if monthsToGoal = 72 and p.m = 12, the milestone card (for the 12-month column) would show ~12 мес. not ~72 мес.. But sh-months would be set to 72. This is the discrepancy!
The user saw "🎯 Цель (~12 мес.)" in the forecast (because monthsToGoal >= p.m caused it to show p.m = 12), but sh-months shows 72 (the actual monthsToGoal value).
Actually wait, if monthsToGoal = 72, would goalCrossed even be true at 12 months? Only if rawNewWt <= dw at 12 months. With such a small defDay (leading to 72 month goal), rawNewWt at 12 months would still be far from dw. So goalCrossed = false, and isMilestone = false. No milestone would show.
So the "12 months" the user saw in results must have been something else - like the "Прогресс за 12 месяцев" header, or a different column showing "12 месяцев" with some progress.
Hmm, this is a bit of a mystery. Let me think about whether there's a simpler explanation.
Actually, maybe the bug is simpler: the user pressed "Calculate" and got 12 months in results + 12 in share card. Then they changed some parameters (like enabling BF or changing weight). The RESULTS section still showed the old 12 months result. Then they pressed "Скопировать результат" which uses the current DOM state of sh-months... which was somehow 72.
But who would set sh-months to 72 between calculations? Nothing does, unless calculate() was called again implicitly.
OR: maybe there's a bug where calculate() is called twice due to some event handling issue, and the second call had different parameters (maybe BF toggle firing twice, or something like that), giving 72 months, and that overrode sh-months while the forecast (rendered visually) still shows the first result.
Actually, actually, I just thought of something. What if the toggleBF function somehow triggers a re-render or re-calculation? Let me check:
javascript
function toggleBF(checked) {
S.isBF = checked;
}
No, it just updates state. No automatic recalculation.
And the toggleNoWork function:
javascript
function toggleNoWork(checked) {
S.noWorkWeek = checked;
// ... show/hide fields ...
}
Also no auto-recalculation.
So calculate() should only be called when the user explicitly clicks "Рассчитать мой план".
OK, I can't find the exact cause of this one-time bug. But I can make the code more robust by:
Ensuring the share card sh-months always shows the SAME value as displayed in the forecast milestone
Adding a cap on monthsToGoal (if > 60 months = 5 years, show as "60+" or '—')
Let me implement these improvements:
Improvement 1: Consistency between forecast display and share card
The forecast milestone shows ~${rnd1(monthsToGoal)} мес. when monthsToGoal < p.m, but if monthsToGoal >= p.m, it shows ~${p.m} мес.. The share card should show the actual monthsToGoal, not the period. But if the user sees "12 мес." in the forecast because p.m=12, they expect "12" in the share card.
Actually, the user might be confused because:
The 12-month forecast column shows "🎯 Цель (~12 мес.)" (the milestone appeared in the 12-month column, showing ~12 months because monthsToGoal was between 6 and 12, or because monthsToGoal=72 but wait that would show ~12 from p.m)
Wait, I think I finally understand. Let's say:
monthsToGoal = 72 (actual)
At 12-month period: goalCrossed = FALSE (with such small defDay)
No milestone would show
So the user must have had a DIFFERENT monthsToGoal in their results than 72. Let me re-read their report:
"В результатах выводилось 12 месяцев до достижения цели, а по кнопке скопировать результат 72 месяца выдало"
They saw "12 months to reach goal" in results, and "72 months" in the copy text. These must both be from the same calculate() call.
The forecast milestone text: 🎯 Цель (~${mLabel}) where mLabel = ~${rnd1(monthsToGoal)} мес. if monthsToGoal < p.m OR ~${p.m} мес. otherwise.
Wait wait wait. The share card shows sh-months from rnd1(monthsToGoal). If the actual monthsToGoal=12, sh-months should be 12. And the forecast would show ~12 months correctly.
There's NO way to get 72 in sh-months if monthsToGoal=12. Unless...
Oh! Maybe the issue is in the ORDER of execution in calculate(). Let me look at what comes AFTER the forecast section in calculate():
Forecast section: computes monthsToGoal, renders forecast
Tips section
Motivation section: uses absDef (Math.abs(defDay))
CTA section (removed)
Share card: shMonths.textContent = rnd1(monthsToGoal)
Wait, what if monthsToGoal is in a DIFFERENT scope? What if the forecast uses a block scope ({}) and monthsToGoal is a const inside that block?
Let me re-read the original calculate() code...
Actually, looking at the code I wrote earlier:
javascript
// Card 6 — forecast (recomputed fresh, without the old f1/f3/f6/f12)
const KCAL_KG_FC = 7700;
const totalDiff = wt - dw;
...
const monthsToGoal = ...
These const declarations are at the TOP LEVEL of calculate(), not inside any block. So they should be accessible throughout the function.
BUT WAIT. What if there's a naming conflict? I also have:
javascript
const totalDiffAbs = Math.abs(wt - dw);
And totalDiff = wt - dw (signed). These are different variables.
Let me look at what happens with the copyResult function more carefully:
javascript
function copyResult() {
var gcal = document.getElementById('r-gcal') ? document.getElementById('r-gcal').textContent : '—';
var prot = document.getElementById('r-prot') ? document.getElementById('r-prot').textContent : '—';
var fat = document.getElementById('r-fat') ? document.getElementById('r-fat').textContent : '—';
var carb = document.getElementById('r-carb') ? document.getElementById('r-carb').textContent : '—';
var months = document.getElementById('sh-months') ? document.getElementById('sh-months').textContent : '—';
var goal = document.getElementById('r-glbl') ? document.getElementById('r-glbl').textContent : '';
This reads from DOM elements. sh-months is the share card months display.
Now looking at where sh-months is set in calculate():
javascript
const shMonths = document.getElementById('sh-months');
if (shMonths) shMonths.textContent = monthsToGoal !== null ? rnd1(monthsToGoal) : '—';
If monthsToGoal = 12 (correct) and rnd1(12) = 12, then sh-months.textContent = "12".
The only way sh-months.textContent could be "72" when the forecast shows "12" is if:
calculate() was called TWICE - once showing 12 months in the forecast (but then sh-months was set to 72 by the second call)
OR some other code modified sh-months
But I don't see any other code that modifies sh-months. And calculate() would only be called once per button click.
UNLESS: the calculate button click fires MULTIPLE TIMES due to some mobile event duplication. If the user tapped "Рассчитать" and it fired twice:
First call: parameters give 12 months → renders forecast with 12 months → sh-months = 12
Second call: different parameters somehow (?) → renders new forecast → sh-months = 72
But the forecast (in DOM) shows the SECOND result...
Hmm but the user said results show 12 months. If calculate() was called twice, the second result would overwrite both forecast AND sh-months. So they'd both show the same value.
Unless the second call had an error that broke the forecast rendering but still set sh-months? That seems unlikely.
OK, I think the most likely explanation is one of these:
A one-time browser glitch (race condition in async rendering)
Some numerical edge case that happened to produce 72 from some combination of inputs
The forecast LABEL showed "12 months" but for a different reason (like the progress bar or a column header), and the ACTUAL monthsToGoal was 72
For robustness, let me:
Add a sanity check: if monthsToGoal > 60 months (5 years), show it differently or cap it
Ensure the copy function shows a value that matches what's displayed in the forecast's milestone (not just the raw monthsToGoal)
Add more defensive programming in the share card update
Actually, the cleanest fix is to store the "displayed months to goal" value in a more reliable way. Instead of just computing monthsToGoal and showing it, I should track what value is DISPLAYED in the forecast milestone and use THAT in the share card.
Let me add a displayedMonthsToGoal variable that gets set from the actual displayed milestone:
Actually, looking at the issue more carefully, I think the real fix is simple: the forecast section computes what value to show in the milestone. I should SET sh-months to the SAME value shown in the milestone, not to a separately computed monthsToGoal.
Here's the revised approach:
Keep monthsToGoal for the milestone computation
After computing fcComputed, find the first milestone entry
Extract what month value was displayed in the milestone
Use THAT value for sh-months
But actually, since the milestone uses:
javascript
const mLabel = monthsToGoal != null && monthsToGoal < p.m
? `~${rnd1(monthsToGoal)} мес.`
: `~${p.m} мес.`;
The displayed months is:
rnd1(monthsToGoal) if monthsToGoal < p.m
p.m if monthsToGoal >= p.m
For a reliable share card, I should use the same value. Let me track what the milestone shows.
Actually, the simplest robust fix: since the forecast and share card both use the same monthsToGoal const in the same function, they SHOULD always be consistent. The one-time bug might have been a browser anomaly.
But to add robustness, I can:
Read back the value from the rendered forecast instead of from monthsToGoal
Or validate that sh-months value matches what's shown somewhere
Actually, the cleanest and most robust fix: make the copyResult() function NOT read from sh-months but instead recalculate from the stored R object. Then we can compute monthsToGoal directly from R.defDay and the stored weight values.
Let me update copyResult to compute monthsToGoal directly:
javascript
function copyResult() {
// Read displayed values
var gcal = ...;
var prot = ...;
var fat = ...;
var carb = ...;
// Compute months to goal directly from R (avoids DOM reading issues)
var months = '—';
if (R && R.defDay && R.wt && R.dw) {
var totalDiff = Math.abs(R.wt - R.dw);
var isMovingRight = (R.defDay < 0 && R.wt > R.dw) || (R.defDay > 0 && R.wt < R.dw);
if (totalDiff > 0.05 && R.defDay !== 0 && isMovingRight) {
var m = totalDiff * 7700 / (Math.abs(R.defDay) * 30.5);
months = String(Math.round(m * 10) / 10);
} else if (totalDiff <= 0.05) {
months = '0';
}
}
// ...
}
This way, copyResult doesn't depend on sh-months DOM value. It directly computes from the stored R object, which should always be consistent.
But wait, R is set in calculate():
javascript
R = { bmr, tdee, adjTdee, gCal, defDay, wt, dw, wk, dur, steps, S };
So R.defDay, R.wt, R.dw are all stored. I can use these in copyResult to compute monthsToGoal directly.
Let me implement this. Also, I should add adjTdee to ensure consistency.
Actually, I realize there's another potential issue. R.defDay is computed as gCal - adjTdee. And monthsToGoal uses R.defDay. But the months to goal in the forecast uses:
javascript
const monthsToGoal = (Math.abs(totalDiff) > 0.05 && defDay !== 0 && movingRight)
? Math.abs(totalDiff) * KCAL_KG_FC / (Math.abs(defDay) * 30.5)
: ...
Using defDay = gCal - adjTdee = R.defDay.
So computing from R in copyResult would give the same formula. Let me implement this as the robust fix.
This also makes the copy function self-contained and not dependent on DOM state, which is generally better.
Let me write this fix.
Ran a command
Now I can see the issue clearly. `copyResult()` reads `sh-months` from the DOM (line 2091). But `sh-months` is set from `monthsToGoal` in calculate() (line 1943-1944).
The potential source of discrepancy: if somehow sh-months shows a different value than what's in the forecast.
The robust fix: make copyResult() compute months to goal directly from the stored R object, not from the DOM.
Let me implement this fix. I'll also update the R object to store wt and dw explicitly for this purpose (which it already does).
Actually wait, looking at the R object: R = { bmr, tdee, adjTdee, gCal, defDay, wt, dw, wk, dur, steps, S };
R.defDay, R.wt, R.dw are all stored. So I can compute monthsToGoal in copyResult from R.
Let me also check what value is in sh-months to verify it matches.
Actually, let me think about what could cause the discrepancy one more time.
In calculate():
const defDay = gCal - adjTdee; (the real daily deficit)
R = { ..., defDay, wt, dw, ... }; (stores defDay)
Forecast: const monthsToGoal = Math.abs(totalDiff) * 7700 / (Math.abs(defDay) * 30.5);
shMonths.textContent = rnd1(monthsToGoal);
These all use the same defDay in the same function call. They SHOULD be consistent.
BUT: what if defDay is recomputed somewhere? Or what if there's a naming collision?
Looking at the code, defDay is declared with const at the top level of calculate(). There shouldn't be any collision.
HOWEVER: I notice that in my earlier edits, I have:
javascript
const defDay = gCal - adjTdee; // negative = deficit (vs full adjusted needs)
And then in the forecast section:
javascript
// Card 6 — forecast (recomputed fresh, without the old f1/f3/f6/f12)
const KCAL_KG_FC = 7700;
const totalDiff = wt - dw;
And later:
javascript
const monthsToGoal = (Math.abs(totalDiff) > 0.05 && defDay !== 0 && movingRight)
? Math.abs(totalDiff) * KCAL_KG_FC / (Math.abs(defDay) * 30.5)
: (Math.abs(totalDiff) <= 0.05 ? 0 : null);
This uses defDay which is the one declared earlier. ✓
But wait - what about the pctAbs and gMeta that reference goalPct? Let me check if goalPct is also fine...
Actually, I think the issue might be that the user pressed "Рассчитать" on a mobile device where button click events can sometimes fire multiple times (we've been dealing with these mobile event issues throughout this session). If the button fires twice:
First calculate() call: parameters X → defDay = -350 → monthsToGoal = 12 → sh-months = "12" → forecast shows "~12 мес."
Second calculate() call (from double-tap): parameters X' (maybe BF toggle changed state between taps?) → defDay = -24 → monthsToGoal = 72 → sh-months = "72" → but the FORECAST DOM update might not have fully rendered yet or was overwritten
Actually, if calculate() fired twice, BOTH the forecast and sh-months would show the second result. Unless there's some async issue.
Hmm. OR: the issue might be simpler. The forecast section renders text like "🎯 Цель (~12 мес.)" - but this shows "~12 мес." because the goal was crossed in the 12-month column. The ACTUAL monthsToGoal might have been something like 7.2 (which rounds to ~7 or shows in the 12-month column as ~12 because monthsToGoal < 12 so it shows ~rnd1(7.2) = "7.2 мес."). No wait, that should show 7.2 not 12.
Actually: the milestone label:
javascript
const mLabel = monthsToGoal != null && monthsToGoal < p.m
? `~${rnd1(monthsToGoal)} мес.` // if goal reached BEFORE this period
: `~${p.m} мес.`; // if goal reached IN this period or later
Wait, this shows p.m when monthsToGoal >= p.m. So if goal takes 10 months and p.m = 12, it would show "~12 мес." not "~10 мес."!
THIS is the bug! The FORECAST shows "~12 мес." (the period column) but the actual monthsToGoal might be 7.2 months. And sh-months would show "7.2" while the user reads "12 months" from the milestone label.
But the user said "72 months in copy" - not "7.2". Unless... rnd1(72.xxx) = 72? And the milestone showed "~12 мес." because monthsToGoal (72) >= p.m (12)?
Wait, if monthsToGoal = 72 and p.m = 12: monthsToGoal >= p.m is true → mLabel = ~${12} мес. = "~12 мес."
BUT: would goalCrossed be true at 12 months with monthsToGoal = 72? Only if the deficit is large enough to cross the goal in 12 months. If monthsToGoal = 72, the deficit is tiny and 12 months wouldn't be enough to reach the goal. So goalCrossed = false and no milestone would show at all.
UNLESS: there's a bug in the goalCrossed calculation that makes it true even when monthsToGoal > p.m.
Actually, goalCrossed is computed as:
javascript
const goalCrossed = movingRight && (defDay < 0 ? rawNewWt <= dw : rawNewWt >= dw);
And rawNewWt = wt + (defDay * p.m * 30.5) / 7700.
If defDay = -24 kcal/day and totalDiff = 6 kg:
monthsToGoal = 6 × 7700 / (24 × 30.5) = 46200 / 732 = 63.1 months
At p.m = 12: rawNewWt = wt + (-24 × 12 × 30.5) / 7700 = wt - 8784/7700 = wt - 1.14 kg
goalCrossed = (wt - 1.14) <= (wt - 6) = false! So no milestone.
So if monthsToGoal = 72, no milestone would be shown at p.m = 12. The user wouldn't see "12 months to goal" in the forecast.
I'm going in circles. Let me just implement the robust fix: compute months in copyResult directly from R instead of from the DOM, and also fix the mLabel to show the actual monthsToGoal (not p.m) for greater clarity.
Actually, I'm now thinking: the mLabel shows ~${p.m} мес. when monthsToGoal >= p.m. This means if goal takes 9 months and you're in the 12-month column (goalCrossed is true because in 12 months you do cross the goal), it shows "~12 мес." This is confusing because the goal is actually reached at 9 months, not 12.
The fix for mLabel:
javascript
const mLabel = monthsToGoal != null && monthsToGoal <= p.m
? `~${rnd1(monthsToGoal)} мес.` // always show actual months to goal
: `~${p.m} мес.`;
Wait, this is the same condition but changes the output. If monthsToGoal=9 and p.m=12: mLabel = ~9 мес. ✓
But wait, monthsToGoal != null && monthsToGoal < p.m was the original condition. If monthsToGoal = 10 and p.m = 12: 10 < 12 is true → mLabel = "~10 мес." ✓
So the original condition monthsToGoal < p.m should work correctly. If goal is at 10 months and we're in 12-month column, it shows "~10 мес." Good.
But the issue the user saw: results show "12 months", copy shows "72". If mLabel shows "~12 мес." in the results, it means either:
monthsToGoal >= 12, or
goalCrossed is false so mLabel isn't shown at all, and "12 months" refers to something else
I think case 2 is more likely. The user might have been reading "12 месяцев" from the column HEADER or LABEL in the forecast, not from the milestone text. The milestone wouldn't be there if monthsToGoal = 72.
In any case, the fix is: make copyResult compute from R (not DOM) so it's guaranteed to match the calculate() run.
Let me implement this.
Нашёл потенциальную причину. copyResult() читает sh-months из DOM, который мог быть обновлён повторным вызовом calculate() (например, из-за двойного тапа на мобильном). При этом DOM прогноза показывает одно, а sh-months — уже другое из второго вызова.
Исправление: copyResult пересчитывает срок прямо из хранимого объекта R — не из DOM:
Edited a file, read a file, and 2 more tools
Два исправления:
Причина бага — copyResult() читал sh-months из DOM, который мог содержать значение от повторного вызова calculate() (двойной тап на мобильном). Прогноз визуально показывал результат первого расчёта (не успев перерисоваться), а sh-months уже перезаписался вторым расчётом с другими параметрами.
Исправление 1 — copyResult() теперь пересчитывает срок напрямую из объекта R (который хранит wt, dw, defDay из последнего расчёта), а не из DOM-элемента. Формула та же: |wt−dw| × 7700 / (|defDay| × 30.5). Значение будет гарантированно согласовано с тем расчётом, который был выполнен.
Исправление 2 — добавлен _calcLock: если кнопка «Рассчитать» сработала дважды за 600 мс (двойной тап), второй вызов молча игнорируется. Это устраняет саму причину расхождения.
Calorie widget
Code·HTML
Лена, доброе утро. Записываю голосовое про тест на сайт. Итак, что я хочу сделать? Я хочу сделать тест, в котором можно будет вбить, тыкнуть на свои исходные данные. Возраст, опыт тренировок, там можно сделать два или три варианта. Занималась когда-то давным-давно, никогда не занималась. Ну или когд
pasted
нужно создать типа такого же виджета, но уже для сайта на тильде
Вот как видит виджет Сабина
The user wants to create a quiz/test widget for a Tilda website. Based on the voice note, here's what Sabina wants:
The quiz should be a lead-qualification tool that:
Questions:
Training experience:
Never trained
Trained long ago (with breaks, understands what workouts are but inconsistently)
Train regularly (have experience)
Age (input field)
Goal:
Lose weight
Build muscle
Recover after childbirth
Improve posture/other
Lifestyle/Activity:
Sedentary (office work)
Mom on maternity leave (active mom, lots of baby care)
Active lifestyle
Childbirth/Pregnancy:
Less than 6 months ago
Less than a year ago
More than a year ago
No, don't plan
No children, don't plan
Planning
Body concerns (multi-select, exclusion method):
Scoliosis/posture
Diastasis
Shoulder hump (холка)
Bladder leakage (подтекание)
Hypertonicity of pelvic floor
None of the above
Training availability:
Days per week (1-7)
Time per session
Output (3 plans):
Soft rehabilitation (recent birth, diastasis, pelvic issues) → "Тазовое дно" program
Moderate (poor posture, sedentary, no birth issues) → "Лёгкое движение" program
Active (want to build muscle, have experience) → Club with strength training
For Tilda specifically, HTML widgets need to be embedded as HTML blocks. So I need to create a standalone HTML file similar to the calorie widget.
Let me design this quiz widget with:
Step-by-step cards (multi-step quiz flow)
Beautiful visual design matching the GetCourse style
Results with program recommendations
Mobile-friendly
Compatible with Tilda embedding
The widget should be a multi-step quiz with:
Progress bar
Question cards with visual options (like cards with icons)
Smooth transitions between questions
Final result page with recommended program + CTA
Let me think about the routing logic:
Scoring/Routing:
Based on responses, categorize into:
"Тазовое дно" route:
Has childbirth < 1 year ago, OR
Has symptoms: diastasis, leakage, pelvic floor hypertonicity
"Лёгкое движение" route:
Sedentary/office lifestyle
Never/rarely trained
No recent birth
No major symptoms OR posture/back issues
"Клуб" route:
Has training experience
Active lifestyle
Clear fitness goals (muscle, weight loss without complications)
"Беременность" route:
Currently pregnant or planning → special program
The questions flow:
Step 1: Training experience (single choice)
Step 2: Age (number input)
Step 3: Main goal (single choice)
Step 4: Lifestyle (single choice)
Step 5: Childbirth/pregnancy status (single choice)
Step 6: Body concerns (multi-select)
Step 7: Time availability (days + duration)
Then → Results
Let me design the visual style to match Tilda aesthetic (clean, modern, minimal) while keeping the GetCourse-compatible patterns from the calorie widget.
For Tilda embedding: The widget should be a single HTML file that can be pasted into Tilda's "HTML" block. Same approach as the calorie widget.
Let me now design and build this quiz widget. I'll make it visually appealing with:
Dark header with brand name
Step indicator/progress bar
Card-based answer options
Smooth transition animations
Results page with program recommendation and CTA button
Design language:
Same orange accent (
#E96B2C)
Same dark button (
#1C1C1E)
Same clean card style with rounded corners
But adapted for Tilda's wider layouts
Let me code this up. I'll use a step-by-step wizard pattern.
Questions structure:
"Какой у вас опыт тренировок?" (experience)
"Ваш возраст?" (age slider or input)
"Какая ваша главная цель?" (goal)
"Как бы вы описали свой образ жизни?" (lifestyle)
"Были ли у вас роды или беременность?" (pregnancy/birth)
"Есть ли у вас что-то из этого?" (symptoms - multi-select)
"Сколько времени вы готовы уделять тренировкам?" (availability)
→ Result
Scoring logic:
First, check for immediate routing:
If step 5 says "pregnant" or "planning" → special route
If has diastasis/leakage/pelvic floor issues → "Тазовое дно" leaning
If childbirth < 6 months → "Тазовое дно" likely
Then:
Low experience + sedentary + no issues = "Лёгкое движение"
Good experience + active = "Клуб"
Any pelvic/postnatal issues = "Тазовое дно"
Results:
Реабилитационный план → "Тазовое дно 2.0" + possibly "Лёгкое движение"
Базовый план → "Лёгкое движение"
Активный план → "Клуб" (subscription)
Перинатальный план → Program for pregnant/recent moms
Let me now build the full widget code. I'll make it beautiful and functional.
Key design decisions:
Mobile-first (single column)
Large tap targets for answer cards
Animated progress
Smooth transitions between steps
Warm, feminine visual language (matching Sabina's brand)
Orange/dark color scheme consistent with their brand
Let me write the full HTML/CSS/JS widget now.
I'll structure it as:
Intro screen with title and start button
7 question screens
Result screen
The scoring algorithm:
Points system:
Experience: Never=0, Long ago=1, Regular=2
Goal: Weight loss=1, Muscle=2, Postnatal=3, Posture=1
Lifestyle: Sedentary=0, Office=0, Mom=1, Active=2
Birth: <6mo=3, <1yr=2, >1yr=1, No=0, Pregnant=4, Planning=3
Symptoms: leakage=3pts, diastasis=2pts, pelvic hypertonus=3pts, posture=1pt, cholka=1pt
Route determination:
If pregnant (birth=4) → "Перинатальный путь"
If birth=4 OR birth=3 (planning) → special consideration
If pelvic_score >= 3 → "Тазовое дно"
If birth >= 2 AND (goal=postnatal OR pelvic_score>0) → "Тазовое дно"
If experience >= 1 AND lifestyle >= 1 AND goal=muscle → "Клуб"
Otherwise → "Лёгкое движение"
Actually, let me simplify the scoring into cleaner categories:
Route 1: "Тазовое дно 2.0" - Pelvic health / postnatal recovery
Triggers:
Birth < 1 year ago
Has: leakage, diastasis, or pelvic floor hypertonicity
Goal: postnatal recovery
Route 2: "Лёгкое движение" - Beginner / gentle start
Triggers:
No/little training experience
Sedentary lifestyle
No major pelvic symptoms
Goal: weight loss, posture, general wellness
Route 3: "Клуб" - Active training / subscription
Triggers:
Has training experience
Goal: muscle gain or consistent fitness
More active lifestyle
Route 4: "Программа для беременных" - Prenatal
Triggers:
Currently pregnant OR planning soon
Let me code this up now. I'll also add the skill reading to check for frontend-design guidelines first.
Actually, let me just write the code directly. I know the design requirements well from working on the calorie widget.
For the quiz widget, I'll create:
A multi-step quiz with 7 questions
Answer cards that are visually appealing
A results page with 4 possible outcomes
CTA buttons linking to programs (with # placeholders)
Compatible with Tilda embedding (standalone HTML)
Let me code this up!
Отличная идея. Перед написанием кода прочитаю гайд по дизайну для фронтенда:
Read frontend design skill for visual guidelines SKILL.md
Now I have the design guidance. Let me plan the quiz widget:
Brief analysis:
Client: Sabina Filiina's fitness club
Audience: Women interested in home workouts, many post-partum
Subject: Fitness program selection quiz
Job: Lead qualifier → routes to correct program (Тазовое дно, Лёгкое движение, Клуб, Беременность)
Design plan:
Color palette:
Base:
#FAFAF8 (warm off-white, not the generic cream)
Surface:
#FFFFFF
Dark:
#1A1A1A
Accent:
#E96B2C (brand orange from existing widget - KEEP for consistency)
Muted text: #888
Border:
#EBEBEB
Typography:
Single system font stack but with careful weight/size use
Display: large, light weight (300), generous letter-spacing
Body: regular weight, comfortable line height
Avoid ALL CAPS labels
Layout concept:
Full-width centered quiz card, max-width 620px
Questions presented one at a time with smooth fade
Large tap-friendly option cards
Progress indicator: simple line, not numbered steps
Warm, calm feel - not clinical or energetic
Unique design choices:
Answer options as large visual cards with left-aligned content, no icons (cleaner)
Smooth fade between questions (not slide)
Progress as a thin orange line that grows
Multi-select uses subtle checkmark treatment
Results card with program photo/illustration placeholder
No ALL-CAPS anywhere
No numbered eyebrows
Now let me structure the quiz:
Questions:
"Как давно вы тренировались?"
Никогда не тренировалась
Тренировалась, но давно или с перерывами
Тренируюсь регулярно
"Сколько вам лет?"
Number input with slider or just input
Ranges: до 25 / 25–35 / 35–45 / старше 45
(Actually from voice note, age might just be needed for routing. Let me make it a simple range select)
"Какая ваша главная цель?"
Снизить вес
Набрать мышечную массу
Восстановиться после родов
Улучшить осанку и самочувствие
"Как бы вы описали свой образ жизни?"
Сижу большую часть дня (офис, работа за компьютером)
Мама в декрете, много с малышом
Активная, много двигаюсь
"Роды или беременность?"
Беременна сейчас
Роды были менее 6 месяцев назад
Роды были от 6 до 12 месяцев назад
Роды были более года назад
Детей нет / не планирую
Планирую беременность
"Отметьте, что из этого у вас есть" (multi-select)
Сутулость, нарушение осанки
Диастаз (расхождение мышц живота)
Холка (горб на шее/верхней части спины)
Подтекание мочи
Гипертонус мышц тазового дна
Ничего из этого нет
"Сколько времени в неделю вы готовы выделить на тренировки?"
До 1 часа (2–3 тренировки по 15–20 мин)
1–2 часа (2–3 тренировки по 30–40 мин)
2–3 часа (3–4 тренировки по 40–60 мин)
Больше 3 часов
Routing logic:
if (pregnant OR planning_pregnancy) → Route: "Программа для беременных"
if (birth < 6mo) → Route: "Тазовое дно"
OR
if (has: leakage OR diastasis OR pelvic_hypertonus) → Route: "Тазовое дно"
OR
if (birth < 1yr AND goal == postnatal) → Route: "Тазовое дно"
if (experience == regular AND goal == muscle AND time >= 2hrs) → Route: "Клуб"
OR
if (experience >= some AND goal == weight_loss AND lifestyle == active) → Route: "Клуб"
Otherwise → Route: "Лёгкое движение"
4 Result routes:
"Тазовое дно 2.0" - Программа восстановления тазового дна
For: postnatal < 1yr, has pelvic issues, leakage, diastasis
CTA: "Начать программу"
"Лёгкое движение" - Мягкий старт для начинающих
For: beginners, sedentary, no major issues
CTA: "Начать программу"
"Клуб" - Полноценные тренировки в клубе
For: experienced, active, muscle/weight goals
CTA: "Вступить в Клуб"
"Программа для беременных" - Перинатальные тренировки
For: pregnant or planning
CTA: "Узнать подробнее"
Now let me think about the design more carefully. I need to avoid the generic SaaS card kit look.
Distinctive choices:
Use a full-bleed warm light background
Question text is large (36-40px) and uses normal/light weight
Answer cards have NO borders, just subtle hover state
Selected state: left orange bar instead of border glow
Progress: thin top line in orange
Single page scroll (no animations for simplicity and reliability in Tilda)
Actually, YES animations - smooth fade between questions
Results: full-width, warm dark background for the recommendation
Let me avoid:
Numbered eyebrows above questions
ALL CAPS labels
Rounded card grid layout (I'll use list-style single-column cards)
Multiple drop shadows
Let me now write the code. I'll make it a single HTML file optimized for Tilda embedding.
For the CSS, I'll scope everything under .sq (for "Sabina Quiz") to avoid conflicts.
Let me structure the JS:
state object: answers for each step
currentStep counter
showStep(n) function with fade transition
selectOption(el, value, question) function
calculate() function that determines the route
showResult(route) function
This will be a well-structured, clean widget. Let me write it now.
Actually, let me think about the Tilda-specific considerations:
Tilda HTML blocks support inline HTML/CSS/JS
No external dependencies (same as calorie widget)
Should work within Tilda's content width
Mobile responsive is critical
Let me now write the full widget. I'll aim for clean, minimal, warm design.
For the file, I need to check the skill files first. Actually I already read the skill file, so let me just build.
Here's my plan for the HTML structure:
html
For the result display, I'll have 4 possible result divs, showing only the one that matches.
Let me code this up properly. The widget should be about 600-700 lines of HTML/CSS/JS.
Key styling:
--acc: #E96B2C orange accent
--dk: #1A1A1A dark
--bg: #FAFAF8 warm off-white
System font
Question: 28-32px, font-weight 300 or 400
Options: cards with left accent bar when selected
Buttons: dark pill shape
Let me write it now. I'll focus on making the routing logic solid and the UI clean.
For the scoring, I'll use a simple object to store answers:
javascript
const A = {
experience: null, // 'never', 'sometimes', 'regular'
age: null, // 'u25', '25-35', '35-45', 'o45'
goal: null, // 'weight', 'muscle', 'postnatal', 'wellness'
lifestyle: null, // 'sedentary', 'mom', 'active'
birth: null, // 'pregnant', 'u6mo', 'u12mo', 'o12mo', 'no', 'planning'
symptoms: new Set(), // 'posture', 'diastasis', 'hump', 'leakage', 'pelvic', 'none'
time: null // 'u1hr', '1-2hr', '2-3hr', 'o3hr'
};
Then the routing function:
javascript
function getRoute() {
// Pregnant or planning
if (A.birth === 'pregnant' || A.birth === 'planning') return 'prenatal';
// Pelvic/postnatal issues
if (A.birth === 'u6mo') return 'pelvic';
if (A.birth === 'u12mo' && (A.goal === 'postnatal' || hasPelvicSymptoms())) return 'pelvic';
if (A.symptoms.has('leakage') || A.symptoms.has('diastasis') || A.symptoms.has('pelvic')) return 'pelvic';
// Club (advanced)
if (A.experience === 'regular' && (A.goal === 'muscle' || (A.goal === 'weight' && A.lifestyle === 'active'))) return 'club';
if (A.experience !== 'never' && A.time === 'o3hr') return 'club';
// Default: gentle movement
return 'gentle';
}
function hasPelvicSymptoms() {
return A.symptoms.has('leakage') || A.symptoms.has('diastasis') || A.symptoms.has('pelvic');
}
This logic gives:
prenatal (pregnant/planning) → pregnancy program
pelvic (recent birth, pelvic issues) → Тазовое дно 2.0
club (experienced, active) → Клуб
gentle (default, beginners, wellness) → Лёгкое движение
Now let me write the complete widget code.
Oh wait, I also need to check the project files to see if there's any info about the existing programs.
Теперь спроектирую квиз. Перед кодом — план:
Дизайн-план:
Палитра: тёплый офф-вайт
#FAFAF8, акцент оранжевый
#E96B2C, тёмный
#1A1A1A — в тон существующему виджету
Вопросы: крупный лёгкий шрифт, карточки-ответы с левой оранжевой полоской при выборе (не стандартные бордеры)
Прогресс: тонкая нарастающая линия сверху
Анимация: только fade при переходе между шагами
7 вопросов → 4 маршрута (Тазовое дно, Лёгкое движение, Клуб, Беременность)
Ran 2 commands, created a file, shared files
Готово. Вот что получилось:
7 вопросов:
Опыт тренировок (никогда / с перерывами / регулярно)
Возраст (4 диапазона)
Главная цель (вес / мышцы / после родов / самочувствие)
Образ жизни (офис / мама в декрете / активная)
Беременность / роды (6 вариантов включая «беременна сейчас» и «планирую»)
Симптомы — мультивыбор (осанка, диастаз, холка, подтекание, гипертонус, ничего)
Время на тренировки (4 варианта)
4 маршрута:
🤰 Программа для беременных — если беременна или планирует
🌿 Тазовое дно 2.0 — роды < 6 мес., диастаз, подтекание, гипертонус таза
🌱 Лёгкое движение — начинающие, офисный образ жизни, проблемы с осанкой
💪 Клуб — опытные, хотят набрать мышцы или тренироваться интенсивно
На странице результата — объяснение «почему именно это», динамически собирается из ответов пользователя.
Для вставки в Tilda: скопируйте содержимое файла в блок «HTML» на Тильде. Замените href="#" у кнопки в результате на ссылку на нужную программу (там 4 CTA, по одному на маршрут — можно сделать разными).
Quiz widget
Code·HTML
как посмотреть код?
Failed to send. A retry control is available on the message.
как посмотреть код?
Claude is AI and can make mistakes.
Quiz widget · HTML
Подобрать программу
Клуб Сабины Филиной
Какая программа подходит вам?
7 вопросов — и вы узнаете, с чего начать тренировки именно в вашей ситуации.
⏱ Займёт 2–3 минуты
Вопрос 1 из 7
Как давно вы занимались физическими упражнениями?
Вопрос 2 из 7
Сколько вам лет?
Вопрос 3 из 7
Какая ваша главная цель прямо сейчас?
Вопрос 4 из 7
Как бы вы описали свой типичный день?
Вопрос 5 из 7
Были ли у вас роды или беременность?
Вопрос 6 из 7
Есть ли у вас что-то из этого?
Можно выбрать несколько вариантов
Вопрос 7 из 7
Сколько времени в неделю вы готовы уделять тренировкам?