/* Shared UI atoms. */ const { useEffect, useRef, useState, useMemo, useCallback } = React; function useReveal() { useEffect(() => { const els = document.querySelectorAll('.reveal'); const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { rootMargin: '0px 0px -8% 0px', threshold: 0.05 }); els.forEach((el) => io.observe(el)); return () => io.disconnect(); }); } const SectionLabel = ({ num, children, dark }) => (
{num} {children}
); const Eyebrow = ({ children, className = "" }) => (
{children}
); const Btn = ({ as: As = "button", variant = "primary", children, className = "", ...rest }) => { const base = "inline-flex items-center gap-2 px-5 h-11 rounded-full text-[14px] font-medium transition-colors duration-150 whitespace-nowrap"; const styles = { primary: "bg-teal-700 text-paper hover:bg-teal-800", primaryGold:"bg-gold text-teal-900 hover:bg-gold-600 hover:text-teal-950", ghost: "bg-transparent text-ink border border-line hover:border-teal-500 hover:text-teal-700", ghostLight: "bg-transparent text-paper border border-paper/30 hover:border-paper", dark: "bg-teal-900 text-paper hover:bg-teal-950", }[variant]; return {children}; }; const Card = ({ children, className = "", as: As = "div" }) => ( {children} ); /* Formatter: ₽ thousand-separator with NBSP */ const fmt = (n) => { const sign = n < 0 ? "−" : ""; const v = Math.abs(Math.round(n)); return sign + v.toString().replace(/\B(?=(\d{3})+(?!\d))/g, "\u202f"); }; const fmtK = (n) => { if (Math.abs(n) >= 1_000_000) return (n / 1_000_000).toFixed(1).replace('.', ',') + " млн"; if (Math.abs(n) >= 1_000) return Math.round(n / 1000) + " тыс"; return fmt(n); }; window.useReveal = useReveal; window.SectionLabel = SectionLabel; window.Eyebrow = Eyebrow; window.Btn = Btn; window.Card = Card; window.fmt = fmt; window.fmtK = fmtK;