НУЖНА КОНСУЛЬТАЦИЯ
ГАЙД: СТРАХОВКА 2026
БЛОГ
КОНТАКТЫ
О НАС
КОНСУЛЬТАЦИИ
СТРАХОВАНИЕ
УСЛУГИ
ИПОТЕКИ
Страхование
Консультации
Ипотеки
Регистрация фирмы
Языковые курсы
ZDP
Ипотека в Чехии
Консультация по ипотеке
Сравнение чешских банков
Ипотечный калькулятор
Предварительное одобрение
Рефинансирование ипотеки
Подача заявки
Калькулятор цен на страховку
Гайд "Страховка для визы 2026"
Комплексная страховка
Страховка для ВНЖ
Страховка для студентов
Страховка при хронических заболеваниях
Студенты
Рабочие
Предприниматели
Воссоединение
ПМЖ
Гражданство
Отказ в визе
ZDP для украинцев
ZDP - Українська версія
Услуги
Страхование
Консультации
Регистрация фирмы
Языковые курсы
ZDP для украинцев
ZDP Українська версія
Страхование
Калькулятор цен на страховку
Гайд "Страховка для визы 2026"
Комплексная страховка
Страховка для ВНЖ
Страховка для студентов
Страховка при хронических заболеваниях
Консультации
Студенты
Рабочие
Предприниматели
Воссоединение
ПМЖ
Гражданство
Отказ в визе
ZDP для украинцев
ZDP Українська версія
Ипотеки
Ипотека в Чехии
Консультация по ипотеке
Ипотечный калькулятор
Сравнение чешских банков
Предварительное одобрение
Рефинансирование ипотеки
Подача заявки
О нас
Контакты
Блог
Партнеры
ГАЙД: Страховка 2026
<!doctype html> <html lang="ru"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> html,body{margin:0;padding:0;background:#f7f6fa;} html { scroll-behavior: smooth; } body.c1-bank-funds-page { --c1-bg: #f7f6fa; --c1-surface: #ffffff; --c1-surface-soft: #fbfaff; --c1-surface-accent: #f0ebff; --c1-text: #171326; --c1-muted: #625c70; --c1-soft: #827b90; --c1-primary: #5d2bd5; --c1-primary-strong: #3e168f; --c1-primary-dark: #28105d; --c1-line: #e4dfeb; --c1-line-strong: #cec4dd; --c1-danger: #9f3048; --c1-warning: #8a5200; --c1-success: #126454; --c1-radius: 20px; --c1-radius-inner: 14px; --c1-radius-control: 12px; --c1-shadow-sm: 0 10px 28px rgba(45, 31, 72, 0.07); --c1-shadow-md: 0 24px 70px rgba(45, 31, 72, 0.12); --c1-focus: 0 0 0 4px rgba(93, 43, 213, 0.22); background: var(--c1-bg); color: var(--c1-text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif; font-size: 16px; line-height: 1.55; margin: 0; min-width: 320px; overflow-x: hidden; text-rendering: optimizeLegibility; } .c1-bank-funds-page *, .c1-bank-funds-page *::before, .c1-bank-funds-page *::after { box-sizing: border-box; } .c1-bank-funds-page [hidden] { display: none !important; } .c1-bank-funds-page a { color: inherit; } .c1-bank-funds-page button, .c1-bank-funds-page input, .c1-bank-funds-page select, .c1-bank-funds-page textarea { font: inherit; } .c1-bank-funds-page button, .c1-bank-funds-page label, .c1-bank-funds-page summary, .c1-bank-funds-page a { touch-action: manipulation; } .c1-bank-funds-page button, .c1-bank-funds-page summary, .c1-bank-funds-page label { cursor: pointer; } .c1-bank-funds-page :focus-visible { border-radius: 8px; box-shadow: var(--c1-focus); outline: 2px solid var(--c1-primary); outline-offset: 3px; } .c1-bf-container { margin-inline: auto; max-width: 1180px; padding-inline: 24px; width: 100%; } .c1-bf-reading-width { max-width: 900px; } .c1-bf-skip-link { background: var(--c1-text); border-radius: 0 0 10px 10px; color: #ffffff !important; font-weight: 700; left: 18px; padding: 10px 16px; position: fixed; text-decoration: none; top: -80px; z-index: 1000; } .c1-bf-skip-link:focus { top: 0; } .c1-bf-site-header { background: rgba(255, 255, 255, 0.94); border-bottom: 1px solid rgba(93, 43, 213, 0.1); position: sticky; top: 0; z-index: 40; } .c1-bf-site-header__inner { align-items: center; display: flex; gap: 28px; min-height: 72px; } .c1-bf-brand { align-items: center; display: inline-flex; flex: 0 0 auto; font-size: 15px; font-weight: 800; gap: 10px; letter-spacing: 0.02em; text-decoration: none; } .c1-bf-brand__mark { align-items: center; background: var(--c1-primary); border-radius: 10px; color: #ffffff; display: inline-flex; font-size: 12px; font-weight: 800; height: 36px; justify-content: center; width: 36px; } .c1-bf-site-nav { align-items: center; display: flex; gap: 24px; margin-left: auto; } .c1-bf-site-nav a { color: var(--c1-muted); font-size: 14px; font-weight: 650; text-decoration: none; } .c1-bf-site-nav a:hover { color: var(--c1-primary-strong); } .c1-bf-header-cta { background: var(--c1-surface-accent); border: 1px solid rgba(93, 43, 213, 0.14); border-radius: var(--c1-radius-control); color: var(--c1-primary-strong) !important; flex: 0 0 auto; font-size: 14px; font-weight: 750; min-height: 44px; padding: 10px 16px; text-decoration: none; } .c1-bf-hero { background: radial-gradient(circle at 92% 4%, rgba(93, 43, 213, 0.15), transparent 33rem), linear-gradient(180deg, #ffffff 0%, #faf8ff 100%); border-bottom: 1px solid rgba(93, 43, 213, 0.09); overflow: hidden; padding: 72px 0 64px; } .c1-bf-hero__grid { align-items: center; display: grid; gap: clamp(38px, 5vw, 68px); grid-template-columns: minmax(0, 1.12fr) minmax(380px, 0.88fr); } .c1-bf-hero__content { max-width: 650px; } .c1-bf-eyebrow, .c1-bf-kicker { color: var(--c1-primary-strong); font-size: 13px; font-weight: 750; letter-spacing: 0.02em; margin: 0 0 16px; } .c1-bf-eyebrow { border: 1px solid rgba(93, 43, 213, 0.18); border-radius: 999px; display: inline-flex; padding: 7px 12px; } .c1-bf-hero h1 { font-size: clamp(45px, 5.2vw, 66px); font-weight: 800; letter-spacing: -0.052em; line-height: 1.01; margin: 0; max-width: 620px; } .c1-bf-hero__lead { color: #4f485d; font-size: clamp(18px, 1.8vw, 21px); line-height: 1.55; margin: 26px 0 0; max-width: 610px; } .c1-bf-hero__actions { align-items: center; display: flex; flex-wrap: wrap; gap: 12px; margin-top: 32px; } .c1-bf-button { align-items: center; border-radius: var(--c1-radius-control); display: inline-flex; font-weight: 750; justify-content: center; min-height: 52px; padding: 13px 21px; text-align: center; text-decoration: none; transition: background-color 180ms ease, border-color 180ms ease, box-shadow 180ms ease, color 180ms ease, transform 180ms ease; } .c1-bf-button:hover { transform: translateY(-1px); } .c1-bf-button:active { transform: translateY(1px); } .c1-bf-button--primary { background: var(--c1-primary); border: 1px solid var(--c1-primary); box-shadow: 0 12px 30px rgba(93, 43, 213, 0.23); color: #ffffff !important; } .c1-bf-button--primary:hover { background: var(--c1-primary-strong); border-color: var(--c1-primary-strong); } .c1-bf-button--quiet { background: transparent; border: 1px solid transparent; color: var(--c1-primary-strong) !important; } .c1-bf-button--quiet:hover { background: var(--c1-surface-accent); } .c1-bf-button--secondary { background: var(--c1-surface-accent); border: 1px solid rgba(93, 43, 213, 0.18); color: var(--c1-primary-strong); } .c1-bf-button--light { background: #ffffff; border: 1px solid rgba(255, 255, 255, 0.7); color: var(--c1-primary-dark) !important; } .c1-bf-hero__visual { min-width: 0; perspective: 1200px; position: relative; } .c1-bf-hero__visual::before { background: rgba(93, 43, 213, 0.1); border: 1px solid rgba(93, 43, 213, 0.12); border-radius: var(--c1-radius); content: ""; inset: 22px -18px -18px 22px; position: absolute; transform: rotate(2deg); } .c1-bf-preview-card { background: rgba(255, 255, 255, 0.96); border: 1px solid rgba(93, 43, 213, 0.18); border-radius: var(--c1-radius); box-shadow: var(--c1-shadow-md); padding: 24px; position: relative; transform: rotateY(-4deg) rotateX(1.5deg); transform-origin: center; } .c1-bf-preview-card__top { align-items: center; border-bottom: 1px solid var(--c1-line); display: flex; font-size: 13px; font-weight: 700; justify-content: space-between; padding-bottom: 18px; } .c1-bf-preview-card__top b { background: var(--c1-surface-accent); border-radius: 9px; color: var(--c1-primary-strong); padding: 6px 9px; } .c1-bf-preview-card__main { padding: 28px 0 24px; } .c1-bf-preview-card__main small { color: var(--c1-muted); display: block; font-size: 13px; font-weight: 650; } .c1-bf-preview-card__main strong { color: var(--c1-primary-strong); display: block; font-size: clamp(40px, 4vw, 56px); font-variant-numeric: tabular-nums; letter-spacing: -0.05em; line-height: 1; margin-top: 10px; } .c1-bf-preview-card__main p, .c1-bf-preview-card__note { color: var(--c1-muted); font-size: 13px; margin: 12px 0 0; } .c1-bf-preview-card__stats { display: grid; gap: 10px; margin: 0; } .c1-bf-preview-card__stats div { align-items: center; background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-radius: var(--c1-radius-inner); display: flex; gap: 16px; justify-content: space-between; padding: 14px 16px; } .c1-bf-preview-card__stats dt { color: var(--c1-muted); } .c1-bf-preview-card__stats dd { font-variant-numeric: tabular-nums; font-weight: 800; margin: 0; text-align: right; } .c1-bf-preview-card__note { border-top: 1px solid var(--c1-line); margin-top: 18px; padding-top: 16px; } .c1-bf-evidence { background: #ffffff; border-bottom: 1px solid var(--c1-line); } .c1-bf-evidence__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); } .c1-bf-evidence__grid > div { display: grid; gap: 4px; padding: 24px 28px; } .c1-bf-evidence__grid > div + div { border-left: 1px solid var(--c1-line); } .c1-bf-evidence strong { font-size: 15px; } .c1-bf-evidence span { color: var(--c1-muted); font-size: 13px; } .c1-bf-calculator-section { padding: 88px 0; } .c1-bf-calculator-shell { background: var(--c1-surface); border: 1px solid var(--c1-line); border-radius: var(--c1-radius); box-shadow: var(--c1-shadow-sm); overflow: clip; } .c1-bf-calculator-intro { background: radial-gradient(circle at 100% 0%, rgba(93, 43, 213, 0.12), transparent 27rem), var(--c1-surface-soft); border-bottom: 1px solid var(--c1-line); padding: 42px 46px; } .c1-bf-calculator-intro h2, .c1-bf-methodology h2, .c1-bf-section-heading h2, .c1-bf-document-check h2, .c1-bf-footer-cta h2 { font-size: clamp(30px, 3.7vw, 46px); font-weight: 780; letter-spacing: -0.04em; line-height: 1.08; margin: 0; } .c1-bf-calculator-intro > p:last-child, .c1-bf-methodology__heading > p:last-child, .c1-bf-section-heading p, .c1-bf-document-check__grid > div > p { color: var(--c1-muted); font-size: 17px; margin: 16px 0 0; max-width: 720px; } .c1-bf-calculator-form { padding: 4px 46px 42px; } .c1-bf-validation-summary { background: #fff4f6; border: 1px solid rgba(159, 48, 72, 0.28); border-radius: var(--c1-radius-inner); color: #742137; margin-top: 28px; padding: 18px 20px; } .c1-bf-validation-summary ul { margin: 8px 0 0; padding-left: 20px; } .c1-bf-step { border: 0; border-bottom: 1px solid var(--c1-line); margin: 0; min-width: 0; padding: 32px 0; } .c1-bf-step legend, .c1-bf-date-step h3 { align-items: center; display: flex; font-size: clamp(20px, 2.2vw, 26px); font-weight: 760; gap: 12px; line-height: 1.25; margin: 0; padding: 0; } .c1-bf-step legend > span, .c1-bf-date-step h3 > span { align-items: center; background: var(--c1-surface-accent); border-radius: 10px; color: var(--c1-primary-strong); display: inline-flex; flex: 0 0 auto; font-size: 14px; height: 32px; justify-content: center; width: 32px; } .c1-bf-field-help { color: var(--c1-muted); margin: 10px 0 0; max-width: 780px; } .c1-bf-choice-grid { display: grid; gap: 12px; margin-top: 22px; } .c1-bf-choice-grid--three { grid-template-columns: repeat(3, minmax(0, 1fr)); } .c1-bf-choice-grid--purposes { grid-template-columns: repeat(2, minmax(0, 1fr)); } .c1-bf-choice { background: var(--c1-surface); border: 1px solid var(--c1-line); border-radius: var(--c1-radius-inner); display: grid; gap: 6px; min-height: 126px; padding: 18px 18px 18px 52px; position: relative; transition: background-color 170ms ease, border-color 170ms ease, box-shadow 170ms ease, transform 170ms ease; } .c1-bf-choice:hover { border-color: var(--c1-line-strong); transform: translateY(-1px); } .c1-bf-choice:focus-within { border-color: var(--c1-primary); box-shadow: 0 0 0 4px rgba(93, 43, 213, 0.18); outline: 2px solid transparent; } .c1-bf-choice.is-selected { background: var(--c1-surface-accent); border-color: rgba(93, 43, 213, 0.58); box-shadow: inset 0 0 0 1px rgba(93, 43, 213, 0.1); } .c1-bf-choice.is-selected:focus-within { border-color: var(--c1-primary); box-shadow: inset 0 0 0 1px rgba(93, 43, 213, 0.1), 0 0 0 4px rgba(93, 43, 213, 0.18); } .c1-bf-choice input { height: 1px; left: 18px; opacity: 0; overflow: hidden; position: absolute; top: 22px; width: 1px; } .c1-bf-choice__control { background: #ffffff; border: 2px solid #a49bad; border-radius: 50%; height: 20px; left: 18px; position: absolute; top: 20px; width: 20px; } .c1-bf-choice.is-selected .c1-bf-choice__control { border: 6px solid var(--c1-primary); } .c1-bf-choice strong { font-size: 16px; line-height: 1.35; } .c1-bf-choice small { color: var(--c1-muted); font-size: 13px; line-height: 1.45; } .c1-bf-choice--wide { grid-column: 1 / -1; min-height: 96px; } .c1-bf-field-error { color: var(--c1-danger); font-size: 13px; font-weight: 650; margin: 8px 0 0; min-height: 0; } .c1-bf-route-summary { background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-left: 4px solid var(--c1-primary); border-radius: var(--c1-radius-inner); display: grid; gap: 7px; margin-top: 28px; padding: 20px 22px; } .c1-bf-route-summary[data-kind="income"], .c1-bf-route-summary[data-kind="individual"] { background: #fffaf0; border-color: #ead2a7; border-left-color: var(--c1-warning); } .c1-bf-route-summary > span { color: var(--c1-primary-strong); font-size: 12px; font-weight: 800; } .c1-bf-route-summary[data-kind="income"] > span, .c1-bf-route-summary[data-kind="individual"] > span { color: #704200; } .c1-bf-route-summary > strong { font-size: 20px; line-height: 1.3; } .c1-bf-route-summary > p { color: var(--c1-muted); margin: 0; max-width: 850px; } .c1-bf-route-summary > a { color: var(--c1-primary-strong); font-weight: 750; justify-self: start; margin-top: 4px; min-height: 44px; padding-block: 10px; } .c1-bf-date-step { border-bottom: 0; padding-bottom: 20px; } .c1-bf-form-grid { display: grid; gap: 18px; grid-template-columns: repeat(2, minmax(0, 1fr)); margin-top: 22px; } .c1-bf-field { display: grid; gap: 7px; min-width: 0; } .c1-bf-field label { cursor: default; font-size: 14px; font-weight: 720; } .c1-bf-field input { background: #ffffff; border: 1px solid var(--c1-line-strong); border-radius: var(--c1-radius-control); color: var(--c1-text); min-height: 52px; min-width: 0; padding: 11px 13px; width: 100%; } .c1-bf-field input:hover { border-color: #a99bbf; } .c1-bf-field input:focus { border-color: var(--c1-primary); box-shadow: var(--c1-focus); outline: 0; } .c1-bf-field input[aria-invalid="true"] { border-color: var(--c1-danger); } .c1-bf-field small { color: var(--c1-muted); font-size: 12px; } .c1-bf-buffer { background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-radius: var(--c1-radius-inner); margin: 8px 0 0; padding: 20px; } .c1-bf-buffer legend { font-weight: 760; padding-inline: 5px; } .c1-bf-buffer > p { color: var(--c1-muted); font-size: 13px; margin: 4px 0 0; } .c1-bf-buffer-options { display: grid; gap: 10px; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 16px; } .c1-bf-buffer-options label { min-width: 0; position: relative; } .c1-bf-buffer-options input { height: 1px; opacity: 0; position: absolute; width: 1px; } .c1-bf-buffer-options span { align-items: center; background: #ffffff; border: 1px solid var(--c1-line); border-radius: var(--c1-radius-control); display: flex; font-size: 14px; font-weight: 650; justify-content: center; min-height: 48px; padding: 9px 12px; text-align: center; } .c1-bf-buffer-options input:checked + span { background: var(--c1-surface-accent); border-color: var(--c1-primary); color: var(--c1-primary-strong); } .c1-bf-buffer-options label:focus-within span { box-shadow: var(--c1-focus); } .c1-bf-form-submit { align-items: center; display: grid; gap: 9px; justify-items: start; padding-top: 24px; } .c1-bf-form-submit button { border: 0; font-weight: 750; min-width: 220px; } .c1-bf-form-submit > p { color: var(--c1-muted); font-size: 13px; margin: 0; } .c1-bf-stale-note { color: var(--c1-warning) !important; font-weight: 700; } .c1-bf-methodology { background: var(--c1-surface-soft); border-top: 1px solid var(--c1-line); padding: 42px 46px; } .c1-bf-methodology__heading { max-width: 760px; } .c1-bf-methodology__grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin-top: 30px; } .c1-bf-methodology__grid article { padding: 0 24px; } .c1-bf-methodology__grid article:first-child { padding-left: 0; } .c1-bf-methodology__grid article + article { border-left: 1px solid var(--c1-line); } .c1-bf-methodology__grid strong { font-size: 17px; } .c1-bf-methodology__grid p { color: var(--c1-muted); font-size: 14px; margin: 9px 0 0; } .c1-bf-sources { align-items: center; border-top: 1px solid var(--c1-line); display: flex; gap: 18px; justify-content: space-between; margin-top: 30px; padding-top: 20px; } .c1-bf-sources > span { color: var(--c1-muted); font-size: 13px; } .c1-bf-sources > div { display: flex; flex-wrap: wrap; gap: 14px 18px; justify-content: flex-end; } .c1-bf-sources a { color: var(--c1-primary-strong); font-size: 13px; font-weight: 720; } .c1-bf-disclaimer { background: #ffffff; border: 1px solid var(--c1-line); border-radius: var(--c1-radius-control); color: var(--c1-muted); font-size: 13px; margin: 20px 0 0; padding: 13px 15px; } .c1-bf-result-layout { align-items: start; border-top: 1px solid var(--c1-line); display: grid; gap: 20px; grid-template-columns: minmax(0, 1.08fr) minmax(0, 0.92fr); padding: 42px 46px 46px; } .c1-bf-result-card, .c1-bf-warnings, .c1-bf-personal-cta { border-radius: var(--c1-radius); min-width: 0; } .c1-bf-result-card { background: #ffffff; border: 1px solid var(--c1-line-strong); box-shadow: var(--c1-shadow-sm); padding: 28px; } .c1-bf-result-card__eyebrow { color: var(--c1-primary-strong); font-size: 13px; font-weight: 760; margin: 0; } .c1-bf-result-card h2, .c1-bf-warnings h2, .c1-bf-personal-cta h2 { font-size: clamp(27px, 3vw, 39px); letter-spacing: -0.04em; line-height: 1.08; margin: 9px 0 0; } #result-title { border-radius: 8px; position: relative; } #result-title:focus, #result-title:focus-visible { background: linear-gradient(90deg, rgba(93, 43, 213, 0.09), rgba(93, 43, 213, 0)); box-shadow: none; outline: 2px solid transparent; } #result-title:focus::before, #result-title:focus-visible::before { background: var(--c1-primary); border-radius: 999px; bottom: 0; content: ""; left: -12px; position: absolute; top: 0; width: 3px; } .c1-bf-result-context { background: var(--c1-surface-soft); border-left: 3px solid var(--c1-primary); border-radius: 0 var(--c1-radius-control) var(--c1-radius-control) 0; color: var(--c1-muted); margin: 18px 0 0; padding: 12px 14px; } .c1-bf-result-card__amount { color: var(--c1-primary-strong); font-size: clamp(48px, 6vw, 72px); font-variant-numeric: tabular-nums; font-weight: 820; letter-spacing: -0.055em; line-height: 1; margin: 28px 0 0; overflow-wrap: normal; white-space: nowrap; } .c1-bf-result-card__buffer { color: var(--c1-muted); margin: 10px 0 0; } .c1-bf-result-stats { display: grid; gap: 10px; margin: 26px 0 0; } .c1-bf-result-stats > div { align-items: center; background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-radius: var(--c1-radius-inner); display: flex; gap: 20px; justify-content: space-between; min-width: 0; padding: 14px 16px; } .c1-bf-result-stats dt { color: var(--c1-muted); font-size: 14px; } .c1-bf-result-stats dd { flex: 0 0 auto; font-variant-numeric: tabular-nums; font-weight: 800; margin: 0; max-width: 52%; overflow-wrap: anywhere; text-align: right; } .c1-bf-formula-details { border: 1px solid var(--c1-line); border-radius: var(--c1-radius-inner); margin-top: 18px; } .c1-bf-formula-details summary { color: var(--c1-primary-strong); font-weight: 730; list-style-position: inside; min-height: 48px; padding: 13px 15px; } .c1-bf-formula-details > div { border-top: 1px solid var(--c1-line); color: var(--c1-muted); font-size: 13px; padding: 14px 16px; } .c1-bf-formula-details ul { margin: 0; padding-left: 20px; } .c1-bf-formula-details li + li { margin-top: 7px; } .c1-bf-formula-details #counted-months p { margin: 10px 0 0; } .c1-bf-result-card > .c1-bf-button { border: 1px solid rgba(93, 43, 213, 0.18); margin-top: 18px; } .c1-bf-result-side { display: grid; gap: 18px; min-width: 0; } .c1-bf-warnings { background: #ffffff; border: 1px solid var(--c1-line); padding: 24px; } .c1-bf-warnings h2 { font-size: clamp(25px, 2.7vw, 34px); } #warnings-list { display: grid; gap: 10px; margin-top: 18px; } .c1-bf-warning-card { background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-left: 4px solid var(--c1-primary); border-radius: var(--c1-radius-inner); padding: 16px; } .c1-bf-warning-card[data-tone="important"] { background: #fff5f7; border-color: #efc8d2; border-left-color: var(--c1-danger); } .c1-bf-warning-card[data-tone="notice"] { background: #fffaf0; border-color: #ead2a7; border-left-color: var(--c1-warning); } .c1-bf-warning-card h3 { font-size: 16px; line-height: 1.35; margin: 0; } .c1-bf-warning-card p { color: var(--c1-muted); font-size: 13px; margin: 7px 0 0; } .c1-bf-personal-cta { background: var(--c1-primary-dark); color: #ffffff; padding: 28px; } .c1-bf-personal-cta .c1-bf-kicker { color: #d8caff; } .c1-bf-personal-cta > p:not(.c1-bf-kicker) { color: #e6ddfb; margin: 15px 0 0; } .c1-bf-personal-cta .c1-bf-button { margin-top: 22px; width: 100%; } .c1-bf-personal-cta__method { color: #ffffff !important; display: block; font-size: 14px; margin-top: 14px; text-align: center; } .c1-bf-section { padding: 88px 0; } .c1-bf-section-heading { max-width: 760px; } .c1-bf-service-grid { display: grid; gap: 14px; grid-template-columns: repeat(12, minmax(0, 1fr)); margin-top: 34px; } .c1-bf-service-card { background: #ffffff; border: 1px solid var(--c1-line); border-radius: var(--c1-radius); display: flex; flex-direction: column; grid-column: span 5; min-height: 190px; padding: 24px; } .c1-bf-service-card--lead { background: radial-gradient(circle at 90% 0%, rgba(93, 43, 213, 0.18), transparent 20rem), var(--c1-surface-accent); grid-column: span 7; grid-row: span 3; justify-content: flex-start; min-height: 598px; padding: 34px; } .c1-bf-service-card > span { color: var(--c1-primary-strong); font-size: 12px; font-weight: 800; } .c1-bf-service-card h3 { font-size: 22px; letter-spacing: -0.025em; line-height: 1.2; margin: 10px 0 0; } .c1-bf-service-card--lead h3 { font-size: clamp(30px, 3.2vw, 43px); max-width: 520px; } .c1-bf-service-card p { color: var(--c1-muted); margin: 10px 0 0; } .c1-bf-service-card a { color: var(--c1-primary-strong); font-weight: 750; margin-top: auto; padding-top: 20px; } .c1-bf-service-visual { display: grid; flex: 1 1 auto; min-height: 210px; padding: 28px 18px 24px; place-items: center; position: relative; } .c1-bf-service-visual::before { background: rgba(255, 255, 255, 0.5); border: 1px solid rgba(93, 43, 213, 0.13); border-radius: 22px; content: ""; height: 72%; position: absolute; transform: rotate(4deg) translate(10px, 10px); width: min(82%, 370px); } .c1-bf-service-visual__document { background: rgba(255, 255, 255, 0.9); border: 1px solid rgba(93, 43, 213, 0.19); border-radius: 22px; box-shadow: 0 18px 45px rgba(61, 27, 139, 0.11); padding: 22px; position: relative; transform: rotate(-1.5deg); width: min(88%, 400px); z-index: 1; } .c1-bf-service-visual__top, .c1-bf-service-visual__row { align-items: center; display: flex; gap: 16px; justify-content: space-between; } .c1-bf-service-visual__top { border-bottom: 1px solid var(--c1-line); padding-bottom: 14px; } .c1-bf-service-visual__top > strong { font-size: 14px; } .c1-bf-service-visual__top > span { background: var(--c1-primary); border-radius: 50%; height: 28px; position: relative; width: 28px; } .c1-bf-service-visual__top > span::after { border-bottom: 2px solid #ffffff; border-right: 2px solid #ffffff; content: ""; height: 9px; left: 10px; position: absolute; top: 7px; transform: rotate(45deg); width: 5px; } .c1-bf-service-visual__line { background: #e8e1f6; border-radius: 999px; height: 8px; margin-top: 14px; width: 58%; } .c1-bf-service-visual__line--long { width: 82%; } .c1-bf-service-visual__row { color: var(--c1-muted); font-size: 12px; margin-top: 14px; } .c1-bf-service-visual__row strong { color: var(--c1-primary-strong); font-size: 12px; } .c1-bf-document-check { background: #ffffff; border-block: 1px solid var(--c1-line); } .c1-bf-document-check__grid { align-items: start; display: grid; gap: 64px; grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); } .c1-bf-document-check ul { background: var(--c1-surface-soft); border: 1px solid var(--c1-line); border-radius: var(--c1-radius); list-style: none; margin: 0; padding: 8px 24px; } .c1-bf-document-check li { align-items: start; display: grid; gap: 5px; padding: 18px 0; } .c1-bf-document-check li + li { border-top: 1px solid var(--c1-line); } .c1-bf-document-check li strong { font-size: 15px; } .c1-bf-document-check li span { color: var(--c1-muted); font-size: 14px; } .c1-bf-faq details { border-bottom: 1px solid var(--c1-line); } .c1-bf-faq details:first-of-type { border-top: 1px solid var(--c1-line); margin-top: 32px; } .c1-bf-faq summary { font-size: 17px; font-weight: 720; list-style: none; min-height: 64px; padding: 19px 42px 19px 0; position: relative; } .c1-bf-faq summary::-webkit-details-marker { display: none; } .c1-bf-faq summary::after { color: var(--c1-primary); content: "+"; font-size: 25px; font-weight: 500; position: absolute; right: 6px; top: 14px; } .c1-bf-faq details[open] summary::after { content: "−"; } .c1-bf-faq details p { color: var(--c1-muted); margin: 0; max-width: 760px; padding: 0 42px 20px 0; } .c1-bf-footer { background: #ffffff; border-top: 1px solid var(--c1-line); padding: 72px 0 30px; } .c1-bf-footer-cta { align-items: center; background: radial-gradient(circle at 100% 0%, rgba(255, 255, 255, 0.13), transparent 26rem), var(--c1-primary-dark); border-radius: var(--c1-radius); color: #ffffff; display: grid; gap: 42px; grid-template-columns: minmax(0, 1fr) minmax(280px, 330px); padding: clamp(30px, 5vw, 58px); } .c1-bf-footer-cta > div:first-child { max-width: 720px; } .c1-bf-footer-cta > div:first-child > p { color: #d7c9f7; font-size: 13px; font-weight: 760; margin: 0 0 14px; } .c1-bf-footer-cta h2 { max-width: 700px; } .c1-bf-footer-cta > div:first-child > span { color: #e7dff8; display: block; margin-top: 18px; max-width: 620px; } .c1-bf-footer-proof { display: flex; flex-wrap: wrap; gap: 9px; list-style: none; margin: 22px 0 0; padding: 0; } .c1-bf-footer-proof li { align-items: center; background: rgba(255, 255, 255, 0.09); border: 1px solid rgba(255, 255, 255, 0.16); border-radius: 999px; color: #f5f0ff; display: inline-flex; font-size: 13px; font-weight: 680; gap: 8px; min-height: 34px; padding: 6px 12px; } .c1-bf-footer-proof li::before { background: #ffffff; border-radius: 50%; box-shadow: inset 0 0 0 4px var(--c1-primary-dark); content: ""; height: 12px; width: 12px; } .c1-bf-footer-cta__actions { display: grid; gap: 12px; justify-items: stretch; min-width: 260px; } .c1-bf-footer-cta__actions > a:last-child { color: #ffffff; font-size: 14px; text-align: center; } .c1-bf-footer-meta { align-items: center; color: var(--c1-muted); display: flex; gap: 24px; justify-content: space-between; padding-top: 28px; } .c1-bf-footer-meta strong { color: var(--c1-text); } .c1-bf-footer-meta p { font-size: 12px; margin: 0; text-align: right; } .c1-bf-noscript { background: #fff4e3; border: 1px solid #e6c58f; color: #6f4100; margin: 16px; padding: 16px; } @media (max-width: 1020px) { .c1-bf-site-nav { display: none; } .c1-bf-header-cta { margin-left: auto; } .c1-bf-hero__grid { grid-template-columns: minmax(0, 1.08fr) minmax(340px, 0.92fr); } .c1-bf-hero h1 { font-size: clamp(43px, 5.5vw, 56px); } .c1-bf-result-layout { grid-template-columns: 1fr; } .c1-bf-service-card--lead { min-height: 520px; } } @media (max-width: 820px) { .c1-bf-hero { padding-top: 52px; } .c1-bf-hero__grid { grid-template-columns: 1fr; } .c1-bf-hero__content { max-width: 680px; } .c1-bf-hero__visual { margin-inline: auto; max-width: 620px; width: 100%; } .c1-bf-evidence__grid { grid-template-columns: 1fr; } .c1-bf-evidence__grid > div { padding: 18px 0; } .c1-bf-evidence__grid > div + div { border-left: 0; border-top: 1px solid var(--c1-line); } .c1-bf-choice-grid--three { grid-template-columns: 1fr; } .c1-bf-methodology__grid { grid-template-columns: 1fr; } .c1-bf-methodology__grid article { padding: 20px 0; } .c1-bf-methodology__grid article + article { border-left: 0; border-top: 1px solid var(--c1-line); } .c1-bf-sources { align-items: flex-start; flex-direction: column; } .c1-bf-sources > div { justify-content: flex-start; } .c1-bf-service-grid { grid-template-columns: 1fr; } .c1-bf-service-card, .c1-bf-service-card--lead { grid-column: auto; grid-row: auto; min-height: 0; } .c1-bf-service-card--lead { padding: 28px; } .c1-bf-service-card a { margin-top: 10px; } .c1-bf-document-check__grid { gap: 32px; grid-template-columns: 1fr; } .c1-bf-footer-cta { align-items: start; grid-template-columns: 1fr; } .c1-bf-footer-cta__actions { min-width: 0; width: 100%; } } @media (max-width: 600px) { .c1-bf-container { padding-inline: 16px; } .c1-bf-site-header__inner { min-height: 64px; } .c1-bf-brand > span:last-child { display: none; } .c1-bf-header-cta { font-size: 13px; min-height: 42px; padding: 9px 12px; } .c1-bf-hero { padding: 38px 0 44px; } .c1-bf-eyebrow { border-radius: 10px; font-size: 12px; line-height: 1.35; padding: 7px 9px; } .c1-bf-hero h1 { font-size: clamp(36px, 10.6vw, 46px); letter-spacing: -0.045em; line-height: 1.04; } .c1-bf-hero__lead { font-size: 17px; margin-top: 20px; } .c1-bf-hero__actions { align-items: stretch; flex-direction: column; margin-top: 26px; } .c1-bf-hero__actions .c1-bf-button { width: 100%; } .c1-bf-hero__visual::before { inset: 14px -8px -10px 14px; } .c1-bf-preview-card { padding: 18px; transform: none; } .c1-bf-preview-card__main strong { font-size: clamp(38px, 12vw, 52px); } .c1-bf-preview-card__stats div { align-items: flex-start; flex-direction: column; gap: 4px; } .c1-bf-preview-card__stats dd { text-align: left; } .c1-bf-calculator-section, .c1-bf-section { padding: 52px 0; } .c1-bf-calculator-shell { border-radius: 16px; } .c1-bf-calculator-intro, .c1-bf-calculator-form, .c1-bf-methodology, .c1-bf-result-layout { padding-left: 20px; padding-right: 20px; } .c1-bf-calculator-intro { padding-bottom: 30px; padding-top: 30px; } .c1-bf-calculator-intro h2, .c1-bf-methodology h2, .c1-bf-section-heading h2, .c1-bf-document-check h2, .c1-bf-footer-cta h2 { font-size: clamp(29px, 8.5vw, 38px); } .c1-bf-calculator-form { padding-bottom: 30px; } .c1-bf-step { padding: 22px 0; } .c1-bf-step legend, .c1-bf-date-step h3 { align-items: flex-start; font-size: 21px; } .c1-bf-choice-grid--purposes, .c1-bf-form-grid { grid-template-columns: 1fr; } .c1-bf-choice { min-height: 98px; padding: 14px 14px 14px 46px; } .c1-bf-choice__control { left: 15px; top: 16px; } .c1-bf-choice--wide { grid-column: auto; } .c1-bf-route-summary { padding: 18px; } .c1-bf-route-summary > strong { font-size: 18px; } .c1-bf-buffer { padding: 16px; } .c1-bf-buffer-options { grid-template-columns: 1fr; } .c1-bf-buffer-options span { justify-content: flex-start; } .c1-bf-form-submit { justify-items: stretch; } .c1-bf-form-submit button { min-width: 0; width: 100%; } .c1-bf-methodology { padding-bottom: 28px; padding-top: 28px; } .c1-bf-result-layout { padding-bottom: 28px; padding-top: 28px; } .c1-bf-result-card, .c1-bf-warnings, .c1-bf-personal-cta { border-radius: 16px; padding: 20px; } .c1-bf-result-card__amount { font-size: clamp(38px, 12vw, 48px); } .c1-bf-result-stats > div { align-items: flex-start; flex-direction: column; gap: 5px; } .c1-bf-result-stats dd { max-width: 100%; text-align: left; } .c1-bf-result-card > .c1-bf-button { width: 100%; } .c1-bf-service-card, .c1-bf-service-card--lead { border-radius: 16px; padding: 20px; } .c1-bf-service-visual { display: none; } .c1-bf-document-check ul { border-radius: 16px; padding-inline: 18px; } .c1-bf-faq summary { font-size: 16px; padding-right: 36px; } .c1-bf-footer { padding-top: 46px; } .c1-bf-footer-cta { border-radius: 16px; gap: 28px; padding: 26px 22px; } .c1-bf-footer-proof { gap: 7px; margin-top: 18px; } .c1-bf-footer-proof li { border-radius: 12px; justify-content: flex-start; width: 100%; } .c1-bf-footer-meta { align-items: flex-start; flex-direction: column; gap: 8px; } .c1-bf-footer-meta p { text-align: left; } } @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } .c1-bf-button, .c1-bf-choice, .c1-bf-preview-card { transition: none; } .c1-bf-button:hover, .c1-bf-button:active, .c1-bf-choice:hover, .c1-bf-preview-card { transform: none; } } </style> </head> <body class="c1-bank-funds-page"> <div id="c1bf-page-root"> <a class="c1-bf-skip-link" href="#main-content">К основному содержанию</a> <main id="main-content"> <section class="c1-bf-hero" id="top"> <div class="c1-bf-container c1-bf-hero__grid"> <div class="c1-bf-hero__content"> <p class="c1-bf-eyebrow">Финансовое подтверждение для Чехии</p> <h1>Сумма на счёте для визы и ВНЖ</h1> <p class="c1-bf-hero__lead">Выберите вид заявления и цель. Получите базовый расчёт и сразу увидьте, если вместо остатка нужен доход.</p> <div class="c1-bf-hero__actions"> <a class="c1-bf-button c1-bf-button--primary" href="#calculator">Начать расчёт</a> <a class="c1-bf-button c1-bf-button--quiet" href="#methodology">Методика и источники</a> </div> </div> <div class="c1-bf-hero__visual" aria-label="Пример результата расчёта для взрослого заявителя на 12 месяцев"> <div class="c1-bf-preview-card"> <div class="c1-bf-preview-card__top"> <span>Пример: 12 месяцев</span> <b>§ 13</b> </div> <div class="c1-bf-preview-card__main"> <small>Плановая сумма с запасом 10%</small> <strong>127 391 Kč</strong> <p>Для взрослого заявителя без дополнительного периода до начала пребывания.</p> </div> <dl class="c1-bf-preview-card__stats"> <div><dt>Базовый расчёт</dt><dd>115 810 Kč</dd></div> <div><dt>Добровольный запас</dt><dd>+10%</dd></div> </dl> <p class="c1-bf-preview-card__note">Конкретный результат зависит от вида заявления и цели пребывания.</p> </div> </div> </div> </section> <section class="c1-bf-evidence" aria-label="Основания доверия к калькулятору"> <div class="c1-bf-container c1-bf-evidence__grid"> <div><strong>§ 13 закона № 326/1999 Sb.</strong><span>база расчёта для применимых сценариев</span></div> <div><strong>Проверено 10.07.2026</strong><span>актуальность суммы 3 130 Kč</span></div> <div><strong>Расчёт в вашем браузере</strong><span>даты не отправляются и не сохраняются</span></div> </div> </section> <section class="c1-bf-calculator-section" id="calculator" aria-labelledby="calculator-title"> <div class="c1-bf-container"> <div class="c1-bf-calculator-shell"> <div class="c1-bf-calculator-intro"> <p class="c1-bf-kicker">Точный маршрут до расчёта</p> <h2 id="calculator-title">Подходит ли вам банковская формула</h2> <p>Вид заявления и цель пребывания влияют не только на сумму, но и на способ подтверждения средств.</p> </div> <form class="c1-bf-calculator-form" id="bank-funds-form" novalidate> <div class="c1-bf-validation-summary" id="validation-summary" role="alert" tabindex="-1" hidden> <strong>Проверьте данные</strong> <ul></ul> </div> <fieldset class="c1-bf-step" id="stay-type-group" data-field="stayType"> <legend><span>1</span> Что вы оформляете?</legend> <p class="c1-bf-field-help">Выберите документ, на который подаёте сейчас.</p> <div class="c1-bf-choice-grid c1-bf-choice-grid--three"> <label class="c1-bf-choice"> <input type="radio" name="stayType" value="long_term_visa"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Долгосрочная виза</strong> <small>Виза на срок больше 90 дней</small> </label> <label class="c1-bf-choice"> <input type="radio" name="stayType" value="long_term_residence"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Первый долгосрочный ВНЖ</strong> <small>Новая подача на разрешение</small> </label> <label class="c1-bf-choice"> <input type="radio" name="stayType" value="residence_extension"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Продление ВНЖ</strong> <small>Продление действующего разрешения</small> </label> </div> <p class="c1-bf-field-error" id="stay-type-error" data-error-for="stayType"></p> </fieldset> <fieldset class="c1-bf-step" id="purpose-type-group" data-field="purposeType" hidden> <legend><span>2</span> С какой целью вы подаёте?</legend> <p class="c1-bf-field-help">Цель пребывания и вид заявления проверяются отдельно. Это исключает ложные комбинации.</p> <div class="c1-bf-choice-grid c1-bf-choice-grid--purposes"> <label class="c1-bf-choice"> <input type="radio" name="purposeType" value="study"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Учёба</strong> <small>Школа, вуз, стипендия или грант</small> </label> <label class="c1-bf-choice"> <input type="radio" name="purposeType" value="entrepreneurship"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Предпринимательство</strong> <small>Бизнес-виза или ВНЖ предпринимателя</small> </label> <label class="c1-bf-choice"> <input type="radio" name="purposeType" value="family"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Воссоединение семьи</strong> <small>Семейная виза или долгосрочный ВНЖ</small> </label> <label class="c1-bf-choice"> <input type="radio" name="purposeType" value="work"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Работа</strong> <small>Рабочая карта, синяя карта или иная трудовая подача</small> </label> <label class="c1-bf-choice c1-bf-choice--wide"> <input type="radio" name="purposeType" value="other"> <span class="c1-bf-choice__control" aria-hidden="true"></span> <strong>Другая цель или не уверен(а)</strong> <small>Калькулятор не будет угадывать требования</small> </label> </div> <p class="c1-bf-field-error" id="purpose-type-error" data-error-for="purposeType"></p> </fieldset> <div class="c1-bf-route-summary" id="route-summary" data-kind="incomplete" role="status" aria-live="polite" hidden> <span id="route-status">Следующий шаг</span> <strong id="route-title">Выберите вид заявления и цель пребывания</strong> <p id="route-description">После выбора появятся только те поля, которые действительно влияют на результат.</p> <a id="route-consult-link" href="/consultation" target="_top" hidden>Уточнить требования на консультации</a> </div> <div id="calculation-fields" hidden> <div class="c1-bf-step c1-bf-date-step" id="dates-panel" data-field="dates"> <h3><span>3</span> Укажите даты и возраст</h3> <p class="c1-bf-field-help">Используйте даты из планируемого заявления. Пограничные месяцы калькулятор отметит отдельно.</p> <div class="c1-bf-form-grid"> <div class="c1-bf-field" data-field="applicationDate"> <label for="application-date">Дата подачи</label> <input id="application-date" name="applicationDate" type="date" aria-describedby="application-date-help application-date-error"> <small id="application-date-help">Когда подаёте или уже подали заявление</small> <p class="c1-bf-field-error" id="application-date-error" data-error-for="applicationDate"></p> </div> <div class="c1-bf-field" data-field="requestedStartDate"> <label for="requested-start-date">Начало пребывания</label> <input id="requested-start-date" name="requestedStartDate" type="date" aria-describedby="requested-start-help requested-start-error"> <small id="requested-start-help">Дата, с которой просите визу или ВНЖ</small> <p class="c1-bf-field-error" id="requested-start-error" data-error-for="requestedStartDate"></p> </div> <div class="c1-bf-field" data-field="requestedEndDate"> <label for="requested-end-date">Конец пребывания</label> <input id="requested-end-date" name="requestedEndDate" type="date" aria-describedby="requested-end-help requested-end-error"> <small id="requested-end-help">Последний день запрашиваемого периода</small> <p class="c1-bf-field-error" id="requested-end-error" data-error-for="requestedEndDate"></p> </div> <div class="c1-bf-field" data-field="dateOfBirth"> <label for="date-of-birth">Дата рождения заявителя</label> <input id="date-of-birth" name="dateOfBirth" type="date" aria-describedby="date-of-birth-help date-of-birth-error"> <small id="date-of-birth-help">Нужна для половинной ставки до 18 лет</small> <p class="c1-bf-field-error" id="date-of-birth-error" data-error-for="dateOfBirth"></p> </div> </div> </div> <fieldset class="c1-bf-buffer" data-field="safetyBufferPercent"> <legend>Добровольный запас CENTR1</legend> <p>Запас не является требованием закона. Вы можете убрать его или выбрать более осторожный ориентир.</p> <div class="c1-bf-buffer-options"> <label><input type="radio" name="safetyBuffer" value="0"><span>Без запаса</span></label> <label><input type="radio" name="safetyBuffer" value="10" checked><span>+10% рекомендованный</span></label> <label><input type="radio" name="safetyBuffer" value="15"><span>+15%</span></label> </div> <p class="c1-bf-field-error" data-error-for="safetyBufferPercent"></p> </fieldset> <div class="c1-bf-form-submit"> <button class="c1-bf-button c1-bf-button--primary" id="calculate-button" type="submit">Рассчитать сумму</button> <p>Расчёт выполняется локально. Введённые даты не отправляются на сервер.</p> <p class="c1-bf-stale-note" id="result-stale-note" role="status"></p> </div> </div> </form> <section class="c1-bf-methodology" id="methodology" aria-labelledby="methodology-title"> <div class="c1-bf-methodology__heading"> <p class="c1-bf-kicker">Методика без скрытых допущений</p> <h2 id="methodology-title">Что считается, а что проверяется отдельно</h2> <p>Базовый расчёт, методика CENTR1 и выбранный запас показываются раздельно.</p> </div> <div class="c1-bf-methodology__grid"> <article> <strong>Базовая сумма</strong> <p>Для применимых сценариев: 15 × 3 130 Kč за первый месяц и 2 × 3 130 Kč за каждый следующий полный месяц.</p> </article> <article> <strong>Возраст заявителя</strong> <p>До 18 лет применяется половина суммы. Смену возраста внутри периода калькулятор помечает как отдельный риск.</p> </article> <article> <strong>Период до начала пребывания</strong> <p>Он показывается отдельно как плановый ориентир CENTR1 и не выдаётся за дополнительное требование закона.</p> </article> </div> <div class="c1-bf-sources"> <span id="constants-version">Актуальность формулы проверена 10.07.2026.</span> <div> <a href="https://ipc.gov.cz/formulare-a-dokumenty/nalezitosti-dokumenty/doklad-o-zajisteni-prostredku-k-pobytu-na-uzemi-finance/" target="_blank" rel="noopener noreferrer">Разъяснение МВД Чехии</a> <a href="https://www.zakonyprolidi.cz/cs/1999-326#p13" target="_blank" rel="noopener noreferrer">Текст § 13</a> <a href="https://mpsv.gov.cz/zivotni-a-existencni-minimum-od-1-dubna-2020" target="_blank" rel="noopener noreferrer">MPSV: минимальные суммы</a> </div> </div> <p class="c1-bf-disclaimer">Калькулятор даёт ориентир для подготовки документов и не гарантирует решение МВД.</p> </section> <section class="c1-bf-result-layout" id="calculation-output" aria-labelledby="result-title" hidden> <article class="c1-bf-result-card" id="result-card"> <p class="c1-bf-result-card__eyebrow">Ваш результат</p> <h2 id="result-title" tabindex="-1">Плановая сумма</h2> <p class="c1-bf-result-context" id="result-context-note"></p> <p class="c1-bf-result-card__amount" id="recommended-amount">0 Kč</p> <p class="c1-bf-result-card__buffer" id="buffer-label"></p> <dl class="c1-bf-result-stats"> <div> <dt id="official-label">Базовый расчёт по § 13</dt> <dd id="official-amount">0 Kč</dd> </div> <div id="conservative-stat"> <dt>Плановый ориентир CENTR1</dt> <dd id="conservative-amount">0 Kč</dd> </div> <div> <dt>Учтённый период</dt> <dd id="months-summary">0 месяцев</dd> </div> </dl> <details class="c1-bf-formula-details"> <summary>Показать детали расчёта</summary> <div> <ul id="formula-explanation"></ul> <div id="counted-months"></div> </div> </details> <button class="c1-bf-button c1-bf-button--secondary" id="edit-inputs" type="button">Изменить данные</button> </article> <div class="c1-bf-result-side"> <section class="c1-bf-warnings" id="warnings-block" aria-labelledby="warnings-title" hidden> <p class="c1-bf-kicker">На что обратить внимание</p> <h2 id="warnings-title">Особенности вашей ситуации</h2> <div id="warnings-list"></div> </section> <section class="c1-bf-personal-cta" id="personal-cta" aria-labelledby="personal-cta-title"> <p class="c1-bf-kicker">Следующий шаг</p> <h2 id="personal-cta-title">Проверить документы</h2> <p id="personal-cta-message"></p> <a class="c1-bf-button c1-bf-button--light" id="personal-cta-link" href="/consultation" target="_top">Записаться на консультацию</a> <a class="c1-bf-personal-cta__method" href="#methodology">Вернуться к методике</a> </section> </div> </section> </div> </div> </section> <section class="c1-bf-section c1-bf-services" id="services"> <div class="c1-bf-container"> <div class="c1-bf-section-heading"> <h2>Цифра готова. Что проверить дальше?</h2> <p>Сумма помогает начать подготовку, но решение зависит от справки и всего комплекта документов.</p> </div> <div class="c1-bf-service-grid"> <article class="c1-bf-service-card c1-bf-service-card--lead"> <span>Комплект перед подачей</span> <h3>Проверка справки и документов</h3> <p>Сверим сумму, владельца счёта, доступность средств, дату документа и остальные обязательные подтверждения.</p> <div class="c1-bf-service-visual" aria-hidden="true"> <div class="c1-bf-service-visual__document"> <div class="c1-bf-service-visual__top"> <strong>Справка из банка</strong> <span></span> </div> <div class="c1-bf-service-visual__line c1-bf-service-visual__line--long"></div> <div class="c1-bf-service-visual__line"></div> <div class="c1-bf-service-visual__row"> <span>Владелец и дата</span> <strong>указаны</strong> </div> <div class="c1-bf-service-visual__row"> <span>Доступный остаток</span> <strong>проверяется</strong> </div> </div> </div> <a href="/consultation" target="_top">Записаться на проверку</a> </article> <article class="c1-bf-service-card"> <span>Доход</span> <h3>ВНЖ, семья и предпринимательство</h3> <p>Проверим, когда нужен регулярный доход и какие документы его подтверждают.</p> <a href="/consultation" target="_top">Проверить требования</a> </article> <article class="c1-bf-service-card"> <span>Банк</span> <h3>Формат справки</h3> <p>Подскажем, что должно быть видно в справке или выписке для вашей подачи.</p> <a href="/consultation" target="_top">Проверить справку</a> </article> <article class="c1-bf-service-card"> <span>Страхование</span> <h3>Страховка для МВД</h3> <p>После расчёта суммы можно отдельно подобрать медицинскую страховку.</p> <a href="https://centr1.cz/insurance-calculator" target="_top">Открыть калькулятор страховки</a> </article> </div> </div> </section> <section class="c1-bf-section c1-bf-document-check"> <div class="c1-bf-container c1-bf-document-check__grid"> <div> <h2>Что должно быть понятно из банковской справки</h2> <p>Хорошая справка не просто показывает цифру. Она позволяет проверить, кому принадлежит счёт и доступны ли средства в Чехии.</p> </div> <ul> <li><strong>Владелец счёта</strong><span>имя заявителя читается однозначно</span></li> <li><strong>Доступный остаток</strong><span>сумма доступна для использования</span></li> <li><strong>Дата документа</strong><span>справка актуальна на момент подачи</span></li> <li><strong>Валюта и доступность</strong><span>понятен пересчёт и возможность распоряжаться деньгами</span></li> </ul> </div> </section> <section class="c1-bf-section c1-bf-faq" id="faq"> <div class="c1-bf-container c1-bf-reading-width"> <div class="c1-bf-section-heading"> <h2>Частые вопросы</h2> <p>Коротко о сумме, справке и сценариях, где банковского остатка недостаточно.</p> </div> <details><summary>Сколько денег нужно показать на долгосрочную визу?</summary><p>Для обычной долгосрочной визы больше 30 дней применяется расчёт 15 × 3 130 Kč за первый месяц и 2 × 3 130 Kč за каждый следующий полный месяц.</p></details> <details><summary>Почему для бизнес-визы другая сумма?</summary><p>Для долгосрочной визы с целью предпринимательства действует отдельная фиксированная база: 50 × 3 130 Kč, то есть 156 500 Kč.</p></details>
<details><summary>Когда вместо остатка на счёте нужен доход?</summary><p>Такой сценарий характерен для ВНЖ предпринимателя и ВНЖ по воссоединению семьи. Доход должен быть регулярным и стабильным.</p></details> <details><summary>Почему калькулятор спрашивает дату подачи?</summary><p>Она нужна только для отдельного планового ориентира CENTR1 на период до начала пребывания. Этот период не является дополнительным требованием § 13.</p></details> <details><summary>Что происходит, если заявителю исполняется 18 лет?</summary><p>Калькулятор показывает помесячный ориентир и отдельно предупреждает о смене ставки. Момент её применения лучше уточнить для конкретной подачи.</p></details> <details><summary>Можно ли показать деньги на иностранном счёте?</summary><p>Нужно проверить владельца, доступность средств в Чехии, валюту, перевод справки и актуальные требования к документу.</p></details> </div> </section> </main> <footer class="c1-bf-footer"> <div class="c1-bf-container"> <div class="c1-bf-footer-cta"> <div> <p>Подготовка к подаче</p> <h2>Проверьте сумму и документы до визита в МВД</h2> <span>Консультация поможет сверить справку, доходы и комплект именно для вашего сценария.</span> <ul class="c1-bf-footer-proof" aria-label="Что можно проверить на консультации"> <li>Сумму и период</li> <li>Формат справки</li> <li>Доходы и комплект</li> </ul> </div> <div class="c1-bf-footer-cta__actions"> <a class="c1-bf-button c1-bf-button--light" href="/consultation" target="_top">Записаться на консультацию</a> <a href="#calculator">Вернуться к расчёту</a> </div> </div> <div class="c1-bf-footer-meta"> <strong>CENTR1</strong> <p>Расчёт ориентировочный и не является гарантией решения МВД. Страница не сохраняет введённые даты.</p> </div> </div> </footer> <noscript> <div class="c1-bf-noscript">Для интерактивного расчёта включите JavaScript. Методика и источники остаются доступными.</div> </noscript> </div> <script> (function () { const root = document.getElementById('c1bf-page-root'); if (!root) return; function getHeight() { const rect = root.getBoundingClientRect(); return Math.ceil(Math.max(rect.height, root.scrollHeight)); } function sendHeight() { parent.postMessage({ type: 'c1bf-calculator-height', height: getHeight() }, '*'); } function sendScrollTo(target) { if (!target) return; const rect = target.getBoundingClientRect(); parent.postMessage({ type: 'c1bf-calculator-scroll', y: Math.max(0, rect.top + window.scrollY) }, '*'); } document.addEventListener('click', function (event) { const link = event.target.closest('a[href^="#"]'); if (!link) return; const selector = link.getAttribute('href'); if (!selector || selector === '#') return; const target = document.querySelector(selector); if (!target) return; event.preventDefault(); sendScrollTo(target); }); document.addEventListener('c1bf:scroll-to', function (event) { const id = event.detail && event.detail.id; if (id) sendScrollTo(document.getElementById(id)); }); window.addEventListener('load', sendHeight); window.addEventListener('resize', sendHeight); if ('ResizeObserver' in window) new ResizeObserver(sendHeight).observe(root); document.addEventListener('toggle', sendHeight, true); setTimeout(sendHeight, 40); setTimeout(sendHeight, 240); })(); </script> <script> (function () { "use strict"; const __modules = Object.create(null); __modules["./constants.js"] = (() => { const EXISTENCE_MINIMUM = 3_130; const ADULT_FIRST_MONTH_AMOUNT = 15 * EXISTENCE_MINIMUM; const ADULT_NEXT_MONTH_AMOUNT = 2 * EXISTENCE_MINIMUM; const MINOR_FIRST_MONTH_AMOUNT = ADULT_FIRST_MONTH_AMOUNT / 2; const MINOR_NEXT_MONTH_AMOUNT = ADULT_NEXT_MONTH_AMOUNT / 2; const BUSINESS_VISA_AMOUNT = 50 * EXISTENCE_MINIMUM; const SAFETY_BUFFER_OPTIONS = Object.freeze([0, 10, 15]); const APPLICATION_TYPES = Object.freeze({ LONG_TERM_VISA_STANDARD: "long_term_visa_standard", LONG_TERM_RESIDENCE_STANDARD: "long_term_residence_standard", EXTENSION_STANDARD: "extension_standard", STUDY: "study", LONG_TERM_VISA_BUSINESS: "long_term_visa_business", FAMILY_PERMANENT_BUSINESS_RESIDENCE: "family_or_permanent_or_business_residence" }); const AGE_SCENARIOS = Object.freeze({ ADULT: "adult_for_entire_period", MINOR: "minor_for_entire_period", TURNS_18: "turns_18_during_period" }); const CALCULATION_MODES = Object.freeze({ QUICK: "quick", DATES: "dates", FIXED: "fixed" }); const CONSTANTS_VERSION = Object.freeze({ existenceMinimumCzk: EXISTENCE_MINIMUM, verifiedOn: "2026-07-10" }); return Object.freeze({ EXISTENCE_MINIMUM, ADULT_FIRST_MONTH_AMOUNT, ADULT_NEXT_MONTH_AMOUNT, MINOR_FIRST_MONTH_AMOUNT, MINOR_NEXT_MONTH_AMOUNT, BUSINESS_VISA_AMOUNT, SAFETY_BUFFER_OPTIONS, APPLICATION_TYPES, AGE_SCENARIOS, CALCULATION_MODES, CONSTANTS_VERSION }); })(); __modules["./calculations.js"] = (() => { const { ADULT_FIRST_MONTH_AMOUNT, ADULT_NEXT_MONTH_AMOUNT, AGE_SCENARIOS, APPLICATION_TYPES, BUSINESS_VISA_AMOUNT, CONSTANTS_VERSION, EXISTENCE_MINIMUM, MINOR_FIRST_MONTH_AMOUNT, MINOR_NEXT_MONTH_AMOUNT, SAFETY_BUFFER_OPTIONS } = __modules["./constants.js"]; const APPLICATION_TYPE_VALUES = new Set(Object.values(APPLICATION_TYPES)); function isLeapYear(year) { return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0); } function daysInMonth(year, month) { if (!Number.isInteger(year) || !Number.isInteger(month) || month < 1 || month > 12) { throw new RangeError("Invalid calendar month"); } if (month === 2) return isLeapYear(year) ? 29 : 28; return [4, 6, 9, 11].includes(month) ? 30 : 31; } function parseLocalDate(value) { if (typeof value !== "string") { throw new TypeError("Date must use YYYY-MM-DD format"); } const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value); if (!match) throw new RangeError("Date must use YYYY-MM-DD format"); const year = Number(match[1]); const month = Number(match[2]); const day = Number(match[3]); if (year < 1 || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) { throw new RangeError("Invalid calendar date"); } return Object.freeze({ year, month, day }); } function compareCalendarDates(left, right) { const leftValue = left.year * 10_000 + left.month * 100 + left.day; const rightValue = right.year * 10_000 + right.month * 100 + right.day; return Math.sign(leftValue - rightValue); } function calendarDayNumber(dateValue) { const previousYear = dateValue.year - 1; const daysBeforeYear = 365 * previousYear + Math.floor(previousYear / 4) - Math.floor(previousYear / 100) + Math.floor(previousYear / 400); const daysBeforeMonth = [0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334]; const leapDay = dateValue.month > 2 && isLeapYear(dateValue.year) ? 1 : 0; return daysBeforeYear + daysBeforeMonth[dateValue.month] + leapDay + dateValue.day; } function countCalendarDaysInclusive(startValue, endValue) { const startDate = typeof startValue === "string" ? parseLocalDate(startValue) : startValue; const endDate = typeof endValue === "string" ? parseLocalDate(endValue) : endValue; if (compareCalendarDates(endDate, startDate) < 0) { throw new RangeError("Requested end date must not be before start date"); } return calendarDayNumber(endDate) - calendarDayNumber(startDate) + 1; } function nextCalendarDay(dateValue) { const lastDay = daysInMonth(dateValue.year, dateValue.month); if (dateValue.day < lastDay) { return Object.freeze({ ...dateValue, day: dateValue.day + 1 }); } if (dateValue.month < 12) { return Object.freeze({ year: dateValue.year, month: dateValue.month + 1, day: 1 }); } return Object.freeze({ year: dateValue.year + 1, month: 1, day: 1 }); } function countWholeMonthsInclusive(startValue, endValue) { const startDate = typeof startValue === "string" ? parseLocalDate(startValue) : startValue; const endDate = typeof endValue === "string" ? parseLocalDate(endValue) : endValue; if (compareCalendarDates(endDate, startDate) < 0) { throw new RangeError("Requested end date must not be before start date"); } const endExclusive = nextCalendarDay(endDate); let months = (endExclusive.year - startDate.year) * 12 + endExclusive.month - startDate.month; if (endExclusive.day < startDate.day) months -= 1; // The long-stay branch of § 13 always starts with one base amount. Validation // separately prevents using this branch for periods of 90 days or less. return Math.max(1, months); } function createMonth(year, month) { return Object.freeze({ year, month, day: 1, key: `${String(year).padStart(4, "0")}-${String(month).padStart(2, "0")}` }); } function addMonth(monthValue) { return monthValue.month === 12 ? createMonth(monthValue.year + 1, 1) : createMonth(monthValue.year, monthValue.month + 1); } function compareMonths(left, right) { return Math.sign(left.year * 12 + left.month - (right.year * 12 + right.month)); } function endOfMonth(monthValue) { return Object.freeze({ year: monthValue.year, month: monthValue.month, day: daysInMonth(monthValue.year, monthValue.month) }); } function addYears(dateValue, years) { const year = dateValue.year + years; const day = Math.min(dateValue.day, daysInMonth(year, dateValue.month)); return Object.freeze({ year, month: dateValue.month, day }); } function buildRequestedStayMonths(startValue, endValue) { const startDate = typeof startValue === "string" ? parseLocalDate(startValue) : startValue; const endDate = typeof endValue === "string" ? parseLocalDate(endValue) : endValue; if (compareCalendarDates(endDate, startDate) < 0) { throw new RangeError("Requested end date must not be before start date"); } const monthCount = countWholeMonthsInclusive(startDate, endDate); const months = []; for (let cursor = createMonth(startDate.year, startDate.month); months.length < monthCount; cursor = addMonth(cursor)) { months.push(cursor); } const lastIndex = months.length - 1; months[lastIndex] = Object.freeze({ ...months[lastIndex], periodEnd: endDate }); return Object.freeze(months); } function buildGapMonths(applicationValue, requestedStartValue) { const applicationDate = typeof applicationValue === "string" ? parseLocalDate(applicationValue) : applicationValue; const requestedStartDate = typeof requestedStartValue === "string" ? parseLocalDate(requestedStartValue) : requestedStartValue; const startMonth = createMonth(requestedStartDate.year, requestedStartDate.month); const months = []; for (let cursor = addMonth(createMonth(applicationDate.year, applicationDate.month)); compareMonths(cursor, startMonth) < 0; cursor = addMonth(cursor)) { months.push(cursor); } return Object.freeze(months); } function isAdultMonth(monthValue, dateOfBirthValue) { const dateOfBirth = typeof dateOfBirthValue === "string" ? parseLocalDate(dateOfBirthValue) : dateOfBirthValue; const eighteenthBirthday = addYears(dateOfBirth, 18); const comparisonDate = monthValue.periodEnd ?? endOfMonth(monthValue); return compareCalendarDates(eighteenthBirthday, comparisonDate) <= 0; } function calculateAmountForMonths(countedMonths, dateOfBirthValue) { if (!Array.isArray(countedMonths) || countedMonths.length === 0) { throw new RangeError("At least one counted month is required"); } const amount = countedMonths.reduce((total, monthValue, index) => { const adult = isAdultMonth(monthValue, dateOfBirthValue); const firstMonth = index === 0; if (firstMonth) { return total + (adult ? ADULT_FIRST_MONTH_AMOUNT : MINOR_FIRST_MONTH_AMOUNT); } return total + (adult ? ADULT_NEXT_MONTH_AMOUNT : MINOR_NEXT_MONTH_AMOUNT); }, 0); return Math.ceil(amount); } function calculateStandardAmount(months, ageScenario = AGE_SCENARIOS.ADULT) { if (!Number.isInteger(months) || months < 1) { throw new RangeError("Months must be a positive integer"); } const adultAmount = ADULT_FIRST_MONTH_AMOUNT + (months - 1) * ADULT_NEXT_MONTH_AMOUNT; if (ageScenario === AGE_SCENARIOS.ADULT) return adultAmount; if (ageScenario === AGE_SCENARIOS.MINOR) return Math.ceil(adultAmount / 2); if (ageScenario === AGE_SCENARIOS.TURNS_18) { throw new RangeError("Mixed-age calculation requires date mode"); } throw new RangeError("Unknown age scenario"); } function applySafetyBuffer(amount, safetyBufferPercent) { if (!Number.isInteger(amount) || amount < 0) throw new RangeError("Amount must be a non-negative integer"); if (!SAFETY_BUFFER_OPTIONS.includes(safetyBufferPercent)) { throw new RangeError("Unsupported safety buffer"); } const numerator = amount * (100 + safetyBufferPercent); return Math.floor((numerator + 99) / 100); } function assertApplicationType(applicationType) { if (!APPLICATION_TYPE_VALUES.has(applicationType)) { throw new RangeError("Unknown application type"); } } function getTypeWarningCodes(applicationType) { const warnings = []; if (applicationType === APPLICATION_TYPES.FAMILY_PERMANENT_BUSINESS_RESIDENCE) { warnings.push("income_warning"); } if (applicationType === APPLICATION_TYPES.EXTENSION_STANDARD) { warnings.push("extension_review"); } if (applicationType === APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS) { warnings.push("business_visa_special_rule"); } if (applicationType === APPLICATION_TYPES.STUDY) { warnings.push("study_special_documents"); } return warnings; } function calculateBusinessVisa({ safetyBufferPercent = 10 } = {}) { const officialAmount = BUSINESS_VISA_AMOUNT; return freezeResult({ calculationMode: "fixed", applicationType: APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS, ageScenario: null, officialMonths: 0, gapMonths: 0, conservativeMonths: 0, officialAmount, conservativeAmount: officialAmount, recommendedAmount: applySafetyBuffer(officialAmount, safetyBufferPercent), safetyBufferPercent, hasGap: false, turns18DuringPeriod: false, ambiguousPartialMonths: false, warnings: ["business_visa_special_rule"], formulaExplanation: [ `Долгосрочная бизнес-виза: 50 × ${EXISTENCE_MINIMUM.toLocaleString("ru-RU")} Kč.` ], countedMonthsOfficial: [], countedMonthsConservative: [] }); } function getAgeScenario(countedMonths, dateOfBirth) { const adultStates = countedMonths.map((monthValue) => isAdultMonth(monthValue, dateOfBirth)); if (adultStates.every(Boolean)) return AGE_SCENARIOS.ADULT; if (adultStates.every((value) => !value)) return AGE_SCENARIOS.MINOR; return AGE_SCENARIOS.TURNS_18; } function isAmbiguousPartialPeriod(startDate, endDate) { return startDate.day !== 1 || endDate.day !== daysInMonth(endDate.year, endDate.month); } function freezeResult(result) { return Object.freeze({ ...result, warnings: Object.freeze([...result.warnings]), formulaExplanation: Object.freeze([...result.formulaExplanation]), countedMonthsOfficial: Object.freeze([...result.countedMonthsOfficial]), countedMonthsConservative: Object.freeze([...result.countedMonthsConservative]), constantsVersion: CONSTANTS_VERSION }); } function calculateQuick({ applicationType, months, ageScenario, safetyBufferPercent = 10 }) { assertApplicationType(applicationType); if (!Number.isInteger(months) || months < 3 || months > 24) { throw new RangeError("Quick months must be between 3 and 24"); } let officialAmount; let formulaExplanation; if (applicationType === APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS) { return calculateBusinessVisa({ safetyBufferPercent }); } else { officialAmount = calculateStandardAmount(months, ageScenario); const ageNote = ageScenario === AGE_SCENARIOS.MINOR ? " Применена половинная ставка." : ""; formulaExplanation = [ `15 × ${EXISTENCE_MINIMUM.toLocaleString("ru-RU")} + ${months - 1} × 2 × ${EXISTENCE_MINIMUM.toLocaleString("ru-RU")}.${ageNote}` ]; } const warnings = getTypeWarningCodes(applicationType); const recommendedAmount = applySafetyBuffer(officialAmount, safetyBufferPercent); return freezeResult({ calculationMode: "quick", applicationType, ageScenario, officialMonths: months, gapMonths: 0, conservativeMonths: months, officialAmount, conservativeAmount: officialAmount, recommendedAmount, safetyBufferPercent, hasGap: false, turns18DuringPeriod: false, ambiguousPartialMonths: false, warnings, formulaExplanation, countedMonthsOfficial: [], countedMonthsConservative: [] }); } function calculateDates({ applicationType, applicationDate: applicationDateValue, requestedStartDate: requestedStartDateValue, requestedEndDate: requestedEndDateValue, dateOfBirth: dateOfBirthValue, safetyBufferPercent = 10 }) { assertApplicationType(applicationType); if (applicationType === APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS) { return calculateBusinessVisa({ safetyBufferPercent }); } const applicationDate = parseLocalDate(applicationDateValue); const requestedStartDate = parseLocalDate(requestedStartDateValue); const requestedEndDate = parseLocalDate(requestedEndDateValue); const dateOfBirth = parseLocalDate(dateOfBirthValue); if (compareCalendarDates(requestedEndDate, requestedStartDate) < 0) { throw new RangeError("Requested end date must not be before start date"); } const countedMonthsOfficial = buildRequestedStayMonths(requestedStartDate, requestedEndDate); const gapMonthsList = buildGapMonths(applicationDate, requestedStartDate); const hasGap = gapMonthsList.length > 0; const countedMonthsConservative = Object.freeze([...gapMonthsList, ...countedMonthsOfficial]); const officialAgeScenario = getAgeScenario(countedMonthsOfficial, dateOfBirth); const conservativeAgeScenario = getAgeScenario(countedMonthsConservative, dateOfBirth); const turns18DuringPeriod = officialAgeScenario === AGE_SCENARIOS.TURNS_18 || conservativeAgeScenario === AGE_SCENARIOS.TURNS_18; let officialAmount; let conservativeAmount; let formulaExplanation; officialAmount = calculateAmountForMonths(countedMonthsOfficial, dateOfBirth); conservativeAmount = calculateAmountForMonths(countedMonthsConservative, dateOfBirth); formulaExplanation = [ `Запрашиваемый период: ${countedMonthsOfficial.length} мес.`, hasGap ? `Отдельный ориентир CENTR1 включает ${gapMonthsList.length} полн. мес. между подачей и началом периода.` : "Дополнительные полные месяцы до начала периода не добавлены.", turns18DuringPeriod ? "Для помесячного ориентира месяц 18-летия и следующие месяцы рассчитаны по взрослой ставке." : "Для всего периода применена единая возрастная ставка." ]; const ambiguousPartialMonths = isAmbiguousPartialPeriod(requestedStartDate, requestedEndDate); const warnings = getTypeWarningCodes(applicationType); if (hasGap) warnings.push("has_gap"); if (turns18DuringPeriod) warnings.push("turns_18_during_period"); if (ambiguousPartialMonths) warnings.push("ambiguous_partial_months"); const recommendedAmount = applySafetyBuffer(conservativeAmount, safetyBufferPercent); return freezeResult({ calculationMode: "dates", applicationType, ageScenario: conservativeAgeScenario, officialMonths: countedMonthsOfficial.length, gapMonths: gapMonthsList.length, conservativeMonths: countedMonthsConservative.length, officialAmount, conservativeAmount, recommendedAmount, safetyBufferPercent, hasGap, turns18DuringPeriod, ambiguousPartialMonths, warnings, formulaExplanation, countedMonthsOfficial: countedMonthsOfficial.map((monthValue) => monthValue.key), countedMonthsConservative: countedMonthsConservative.map((monthValue) => monthValue.key) }); } return Object.freeze({ isLeapYear, daysInMonth, parseLocalDate, compareCalendarDates, countCalendarDaysInclusive, countWholeMonthsInclusive, buildRequestedStayMonths, buildGapMonths, isAdultMonth, calculateAmountForMonths, calculateStandardAmount, applySafetyBuffer, calculateBusinessVisa, calculateQuick, calculateDates }); })(); __modules["./copy.js"] = (() => { const WARNING_COPY = Object.freeze({ income_warning: Object.freeze({ title: "Одного остатка на счёте может быть недостаточно", description: "Для этого вида ВНЖ проверяют регулярный и стабильный доход. Банковский ориентир не заменяет расчёт дохода.", tone: "important" }), has_gap: Object.freeze({ title: "Между подачей и началом периода есть ожидание", description: "Калькулятор показывает его только в отдельном плановом ориентире CENTR1. Эти месяцы не являются дополнительным требованием § 13.", tone: "notice" }), turns_18_during_period: Object.freeze({ title: "Возраст меняется во время запрашиваемого периода", description: "Помесячный ориентир применяет взрослую ставку с месяца 18-летия. Для конкретной подачи лучше отдельно уточнить момент применения ставки.", tone: "notice" }), business_visa_special_rule: Object.freeze({ title: "Для бизнес-визы действует отдельное правило", description: "Базовая сумма равна 50-кратному размеру экзистенциального минимума Чехии. Срок пребывания и возраст не меняют эту базу.", tone: "special" }), study_special_documents: Object.freeze({ title: "Для учёбы возможны другие подтверждения", description: "Стипендия, грант, обязательство школы или оплаченное проживание могут заменить или дополнить деньги на счёте.", tone: "special" }), ambiguous_partial_months: Object.freeze({ title: "Начало или конец периода приходится не на границу месяца", description: "Базовая сумма учитывает только полные месяцы длительности периода, как указано в § 13. Неполный остаток месяца не округляется вверх; проверьте, что даты совпадают с заявлением.", tone: "notice" }), family_documents: Object.freeze({ title: "Для семейной подачи важен весь комплект", description: "Базовая сумма рассчитана для долгосрочной визы. Состав семьи и дополнительные документы проверяются отдельно.", tone: "special" }), extension_review: Object.freeze({ title: "При продлении требования зависят от действующего разрешения", description: "Калькулятор показывает банковский ориентир для учебного сценария. До подачи проверьте актуальный список документов для продления.", tone: "notice" }) }); return Object.freeze({ WARNING_COPY }); })(); __modules["./funnel.js"] = (() => { const { APPLICATION_TYPES } = __modules["./constants.js"]; const CONSULTATION_URL = "/consultation"; const FUNNEL_STATES = Object.freeze({ standard: Object.freeze({ primaryCta: "Проверить справку и документы", href: CONSULTATION_URL, message: "Если сумма уже понятна, следующий шаг - проверить владельца счёта, доступность средств, дату справки и весь комплект документов." }), gap: Object.freeze({ primaryCta: "Проверить даты и справку", href: CONSULTATION_URL, message: "Период до начала пребывания показан отдельно как методика CENTR1. На консультации можно сверить его с датами вашей подачи." }), mixedAge: Object.freeze({ primaryCta: "Проверить возрастной сценарий", href: CONSULTATION_URL, message: "Возраст меняется внутри периода. Лучше проверить дату, с которой для вашей подачи должна применяться взрослая ставка." }), study: Object.freeze({ primaryCta: "Проверить учебную подачу", href: CONSULTATION_URL, message: "Для учёбы деньги на счёте могут сочетаться со стипендией, грантом, обязательством школы или оплаченным проживанием." }), business: Object.freeze({ primaryCta: "Проверить документы для бизнес-визы", href: CONSULTATION_URL, message: "Фиксированная сумма - только одна часть подачи. Отдельно проверьте документы по предпринимательству и подтверждение доступности средств." }) }); function resolveFunnel({ applicationType, warnings = [] }) { const warningSet = new Set(warnings); if (applicationType === APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS) return FUNNEL_STATES.business; if (warningSet.has("turns_18_during_period")) return FUNNEL_STATES.mixedAge; if (warningSet.has("has_gap")) return FUNNEL_STATES.gap; if (applicationType === APPLICATION_TYPES.STUDY) return FUNNEL_STATES.study; return FUNNEL_STATES.standard; } return Object.freeze({ resolveFunnel }); })(); __modules["./routes.js"] = (() => { const { APPLICATION_TYPES } = __modules["./constants.js"]; const STAY_TYPES = Object.freeze({ LONG_TERM_VISA: "long_term_visa", LONG_TERM_RESIDENCE: "long_term_residence", RESIDENCE_EXTENSION: "residence_extension" }); const PURPOSE_TYPES = Object.freeze({ STUDY: "study", ENTREPRENEURSHIP: "entrepreneurship", FAMILY: "family", WORK: "work", OTHER: "other" }); const ROUTE_KINDS = Object.freeze({ INCOMPLETE: "incomplete", CALCULATOR: "calculator", FIXED: "fixed", INCOME: "income", INDIVIDUAL: "individual" }); const STAY_TYPE_VALUES = new Set(Object.values(STAY_TYPES)); const PURPOSE_TYPE_VALUES = new Set(Object.values(PURPOSE_TYPES)); function route(kind, options = {}) { return Object.freeze({ kind, applicationType: null, warnings: Object.freeze([]), ...options, warnings: Object.freeze([...(options.warnings ?? [])]) }); } function resolveApplicationRoute({ stayType, purposeType }) { if (!STAY_TYPE_VALUES.has(stayType) || !PURPOSE_TYPE_VALUES.has(purposeType)) { return route(ROUTE_KINDS.INCOMPLETE, { title: "Выберите вид заявления и цель пребывания", description: "После выбора появятся только те поля, которые действительно влияют на результат." }); } if (purposeType === PURPOSE_TYPES.OTHER) { return route(ROUTE_KINDS.INDIVIDUAL, { title: "Для этой цели нельзя надёжно угадать формулу", description: "Требования зависят от конкретного разрешения и комплекта документов. Сначала уточните сценарий подачи." }); } if (purposeType === PURPOSE_TYPES.WORK) { return route(ROUTE_KINDS.INDIVIDUAL, { title: "Для трудовой подачи банковская сумма не всегда является основным требованием", description: "Рабочая или синяя карта подтверждается отдельным комплектом документов. Калькулятор не будет показывать потенциально неверную сумму." }); } if (purposeType === PURPOSE_TYPES.ENTREPRENEURSHIP) { if (stayType === STAY_TYPES.LONG_TERM_VISA) { return route(ROUTE_KINDS.FIXED, { applicationType: APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS, title: "Для долгосрочной бизнес-визы действует фиксированная сумма", description: "Базовая сумма равна 50-кратному размеру экзистенциального минимума Чехии. Даты периода и возраст на неё не влияют." }); } return route(ROUTE_KINDS.INCOME, { title: "Для ВНЖ предпринимателя нужен регулярный и стабильный доход", description: "Разовый остаток на банковском счёте не заменяет подтверждение совокупного месячного дохода и связанных документов." }); } if (purposeType === PURPOSE_TYPES.FAMILY) { if (stayType === STAY_TYPES.LONG_TERM_VISA) { return route(ROUTE_KINDS.CALCULATOR, { applicationType: APPLICATION_TYPES.LONG_TERM_VISA_STANDARD, warnings: ["family_documents"], title: "Для семейной долгосрочной визы доступен расчёт по датам", description: "Калькулятор покажет базовую сумму. Состав семьи и дополнительные документы нужно проверить отдельно." }); } return route(ROUTE_KINDS.INCOME, { title: "Для ВНЖ по воссоединению обычно проверяют доход семьи", description: "Одной справки с остатком на счёте может быть недостаточно. Нужен расчёт регулярного и стабильного дохода совместно оцениваемых лиц." }); } if (purposeType === PURPOSE_TYPES.STUDY) { const warnings = ["study_special_documents"]; if (stayType === STAY_TYPES.RESIDENCE_EXTENSION) warnings.push("extension_review"); return route(ROUTE_KINDS.CALCULATOR, { applicationType: stayType === STAY_TYPES.RESIDENCE_EXTENSION ? APPLICATION_TYPES.EXTENSION_STANDARD : APPLICATION_TYPES.STUDY, warnings, title: "Для учебной подачи доступен расчёт по датам", description: "Калькулятор покажет базовый ориентир. Стипендия, грант или оплаченное проживание могут изменить способ подтверждения средств." }); } return route(ROUTE_KINDS.INDIVIDUAL, { title: "Сценарий требует отдельной проверки", description: "Калькулятор не будет показывать сумму без подтверждённой формулы для выбранной комбинации." }); } return Object.freeze({ STAY_TYPES, PURPOSE_TYPES, ROUTE_KINDS, resolveApplicationRoute }); })(); __modules["./validation.js"] = (() => { const { AGE_SCENARIOS, APPLICATION_TYPES, CALCULATION_MODES, SAFETY_BUFFER_OPTIONS } = __modules["./constants.js"]; const { compareCalendarDates, countCalendarDaysInclusive, parseLocalDate } = __modules["./calculations.js"]; const { PURPOSE_TYPES, STAY_TYPES } = __modules["./routes.js"]; const APPLICATION_TYPE_VALUES = new Set(Object.values(APPLICATION_TYPES)); const STAY_TYPE_VALUES = new Set(Object.values(STAY_TYPES)); const PURPOSE_TYPE_VALUES = new Set(Object.values(PURPOSE_TYPES)); function getTodayCalendarDate() { const now = new Date(); return Object.freeze({ year: now.getFullYear(), month: now.getMonth() + 1, day: now.getDate() }); } function parseDateField(value, field, errors, requiredMessage) { if (!value) { errors[field] = requiredMessage; return null; } try { return parseLocalDate(value); } catch { errors[field] = "Укажите корректную календарную дату."; return null; } } function validateCalculatorInput(input, today = getTodayCalendarDate()) { const errors = {}; if (!STAY_TYPE_VALUES.has(input.stayType)) { errors.stayType = "Выберите вид заявления."; } if (!PURPOSE_TYPE_VALUES.has(input.purposeType)) { errors.purposeType = "Выберите цель пребывания."; } if (!APPLICATION_TYPE_VALUES.has(input.applicationType)) { errors.applicationType = "Выберите тип заявления."; } if (![CALCULATION_MODES.QUICK, CALCULATION_MODES.DATES, CALCULATION_MODES.FIXED].includes(input.calculationMode)) { errors.calculationMode = "Выберите способ расчёта."; } if (!SAFETY_BUFFER_OPTIONS.includes(input.safetyBufferPercent)) { errors.safetyBufferPercent = "Выберите доступный запас: 0%, 10% или 15%."; } if (input.calculationMode === CALCULATION_MODES.QUICK) { if (!Number.isInteger(input.months) || input.months < 3 || input.months > 24) { errors.months = "Выберите количество месяцев от 3 до 24."; } if (input.ageScenario === AGE_SCENARIOS.TURNS_18) { errors.ageScenario = "Для смешанного возрастного расчёта перейдите к расчёту по датам."; } else if (![AGE_SCENARIOS.ADULT, AGE_SCENARIOS.MINOR].includes(input.ageScenario)) { errors.ageScenario = "Выберите возрастной сценарий."; } } if (input.calculationMode === CALCULATION_MODES.DATES) { const applicationDate = parseDateField( input.applicationDate, "applicationDate", errors, "Укажите дату подачи." ); const requestedStartDate = parseDateField( input.requestedStartDate, "requestedStartDate", errors, "Укажите начало запрашиваемого периода." ); const requestedEndDate = parseDateField( input.requestedEndDate, "requestedEndDate", errors, "Укажите конец запрашиваемого периода." ); const dateOfBirth = parseDateField( input.dateOfBirth, "dateOfBirth", errors, "Укажите дату рождения." ); if ( requestedStartDate && requestedEndDate && compareCalendarDates(requestedEndDate, requestedStartDate) < 0 ) { errors.requestedEndDate = "Дата окончания не может быть раньше даты начала."; } if ( requestedStartDate && requestedEndDate && compareCalendarDates(requestedEndDate, requestedStartDate) >= 0 && countCalendarDaysInclusive(requestedStartDate, requestedEndDate) <= 90 ) { errors.requestedEndDate = "Для долгосрочной визы или ВНЖ укажите период длиннее 90 дней."; } if ( applicationDate && requestedStartDate && compareCalendarDates(applicationDate, requestedStartDate) > 0 ) { errors.applicationDate = "Дата подачи должна быть не позже начала запрашиваемого периода."; } if (dateOfBirth && compareCalendarDates(dateOfBirth, today) > 0) { errors.dateOfBirth = "Дата рождения не может быть в будущем."; } if (dateOfBirth && today.year - dateOfBirth.year > 120) { errors.dateOfBirth = "Проверьте дату рождения."; } if (applicationDate && dateOfBirth && compareCalendarDates(dateOfBirth, applicationDate) > 0) { errors.dateOfBirth = "Дата рождения должна быть раньше даты подачи."; } } return Object.freeze({ valid: Object.keys(errors).length === 0, errors: Object.freeze(errors) }); } return Object.freeze({ getTodayCalendarDate, validateCalculatorInput }); })(); __modules["./app.js"] = (() => { const { APPLICATION_TYPES, CALCULATION_MODES, CONSTANTS_VERSION } = __modules["./constants.js"]; const { calculateBusinessVisa, calculateDates } = __modules["./calculations.js"]; const { WARNING_COPY } = __modules["./copy.js"]; const { resolveFunnel } = __modules["./funnel.js"]; const { ROUTE_KINDS, resolveApplicationRoute } = __modules["./routes.js"]; const { validateCalculatorInput } = __modules["./validation.js"]; const form = document.querySelector("#bank-funds-form"); const output = document.querySelector("#calculation-output"); const resultCard = document.querySelector("#result-card"); const validationSummary = document.querySelector("#validation-summary"); const datesPanel = document.querySelector("#dates-panel"); const purposeStep = document.querySelector("#purpose-type-group"); const calculationFields = document.querySelector("#calculation-fields"); const routeSummary = document.querySelector("#route-summary"); const routeStatus = document.querySelector("#route-status"); const routeTitle = document.querySelector("#route-title"); const routeDescription = document.querySelector("#route-description"); const routeConsultLink = document.querySelector("#route-consult-link"); const calculateButton = document.querySelector("#calculate-button"); const warningsBlock = document.querySelector("#warnings-block"); const warningsList = document.querySelector("#warnings-list"); const resultTitle = document.querySelector("#result-title"); const resultContextNote = document.querySelector("#result-context-note"); const resultEyebrow = document.querySelector(".c1-bf-result-card__eyebrow"); const staleNote = document.querySelector("#result-stale-note"); const dateInputs = [...datesPanel.querySelectorAll("input[type='date']")]; const choiceCards = [...document.querySelectorAll(".c1-bf-choice")]; let currentRoute = resolveApplicationRoute({ stayType: "", purposeType: "" }); const currencyFormatter = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 0, style: "currency", currency: "CZK" }); function formatCzk(value) { return currencyFormatter.format(value).replace("CZK", "Kč"); } function formatVerifiedDate(value) { const parts = value.split("-"); return parts.length === 3 ? parts[2] + "." + parts[1] + "." + parts[0] : value; } function getCheckedValue(name) { return form.querySelector("input[name='" + name + "']:checked")?.value ?? ""; } function clearErrors() { validationSummary.hidden = true; validationSummary.querySelector("ul").replaceChildren(); form.querySelectorAll("[aria-invalid='true']").forEach((element) => element.removeAttribute("aria-invalid")); form.querySelectorAll("[data-error-for]").forEach((element) => { element.textContent = ""; }); } function renderErrors(errors) { clearErrors(); const list = validationSummary.querySelector("ul"); Object.entries(errors).forEach(([field, message]) => { form.querySelectorAll("[data-error-for='" + field + "']").forEach((target) => { if (!target.closest("[hidden]")) target.textContent = message; }); const container = form.querySelector("[data-field='" + field + "']"); if (container) { container.setAttribute("aria-invalid", "true"); container.querySelectorAll("input, select, textarea").forEach((control) => { control.setAttribute("aria-invalid", "true"); }); } const item = document.createElement("li"); item.textContent = message; list.append(item); }); validationSummary.hidden = false; validationSummary.focus(); } function updateChoiceStates() { choiceCards.forEach((choice) => { const input = choice.querySelector("input"); choice.classList.toggle("is-selected", input?.checked === true); }); } function setDateFieldsEnabled(enabled) { dateInputs.forEach((input) => { input.disabled = !enabled; input.required = enabled; }); } function routeStatusText(kind) { if (kind === ROUTE_KINDS.CALCULATOR) return "Расчёт доступен"; if (kind === ROUTE_KINDS.FIXED) return "Отдельное правило"; if (kind === ROUTE_KINDS.INCOME) return "Нужен расчёт дохода"; if (kind === ROUTE_KINDS.INDIVIDUAL) return "Нужна отдельная проверка"; return "Следующий шаг"; } function renderRoute(route) { const hasStayType = Boolean(getCheckedValue("stayType")); const hasPurposeType = Boolean(getCheckedValue("purposeType")); routeSummary.dataset.kind = route.kind; routeStatus.textContent = routeStatusText(route.kind); routeTitle.textContent = route.title; routeDescription.textContent = route.description; const canCalculate = route.kind === ROUTE_KINDS.CALCULATOR || route.kind === ROUTE_KINDS.FIXED; const needsConsultation = route.kind === ROUTE_KINDS.INCOME || route.kind === ROUTE_KINDS.INDIVIDUAL; purposeStep.hidden = !hasStayType; routeSummary.hidden = !hasStayType || !hasPurposeType; routeConsultLink.hidden = !needsConsultation; calculationFields.hidden = !canCalculate; datesPanel.hidden = route.kind !== ROUTE_KINDS.CALCULATOR; setDateFieldsEnabled(route.kind === ROUTE_KINDS.CALCULATOR); calculateButton.textContent = route.kind === ROUTE_KINDS.FIXED ? "Показать фиксированную сумму" : "Рассчитать сумму"; form.dataset.routeKind = route.kind; output.hidden = true; staleNote.textContent = ""; updateChoiceStates(); } function updateRoute() { currentRoute = resolveApplicationRoute({ stayType: getCheckedValue("stayType"), purposeType: getCheckedValue("purposeType") }); clearErrors(); renderRoute(currentRoute); } function collectInput(route) { const base = { stayType: getCheckedValue("stayType"), purposeType: getCheckedValue("purposeType"), applicationType: route.applicationType, safetyBufferPercent: Number(getCheckedValue("safetyBuffer")) }; if (route.kind === ROUTE_KINDS.FIXED) { return { ...base, calculationMode: CALCULATION_MODES.FIXED }; } return { ...base, calculationMode: CALCULATION_MODES.DATES, applicationDate: form.elements.namedItem("applicationDate")?.value ?? "", requestedStartDate: form.elements.namedItem("requestedStartDate")?.value ?? "", requestedEndDate: form.elements.namedItem("requestedEndDate")?.value ?? "", dateOfBirth: form.elements.namedItem("dateOfBirth")?.value ?? "" }; } function monthRangeText(months) { if (!months.length) return ""; return months.length <= 6 ? months.join(", ") : months[0] + " - " + months.at(-1) + " (" + months.length + " мес.)"; } function renderWarnings(result) { warningsList.replaceChildren(); warningsBlock.hidden = result.warnings.length === 0; result.warnings.forEach((warningCode) => { const copy = WARNING_COPY[warningCode]; if (!copy) return; const article = document.createElement("article"); article.className = "c1-bf-warning-card"; article.dataset.tone = copy.tone; const heading = document.createElement("h3"); heading.textContent = copy.title; const description = document.createElement("p"); description.textContent = copy.description; article.append(heading, description); warningsList.append(article); }); } function renderFunnel(result) { const funnel = resolveFunnel(result); document.querySelector("#personal-cta-title").textContent = funnel.primaryCta; document.querySelector("#personal-cta-message").textContent = funnel.message; const link = document.querySelector("#personal-cta-link"); link.textContent = funnel.primaryCta; link.href = funnel.href; } function withRouteWarnings(result, route) { const warnings = [...new Set([...result.warnings, ...route.warnings])]; return Object.freeze({ ...result, warnings: Object.freeze(warnings) }); } function renderResult(result) { const businessVisa = result.applicationType === APPLICATION_TYPES.LONG_TERM_VISA_BUSINESS; const hasPlanningDifference = result.conservativeAmount !== result.officialAmount; resultEyebrow.textContent = businessVisa ? "Долгосрочная бизнес-виза" : "Плановый ориентир CENTR1"; resultTitle.textContent = result.safetyBufferPercent > 0 ? "Сумма с выбранным запасом" : businessVisa ? "Фиксированная базовая сумма" : "Базовая сумма"; resultContextNote.textContent = businessVisa ? "Основа расчёта: 50-кратный размер экзистенциального минимума Чехии." : hasPlanningDifference ? "Главная сумма включает отдельный ориентир на период до начала пребывания и выбранный запас." : "Главная сумма включает выбранный добровольный запас."; document.querySelector("#recommended-amount").textContent = formatCzk(result.recommendedAmount); document.querySelector("#buffer-label").textContent = result.safetyBufferPercent === 0 ? "Без добровольного запаса CENTR1" : "Добровольный запас +" + result.safetyBufferPercent + "% включён в главную сумму"; document.querySelector("#official-label").textContent = businessVisa ? "Фиксированная сумма" : "Базовый расчёт по § 13"; document.querySelector("#official-amount").textContent = formatCzk(result.officialAmount); document.querySelector("#conservative-amount").textContent = formatCzk(result.conservativeAmount); document.querySelector("#conservative-stat").hidden = businessVisa || !hasPlanningDifference; document.querySelector("#months-summary").textContent = businessVisa ? "Срок не меняет базовую сумму" : result.gapMonths > 0 ? result.officialMonths + " мес. пребывания, " + result.gapMonths + " мес. отдельно" : result.officialMonths + " мес. пребывания"; const explanation = document.querySelector("#formula-explanation"); explanation.replaceChildren(); result.formulaExplanation.forEach((line) => { const item = document.createElement("li"); item.textContent = line; explanation.append(item); }); const countedMonths = document.querySelector("#counted-months"); countedMonths.replaceChildren(); if (result.countedMonthsOfficial.length) { const official = document.createElement("p"); const label = document.createElement("strong"); label.textContent = "Запрашиваемый период: "; official.append(label, monthRangeText(result.countedMonthsOfficial)); countedMonths.append(official); } if (hasPlanningDifference && result.countedMonthsConservative.length) { const planning = document.createElement("p"); const label = document.createElement("strong"); label.textContent = "Плановый период CENTR1: "; planning.append(label, monthRangeText(result.countedMonthsConservative)); countedMonths.append(planning); } renderWarnings(result); renderFunnel(result); staleNote.textContent = ""; output.hidden = false; resultTitle.focus({ preventScroll: true }); resultCard.scrollIntoView({ behavior: "smooth", block: "start" }); window.setTimeout(() => { document.dispatchEvent(new CustomEvent("c1bf:scroll-to", { detail: { id: "result-card" } })); }, 160); } function handleSubmit(event) { event.preventDefault(); currentRoute = resolveApplicationRoute({ stayType: getCheckedValue("stayType"), purposeType: getCheckedValue("purposeType") }); if (currentRoute.kind === ROUTE_KINDS.INCOMPLETE) { const errors = {}; if (!getCheckedValue("stayType")) errors.stayType = "Выберите вид заявления."; if (!getCheckedValue("purposeType")) errors.purposeType = "Выберите цель пребывания."; renderErrors(errors); return; } if (![ROUTE_KINDS.CALCULATOR, ROUTE_KINDS.FIXED].includes(currentRoute.kind)) { routeConsultLink.focus(); return; } const input = collectInput(currentRoute); const validation = validateCalculatorInput(input); if (!validation.valid) { renderErrors(validation.errors); return; } clearErrors(); try { const calculated = currentRoute.kind === ROUTE_KINDS.FIXED ? calculateBusinessVisa({ safetyBufferPercent: input.safetyBufferPercent }) : calculateDates(input); renderResult(withRouteWarnings(calculated, currentRoute)); } catch { renderErrors({ calculationMode: "Не удалось выполнить расчёт. Проверьте введённые данные." }); } } function markResultStale() { if (!output.hidden) { output.hidden = true; staleNote.textContent = "Данные изменились. Рассчитайте сумму ещё раз."; } } function initialize() { document.querySelector("#constants-version").textContent = "Актуальность формулы проверена " + formatVerifiedDate(CONSTANTS_VERSION.verifiedOn) + "."; renderRoute(currentRoute); form.addEventListener("submit", handleSubmit); form.addEventListener("change", (event) => { if (["stayType", "purposeType"].includes(event.target.name)) { updateRoute(); return; } markResultStale(); }); form.addEventListener("input", (event) => { if (!["stayType", "purposeType"].includes(event.target.name)) markResultStale(); }); document.querySelector("#edit-inputs").addEventListener("click", () => { document.querySelector("#calculator-title").scrollIntoView({ behavior: "smooth", block: "start" }); document.dispatchEvent(new CustomEvent("c1bf:scroll-to", { detail: { id: "calculator-title" } })); const target = currentRoute.kind === ROUTE_KINDS.CALCULATOR ? datesPanel.querySelector("input:not([disabled])") : form.querySelector("input[name='stayType']"); target?.focus({ preventScroll: true }); }); } initialize(); return Object.freeze({ }); })(); })(); </script> </body> </html>