// Feora — Create. Share. Print.
// 4-second contour-cartoon explainer video.
// Composition uses one SVG stage; useTime() drives all element animations.

// Tweaks context — components read overrides from here
const TweaksContext = React.createContext({});
const useTweaks_ = () => React.useContext(TweaksContext);

const COLORS = {
  paper: '#f6f1e8',
  paperDeep: '#e7dfce',
  ink: '#1d1b18',
  walnut: '#6a4a2b',
  gold: '#b08856',
  terracotta: '#c46a3e',
  ochre: '#d99a3d',
  sage: '#7a8e6a',
  rose: '#c97a86',
  cream: '#f1e4c6',
};

// ── Section timings (seconds) ───────────────────────────────────────────────
// Total 10 seconds.
const T = {
  // Create
  createTitleIn:    [0.10, 0.90],
  taglineIn:        [0.90, 1.40],   // "images with words" appears under Create
  textboxIn:        [0.40, 1.10],
  typeStart:        1.10,
  typeEnd:          2.60,
  textDissolve:     [2.60, 3.30],
  splashesIn:       [2.80, 3.50],
  horsesStroke:     [3.10, 4.10],
  horsesFill:       [3.70, 4.40],
  createOut:        [3.70, 4.20],

  // Share
  shareTitleIn:     [4.10, 4.70],
  shrinkArt:        [4.00, 4.70],
  shareBtnIn:       [4.40, 5.00],
  cursorIn:         [4.50, 5.10],
  cursorClick:      [5.10, 5.40],
  iconsFan:         [5.30, 5.80],
  shareOut:         [5.70, 6.20],

  // Print
  printTitleIn:     [6.10, 6.60],
  iconsOut:         [5.90, 6.30],
  cursorOut:        [5.90, 6.30],
  shareBtnOut:      [5.90, 6.30],
  frameSnap:        [6.40, 7.10],
  wallIn:           [6.60, 7.30],
  hangDrop:         [7.10, 7.80],

  // Mail delivery (after the print hangs)
  mailboxIn:        [7.80, 8.30],   // mailbox slides in from right
  envelopePeel:     [8.20, 8.70],   // envelope emerges from the painting
  envelopeFly:      [8.70, 9.50],   // envelope arcs across to the mailbox
  flagRaise:        [9.30, 9.90],   // mailbox flag flips up
};

// ── helpers ────────────────────────────────────────────────────────────────
const lerp = (a,b,t) => a + (b-a) * t;
const range = (t, [a,b], easeFn=Easing.linear) => {
  const v = (t - a) / (b - a);
  if (v <= 0) return 0;
  if (v >= 1) return 1;
  return easeFn(v);
};

// ── Sub-components ──────────────────────────────────────────────────────────

function SectionTitle({ time, label, inRange, outRange, x }) {
  const inAmt = range(time, inRange, Easing.easeOutBack);
  const outAmt = range(time, outRange, Easing.easeInCubic);
  const y = lerp(-120, 140, inAmt) + outAmt * -180;
  const opacity = inAmt * (1 - outAmt);
  if (opacity <= 0.01) return null;
  return (
    <g transform={`translate(${x} ${y})`} opacity={opacity}>
      {/* serif title with hand-drawn underline */}
      <text x="0" y="0" textAnchor="middle"
            fontFamily="Cormorant Garamond, serif" fontSize="120" fontWeight="500"
            fill={COLORS.ink} letterSpacing="2">
        {label}
      </text>
      <path d={`M -200 30 Q 0 ${50 + Math.sin(time*4)*3} 200 30`}
            fill="none" stroke={COLORS.walnut} strokeWidth="6"
            strokeLinecap="round" />
    </g>
  );
}

// Text box that types out a prompt
function PromptBox({ time }) {
  const inAmt = range(time, T.textboxIn, Easing.easeOutBack);
  const dissolveAmt = range(time, T.textDissolve, Easing.easeInQuad);
  const scale = lerp(0.7, 1, inAmt);
  const opacity = inAmt * (1 - dissolveAmt);
  if (opacity <= 0.01) return null;

  const full = 'painting of running horses';
  const typeT = range(time, [T.typeStart, T.typeEnd], Easing.linear);
  const charsShown = Math.floor(typeT * full.length);
  const text = full.slice(0, charsShown);
  const caretBlink = Math.floor(time * 4) % 2 === 0 ? 1 : 0.2;

  // dissolve: letters drift up + scatter
  const driftY = dissolveAmt * -80;

  return (
    <g transform={`translate(960 540) scale(${scale}) translate(-560 -110)`} opacity={opacity}>
      {/* shadow */}
      <rect x="14" y="22" width="1120" height="220" rx="0" fill={COLORS.ink} opacity="0.08"/>
      {/* paper card */}
      <rect x="0" y="0" width="1120" height="220" rx="0" fill="#ffffff" className="contour"
            stroke={COLORS.ink} strokeWidth="6"/>
      {/* eyebrow label */}
      <text x="40" y="56" fontFamily="Inter, sans-serif" fontSize="22" fontWeight="500"
            letterSpacing="6" fill={COLORS.walnut}>
        DESCRIBE YOUR WORK
      </text>
      {/* divider */}
      <line x1="40" y1="76" x2="200" y2="76" stroke={COLORS.walnut} strokeWidth="3"/>
      {/* typed prompt */}
      <g transform={`translate(0 ${driftY})`} opacity={1 - dissolveAmt}>
        <text x="40" y="160" fontFamily="Cormorant Garamond, serif" fontStyle="italic"
              fontSize="64" fontWeight="500" fill={COLORS.ink}>
          {text}
          <tspan fill={COLORS.walnut} opacity={caretBlink}>|</tspan>
        </text>
      </g>
    </g>
  );
}

// Watercolour underpaint — brief texture during transition, fades as the real painting reveals.
function PaintSplashes({ time }) {
  const inAmt = range(time, T.splashesIn, Easing.easeOutCubic);
  // splashes fade as painting reveals
  const fadeOut = range(time, T.horsesStroke, Easing.easeInCubic);
  const opacity = inAmt * (1 - fadeOut * 0.95);
  if (opacity <= 0.01) return null;

  const shrink = range(time, T.shrinkArt, Easing.easeInOutCubic);
  const baseScale = lerp(1, 0.62, shrink);
  const baseY = lerp(0, -60, shrink);
  const bloom = lerp(0.6, 1, inAmt);

  return (
    <g transform={`translate(960 540) translate(0 ${baseY}) scale(${baseScale * bloom})`} opacity={opacity}>
      <path d="M -360 -120 C -440 -80 -460 40 -380 100 C -300 160 -180 140 -120 80 C -60 20 -80 -120 -180 -160 C -280 -200 -300 -160 -360 -120 Z"
            fill={COLORS.terracotta} opacity="0.92"/>
      <path d="M 80 -180 C 0 -150 -40 -40 40 20 C 120 80 240 60 300 -10 C 360 -80 320 -200 220 -220 C 140 -240 120 -200 80 -180 Z"
            fill={COLORS.ochre} opacity="0.9"/>
      <path d="M -120 80 C -200 120 -180 240 -80 260 C 40 280 140 240 180 160 C 220 80 160 0 80 20 C 0 40 -60 60 -120 80 Z"
            fill={COLORS.sage} opacity="0.85"/>
      <path d="M 200 100 C 140 140 160 240 260 240 C 360 240 420 180 400 100 C 380 30 280 40 200 100 Z"
            fill={COLORS.rose} opacity="0.85"/>
      <path d="M -260 -180 C -300 -200 -340 -180 -340 -140 C -340 -100 -300 -80 -260 -100 C -220 -120 -220 -160 -260 -180 Z"
            fill={COLORS.gold} opacity="0.9"/>
    </g>
  );
}

// ──────────────────────────────────────────────────────────────────────────
// THE PAINTING — uses the uploaded oil painting as the artwork that the AI
// "generates". Rendered as an HTML <img> overlaid on the SVG (SVG <image>
// with local URLs sometimes fails to paint). Brush-reveal via CSS clip-path.
// ──────────────────────────────────────────────────────────────────────────
const PAINTING_W = 1100;
const PAINTING_H = 825;  // 4:3 ratio matches the reference image

function Painting({ time }) {
  const reveal = range(time, T.horsesStroke, Easing.easeInOutCubic);
  const settle = range(time, T.horsesFill, Easing.easeOutCubic);
  // Hide once the framed-print snaps in (frame contains its own copy)
  const frameSnap = range(time, T.frameSnap, Easing.easeInOutCubic);
  if (reveal <= 0 || frameSnap >= 0.9) return null;

  const shrink = range(time, T.shrinkArt, Easing.easeInOutCubic);
  const baseScale = lerp(1, 0.62, shrink);
  const baseY = lerp(0, -60, shrink);

  const cx = 960;
  const cy = 540 + baseY;
  const w = PAINTING_W * baseScale;
  const h = PAINTING_H * baseScale;
  const x = cx - w/2;
  const y = cy - h/2;

  const popScale = lerp(0.94, 1, settle);
  const cliprightPct = (1 - reveal) * 100;

  return (
    <div style={{
      position: 'absolute', left: x, top: y, width: w, height: h,
      transform: `scale(${popScale})`, transformOrigin: 'center',
      opacity: 1 - frameSnap * 0.7,
      pointerEvents: 'none',
    }}>
      {/* drop shadow */}
      <div style={{
        position: 'absolute', inset: 0,
        boxShadow: settle > 0 ? `0 ${10*settle}px ${40*settle}px rgba(29,27,24,${0.18*settle})` : 'none'
      }}/>
      {/* the painting itself, clip-wiped from the left */}
          <img src="horses-painting.png" alt=""
           style={{
             position: 'absolute', inset: 0,
             width: '100%', height: '100%',
             objectFit: 'cover',
             clipPath: `inset(0 ${cliprightPct}% 0 0)`,
             WebkitClipPath: `inset(0 ${cliprightPct}% 0 0)`,
           }}/>
      {/* leading brush-stroke band */}
      {reveal < 1 && reveal > 0 && (
        <div style={{
          position: 'absolute', top: 0, bottom: 0,
          left: `${reveal * 100}%`, width: 36, marginLeft: -18,
          background: `linear-gradient(90deg, transparent, ${COLORS.gold} 50%, transparent)`,
          opacity: 0.7,
          filter: 'blur(1px)',
        }}/>
      )}
      {/* subtle border once settled */}
      {settle > 0.3 && (
        <div style={{
          position: 'absolute', inset: 0,
          border: `2px solid ${COLORS.ink}`,
          opacity: settle * 0.4,
        }}/>
      )}
    </div>
  );
}

// Static painting used inside the framed print scene (absolute-positioned img).
function PaintingStatic({ left, top, width, height }){
  return (
      <img src="horses-painting.png" alt=""
         style={{
           position: 'absolute', left, top, width, height,
           objectFit: 'cover', display: 'block',
           pointerEvents: 'none',
         }}/>
  );
}

// HorseShape — cleaner cartoon horse. Head is a distinct oval ON TOP of a tall
// curved neck. Body is a bean. Legs are thin spread-out strokes in gallop pose.
// viewBox-local: head at top (y≈-110), body at center, feet at y≈80.
function HorseShape({ color, fillOpacity = 1 }) {
  const stroke = COLORS.ink;
  const sw = 7;
  return (
    <g strokeLinejoin="round" strokeLinecap="round">
      {/* TAIL — thick flowing stroke off the back */}
      <path d="M -68 -8 C -100 -20 -130 -10 -148 18 C -154 30 -148 38 -136 34"
            fill={color} fillOpacity={fillOpacity} stroke={stroke} strokeWidth={sw}/>

      {/* LEGS — 4 in flying-gallop pose */}
      {/* back stretched */}
      <path d="M -52 16 L -98 70" fill="none" stroke={stroke} strokeWidth={sw+1}/>
      <path d="M -106 70 L -90 74" stroke={stroke} strokeWidth={sw+3}/>
      {/* back bent */}
      <path d="M -28 22 L -42 50 L -32 72" fill="none" stroke={stroke} strokeWidth={sw+1}/>
      <path d="M -40 72 L -22 74" stroke={stroke} strokeWidth={sw+3}/>
      {/* front bent */}
      <path d="M 24 22 L 14 50 L 26 72" fill="none" stroke={stroke} strokeWidth={sw+1}/>
      <path d="M 18 72 L 36 74" stroke={stroke} strokeWidth={sw+3}/>
      {/* front stretched */}
      <path d="M 50 16 L 108 70" fill="none" stroke={stroke} strokeWidth={sw+1}/>
      <path d="M 100 70 L 116 74" stroke={stroke} strokeWidth={sw+3}/>

      {/* BODY — wide bean, tilted slightly up at front */}
      <g transform="rotate(-7 0 0)">
        <ellipse cx="0" cy="0" rx="74" ry="28"
                 fill={color} fillOpacity={fillOpacity}
                 stroke={stroke} strokeWidth={sw}/>
      </g>

      {/* NECK — tall S-curve connecting front of body up to head base */}
      <path d="M 38 -22 C 52 -54 70 -78 88 -90 L 102 -78 C 86 -64 70 -42 56 -8 Z"
            fill={color} fillOpacity={fillOpacity}
            stroke={stroke} strokeWidth={sw}/>

      {/* HEAD — clear elongated oval, tilted forward */}
      <g transform="rotate(-22 96 -98)">
        <ellipse cx="106" cy="-98" rx="32" ry="16"
                 fill={color} fillOpacity={fillOpacity}
                 stroke={stroke} strokeWidth={sw}/>
      </g>

      {/* EARS — two pointed triangles on top of head */}
      <path d="M 84 -106 L 84 -126 L 96 -110 Z"
            fill={color} fillOpacity={fillOpacity}
            stroke={stroke} strokeWidth={sw-1}/>
      <path d="M 96 -108 L 102 -122 L 110 -106 Z"
            fill={color} fillOpacity={fillOpacity}
            stroke={stroke} strokeWidth={sw-1}/>

      {/* MANE — bold flowing stroke down the back of the neck */}
      <path d="M 96 -100 C 76 -86 56 -64 38 -32"
            fill="none" stroke={stroke} strokeWidth={sw+1}/>
      <path d="M 86 -94 C 70 -76 54 -54 36 -22"
            fill="none" stroke={stroke} strokeWidth={sw-1}/>

      {/* EYE + nostril on the head */}
      <circle cx="108" cy="-100" r="3.5" fill={stroke}/>
      <circle cx="128" cy="-94" r="2.5" fill={stroke}/>

      {/* speed lines behind */}
      <g stroke={stroke} strokeWidth={sw-2} strokeLinecap="round" opacity={fillOpacity * 0.7}>
        <line x1="-110" y1="-14" x2="-150" y2="-14"/>
        <line x1="-110" y1="4" x2="-156" y2="4"/>
        <line x1="-110" y1="-30" x2="-144" y2="-30"/>
      </g>
    </g>
  );
}

function Horses({ time }) {
  // shrink + recentre when transitioning into Share/Print
  const shrink = range(time, T.shrinkArt, Easing.easeInOutCubic);
  const baseScale = lerp(1, 0.62, shrink);
  const baseY = lerp(0, -60, shrink);
  return (
    <g transform={`translate(960 540) translate(0 ${baseY}) scale(${baseScale})`}>
      <Horse time={time} x={-220} y={20}  scale={1.7} color={COLORS.walnut}      delay={0}/>
      <Horse time={time} x={40}   y={50}  scale={1.6} color={COLORS.terracotta} delay={0.08}/>
      <Horse time={time} x={260}  y={10}  scale={1.5} color={COLORS.gold}       delay={0.16}/>
    </g>
  );
}

// Share button + cursor + icons
function ShareScene({ time }) {
  const btnIn = range(time, T.shareBtnIn, Easing.easeOutBack);
  const btnOut = range(time, T.shareBtnOut, Easing.easeInCubic);
  const btnY = lerp(80, 0, btnIn) + btnOut * -40;
  const btnOpacity = btnIn * (1 - btnOut);

  // press animation
  const pressed = range(time, T.cursorClick, Easing.easeInOutCubic);
  const scale = lerp(1, 0.92, pressed > 0.4 ? 1 : pressed * 2.5);

  if (btnOpacity <= 0.01) return null;
  return (
    <g transform={`translate(960 820) translate(0 ${btnY}) scale(${scale})`} opacity={btnOpacity}>
      {/* shadow */}
      <rect x="-178" y="-44" width="356" height="92" fill={COLORS.ink} opacity="0.15"/>
      {/* button */}
      <rect x="-180" y="-46" width="360" height="92" fill={COLORS.ink} className="contour"
            stroke={COLORS.ink} strokeWidth="6"/>
      <text x="0" y="18" textAnchor="middle"
            fontFamily="Inter, sans-serif" fontSize="32" fontWeight="500"
            letterSpacing="8" fill={COLORS.paper}>
        SHARE
      </text>
      {/* paper-airplane icon */}
      <g transform="translate(-130 0)" stroke={COLORS.paper} strokeWidth="4" fill="none" strokeLinejoin="round" strokeLinecap="round">
        <path d="M -16 0 L 20 -14 L 12 14 L 0 4 L -16 0 Z"/>
        <line x1="0" y1="4" x2="20" y2="-14"/>
      </g>

      {/* click ripple */}
      <ClickRipple time={time}/>
    </g>
  );
}

function ClickRipple({ time }) {
  const t = range(time, [T.cursorClick[0]+0.05, T.iconsFan[0]+0.15], Easing.easeOutCubic);
  if (t <= 0 || t >= 1) return null;
  const r = lerp(20, 220, t);
  return <circle cx="0" cy="0" r={r} fill="none" stroke={COLORS.gold} strokeWidth={lerp(8,1,t)} opacity={1-t}/>;
}

function Cursor({ time }) {
  const inAmt = range(time, T.cursorIn, Easing.easeOutCubic);
  const outAmt = range(time, T.cursorOut, Easing.easeInQuad);
  if (inAmt <= 0 || outAmt >= 1) return null;

  // travel: from off-screen right to share button at (960, 820)
  const targetX = 960 + 80;
  const targetY = 820 - 20;
  const startX = 1680, startY = 320;
  const x = lerp(startX, targetX, inAmt);
  const y = lerp(startY, targetY, inAmt);

  // click pulse
  const clickT = range(time, T.cursorClick, Easing.easeInOutQuad);
  const scale = 1 - (clickT > 0.5 ? (1 - clickT) : clickT) * 0.18;

  return (
    <g transform={`translate(${x} ${y}) scale(${scale})`} opacity={(1 - outAmt) * inAmt}>
      <path d="M 0 0 L 0 40 L 10 30 L 18 48 L 24 46 L 16 28 L 28 28 Z"
            fill={COLORS.paper} stroke={COLORS.ink} strokeWidth="4" strokeLinejoin="round"/>
    </g>
  );
}

function ShareIcons({ time }) {
  const fan = range(time, T.iconsFan, Easing.easeOutBack);
  const out = range(time, T.iconsOut, Easing.easeInQuad);
  const opacity = fan * (1 - out);
  if (opacity <= 0.01) return null;

  // 3 icons fan out from button center
  const items = [
    { angle: -50, label: 'link',   draw: <LinkIcon/> },
    { angle:   0, label: 'mail',   draw: <MailIcon/> },
    { angle:  50, label: 'gram',   draw: <GramIcon/> },
  ];
  const radius = lerp(0, 220, fan);

  return (
    <g transform="translate(960 820)" opacity={opacity}>
      {items.map((it, i) => {
        const rad = (it.angle * Math.PI) / 180;
        const x = Math.sin(rad) * radius;
        const y = -Math.abs(Math.cos(rad)) * radius - 80;
        return (
          <g key={i} transform={`translate(${x} ${y}) scale(${lerp(0.4,1,fan)})`}>
            {/* paper chip */}
            <circle cx="0" cy="0" r="44" fill={COLORS.paper} stroke={COLORS.ink} strokeWidth="5"/>
            <g transform="translate(0 0)">{it.draw}</g>
          </g>
        );
      })}
    </g>
  );
}
function LinkIcon(){return (
  <g stroke={COLORS.ink} strokeWidth="4" fill="none" strokeLinecap="round" transform="translate(-18 -18)">
    <path d="M 8 22 a 10 10 0 0 1 0 -14 l 6 -6 a 10 10 0 0 1 14 14 l -3 3"/>
    <path d="M 28 14 a 10 10 0 0 1 0 14 l -6 6 a 10 10 0 0 1 -14 -14 l 3 -3"/>
  </g>
);}
function MailIcon(){return (
  <g stroke={COLORS.ink} strokeWidth="4" fill="none" strokeLinejoin="round" transform="translate(-22 -16)">
    <rect x="0" y="0" width="44" height="32" />
    <path d="M 0 0 L 22 18 L 44 0"/>
  </g>
);}
function GramIcon(){return (
  <g stroke={COLORS.ink} strokeWidth="4" fill="none" transform="translate(-18 -18)">
    <rect x="0" y="0" width="36" height="36" rx="8"/>
    <circle cx="18" cy="18" r="9"/>
    <circle cx="28" cy="8" r="2.5" fill={COLORS.ink}/>
  </g>
);}

// Wall + nail (Print scene)
function Wall({ time }) {
  const inAmt = range(time, T.wallIn, Easing.easeOutCubic);
  if (inAmt <= 0) return null;
  return (
    <g opacity={inAmt}>
      {/* warm wall wash */}
      <rect x="0" y="0" width="1920" height="780" fill={COLORS.cream} opacity="0.6"/>
      {/* picture rail line */}
      <line x1="0" y1="240" x2="1920" y2="240" stroke={COLORS.walnut} strokeWidth="3" opacity="0.4"/>
      {/* nail */}
      <circle cx="960" cy="320" r="8" fill={COLORS.ink}/>
      <line x1="960" y1="320" x2="950" y2="304" stroke={COLORS.ink} strokeWidth="4" strokeLinecap="round"/>
      {/* string for hung painting (renders behind frame) */}
      <path d="M 850 480 Q 960 320 1070 480" fill="none" stroke={COLORS.ink} strokeWidth="2.5"/>
    </g>
  );
}

// Wooden frame snapping around the artwork + hang on wall.
// Built as HTML divs (not SVG) so the embedded painting <img> renders reliably.
function FramedPainting({ time }) {
  const snap = range(time, T.frameSnap, Easing.easeOutBack);
  const hang = range(time, T.hangDrop, Easing.easeOutBack);
  if (snap <= 0) return null;

  const frameW = 760, frameH = 570;
  const cx = 960;
  const startCY = 480;
  const endCY = 540;
  const cy = lerp(startCY, endCY, hang);
  const wobble = Math.sin(hang * Math.PI * 2.2) * (1 - hang) * 3.5;
  const scale = lerp(0.94, 1, snap);

  const mat = 28;

  return (
    <div style={{
      position: 'absolute',
      left: cx - frameW/2, top: cy - frameH/2,
      width: frameW, height: frameH,
      transform: `rotate(${wobble}deg) scale(${scale})`,
      transformOrigin: 'center',
      opacity: snap,
      pointerEvents: 'none',
    }}>
      {/* drop shadow on wall */}
      <div style={{
        position: 'absolute',
        left: 14, top: 22, right: -14, bottom: -22,
        background: COLORS.ink,
        opacity: 0.18 * hang,
      }}/>
      {/* outer walnut frame */}
      <div style={{
        position: 'absolute', inset: 0,
        background: COLORS.walnut,
        border: `6px solid ${COLORS.ink}`,
        boxSizing: 'border-box',
      }}>
        {/* gold bevel highlight */}
        <div style={{
          position: 'absolute', left: 12, top: 12, right: 12, bottom: 12,
          border: `2px solid ${COLORS.gold}`,
        }}/>
        {/* cream mat with painting inside */}
        <div style={{
          position: 'absolute',
          left: mat, top: mat, right: mat, bottom: mat,
          background: COLORS.paper,
          border: `3px solid ${COLORS.ink}`,
          overflow: 'hidden',
        }}>
          <img src="assets/horses-painting.png" alt=""
               style={{
                 position: 'absolute', inset: 0,
                 width: '100%', height: '100%',
                 objectFit: 'cover',
                 display: 'block',
               }}/>
        </div>
      </div>
    </div>
  );
}

// static (non-time-driven) versions used inside the framed picture
function PaintSplashesStatic(){return (
  <g opacity="0.95">
    <path d="M -360 -120 C -440 -80 -460 40 -380 100 C -300 160 -180 140 -120 80 C -60 20 -80 -120 -180 -160 C -280 -200 -300 -160 -360 -120 Z" fill={COLORS.terracotta}/>
    <path d="M 80 -180 C 0 -150 -40 -40 40 20 C 120 80 240 60 300 -10 C 360 -80 320 -200 220 -220 C 140 -240 120 -200 80 -180 Z" fill={COLORS.ochre}/>
    <path d="M -120 80 C -200 120 -180 240 -80 260 C 40 280 140 240 180 160 C 220 80 160 0 80 20 C 0 40 -60 60 -120 80 Z" fill={COLORS.sage}/>
    <path d="M 200 100 C 140 140 160 240 260 240 C 360 240 420 180 400 100 C 380 30 280 40 200 100 Z" fill={COLORS.rose}/>
    <path d="M -260 -180 C -300 -200 -340 -180 -340 -140 C -340 -100 -300 -80 -260 -100 C -220 -120 -220 -160 -260 -180 Z" fill={COLORS.gold}/>
  </g>
);}
function HorsesStatic(){
  const horses = [
    {x:-160,y:-30,s:2.4,c:COLORS.walnut},
    {x: 60, y: 20,s:2.1,c:COLORS.terracotta},
    {x: 220,y:-20,s:1.9,c:COLORS.gold},
  ];
  return (
    <g>
      {horses.map((h,i)=>(
        <g key={i} transform={`translate(${h.x} ${h.y}) scale(${h.s})`}>
          <HorseShape color={h.c} fillOpacity={0.95}/>
        </g>
      ))}
    </g>
  );
}

// Paper background texture (subtle horizontal striations for warmth)
function Paper() {
  const tw = useTweaks_();
  const paper = tw.paperColor || COLORS.paper;
  return (
    <g>
      <rect x="0" y="0" width="1920" height="1080" fill={paper}/>
      {[...Array(8)].map((_,i)=>(
        <rect key={i} x="0" y={i*135} width="1920" height="2" fill={COLORS.paperDeep} opacity="0.5"/>
      ))}
    </g>
  );
}

// Section label captions at the bottom-left (small Caveat handwritten note)
function Caption({ time }) {
  const tw = useTweaks_();
  if (tw.showCaption === false) return null;
  let label = '';
  if (time < 4.0) label = '01 · Create';
  else if (time < 6.1) label = '02 · Share';
  else label = '03 · Print';
  return (
    <g>
      <text x="80" y="1020"
            fontFamily="Caveat, cursive" fontSize="48" fill={COLORS.walnut}>
        {label}
      </text>
    </g>
  );
}

// Tagline that appears under the Create title in a handwritten font
// ────────────────────────────────────────────────────────────────────
// MAIL DELIVERY — a cartoon contour mailbox slides in on a wooden post.
// An envelope (with a mini horse painting peek) flies from the framed
// print across to the mailbox; the flag flips up to confirm "sent".
// ────────────────────────────────────────────────────────────────────
const MAILBOX = { x: 1520, y: 760 };  // mailbox base position
const PAINTING_HANG = { x: 960, y: 540 };  // framed painting centre when hung

function Mailbox({ time }) {
  const tw = useTweaks_();
  if (tw.showMail === false) return null;
  const slideIn = range(time, T.mailboxIn, Easing.easeOutBack);
  if (slideIn <= 0) return null;
  const tx = lerp(500, 0, slideIn);  // slides in from right

  // flag raise
  const flag = range(time, T.flagRaise, Easing.easeOutBack);
  const flagAngle = lerp(-90, 0, flag);  // -90 = horizontal (down), 0 = vertical (up)

  const stroke = COLORS.ink;
  const sw = 5;

  return (
    <g transform={`translate(${MAILBOX.x + tx} ${MAILBOX.y})`}>
      {/* wooden post */}
      <rect x="-12" y="40" width="24" height="260"
            fill={COLORS.walnut} stroke={stroke} strokeWidth={sw}/>
      <line x1="-12" y1="100" x2="12" y2="100" stroke={stroke} strokeWidth={sw-2} opacity="0.6"/>
      <line x1="-12" y1="180" x2="12" y2="180" stroke={stroke} strokeWidth={sw-2} opacity="0.6"/>

      {/* mailbox body — classic rounded-top */}
      <g transform="translate(-90 -90)">
        {/* main box (rounded top) */}
        <path d="M 0 60 L 0 30 A 90 60 0 0 1 180 30 L 180 60 L 180 130 L 0 130 Z"
              fill={tw.mailboxColor || COLORS.terracotta} stroke={stroke} strokeWidth={sw+1}/>
        {/* mail slot */}
        <rect x="30" y="50" width="120" height="8" fill={COLORS.ink} opacity="0.7"/>
        {/* door circle */}
        <circle cx="150" cy="95" r="6" fill="none" stroke={stroke} strokeWidth={sw-1}/>
      </g>

      {/* flag on the side — pivots at base */}
      <g transform={`translate(82 -30) rotate(${flagAngle})`}>
        {/* flag pole */}
        <line x1="0" y1="0" x2="0" y2="-44" stroke={stroke} strokeWidth={sw}/>
        {/* flag fabric */}
        <path d="M 0 -44 L 32 -36 L 28 -22 L 0 -28 Z"
              fill={COLORS.danger || '#8a3a3a'} stroke={stroke} strokeWidth={sw-1}/>
      </g>

      {/* tiny grass/ground at base */}
      <g stroke={stroke} strokeWidth={sw-2} strokeLinecap="round" opacity="0.7">
        <line x1="-30" y1="300" x2="-26" y2="294"/>
        <line x1="-18" y1="300" x2="-14" y2="292"/>
        <line x1="18" y1="300" x2="22" y2="294"/>
        <line x1="30" y1="300" x2="34" y2="292"/>
      </g>
    </g>
  );
}

function FlyingEnvelope({ time }) {
  const tw = useTweaks_();
  if (tw.showMail === false) return null;
  const peel = range(time, T.envelopePeel, Easing.easeOutCubic);
  const fly = range(time, T.envelopeFly, Easing.easeInOutCubic);
  const done = range(time, [T.envelopeFly[1], T.envelopeFly[1] + 0.15], Easing.easeInQuad);
  if (peel <= 0 || done >= 1) return null;

  // start: painting centre  →  end: mailbox slot
  const sx = PAINTING_HANG.x;
  const sy = PAINTING_HANG.y;
  const ex = MAILBOX.x - 40;
  const ey = MAILBOX.y - 130;
  const cx = (sx + ex) / 2;
  const cy = Math.min(sy, ey) - 280;  // arc high

  const t = fly;
  const u = 1 - t;
  const px = u*u*sx + 2*u*t*cx + t*t*ex;
  const py = u*u*sy + 2*u*t*cy + t*t*ey;

  // peel-out: envelope grows from painting before flying
  const scale = lerp(0.4, 1, peel) * lerp(1, 0.65, fly);
  // rotation along arc (tangent)
  const dxdt = 2*u*(cx-sx) + 2*t*(ex-cx);
  const dydt = 2*u*(cy-sy) + 2*t*(ey-cy);
  const angle = fly > 0.02 ? Math.atan2(dydt, dxdt) * 180 / Math.PI : 0;
  // tilt envelope slightly during flight (paper airplane style)
  const tilt = lerp(-5, 8, fly);

  const opacity = peel * (1 - done);

  return (
    <g transform={`translate(${px} ${py}) rotate(${tilt + angle * 0.15}) scale(${scale})`}
       opacity={opacity}>
      {/* envelope back */}
      <rect x="-70" y="-46" width="140" height="92"
            fill={COLORS.cream} stroke={COLORS.ink} strokeWidth="5"/>
      {/* envelope flap (triangle) */}
      <path d="M -70 -46 L 0 8 L 70 -46 Z"
            fill={COLORS.paper} stroke={COLORS.ink} strokeWidth="5"/>
      {/* wax seal */}
      <circle cx="0" cy="8" r="10" fill={COLORS.walnut} stroke={COLORS.ink} strokeWidth="3"/>
      <text x="0" y="13" textAnchor="middle"
            fontFamily="Cormorant Garamond, serif" fontSize="14" fontWeight="500"
            fill={COLORS.paper}>F</text>
      {/* motion trail dots behind envelope */}
      {fly > 0.1 && fly < 0.95 && (
        <g opacity={(1 - fly) * 0.5}>
          <circle cx="-110" cy="4" r="4" fill={COLORS.walnut}/>
          <circle cx="-150" cy="8" r="3" fill={COLORS.walnut}/>
          <circle cx="-190" cy="12" r="2" fill={COLORS.walnut}/>
        </g>
      )}
    </g>
  );
}

// Final callout: "Sent." caption above mailbox after the flag flips
function MailSentCallout({ time }) {
  const tw = useTweaks_();
  if (tw.showMail === false) return null;
  const inAmt = range(time, [T.flagRaise[1] - 0.2, T.flagRaise[1] + 0.1], Easing.easeOutBack);
  if (inAmt <= 0) return null;
  const tilt = -4 + Math.sin(time * 3) * 1.5;
  return (
    <g transform={`translate(${MAILBOX.x - 60} ${MAILBOX.y - 180}) rotate(${tilt})`}
       opacity={inAmt}>
      <text x="0" y="0" textAnchor="middle"
            fontFamily="Caveat, cursive" fontSize="56" fontWeight="700"
            fill={COLORS.success || '#3a5a4a'}>
        sent!
      </text>
      {/* underline scribble */}
      <path d="M -50 12 Q 0 22 50 12" fill="none"
            stroke={COLORS.success || '#3a5a4a'} strokeWidth="4"
            strokeLinecap="round" opacity={inAmt * 0.8}/>
    </g>
  );
}

// Tweaks-aware tagline
function CreateTagline({ time }) {
  const tw = useTweaks_();
  if (tw.showTagline === false) return null;
  const inAmt = range(time, T.taglineIn, Easing.easeOutBack);
  const outAmt = range(time, T.createOut, Easing.easeInCubic);
  const opacity = inAmt * (1 - outAmt);
  if (opacity <= 0.01) return null;
  const ty = lerp(20, 0, inAmt) + outAmt * -16;
  // gentle drift
  const drift = Math.sin(time * 2.2) * 1.5;
  return (
    <g transform={`translate(960 ${230 + ty}) rotate(${-3 + drift * 0.4})`} opacity={opacity}>
      <text x="0" y="0" textAnchor="middle"
            fontFamily="Caveat, cursive" fontSize="68" fontWeight="700"
            fill={tw.accentColor || COLORS.walnut}>
        {tw.taglineText || 'images with words'}
      </text>
      {/* tiny squiggle accent */}
      <path d="M -120 24 q 30 12 60 0 t 60 0 t 60 0"
            fill="none" stroke={COLORS.gold} strokeWidth="4"
            strokeLinecap="round" opacity={inAmt * 0.7}/>
    </g>
  );
}

// ── MASTER COMPOSITION ──────────────────────────────────────────────────────

function FeoraVideo({ tweaks = {} }) {
  const time = useTime();

  // update timestamp label on root for comment context
  React.useEffect(()=>{
    const r = document.querySelector('[data-screen-label]');
    if (r) {
      const t = Math.floor(time*100)/100;
      r.setAttribute('data-screen-label', `t=${t.toFixed(2)}s`);
    }
  }, [time]);

  return (
    <TweaksContext.Provider value={tweaks}>
      {/* Back layer — SVG: paper background, wall, prompt box, paint splashes */}
      <svg viewBox="0 0 1920 1080" width="1920" height="1080"
           style={{position:'absolute', inset:0, display:'block'}}>
        <Paper/>
        <Wall time={time}/>
        <PromptBox time={time}/>
        <PaintSplashes time={time}/>
      </svg>

      {/* Painting layer — HTML img with brush-wipe reveal */}
      <Painting time={time}/>

      {/* Framed print — HTML divs (replaces raw painting once frame snaps in) */}
      <FramedPainting time={time}/>

      {/* Front layer — SVG: share UI, cursor, mail delivery, titles, caption */}
      <svg viewBox="0 0 1920 1080" width="1920" height="1080"
           style={{position:'absolute', inset:0, display:'block', pointerEvents:'none'}}>
        <ShareScene time={time}/>
        <ShareIcons time={time}/>
        <Cursor time={time}/>

        {/* Mail delivery — after the print hangs */}
        <Mailbox time={time}/>
        <FlyingEnvelope time={time}/>
        <MailSentCallout time={time}/>

        <SectionTitle time={time} label="Create"
          inRange={T.createTitleIn} outRange={T.createOut} x={960}/>
        <CreateTagline time={time}/>
        <SectionTitle time={time} label="Share"
          inRange={T.shareTitleIn} outRange={T.shareOut} x={960}/>
        <SectionTitle time={time} label="Print"
          inRange={T.printTitleIn} outRange={[10.0, 10.2]} x={960}/>

        <Caption time={time}/>
      </svg>
    </TweaksContext.Provider>
  );
}

// expose to global scope (Babel scopes per <script type="text/babel">)
window.FeoraVideo = FeoraVideo;
