// ─── K&N Elite — Shared components (light theme unified) ───
const { useState, useEffect, useRef, useMemo, useCallback } = React;
// Unsplash luxury real estate photos — stable IDs
const PHOTOS = {
hero: "https://images.unsplash.com/photo-1600585154340-be6161a56a0c?w=2000&q=80&auto=format&fit=crop",
apt1: "https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?w=1200&q=80&auto=format&fit=crop",
apt2: "https://images.unsplash.com/photo-1560518883-ce09059eeffa?w=1200&q=80&auto=format&fit=crop",
apt3: "https://images.unsplash.com/photo-1600566753190-17f0baa2a6c3?w=1200&q=80&auto=format&fit=crop",
apt4: "https://images.unsplash.com/photo-1600596542815-ffad4c1539a9?w=1200&q=80&auto=format&fit=crop",
apt5: "https://images.unsplash.com/photo-1600607687644-c7171b42498f?w=1200&q=80&auto=format&fit=crop",
apt6: "https://images.unsplash.com/photo-1613977257363-707ba9348227?w=1200&q=80&auto=format&fit=crop",
interior1: "https://images.unsplash.com/photo-1600210492486-724fe5c67fb0?w=1200&q=80&auto=format&fit=crop",
interior2: "https://images.unsplash.com/photo-1600566753051-6057e2f04f83?w=1200&q=80&auto=format&fit=crop",
interior3: "https://images.unsplash.com/photo-1616594039964-ae9021a400a0?w=1200&q=80&auto=format&fit=crop",
interior4: "https://images.unsplash.com/photo-1600585154526-990dced4db0d?w=1200&q=80&auto=format&fit=crop",
};
window.PHOTOS = PHOTOS;
// ─── Logo mark (real PNG — gold key in circle) ───
function BrandMark({ size = 40 }) {
return (
);
}
// Full logo as image (for hero use where needed)
function BrandFull({ height = 64, variant = "dark" }) {
// dark = original (white text + gold key, for dark bg)
// light = inverted text (dark text + gold key, for light bg)
const filter = variant === "light"
? "invert(0.85) hue-rotate(0deg) brightness(0.75) saturate(1.2)"
: "none";
return (
);
}
function Brand({ onClick, floating = false, compact = false }) {
const { t } = useI18n();
return (
K&N ELITE
{!compact &&
{t("brand_sub")}}
);
}
// ─── Language switcher ───
function LangSwitch() {
const { lang, setLang } = useI18n();
return (
{["es", "en", "ru"].map((l) => (
))}
);
}
// ─── Topbar ───
function Topbar({ onNav, current, floating = false }) {
const { t } = useI18n();
return (
);
}
// ─── Footer ───
function Footer() {
const { t } = useI18n();
return (
);
}
// ─── Toast ───
function useToast() {
const [msg, setMsg] = useState(null);
const show = useCallback((m) => {
setMsg(m);
setTimeout(() => setMsg(null), 2600);
}, []);
const node = msg ? ✓ {msg}
: null;
return { showToast: show, toastNode: node };
}
// ─── CountUp hook (animated number) ───
function useCountUp(target, duration = 1800, fmt = (v) => Math.round(v).toLocaleString("es-ES").replace(/,/g, " ")) {
const [value, setValue] = useState(0);
const startRef = useRef(null);
const targetRef = useRef(target);
useEffect(() => {
targetRef.current = target;
let raf;
startRef.current = performance.now();
const startVal = 0;
const tick = (now) => {
const p = Math.min(1, (now - startRef.current) / duration);
const eased = 1 - Math.pow(1 - p, 4);
setValue(startVal + (targetRef.current - startVal) * eased);
if (p < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [target, duration]);
return fmt(value);
}
// ─── Progress bar (under topbar) ───
function ProgressBar({ step, total = 3 }) {
const pct = total === 0 ? 0 : ((step + 1) / total) * 100;
return (
);
}
// ─── Big step header (HUGE number + meta) ───
function StepHeader({ step, total = 3, labels }) {
const num = String(step + 1).padStart(2, "0");
return (
{num}
Step {num} / {String(total).padStart(2, "0")}
{labels[step]}
);
}
// ─── Horizontal stepper (compact) ───
function StepperH({ step, labels }) {
return (
{labels.map((lab, i) => (
))}
);
}
// ─── Checkbox ───
function Checkbox({ checked, onChange, children, required = false }) {
return (
);
}
// ─── Segmented toggle ───
function SegmentedToggle({ options, value, onChange }) {
return (
{options.map((o) => (
))}
);
}
// ─── Chip multi-select ───
function ChipMulti({ options, values, onToggle }) {
return (
{options.map((o) => {
const active = values.includes(o.value);
return (
);
})}
);
}
function fmt(n) { return Math.round(n).toLocaleString("es-ES").replace(/,/g, " "); }
function Ornament() {
return (
✦
);
}
Object.assign(window, {
BrandMark, BrandFull, Brand, LangSwitch, Topbar, Footer,
useToast, useCountUp, ProgressBar, StepHeader, StepperH,
Checkbox, SegmentedToggle, ChipMulti, Ornament, fmt,
});