// ZAO "FC" — Chemical products for wood protection
const { useState, useEffect, useMemo, useRef } = React;
const Icon = ({ path, size = 20, ...rest }) => (
{path}
);
const I = {
spark: <> >,
arrowRight: <> >,
check: ,
x: <> >,
plus: <> >,
minus: ,
compass: <> >,
target: <> >,
map: <> >,
layers: <> >,
zap: ,
dollar: <> >,
users: <> >,
briefcase: <> >,
mic: <> >,
building: <> >,
dot: ,
clock: <> >,
calendar: <> >,
mail: <> >,
moon: ,
sun: <> >,
sliders: <> >,
menu: <> >,
truck: <> >,
package: <> >,
shield: <> >,
calculator: <> >,
chevronDown: ,
};
// ----- CSS переменные для лесной темы -----
const surfaceAltLight = "#F0F7EE";
const surfaceAltDark = "#1A2E1E";
// ----- Цветовая тема: ЛЕСНАЯ (forest) -----
const colorTheme = {
primary: "from-emerald-600 to-green-600",
secondary: "from-emerald-700 to-green-600",
gradient: "from-emerald-600 via-green-600 to-emerald-500",
gradientBg: "from-emerald-100 to-green-100",
gradientBgDark: "from-emerald-500/20 to-green-500/20",
button: "bg-gradient-to-r from-emerald-700 to-green-700",
buttonHover: "hover:bg-gradient-to-l from-emerald-700 to-green-700",
text: "text-emerald-600",
textHover: "text-emerald-700",
light: "from-emerald-50 to-green-50",
dark: "from-emerald-900/30 to-green-900/30",
badge: "from-emerald-500 to-green-500",
card: "from-emerald-800 via-green-800 to-emerald-700",
accent: "from-emerald-900 via-green-900 to-emerald-800",
};
// ----- Компонент для обновления CSS переменных -----
function ThemeStyleUpdater({ dark }) {
React.useEffect(() => {
const root = document.documentElement;
if (!dark) {
root.style.setProperty("--surface-alt", surfaceAltLight);
} else {
root.style.setProperty("--surface-alt", surfaceAltDark);
}
}, [dark]);
return null;
}
// ---------- Header ----------
function Header({ t, lang, onSetLang, dark, onSetDark }) {
const [scrolled, setScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const drawerRef = useRef(null);
const hamburgerRef = useRef(null);
const firstLinkRef = useRef(null);
useEffect(() => {
const on = () => setScrolled(window.scrollY > 20);
window.addEventListener("scroll", on, { passive: true });
return () => window.removeEventListener("scroll", on);
}, []);
useEffect(() => {
if (!menuOpen) return;
const onKey = (e) => { if (e.key === "Escape") setMenuOpen(false); };
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [menuOpen]);
useEffect(() => {
if (!menuOpen) return;
const onClick = (e) => {
if (
drawerRef.current && !drawerRef.current.contains(e.target) &&
hamburgerRef.current && !hamburgerRef.current.contains(e.target)
) {
setMenuOpen(false);
}
};
document.addEventListener("mousedown", onClick);
document.addEventListener("touchstart", onClick);
return () => {
document.removeEventListener("mousedown", onClick);
document.removeEventListener("touchstart", onClick);
};
}, [menuOpen]);
useEffect(() => {
if (menuOpen && firstLinkRef.current) {
firstLinkRef.current.focus();
}
}, [menuOpen]);
useEffect(() => {
const mql = window.matchMedia("(min-width: 768px)");
const onChange = (e) => { if (e.matches) setMenuOpen(false); };
if (mql.addEventListener) mql.addEventListener("change", onChange);
else mql.addListener(onChange);
return () => {
if (mql.removeEventListener) mql.removeEventListener("change", onChange);
else mql.removeListener(onChange);
};
}, []);
const closeDrawer = () => setMenuOpen(false);
const toggleTheme = () => onSetDark(!dark);
return (
ЗАО «ФК»
{t.nav.map(l => (
{l.label}
))}
{["ru", "en"].map(l => (
onSetLang(l)}
aria-pressed={lang === l}
className={`px-2 sm:px-3 py-0.5 sm:py-1 text-[11px] sm:text-xs font-semibold rounded-full transition-all uppercase ${
lang === l
? `${colorTheme.button} text-white shadow-sm`
: "text-[var(--fg-muted)] hover:text-[var(--fg)]"
}`}
>
{l}
))}
{t.navCta}
setMenuOpen(o => !o)}
aria-expanded={menuOpen}
aria-controls="mobile-drawer"
aria-label={menuOpen ? "Close menu" : "Open menu"}
className="md:hidden inline-flex items-center justify-center w-11 h-11 rounded-full text-[var(--fg)] hover:bg-[var(--surface-alt)] transition-colors"
>
{t.nav.map((l, i) => (
{l.label}
))}
{ toggleTheme(); closeDrawer(); }}
className="mt-2 flex items-center justify-center gap-2 py-3 text-base font-semibold text-[var(--fg)] border-b border-[var(--border)] min-h-[44px]"
>
{dark ? "Светлая тема" : "Тёмная тема"}
{t.navCta}
);
}
// ---------- Hero ----------
function Hero({ t }) {
return (
{t.hero.badge}
{t.hero.h1pre}{" "}
{t.hero.h1post}
{t.hero.lead}
{t.hero.meta.map((m, i) => (
{m}
))}
);
}
// ---------- Products ----------
function Products({ t, onOpenDetails }) {
return (
{t.products.eyebrow}
{t.products.title}
{t.products.sub}
{t.products.items.map((p, i) => (
{p.name}
{p.shortDesc}
{p.tu}
{p.certificateLabel}
{p.price}
onOpenDetails(p.id)}
className={`inline-flex items-center gap-1 text-sm font-medium ${colorTheme.text} hover:${colorTheme.textHover} transition-colors`}
>
{t.products.detailsButton}
))}
);
}
// ---------- Product Details ----------
function ProductDetails({ t, activeTab, setActiveTab, isOpen, setIsOpen, lang }) {
const tabs = t.productDetails.tabs;
const currentTab = tabs.find(tab => tab.id === activeTab);
const closeDetails = () => {
setIsOpen(false);
setActiveTab(undefined);
};
if (!isOpen || activeTab === undefined || !currentTab) {
return (
{t.productDetails.eyebrow}
{t.productDetails.title}
{t.productDetails.sub}
{lang === 'ru' ? 'Нажмите "Подробнее" на карточке товара, чтобы увидеть характеристики' : 'Click "More details" on a product card to see specifications'}
);
}
const content = currentTab.content;
return (
{t.productDetails.eyebrow}
{t.productDetails.title}
{t.productDetails.sub}
{currentTab.name}
Состав: {content.composition}
Область применения: {content.application}
Расход: {content.consumption}
Фасовка: {content.packaging}
Срок годности: {content.shelfLife}
Условия хранения: {content.storage}
Преимущества: {content.advantages}
{lang === 'ru' ? 'Свернуть' : 'Collapse'}
);
}
// ---------- Calculator ----------
function Calculator({ t }) {
const [selectedProduct, setSelectedProduct] = useState(t.products.items[0]?.name || "");
const [area, setArea] = useState("");
const [treatmentType, setTreatmentType] = useState("planed");
const [fireGroup, setFireGroup] = useState("I");
const [khfMethod, setKhfMethod] = useState("surface");
const [f403Method, setF403Method] = useState("transport");
const [result, setResult] = useState(0);
const [resultUnit, setResultUnit] = useState("кг");
const [dryResult, setDryResult] = useState(0); // только для Фоскон-Кострома-Плюс
const getNumericArea = () => {
if (area === "" || area === null || area === undefined) return 0;
const num = parseFloat(area);
return isNaN(num) ? 0 : num;
};
React.useEffect(() => {
const numericArea = getNumericArea();
let calculated = 0;
let unit = "кг";
let calculatedDry = 0;
if (numericArea === 0) {
setResult(0);
setResultUnit("—");
setDryResult(0);
return;
}
switch (selectedProduct) {
case "Фоскон-Кострома-Плюс":
// Расход раствора
if (fireGroup === "I") {
calculated = numericArea * 500; // г/м² раствора
} else {
calculated = numericArea * 300; // г/м² раствора
}
unit = "кг (раствор)";
calculated = calculated / 1000;
// Расход сухого порошка (30% от раствора)
calculatedDry = calculated * 0.30;
break;
case "Фоскон 403":
if (f403Method === "transport") {
calculated = numericArea * 0.25;
} else {
calculated = numericArea * 0.75;
}
unit = "кг (на м³)";
break;
case "ХМ-32":
if (khfMethod === "surface") {
if (treatmentType === "planed") {
calculated = numericArea * 450;
} else {
calculated = numericArea * 750;
}
unit = "кг";
calculated = calculated / 1000;
} else if (khfMethod === "soaking") {
calculated = numericArea * 6;
unit = "кг (на м³)";
} else {
calculated = numericArea * 14;
unit = "кг (на м³)";
}
break;
case "ХФ":
if (khfMethod === "surface") {
if (treatmentType === "planed") {
calculated = numericArea * 450;
} else {
calculated = numericArea * 750;
}
unit = "кг";
calculated = calculated / 1000;
} else if (khfMethod === "soaking") {
calculated = numericArea * 6;
unit = "кг (на м³)";
} else {
calculated = numericArea * 12;
unit = "кг (на м³)";
}
break;
default:
calculated = numericArea * 0.2;
unit = "кг";
}
setResult(Math.round(calculated * 10) / 10);
setResultUnit(unit);
setDryResult(Math.round(calculatedDry * 10) / 10);
}, [selectedProduct, area, treatmentType, fireGroup, khfMethod, f403Method]);
const productOptions = t.products.items.map(p => p.name);
const isFireProtect = selectedProduct === "Фоскон-Кострома-Плюс";
const isF403 = selectedProduct === "Фоскон 403";
const isDeepBioprotect = selectedProduct === "ХМ-32";
const isKhf = selectedProduct === "ХФ";
const showVolume = isF403 || (isKhf && khfMethod !== "surface") || (isDeepBioprotect && khfMethod !== "surface");
// Количество мешков (для сухого порошка)
const bagsNeeded = isFireProtect && dryResult > 0 ? Math.ceil(dryResult / 30) : 0;
return (
{t.calculator.eyebrow}
{t.calculator.title}
{t.calculator.sub}
{t.calculator.productLabel}
setSelectedProduct(e.target.value)}
className="w-full p-3 rounded-xl bg-[var(--surface-alt)] border border-[var(--border)] text-[var(--fg)] focus:outline-none focus:ring-2 focus:ring-emerald-500"
>
{productOptions.map(p => (
{p}
))}
{/* Выбор способа нанесения для ХМ-32 и ХФ */}
{(isDeepBioprotect || isKhf) && (
Способ нанесения
setKhfMethod(e.target.value)}
className="w-full p-3 rounded-xl bg-[var(--surface-alt)] border border-[var(--border)] text-[var(--fg)] focus:outline-none focus:ring-2 focus:ring-emerald-500"
>
Поверхностное нанесение (кисть/распыление)
Длительное вымачивание (погружение)
Автоклавная пропитка
)}
{/* Выбор типа обработки для Фоскон 403 */}
{isF403 && (
Тип обработки
setF403Method(e.target.value)}
className="w-full p-3 rounded-xl bg-[var(--surface-alt)] border border-[var(--border)] text-[var(--fg)] focus:outline-none focus:ring-2 focus:ring-emerald-500"
>
Транспортный антисептик (250 г/м³)
Капитальная защита (750 г/м³)
)}
{/* Выбор типа древесины для поверхностного нанесения */}
{((isDeepBioprotect || isKhf) && khfMethod === "surface") && (
Тип обрабатываемой поверхности
setTreatmentType(e.target.value)}
className="w-full p-3 rounded-xl bg-[var(--surface-alt)] border border-[var(--border)] text-[var(--fg)] focus:outline-none focus:ring-2 focus:ring-emerald-500"
>
Строганая древесина
Пиленая древесина
)}
{/* Выбор группы огнезащиты для Фоскон-Кострома-Плюс */}
{isFireProtect && (
Группа огнезащитной эффективности
setFireGroup(e.target.value)}
className="w-full p-3 rounded-xl bg-[var(--surface-alt)] border border-[var(--border)] text-[var(--fg)] focus:outline-none focus:ring-2 focus:ring-emerald-500"
>
I группа (500 г/м² раствора)
II группа (300 г/м² раствора)
)}
{/* Дополнительная информация */}
{isF403 && f403Method === "transport" && (
🚚 Транспортный антисептик: 250 г/м³ (одна обработка).
)}
{isF403 && f403Method === "capital" && (
🛡️ Капитальная защита: 750 г/м³ (2-3 слоя).
)}
{isFireProtect && (
💡 Расход сухого порошка — 30% от расхода раствора. Мешки по 30 кг.
)}
{t.calculator.resultLabel}
{getNumericArea() === 0 ? "—" : `${result} ${resultUnit}`}
{isFireProtect && dryResult > 0 && (
<>
Сухой порошок
{dryResult} кг
{bagsNeeded > 0 && (
(≈ {bagsNeeded}
{bagsNeeded === 1 ? ' мешок' :
bagsNeeded >= 2 && bagsNeeded <= 4 ? ' мешка' :
' мешков'} по 30 кг)
)}
>
)}
* Расчёт ориентировочный
{
const numericArea = getNumericArea();
document.getElementById('form-product').value = selectedProduct;
document.getElementById('form-area').value = numericArea;
document.getElementById('form-consumption').value = result;
document.getElementById('product_calc').value = selectedProduct;
document.getElementById('product_text').value = selectedProduct;
document.getElementById('area_text').value = numericArea;
let consumptionText = numericArea === 0 ? "не указан" : result + ' ' + resultUnit;
if (isFireProtect && numericArea !== 0) {
consumptionText = `Раствор: ${result} кг, Порошок: ${dryResult} кг (≈ ${bagsNeeded} мешков)`;
}
if (isF403 && numericArea !== 0) {
consumptionText = result + ' ' + resultUnit + (f403Method === "transport" ? ' (транспортный)' : ' (капитальная)');
}
if ((isKhf || isDeepBioprotect) && numericArea !== 0) {
if (khfMethod === "surface") consumptionText = result + ' ' + resultUnit + ' (поверхностно)';
else if (khfMethod === "soaking") consumptionText = result + ' ' + resultUnit + ' (вымачивание)';
else consumptionText = result + ' ' + resultUnit + ' (автоклав)';
}
document.getElementById('consumption_text').value = consumptionText;
document.getElementById('cta').scrollIntoView({ behavior: 'smooth' });
}}
className={`inline-flex items-center justify-center px-6 py-3 text-base font-semibold text-white rounded-lg ${colorTheme.button} ${colorTheme.buttonHover} transition-all shadow-md`}
>
{t.calculator.button}
);
}
// ---------- Advantages ----------
function Advantages({ t }) {
const icons = [I.shield, I.building, I.check, I.truck];
return (
{t.advantages.eyebrow}
{t.advantages.title}
{t.advantages.sub}
{t.advantages.items.map((a, i) => (
{a.title}
{a.description}
))}
);
}
// ---------- How to Order ----------
function HowToOrder({ t }) {
return (
{t.order.eyebrow}
{t.order.title}
{t.order.sub}
{t.order.steps.map((s, i) => (
{i + 1}
{s.title}
{s.description}
))}
);
}
// ---------- About Company ----------
function AboutCompany({ t }) {
return (
{t.about.eyebrow}
{t.about.title}
{t.about.description}
{t.about.stats.map((s, i) => (
))}
🏭
{t.about.cardTitle}
{t.about.cardText}
);
}
// ---------- Not This ----------
function NotThis({ t }) {
return (
{t.notThis.eyebrow}
{t.notThis.title}
{t.notThis.items.map((it, i) => (
{it.title}
{it.description}
))}
);
}
// ---------- FAQ ----------
function FAQ({ t }) {
const [open, setOpen] = useState(0);
return (
{t.faq.eyebrow}
{t.faq.title}
{t.faq.items.map((it, i) => {
const isOpen = open === i;
return (
setOpen(isOpen ? -1 : i)}
aria-expanded={isOpen}
className="w-full flex items-start justify-between gap-4 p-5 lg:p-6 text-left min-h-[56px]"
>
{it.q}
{isOpen && (
)}
);
})}
);
}
// ---------- Final CTA ----------
function FinalCTA({ t }) {
const [formSent, setFormSent] = useState(false);
const handleSubmit = async (e) => {
e.preventDefault();
const form = e.target;
const formData = new FormData(form);
const response = await fetch('/send.php', { method: 'POST', body: formData });
if (response.ok) {
setFormSent(true);
// Отправка события в Яндекс.Метрику (счётчик 109592018)
if (typeof ym !== 'undefined') {
ym(109592018, 'reachGoal', 'form_submit');
}
}
};
if (formSent) {
return (
Спасибо за заявку!
Мы свяжемся с вами в ближайшее время.
);
}
return (
1 запрос · 24ч ответ
{t.cta.title}
{t.cta.sub}
);
}
// ---------- Footer ----------
function Footer({ t }) {
return (
{t.footer.tag}
{t.footer.desc}
{t.footer.cols.map((col, i) => (
{col.title}
{col.links.map((l, j) => (
{l.href ? (
{l.label}
) : (
{l.label}
)}
))}
))}
);
}
// ---------- App ----------
function App() {
const [lang, setLang] = useState(() => {
try {
const saved = localStorage.getItem("landing-lang");
if (saved === "ru" || saved === "en") return saved;
} catch (e) {}
return "ru";
});
const [dark, setDark] = useState(() => {
try {
const saved = localStorage.getItem("landing-dark");
if (saved === "true" || saved === "false") return saved === "true";
} catch (e) {}
return true;
});
const [activeDetailsTab, setActiveDetailsTab] = useState(undefined);
const [detailsOpen, setDetailsOpen] = useState(false);
const t = useMemo(() => window.DATA[lang], [lang]);
React.useEffect(() => {
document.documentElement.classList.toggle("dark", dark);
document.documentElement.setAttribute("lang", lang);
}, [dark, lang]);
React.useEffect(() => {
try { localStorage.setItem("landing-lang", lang); } catch (e) {}
try { localStorage.setItem("landing-dark", String(dark)); } catch (e) {}
}, [lang, dark]);
const handleOpenDetails = (productId) => {
setActiveDetailsTab(productId);
setDetailsOpen(true);
setTimeout(() => {
const detailsSection = document.getElementById("details");
if (detailsSection) {
const offset = 80;
const elementPosition = detailsSection.getBoundingClientRect().top + window.pageYOffset;
window.scrollTo({ top: elementPosition - offset, behavior: "smooth" });
}
}, 50);
};
if (!t) return
Loading...
;
return (
<>
>
);
}
ReactDOM.createRoot(document.getElementById("root")).render( );