// Training section: Chardae's pixie training video as four step cards. // Desktop: hover a card to play. Phones: swipe, the card in view plays. const TRAIN_STEPS = [ ['step-1-sculpt', '01', 'Sculpt', 'Waves shaped by hand, close to the head'], ['step-2-set', '02', 'Set', 'Every curl placed and locked in'], ['step-3-style', '03', 'Style', 'Heat, shape and the soft finish'], ['step-4-reveal', '04', 'Reveal', 'The dollhouse pixie, done right'], ]; function CPTrainFull({ open, onClose }) { React.useEffect(() => { if (!open) return; const k = e => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', k); return () => window.removeEventListener('keydown', k); }, [open]); if (!open) return null; return (
e.stopPropagation()}>
); } function CPTrainCard({ id, n, t, d, mobile }) { const ref = React.useRef(null); React.useEffect(() => { const v = ref.current; if (!mobile || !v) return; // phones: play the card that is mostly on screen const io = new IntersectionObserver(([e]) => { e.intersectionRatio > 0.6 ? v.play().catch(() => {}) : v.pause(); }, { threshold: [0, 0.6, 1] }); io.observe(v); return () => io.disconnect(); }, [mobile]); const play = () => { const v = ref.current; if (v) v.play().catch(() => {}); }; const stop = () => { const v = ref.current; if (v) { v.pause(); v.currentTime = 0; } }; const toggle = () => { const v = ref.current; if (v) v.paused ? play() : v.pause(); }; return (
); } function CPTrainSteps() { const [full, setFull] = React.useState(false); const mobile = React.useMemo(() => window.matchMedia('(hover: none), (max-width: 900px)').matches, []); return (
Inside the dollhouse

How a dollhouse pixie is made.

Trained for the perfect pixie by @hairfetishshop in Dallas, Texas.

Book your pixie

Hover to playSwipe

{TRAIN_STEPS.map(([id, n, t, d]) => )}
setFull(false)} />
); } Object.assign(window, { CPTraining: CPTrainSteps });