// kit.jsx — the cut-paper folk kit: palette, motifs, UI primitives.
// Everything looks like matte cut paper: flat fills, hard offset shadows,
// a fine grain. No gloss, no gradients-as-decoration.

const PAL = {
  paper: '#FFFFFF', card: '#FFFFFF', ink: '#2A211A', line: '#ECE7E0', soft: '#F7F4EF',
  orange: '#EE5A24', brick: '#C8462C', sun: '#F4A93C',
  blue: '#5A63C0', sage: '#5E9A6E', rose: '#DB7A66', plum: '#7E4A6E',
};
// soft tints for fills/backgrounds
function tint(hex, a) { return hex + Math.round(a * 255).toString(16).padStart(2, '0'); }

// soft, minimal shadow
const PAPER_SHADOW = '0 1px 3px rgba(42,33,26,0.07)';
const PAPER_SHADOW_SM = '0 1px 2px rgba(42,33,26,0.06)';

const ICONS = {
  search: '<circle cx="11" cy="11" r="7"/><path d="M20 20l-3.6-3.6"/>',
  pin: '<path d="M12 21s7-6.5 7-11a7 7 0 1 0-14 0c0 4.5 7 11 7 11Z"/><circle cx="12" cy="10" r="2.4"/>',
  cal: '<rect x="3.5" y="5" width="17" height="15" rx="2.5"/><path d="M3.5 9.5h17M8 3v4M16 3v4"/>',
  arrow: '<path d="M5 12h14M13 6l6 6-6 6"/>',
  star: '<path d="M12 3.5l2.6 5.3 5.9.9-4.3 4.1 1 5.8-5.2-2.7-5.2 2.7 1-5.8L3.5 9.7l5.9-.9Z"/>',
  check: '<path d="M4 12.5l5 5 11-12"/>',
  heart: '<path d="M12 20s-7-4.3-7-9.3A4 4 0 0 1 12 8a4 4 0 0 1 7-2.7 4 4 0 0 1 0 5.4C18 14 12 20 12 20Z"/>',
  bolt: '<path d="M13 3 5 13h5l-1 8 8-10h-5Z"/>',
  shield: '<path d="M12 3l7 2.5v5.5c0 4.5-3 8-7 9.5-4-1.5-7-5-7-9.5V5.5Z"/><path d="M9 12l2 2 4-4.5"/>',
  msg: '<path d="M4 5.5h16v11H9l-4 3.5v-3.5H4Z"/>',
  plus: '<path d="M12 5v14M5 12h14"/>',
  ticket: '<path d="M4 7.5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2 2 2 0 0 0 0 4 2 2 0 0 1-2 2H6a2 2 0 0 1-2-2 2 2 0 0 0 0-4Z"/>',
  box: '<path d="M4 8l8-4 8 4-8 4-8-4Z"/><path d="M4 8v8l8 4 8-4V8"/>',
  tray: '<path d="M3.5 13h17a8.5 8.5 0 0 0-17 0Z"/><path d="M12 5V3M5 17h14"/>',
};
function Icon({ name, size = 22, c, sw = 2, style }) {
  return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={c || 'currentColor'}
    strokeWidth={sw} strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flex: 'none', ...style }}
    dangerouslySetInnerHTML={{ __html: ICONS[name] || '' }} />;
}

function Label({ children, c, style }) {
  return <p style={{ font: '700 12px/1 var(--mono)', letterSpacing: '0.18em', textTransform: 'uppercase', color: c || PAL.orange, margin: 0, ...style }}>{children}</p>;
}

function Btn({ children, kind = 'solid', size = 'md', icon, style, disabled, ...rest }) {
  const pad = size === 'lg' ? '15px 26px' : size === 'sm' ? '9px 15px' : '12px 20px';
  const fs = size === 'lg' ? 16.5 : size === 'sm' ? 13.5 : 15;
  const base = { display: 'inline-flex', alignItems: 'center', gap: 9, padding: pad, borderRadius: 999,
    font: `700 ${fs}px/1 var(--body)`, cursor: disabled ? 'default' : 'pointer', border: '1px solid transparent', textDecoration: 'none',
    whiteSpace: 'nowrap', transition: 'transform .18s ease, box-shadow .18s ease, background .18s ease',
    opacity: disabled ? 0.55 : 1, pointerEvents: disabled ? 'none' : undefined,
    boxShadow: '0 1px 0 rgba(255,255,255,0.28) inset, 0 3px 8px -2px rgba(42,33,26,0.30)' };
  const kinds = {
    solid: { background: PAL.orange, color: '#fff' },
    ink: { background: PAL.ink, color: '#fff' },
    paper: { background: '#fff', color: PAL.ink, border: `1px solid ${PAL.line}` },
    sun: { background: PAL.sun, color: PAL.ink },
  };
  // clickable Btns must also work from the keyboard (span, not <button>)
  const keyable = rest.onClick && !disabled ? {
    tabIndex: 0,
    onKeyDown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); rest.onClick(e); } },
  } : {};
  return <span role="button" {...rest} {...keyable} className="pbtn" style={{ ...base, ...kinds[kind], ...style }}>{children}{icon && <Icon name={icon} size={fs + 2} sw={2.4} />}</span>;
}

const CAT = {
  festival:    { color: PAL.orange, label: 'Festivals & Events',    icon: 'ticket', motif: 'sun',   hero: 'assets/hero-festival.jpg' },
  volunteer:   { color: PAL.sage,   label: 'Volunteer & Community', icon: 'heart',  motif: 'heart', hero: 'assets/hero-volunteer.jpg' },
  gig:         { color: PAL.blue,   label: 'Gig & Day Labor',       icon: 'box',    motif: 'bolt',  hero: 'assets/gig-1.jpg' },
  hospitality: { color: PAL.brick,  label: 'Hospitality & Service', icon: 'tray',   motif: 'star',  hero: 'assets/hero-hospitality.jpg' },
};

// ── cut-paper motifs ─────────────────────────────────────────────
function Scallop({ r = 60, fill = PAL.orange, style }) {
  // a clean solid disc — the brand mark
  return (
    <svg width={r} height={r} viewBox="0 0 100 100" style={{ display: 'block', ...style }} aria-hidden="true">
      <circle cx="50" cy="50" r="44" fill={fill} />
    </svg>
  );
}
function Sunburst() { return null; }
// a soft cut-paper hill range
function Hills({ w = 1440, h = 120, fill = PAL.sage, style }) {
  return (
    <svg viewBox={`0 0 ${w} ${h}`} preserveAspectRatio="none" width="100%" height={h} style={{ display: 'block', ...style }} aria-hidden="true">
      <path d={`M0,${h} C ${w*0.2},${h*0.3} ${w*0.36},${h*0.4} ${w*0.5},${h*0.55} C ${w*0.66},${h*0.72} ${w*0.82},${h*0.3} ${w},${h*0.5} L${w},${h} Z`} fill={fill} />
    </svg>
  );
}

// Left-to-right, row-by-row reveal delays for a grid where some items span
// more than one column (e.g. a featured card spanning 2). Returns a ms delay
// per item so the LEFTMOST card in every row always reveals first — a naive
// index%cols breaks once a wide item shifts the column alignment.
function rowMajorDelays(items, cols = 4, step = 80) {
  let col = 0;
  return items.map((o) => {
    const span = o && o.feature ? 2 : 1;
    if (col + span > cols) col = 0; // wide item wrapped to a new row
    const c = col;
    col = (col + span) % cols;
    return c * step;
  });
}

// reveal: dramatic scroll-triggered entrance. Base state is VISIBLE; the
// animation class is added ONLY when the scroll observer sees the element —
// so content can never get stuck hidden if observers/animations are frozen.
function Reveal({ children, variant = 'rise', d = 0, style, className }) {
  const ref = React.useRef(null);
  React.useEffect(() => {
    const el = ref.current; if (!el) return;
    if (typeof IntersectionObserver === 'undefined') { el.classList.add('play'); return; }
    const io = new IntersectionObserver((es) => es.forEach((e) => {
      if (e.isIntersecting) { el.classList.add('play'); io.unobserve(el); }
    }), { threshold: 0.16, rootMargin: '0px 0px -8% 0px' });
    io.observe(el);
    return () => io.disconnect();
  }, []);
  return <div ref={ref} className={`anim anim-${variant}${className ? ' ' + className : ''}`}
    style={{ animationDelay: `${d}ms`, ...style }}>{children}</div>;
}

// cut-paper duotone photo placeholder
function Photo({ cat = 'festival', h = 170, label, style }) {
  const c = CAT[cat] || CAT.festival;
  return (
    <div style={{ position: 'relative', height: h, overflow: 'hidden', background: tint(c.color, 0.18), ...style }}>
      <div style={{ position: 'absolute', inset: 0, background: `radial-gradient(140% 90% at 22% 0%, ${tint(c.color, 0.34)}, transparent 60%)` }} />
      {/* paper sun + hills inside the "photo" */}
      <div style={{ position: 'absolute', right: 14, top: 14 }}><Scallop r={56} fill={tint(c.color, 0.85)} /></div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0 }}><Hills h={Math.round(h * 0.42)} fill={c.color} /></div>
      <div style={{ position: 'absolute', left: 0, right: 0, bottom: 0 }}><Hills h={Math.round(h * 0.3)} fill={PAL.ink} style={{ opacity: 0.85 }} /></div>
      {label && <span style={{ position: 'absolute', left: 12, top: 12, font: '700 11px/1 var(--mono)', letterSpacing: '0.06em', color: '#fff', background: PAL.ink, padding: '6px 10px', borderRadius: 999 }}>{label}</span>}
    </div>
  );
}

// the orange character — logo mark (on-model to the orb reference)
function LogoOrb({ size = 34, style }) {
  const ink = '#2A211A', cream = '#FFFCF6';
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" style={{ display: 'block', overflow: 'visible', ...style }} aria-hidden="true">
      <ellipse cx="50" cy="93" rx="22" ry="3.2" fill="rgba(42,33,26,0.15)" />
      <circle cx="50" cy="50" r="40" fill={PAL.orange} stroke={ink} strokeWidth="2.6" />
      <path d="M26 40 A 40 40 0 0 1 54 18" fill="none" stroke="rgba(255,255,255,0.45)" strokeWidth="7" strokeLinecap="round" />
      <g opacity="0.5"><ellipse cx="29" cy="60" rx="5" ry="2.6" fill="#B23F18" /><ellipse cx="71" cy="60" rx="5" ry="2.6" fill="#B23F18" /></g>
      <circle cx="38" cy="49" r="9" fill={cream} stroke={ink} strokeWidth="2" />
      <circle cx="62" cy="49" r="9" fill={cream} stroke={ink} strokeWidth="2" />
      <circle cx="39" cy="50" r="3.6" fill={ink} /><circle cx="63" cy="50" r="3.6" fill={ink} />
      <circle cx="37.6" cy="48.4" r="1.3" fill={cream} /><circle cx="61.6" cy="48.4" r="1.3" fill={cream} />
      <path d="M44 63 Q50 68 56 63" fill="none" stroke={ink} strokeWidth="2.2" strokeLinecap="round" />
    </svg>
  );
}

// a storybook "fable" picture frame: clean box + double keyline + corner ornaments
function FableFrame({ children, h = 200, bg = '#fff', label, style }) {
  const corners = [['top', 'left'], ['top', 'right'], ['bottom', 'left'], ['bottom', 'right']];
  return (
    <div style={{ position: 'relative', background: '#fff', padding: 14, border: `1px solid ${PAL.line}`, borderRadius: 16, boxShadow: '0 16px 36px rgba(42,33,26,0.13)', ...style }}>
      <div style={{ position: 'relative', height: h, borderRadius: 4, overflow: 'hidden', background: bg, boxShadow: `inset 0 0 0 1px ${tint(PAL.ink, 0.1)}` }}>
        {children}
        {label && <span style={{ position: 'absolute', left: 12, bottom: 12, font: '700 11px/1 var(--mono)', letterSpacing: '0.06em', color: '#fff', background: PAL.ink, padding: '6px 10px', borderRadius: 999, pointerEvents: 'none' }}>{label}</span>}
      </div>
      <div style={{ position: 'absolute', inset: 7, borderRadius: 9, border: `1px solid ${tint(PAL.ink, 0.2)}`, pointerEvents: 'none' }} />
      {corners.map(([v, hh], i) => (
        <span key={i} style={{ position: 'absolute', [v]: 4, [hh]: 4, width: 7, height: 7, background: PAL.orange, transform: 'rotate(45deg)', borderRadius: 1, boxShadow: '0 0 0 2px #fff', pointerEvents: 'none' }} />
      ))}
    </div>
  );
}

// a stylized ornate "gilt" picture frame — carved baroque vibe in the warm
// brand palette (not photoreal gold), beveled for a little 3D depth.
let _ornN = 0;
function OrnateFrame({ children, h = 200, bg = '#fff', label, frame = 22, style }) {
  const id = `gf${_ornN++}`;
  const gold = 'linear-gradient(135deg,#FBDA86 0%,#EFAE4E 32%,#D8862F 64%,#B0641E 100%)';
  const cove = 'linear-gradient(135deg,#BE7322 0%,#E7A647 48%,#F8D27E 100%)';
  const corners = [
    { rot: 0,   pos: { top: -frame * 0.55, left: -frame * 0.55 } },
    { rot: 90,  pos: { top: -frame * 0.55, right: -frame * 0.55 } },
    { rot: 180, pos: { bottom: -frame * 0.55, right: -frame * 0.55 } },
    { rot: 270, pos: { bottom: -frame * 0.55, left: -frame * 0.55 } },
  ];
  const cs = frame * 2.2;
  return (
    <div style={{ position: 'relative', ...style }}>
      <div style={{ position: 'relative', padding: frame, borderRadius: 9, background: gold,
        boxShadow: 'inset 2px 2px 3px rgba(255,243,200,0.8), inset -3px -3px 6px rgba(108,58,14,0.6), 0 24px 42px -16px rgba(42,33,26,0.5)' }}>
        <div style={{ position: 'relative', padding: 8, borderRadius: 5, background: cove,
          boxShadow: 'inset 2px 2px 4px rgba(108,58,14,0.55), inset -2px -2px 4px rgba(255,240,200,0.6)' }}>
          <div style={{ position: 'relative', height: h, borderRadius: 2, overflow: 'hidden', background: bg,
            boxShadow: 'inset 0 0 0 2px rgba(42,33,26,0.82), inset 4px 4px 9px rgba(70,40,8,0.4)' }}>
            {children}
            {label && <span style={{ position: 'absolute', left: 12, bottom: 12, font: '700 11px/1 var(--mono)', letterSpacing: '0.06em', color: '#fff', background: PAL.ink, padding: '6px 10px', borderRadius: 999, pointerEvents: 'none' }}>{label}</span>}
          </div>
        </div>
      </div>
      {corners.map((c, i) => (
        <svg key={i} viewBox="0 0 64 64" width={cs} height={cs} aria-hidden="true"
          style={{ position: 'absolute', ...c.pos, transform: `rotate(${c.rot}deg)`, overflow: 'visible', pointerEvents: 'none', filter: 'drop-shadow(0 1.5px 1.5px rgba(60,30,5,0.45))' }}>
          <defs>
            <linearGradient id={`${id}-${c.rot}`} x1="0" y1="0" x2="1" y2="1">
              <stop offset="0" stopColor="#FCE19A" /><stop offset="0.5" stopColor="#E7A647" /><stop offset="1" stopColor="#BE7322" />
            </linearGradient>
          </defs>
          <g fill={`url(#${id}-${c.rot})`} stroke="#6E3E10" strokeWidth="1.4" strokeLinejoin="round" strokeLinecap="round">
            <path d="M2,62 C2,40 9,19 30,7 C19,21 14,37 18,52 C30,39 47,37 60,44 C46,41 34,49 30,61 C25,52 13,54 2,62 Z" />
            <path d="M30,7 C40,7 51,4 62,0 C57,12 48,17 37,16 C34,12 32,9 30,7 Z" />
            <circle cx="14" cy="50" r="5.5" /><circle cx="14" cy="50" r="2.2" fill="#6E3E10" />
            <path d="M27,30 C34,30 40,34 43,41" fill="none" />
          </g>
        </svg>
      ))}
    </div>
  );
}

// a small mascot orb (on-model to LogoOrb) with a couple of expressions —
// for tucking into corners here and there. mood: 'happy' | 'wink' | 'peek'
function MascotOrb({ size = 44, fill = PAL.orange, mood = 'happy', expr, top, yaw, pitch, look, blush, glow, style }) {
  // when the 3D orb is on the page, use it so each character can differ
  if (typeof Orb3D !== 'undefined') {
    return <Orb3D size={size} fill={fill} expr={expr || 'open'} top={top} yaw={yaw || 0} pitch={pitch || 0} look={look} blush={blush} glow={glow} style={style} />;
  }
  const ink = '#2A211A', cream = '#FFFCF6';
  const eyeR = 9;
  return (
    <svg width={size} height={size} viewBox="0 0 100 100" style={{ display: 'block', overflow: 'visible', ...style }} aria-hidden="true">
      <ellipse cx="50" cy="93" rx="22" ry="3.2" fill="rgba(42,33,26,0.15)" />
      <circle cx="50" cy="50" r="40" fill={fill} stroke={ink} strokeWidth="2.6" />
      <path d="M26 40 A 40 40 0 0 1 54 18" fill="none" stroke="rgba(255,255,255,0.45)" strokeWidth="7" strokeLinecap="round" />
      <g opacity="0.5"><ellipse cx="29" cy="60" rx="5" ry="2.6" fill="#B23F18" /><ellipse cx="71" cy="60" rx="5" ry="2.6" fill="#B23F18" /></g>
      <circle cx="38" cy="49" r={eyeR} fill={cream} stroke={ink} strokeWidth="2" />
      <circle cx="62" cy="49" r={eyeR} fill={cream} stroke={ink} strokeWidth="2" />
      <circle cx="39" cy="50" r="3.6" fill={ink} /><circle cx="63" cy="50" r="3.6" fill={ink} />
      <circle cx="37.6" cy="48.4" r="1.3" fill={cream} /><circle cx="61.6" cy="48.4" r="1.3" fill={cream} />
    </svg>
  );
}

// a mascot peeking from behind an edge — wrap with a clip so only the top shows
function PeekOrb({ size = 64, fill = PAL.orange, mood = 'peek', flip = false, style }) {
  return (
    <div className="paper-bob" style={{ width: size, height: size * 0.62, overflow: 'hidden', transform: flip ? 'scaleX(-1)' : 'none', ...style }} aria-hidden="true">
      <MascotOrb size={size} fill={fill} mood={mood === 'peek' ? 'happy' : mood} />
    </div>
  );
}

// a mascot resting cozily in/on a box (parent must be position:relative).
// `seat` makes it sit with a little weight: slight squash + tilt, no bob.
function CornerOrb({ size = 44, fill = PAL.orange, flip = false, pos, style, expr, top, yaw, look, blush, seat = false, tilt = 0 }) {
  const place = pos || { right: 14, bottom: 12 };
  const sx = (flip ? -1 : 1) * (seat ? 1.06 : 1);
  const sy = seat ? 0.94 : 1;
  const innerT = `rotate(${tilt}deg) scale(${sx}, ${sy})`;
  return (
    <div className={seat ? '' : 'paper-bob'} style={{ position: 'absolute', ...place, transform: flip && !seat ? 'scaleX(-1)' : 'none', pointerEvents: 'none', zIndex: 2, ...style }} aria-hidden="true">
      <div style={{ transform: innerT, transformOrigin: 'center bottom' }}>
        <MascotOrb size={size} fill={fill} expr={expr} top={top} yaw={yaw} look={look} blush={blush} />
      </div>
    </div>
  );
}

// orange depths for the mascots — always orange, depth can vary
const ORB_TINTS = ['#FF9356', '#EE5A24', '#C8462C'];

Object.assign(window, { PAL, tint, PAPER_SHADOW, PAPER_SHADOW_SM, Icon, Label, Btn, CAT, Scallop, Sunburst, Hills, Reveal, rowMajorDelays, Photo, LogoOrb, FableFrame, OrnateFrame, MascotOrb, PeekOrb, CornerOrb, ORB_TINTS });
