/* hand-drawn.jsx — Icon helper + hand-drawn SVG accents + reveal hook */
const CHECKOUT_URL = "https://checkout.theborntoknow.com/checkout/buy/8edcc250-1c08-4dd6-a88a-a0081454cc63";
/* ===== Icon =====
lucide vanilla UMD exposes icons in PascalCase on window.lucide and also on
window.lucide.icons. Each entry is an array: ["svg", attrs, children[]],
where children = [[tagName, attrsObj], ...].
*/
function _pascal(name) {
if (!name) return name;
return name[0].toUpperCase() + name.slice(1);
}
function _kebabToCamelAttrs(attrs) {
const out = {};
for (const k of Object.keys(attrs || {})) {
let nk = k;
if (k === "stroke-width") nk = "strokeWidth";
else if (k === "stroke-linecap") nk = "strokeLinecap";
else if (k === "stroke-linejoin") nk = "strokeLinejoin";
else if (k === "fill-rule") nk = "fillRule";
else if (k === "clip-rule") nk = "clipRule";
else if (k === "stop-color") nk = "stopColor";
else if (k === "stop-opacity") nk = "stopOpacity";
else if (k.startsWith("aria-") || k.startsWith("data-")) nk = k;
else if (k.includes("-")) {
nk = k.replace(/-([a-z])/g, (_, c) => c.toUpperCase());
}
out[nk] = attrs[k];
}
return out;
}
function Icon({ name, size = 20, color = "currentColor", stroke = 1.5, className = "", style = {} }) {
const L = (typeof window !== "undefined" && window.lucide) || {};
const key = _pascal(name);
const entry = (L.icons && L.icons[key]) || L[key];
let children = null;
if (Array.isArray(entry) && entry.length >= 3 && Array.isArray(entry[2])) {
children = entry[2];
} else if (entry && entry.iconNode) {
children = entry.iconNode;
}
return (
);
}
/* ===== useReveal: IntersectionObserver based scroll reveal ===== */
function useReveal(threshold = 0.15) {
const ref = React.useRef(null);
const [shown, setShown] = React.useState(false);
React.useEffect(() => {
if (!ref.current) return;
if (shown) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) { setShown(true); io.disconnect(); }
});
}, { threshold, rootMargin: "-40px 0px" });
io.observe(ref.current);
return () => io.disconnect();
}, [shown, threshold]);
return [ref, shown];
}
/* ===== Reveal wrapper ===== */
function Reveal({ as: As = "div", delay = 0, className = "", children, ...rest }) {
const [ref, shown] = useReveal();
return (
{children}
);
}
/* ===== Hand-drawn marker underline (wavy gold stroke under emphasis word) ===== */
function MarkerUnderline({ children, color = "#C8A24A", opacity = 0.7, thickness = 8 }) {
const [ref, shown] = useReveal();
// Slightly wavy path drawn as a single hand-drawn stroke
// path covers from left to right with subtle tremor
const d = "M 4 11 C 50 6, 110 14, 160 9 S 280 6, 340 11 S 460 14, 520 9";
return (
{children}
);
}
/* ===== Hand-drawn strikethrough for "wrong"-type emphasis ===== */
function MarkerStrike({ children, color = "#C8A24A", opacity = 0.85, thickness = 7 }) {
const [ref, shown] = useReveal();
const d = "M 6 50 C 60 38, 140 60, 210 45 S 360 50, 430 38";
return (
{children}
);
}
/* ===== Hand-drawn arrow (curved, slight tremor) =====
direction: 'down-right' | 'down-left' | 'right' | 'left' | 'up-left' | 'up-right'
*/
function HandArrow({ direction = "down-right", color = "#C8A24A", width = 90, height = 90, style = {}, className = "" }) {
// Clean, legible hand-drawn arrows on a 0 0 100 100 grid.
// Each is a single gently-curved shaft ending in a clear two-stroke arrowhead.
const paths = {
"down-right": {
d: "M 14 16 Q 44 30 56 50 T 82 82",
head: "M 82 82 L 64 80 M 82 82 L 80 64"
},
"down-left": {
d: "M 86 16 Q 56 30 44 50 T 18 82",
head: "M 18 82 L 36 80 M 18 82 L 20 64"
},
"right": {
d: "M 12 46 Q 44 34 70 46 T 88 50",
head: "M 88 50 L 74 42 M 88 50 L 74 58"
},
"left": {
d: "M 88 46 Q 56 34 30 46 T 12 50",
head: "M 12 50 L 26 42 M 12 50 L 26 58"
},
"up-right": {
d: "M 16 84 Q 44 66 54 48 T 84 16",
head: "M 84 16 L 66 20 M 84 16 L 80 34"
},
"down": {
d: "M 50 12 Q 44 42 50 60 T 50 86",
head: "M 50 86 L 38 72 M 50 86 L 62 72"
}
};
const p = paths[direction] || paths["down-right"];
return (
);
}
/* ===== Sticky-note testimonial ===== */
function StickyNote({ quote, attribution, rotation = -4, width = 230, style = {}, className = "" }) {
return (
);
}
/* ===== Polaroid frame ===== */
function Polaroid({ children, caption, rotation = -3, width = 360 }) {
return (
{children}
{caption && (
{caption}
)}
);
}
/* ===== Count-up number animation ===== */
function CountUp({ to = 1000000, duration = 2000, format }) {
const [val, setVal] = React.useState(0);
const [ref, shown] = useReveal();
const started = React.useRef(false);
React.useEffect(() => {
if (!shown || started.current) return;
started.current = true;
const start = performance.now();
const tick = (now) => {
const progress = Math.min((now - start) / duration, 1);
const ease = 1 - Math.pow(1 - progress, 3);
setVal(Math.round(ease * to));
if (progress < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}, [shown]);
const display = format ? format(val) : val.toLocaleString();
return {display};
}
Object.assign(window, {
CHECKOUT_URL, Icon, useReveal, Reveal,
MarkerUnderline, MarkerStrike, HandArrow,
CountUp, Polaroid, StickyNote
});