// oneway-kit.jsx — brand tokens + shared animation helpers for the launch video.
// Loaded after animations.jsx. Exports helpers to window.

const OW = {
  ink:      '#080A0D',
  ink2:     '#0C0F14',
  surface:  '#141923',
  surface2: '#1B212D',
  line:     'rgba(255,255,255,0.09)',
  lineSoft: 'rgba(255,255,255,0.05)',
  text:     '#F1F5F2',
  muted:    '#9AA3AF',
  faint:    '#6B7480',
  accent:   '#35E0A1',   // signature spring-green
  accentDp: '#15B97E',
  accentGl: 'rgba(53,224,161,0.35)',
  warm:     '#FF7E6B',   // "expensive" comparison
  warmDim:  'rgba(255,126,107,0.14)',
  amber:    '#F6C453',
};

const DISPLAY = "'Clash Display', system-ui, sans-serif";
const BODY    = "'General Sans', system-ui, sans-serif";
const MONO    = "'Space Mono', ui-monospace, monospace";

// ── Number formatting ────────────────────────────────────────────────
const fmtUSD = (v) => '$' + Math.round(v).toLocaleString('en-US');
const fmtUSDk = (v) => '$' + Math.round(v / 1000) + 'K';

// CountUp — tween a number across [start,end] seconds on the master timeline.
function CountUp({ from = 0, to, start, end, ease = Easing.easeOutCubic, format = (v) => Math.round(v) }) {
  const time = useTime();
  const p = clamp((time - start) / (end - start), 0, 1);
  const v = from + (to - from) * ease(p);
  return format(v);
}

// Fade/slide wrapper driven by the master timeline (absolute seconds).
// Holds visible after `in`; optionally fades out starting at `out`.
function Reveal({
  inAt, dur = 0.6, y = 22, x = 0, blur = 0,
  outAt = null, outDur = 0.5,
  ease = Easing.easeOutCubic, style = {}, children,
}) {
  const time = useTime();
  let op = 1, ty = 0, tx = 0, bl = 0;
  const pin = clamp((time - inAt) / dur, 0, 1);
  const ein = ease(pin);
  op = ein;
  ty = (1 - ein) * y;
  tx = (1 - ein) * x;
  bl = (1 - ein) * blur;
  if (outAt != null && time > outAt) {
    const po = clamp((time - outAt) / outDur, 0, 1);
    const eo = Easing.easeInCubic(po);
    op = 1 - eo;
    ty = -eo * 10;
  }
  return (
    <div style={{
      transform: `translate(${tx}px, ${ty}px)`,
      opacity: op,
      filter: bl ? `blur(${bl}px)` : 'none',
      willChange: 'transform, opacity',
      ...style,
    }}>{children}</div>
  );
}

// Scene — absolute-positioned full-canvas group visible within [start,end].
// Provides a re-based Sprite context so children can use useSprite() too.
function Scene({ start, end, children, style = {} }) {
  const time = useTime();
  if (time < start - 0.01 || time > end + 0.01) return null;
  return (
    <Sprite start={start} end={end}>
      <div style={{ position: 'absolute', inset: 0, ...style }}>{children}</div>
    </Sprite>
  );
}

// Animated wordmark "OneWay." — clip wipe in, accent dot pops.
function Wordmark({ size = 64, inAt = 0, color = OW.text, dotColor = OW.accent }) {
  const time = useTime();
  const wipe = Easing.easeOutCubic(clamp((time - inAt) / 0.7, 0, 1));
  const dotP = Easing.easeOutBack(clamp((time - inAt - 0.55) / 0.45, 0, 1));
  return (
    <div style={{ display: 'inline-flex', alignItems: 'baseline', fontFamily: DISPLAY, fontWeight: 600, fontSize: size, color, letterSpacing: '-0.02em', lineHeight: 1 }}>
      <span style={{
        display: 'inline-block',
        clipPath: `inset(0 ${(1 - wipe) * 100}% 0 0)`,
        whiteSpace: 'pre',
      }}>OneWay</span>
      <span style={{
        display: 'inline-block',
        color: dotColor,
        transform: `scale(${dotP})`,
        transformOrigin: 'center bottom',
        opacity: clamp(dotP, 0, 1),
      }}>.</span>
    </div>
  );
}

// Directional "one-way" line that draws across, with an arrowhead.
function OneWayLine({ width = 420, inAt = 0, color = OW.accent, stroke = 2 }) {
  const time = useTime();
  const p = Easing.easeInOutCubic(clamp((time - inAt) / 0.9, 0, 1));
  const len = width - 18;
  return (
    <svg width={width} height="16" viewBox={`0 0 ${width} 16`} fill="none" style={{ display: 'block', overflow: 'visible' }}>
      <line x1="0" y1="8" x2={len} y2="8" stroke={color} strokeWidth={stroke}
        strokeDasharray={len} strokeDashoffset={len * (1 - p)} strokeLinecap="round" />
      <path d={`M${len - 9} 2 L${len + 5} 8 L${len - 9} 14`} stroke={color} strokeWidth={stroke}
        strokeLinecap="round" strokeLinejoin="round" fill="none"
        opacity={clamp((p - 0.7) / 0.3, 0, 1)} />
    </svg>
  );
}

// Eyebrow label (mono, tracked-out, accent tick).
function Eyebrow({ children, color = OW.accent, inAt = 0 }) {
  return (
    <Reveal inAt={inAt} y={10} dur={0.5} style={{ display: 'inline-flex', alignItems: 'center', gap: 16 }}>
      <span style={{ width: 40, height: 2, background: color, display: 'inline-block' }} />
      <span style={{ fontFamily: MONO, fontSize: 24, letterSpacing: '0.2em', textTransform: 'uppercase', color, fontWeight: 400 }}>{children}</span>
    </Reveal>
  );
}

// Soft ambient background: ink + faint dot grid + accent glow blob.
function Ambient({ glowX = '70%', glowY = '30%', glow = OW.accentGl, intensity = 1 }) {
  return (
    <div style={{ position: 'absolute', inset: 0, background: OW.ink, overflow: 'hidden' }}>
      <div style={{
        position: 'absolute', inset: 0,
        backgroundImage: `radial-gradient(rgba(255,255,255,0.045) 1px, transparent 1px)`,
        backgroundSize: '38px 38px',
        maskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 80%)',
        WebkitMaskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, #000 30%, transparent 80%)',
      }} />
      <div style={{
        position: 'absolute', left: glowX, top: glowY, width: 900, height: 900,
        transform: 'translate(-50%,-50%)',
        background: `radial-gradient(circle, ${glow} 0%, transparent 62%)`,
        opacity: 0.55 * intensity, filter: 'blur(20px)',
      }} />
    </div>
  );
}

// Signature transition: a glowing "one-way" light blade sweeps across at each cut.
function SceneSweep({ boundaries = [], dur = 0.6, color = OW.accent }) {
  const t = useTime();
  let active = null;
  for (const b of boundaries) {
    if (t >= b - dur * 0.5 && t <= b + dur * 0.5) { active = b; break; }
  }
  if (active == null) return null;
  const p = Easing.easeInOutCubic(clamp((t - (active - dur * 0.5)) / dur, 0, 1));
  const x = -25 + p * 150; // vw-ish percent
  const fade = Math.sin(clamp(p, 0, 1) * Math.PI); // brighten mid-sweep
  return (
    <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 50, overflow: 'hidden' }}>
      <div style={{ position: 'absolute', top: '-10%', height: '120%', left: `${x}%`, width: 360,
        transform: 'translateX(-100%) skewX(-10deg)',
        background: `linear-gradient(90deg, transparent, ${OW.accentGl})`, opacity: 0.85 * fade }} />
      <div style={{ position: 'absolute', top: '-10%', height: '120%', left: `${x}%`, width: 5,
        transform: 'skewX(-10deg)', background: color,
        boxShadow: `0 0 30px 7px ${color}`, opacity: fade }} />
    </div>
  );
}

Object.assign(window, {
  OW, DISPLAY, BODY, MONO, fmtUSD, fmtUSDk,
  CountUp, Reveal, Scene, Wordmark, OneWayLine, Eyebrow, Ambient, SceneSweep,
});
