Перейти к содержанию
CENTR1
поддержка иностранцев
Страхование
Страховка для визы, ВНЖ и жизни в Чехии
Сравним доступные варианты после проверки вашей цели, срока и исходных данных.
Получить варианты и цену
Выберите задачу
Медицинское страхование
Начните с цели и типа пребывания
Гид по страхованию 2026
Покрытие, документы и проверка
Для продления ВНЖ
Полис под срок и место подачи
Для студентов
Учёба, продление и условия полиса
Особые медицинские условия
Предварительная проверка ограничений
Визы и ВНЖ
Документы и следующий шаг
Письмо МВД, смена цели, ПМЖ, гражданство и другие вопросы пребывания.
Визовые консультации
Актуально в 2026 году
Продление визы и ВНЖ
Сроки подачи, документы, страховка и порядок продления в Чехии.
Открыть гид 2026
Срок подачи уже близко?
Откройте отдельную инструкцию для продления в последний момент.
Срочное продление
Студентам
Виза, ВНЖ, письма и смена цели
Рабочее пребывание
Работа и смена работодателя
OSVČ
Предпринимательство и документы
Семья
Воссоединение и совместное проживание
ПМЖ
Условия и история пребывания
Гражданство
Этапы, документы и подготовка
Отказ и правовая оценка
Сроки и возможные действия
ZDP
Материал о специальном статусе
Ипотека
Ипотечный маршрут для иностранцев
Проверьте бюджет и требования банков до выбора объекта.
Проверить бюджет и шансы
Ипотечные инструменты
Ипотека в Чехии
Главная ипотечная страница
Консультация
Персональный разбор ситуации
Сравнение банков
Условия и логика выбора
Предварительное одобрение
Проверка до выбора объекта
Рефинансирование
Пересмотр действующей ипотеки
Компания
Команда, знания и контакты
Узнайте, кто отвечает за ваш запрос и как устроена работа CENTR1.
Выбрать способ связи
О компании
О CENTR1
Команда, роли и формат работы
Партнёры
Страхование, финансы, право и медиа
Блог и инструкции
Материалы о жизни в Чехии
Курсы
Язык и интеграционная подготовка
Контакты
Онлайн и офис в Брно
RU
UK
EN
CS
Выбрать ситуацию
Выбрать ситуацию
Выберите ближайшую задачу
Нужна страховка
Получить варианты и цену
Срок продления близко
Открыть срочную инструкцию
Пришло письмо или отказ
Понять, что можно сделать дальше
Планирую ипотеку
Проверить бюджет и шансы
Страхование
Медицинское страхование
Маршрут по цели пребывания
Гид 2026
Покрытие и документы
Для продления ВНЖ
Полис под срок подачи
Для студентов
Учёба и продление
Особые условия
Предварительная проверка
Визы и ВНЖ
Визовые консультации
Форматы и результаты
Продление визы и ВНЖ в 2026 году
Сроки, документы и страховка
Студентам
Виза, ВНЖ и письма
Рабочее пребывание
Работа и смена работодателя
OSVČ
Предпринимательство
Семья
Воссоединение
ПМЖ
История пребывания
Гражданство
Этапы и документы
Срочное продление
Инструкция последнего момента
Отказ
Правовая оценка
Ипотека
Ипотека в Чехии
Главный маршрут
Консультация
Разбор ситуации
Сравнение банков
Условия выбора
Предварительное одобрение
Проверка до объекта
Рефинансирование
Пересмотр ипотеки
Компания
О CENTR1
Команда и роли
Партнёры
Страхование, финансы и право
Блог
Инструкции и обновления
Курсы
Язык и интеграция
Контакты
Онлайн и офис в Брно
RU
UK
EN
CS
Позвонить
WhatsApp
<!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>