// CelebrationButton.jsx — interactive race-the-clock "Mark as reviewed" demo.
// A thin countdown strip drains; tap the button before it hits zero to
// trigger a confetti burst. Miss it and the bar fails (no confetti),
// then auto-resets a moment later. Sits inline inside the 24-hour rule
// section so the metaphor is literal: beat the deadline.
//
// Tone: calm site-blue palette (--blue) — matches the section's overall feel.

const CB_CONFETTI_COLORS = [
  "#38B6E6", "#2BBFB0", "#5BC0E8",
  "#B19BE0", "#F2C14E", "#1F8AB8",
];

const cbMakeBurst = (count = 36) => {
  return Array.from({ length: count }, (_, i) => {
    const angle = (Math.PI * 2 * i) / count + (Math.random() - 0.5) * 0.4;
    const speed = 110 + Math.random() * 160;
    const dx = Math.cos(angle) * speed;
    const dy = Math.sin(angle) * speed * 0.6 - Math.abs(Math.sin(angle)) * 80;
    return {
      id: `${Date.now()}-${i}`,
      dx: Math.round(dx),
      dy: Math.round(dy),
      fall: 200 + Math.random() * 160,
      rot: Math.round((Math.random() - 0.5) * 720),
      rotEnd: Math.round((Math.random() - 0.5) * 1440),
      delay: Math.random() * 0.06,
      duration: 1.1 + Math.random() * 0.8,
      color: CB_CONFETTI_COLORS[i % CB_CONFETTI_COLORS.length],
      shape: i % 4,
      size: 6 + Math.random() * 7,
    };
  });
};

const CBConfetti = ({ particles }) => {
  if (!particles) return null;
  return (
    <div className="cb-confetti" aria-hidden="true">
      {particles.map((p) => {
        const isCircle = p.shape === 1;
        const isRibbon = p.shape === 3;
        const w = isRibbon ? p.size * 0.4 : p.size;
        const h = isRibbon ? p.size * 1.8 : isCircle ? p.size : p.size * 0.6;
        return (
          <span
            key={p.id}
            className="cb-confetti-piece"
            style={{
              "--dx": `${p.dx}px`, "--dy": `${p.dy}px`,
              "--fall": `${p.fall}px`,
              "--rot": `${p.rot}deg`, "--rot-end": `${p.rotEnd}deg`,
              "--delay": `${p.delay}s`, "--duration": `${p.duration}s`,
              background: p.color,
              width: `${w}px`, height: `${h}px`,
              borderRadius: isCircle ? "50%" : isRibbon ? "2px" : "1.5px",
            }}
          />
        );
      })}
    </div>
  );
};

// Countdown duration in seconds. Maps to a metaphorical 24h window:
// the bar drains over 24s and the time readout counts down 24h → 0h.
// Slow enough that you almost certainly catch it on first try, but
// tight enough to feel like a real race once the bar gets near zero.
const CB_DURATION_MS = 24000;
// After a tap (success or fail), pause briefly, then auto-reset for another go.
const CB_RESET_DELAY_SUCCESS = 1600;
const CB_RESET_DELAY_FAIL = 1800;

const CelebrationButton = () => {
  // status: 'idle' (timer not started) | 'running' | 'success' | 'failed'
  const [status, setStatus] = React.useState("idle");
  const [progress, setProgress] = React.useState(1); // 1 = full, 0 = empty
  const [bursts, setBursts] = React.useState([]);
  const [pressed, setPressed] = React.useState(false);
  const [wins, setWins] = React.useState(0);
  const [misses, setMisses] = React.useState(0);
  // Arriving-notification phases:
  //   'pending'  – out of frame, nothing shown yet
  //   'arriving' – sliding down from above the card, fading in
  //   'visible'  – settled, hovering above the card briefly
  //   'tucking'  – animating into the strip (slide down + scale + fade)
  //   'gone'     – removed from DOM; countdown is running or has run
  const [notifPhase, setNotifPhase] = React.useState("pending");
  const wrapRef = React.useRef(null);
  const rafRef = React.useRef(null);
  const startRef = React.useRef(0);
  const resetTimerRef = React.useRef(null);
  const startedOnceRef = React.useRef(false);
  const arrivalTimersRef = React.useRef([]);

  // Convert progress -> remaining "hours" shown to user (24h → 0h).
  // Always rounds up so it never sits at 0 while the bar is still draining.
  const remainingHours = progress > 0 ? Math.max(1, Math.ceil(progress * 24)) : 0;

  const stopRaf = () => {
    if (rafRef.current) {
      cancelAnimationFrame(rafRef.current);
      rafRef.current = null;
    }
  };

  const startCountdown = React.useCallback(() => {
    stopRaf();
    if (resetTimerRef.current) {
      clearTimeout(resetTimerRef.current);
      resetTimerRef.current = null;
    }
    setStatus("running");
    setProgress(1);
    startRef.current = performance.now();
    const tick = (now) => {
      const elapsed = now - startRef.current;
      const p = Math.max(0, 1 - elapsed / CB_DURATION_MS);
      setProgress(p);
      if (p <= 0) {
        // Time's up
        setStatus((s) => {
          if (s === "running") {
            setMisses((m) => m + 1);
            return "failed";
          }
          return s;
        });
        return;
      }
      rafRef.current = requestAnimationFrame(tick);
    };
    rafRef.current = requestAnimationFrame(tick);
  }, []);

  // Run the arrival cinematic, then start the countdown.
  const playArrivalSequence = React.useCallback(() => {
    // Clean up any leftover timers
    arrivalTimersRef.current.forEach(clearTimeout);
    arrivalTimersRef.current = [];
    setNotifPhase("arriving");
    // Sequence:
    //   0ms     -> arriving (slide+fade in over ~700ms)
    //   ~800ms  -> visible (settle for ~1500ms so user reads it)
    //   ~2300ms -> tucking (slide into strip over ~600ms)
    //   ~2700ms -> kick off the actual countdown (a hair before tuck finishes)
    //   ~2900ms -> gone (remove from DOM)
    const t1 = setTimeout(() => setNotifPhase("visible"), 800);
    const t2 = setTimeout(() => setNotifPhase("tucking"), 2300);
    const t3 = setTimeout(() => startCountdown(), 2700);
    const t4 = setTimeout(() => setNotifPhase("gone"), 2900);
    arrivalTimersRef.current.push(t1, t2, t3, t4);
  }, []);

  // Auto-trigger the arrival when the card scrolls into view (once)
  React.useEffect(() => {
    const el = wrapRef.current;
    if (!el) return;
    const obs = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting && !startedOnceRef.current) {
          startedOnceRef.current = true;
          // Tiny beat before the notification drops in
          setTimeout(() => playArrivalSequence(), 450);
        }
      },
      { threshold: 0.5 }
    );
    obs.observe(el);
    return () => obs.disconnect();
  }, [playArrivalSequence]);

  // Cleanup arrival timers on unmount
  React.useEffect(() => () => {
    arrivalTimersRef.current.forEach(clearTimeout);
  }, []);

  // After a miss, auto-reset for another go. After success, leave the
  // celebrated state in place — the user has earned their win.
  React.useEffect(() => {
    if (status === "failed") {
      resetTimerRef.current = setTimeout(() => {
        startCountdown();
      }, CB_RESET_DELAY_FAIL);
    }
    return () => {
      if (resetTimerRef.current) {
        clearTimeout(resetTimerRef.current);
        resetTimerRef.current = null;
      }
    };
  }, [status, startCountdown]);

  React.useEffect(() => () => stopRaf(), []);

  const handleClick = () => {
    if (status === "running") {
      // SUCCESS — beat the clock
      stopRaf();
      setStatus("success");
      setPressed(true);
      setWins((w) => w + 1);
      const id = Date.now() + Math.random();
      setBursts((b) => [...b, { id, particles: cbMakeBurst(38) }]);
      setTimeout(() => setPressed(false), 220);
      setTimeout(() => {
        setBursts((b) => b.filter((x) => x.id !== id));
      }, 2400);
    } else if (status === "failed") {
      // Tapping after the deadline does nothing dramatic — just a soft press feedback
      setPressed(true);
      setTimeout(() => setPressed(false), 180);
    } else if (status === "idle" || status === "success") {
      // Manual restart by tapping
      startCountdown();
    }
  };

  // Color thresholds for the bar
  const isUrgent = status === "running" && progress < 0.35;
  const isCritical = status === "running" && progress < 0.18;

  let stripState = "running";
  if (status === "success") stripState = "success";
  else if (status === "failed") stripState = "failed";
  else if (isCritical) stripState = "critical";
  else if (isUrgent) stripState = "urgent";

  // While the notification is doing its arrival cinematic, the strip
  // shows a calm "waiting for a rep" state and the button is muted.
  const isWaiting = status === "idle" && notifPhase !== "gone";

  return (
    <div ref={wrapRef} className="cb-wrap">
      {/* ---- Arriving notification (drops in from above) ---- */}
      {notifPhase !== "gone" && notifPhase !== "pending" && (
        <div className={`cb-notif cb-notif-${notifPhase}`} aria-hidden="true">
          <div className="cb-notif-icon">
            <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
              <rect x="3" y="3" width="18" height="18" rx="4.5" fill="white" fillOpacity="0.92" />
              <rect x="6.5" y="7.5" width="11" height="2.2" rx="1.1" fill="#38B6E6" />
              <rect x="6.5" y="11" width="11" height="2.2" rx="1.1" fill="#5BC0E8" />
              <rect x="6.5" y="14.5" width="11" height="2.2" rx="1.1" fill="#9DD5EE" />
            </svg>
          </div>
          <div className="cb-notif-text">
            <div className="cb-notif-row">
              <div className="cb-notif-title">Time to review</div>
              <div className="cb-notif-time">now</div>
            </div>
            <div className="cb-notif-body">Spanish 101 — your 24 hours start now.</div>
          </div>
        </div>
      )}

      {/* ---- Race strip (timer + label) ---- */}
      <div className={`cb-strip cb-strip-${stripState} ${isWaiting ? "cb-strip-waiting" : ""}`}>
        <div className="cb-strip-row">
          <div className="cb-strip-label">
            <span className="cb-strip-dot" aria-hidden="true" />
            {isWaiting ? "Awaiting a rep" :
             status === "success" ? "Reviewed in time" :
             status === "failed"  ? "Missed the window" :
             "Beat the clock"}
          </div>
          <div className="cb-strip-time" aria-live="polite">
            {isWaiting ? "—" :
             status === "failed" ? "0h left" :
             status === "success" ? "Saved!" :
             `${remainingHours}h left`}
          </div>
        </div>
        <div className="cb-bar">
          <div
            className="cb-bar-fill"
            style={{
              width: `${Math.max(0, progress * 100)}%`,
              transition: status === "running" ? "none" : "width 0.45s cubic-bezier(.32,.72,0,1)",
            }}
          />
          <div className="cb-bar-shimmer" aria-hidden="true" />
        </div>
        <div className="cb-strip-stats">
          <span className="cb-stat-win">
            <svg width="11" height="11" viewBox="0 0 14 14" fill="none">
              <path d="M2 7.5L5.5 11L12 3.5" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" />
            </svg>
            {wins} caught
          </span>
          <span className="cb-stat-sep">·</span>
          <span className="cb-stat-miss">
            <svg width="9" height="9" viewBox="0 0 12 12" fill="none">
              <path d="M3 3l6 6M9 3l-6 6" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
            </svg>
            {misses} missed
          </span>
        </div>
      </div>

      {/* ---- Button area ---- */}
      <div className="cb-btn-stage">
        {/* Idle pulse — only while running and getting urgent */}
        <span className={`cb-pulse ${isUrgent ? "cb-pulse-on" : ""}`} aria-hidden="true" />
        <span className={`cb-pulse cb-pulse-2 ${isUrgent ? "cb-pulse-on" : ""}`} aria-hidden="true" />

        {/* Ring flash on success */}
        <span className={`cb-ring-flash ${pressed && status === "success" ? "is-on" : ""}`} aria-hidden="true" />

        <button
          type="button"
          onClick={handleClick}
          disabled={isWaiting}
          className={`cb-btn ${pressed ? "is-pressed" : ""} cb-btn-${stripState} ${isWaiting ? "cb-btn-waiting" : ""}`}
        >
          <span className="cb-btn-shine" aria-hidden="true" />
          <span className="cb-btn-label">
            {status === "failed" ? "Too late — try again" : "Mark as reviewed"}
          </span>
          <span className={`cb-btn-icon ${pressed && status === "success" ? "is-celebrate" : ""}`} aria-hidden="true">
            {status === "failed" ? (
              // Sad clock for the miss state
              <svg width="22" height="22" viewBox="0 0 24 24" fill="none">
                <circle cx="12" cy="12" r="9" stroke="white" strokeWidth="1.7" />
                <path d="M12 7v5l3 1.5" stroke="white" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
            ) : (
              // Party popper
              <svg width="22" height="22" viewBox="0 0 32 32" fill="none">
                <path d="M5 27 L 11 9 L 23 21 Z" fill="#F2C14E" stroke="#D89F2E" strokeWidth="1.2" strokeLinejoin="round" />
                <path d="M5 27 L 11 9" stroke="#D89F2E" strokeWidth="1.2" strokeLinecap="round" opacity="0.5" />
                <path d="M22 8 Q 27 8 28 4" stroke="#38B6E6" strokeWidth="1.8" strokeLinecap="round" fill="none" />
                <path d="M25 14 Q 30 14 30 11" stroke="#F18A7E" strokeWidth="1.8" strokeLinecap="round" fill="none" />
                <path d="M19 5 Q 22 2 26 2" stroke="#2BBFB0" strokeWidth="1.8" strokeLinecap="round" fill="none" />
                <circle cx="29" cy="8" r="1.4" fill="#B19BE0" />
                <circle cx="22" cy="2" r="1.2" fill="#F2C14E" />
                <circle cx="30" cy="16" r="1.1" fill="#38B6E6" />
              </svg>
            )}
          </span>
        </button>

        {bursts.map((b) => <CBConfetti key={b.id} particles={b.particles} />)}

        {pressed && status === "success" && <span className="cb-plusone" aria-hidden="true">+1</span>}
      </div>

      <style>{`
        .cb-wrap {
          position: relative;
          display: flex;
          flex-direction: column;
          align-items: stretch;
          gap: 16px;
          width: 100%;
          isolation: isolate;
        }

        /* ---- Arriving notification overlay ----
           Positioned absolutely above the strip. Slides in from above,
           hovers, then "tucks" down into the strip and fades — at which
           point the countdown takes over. */
        .cb-notif {
          position: absolute;
          left: 50%;
          top: -10px;
          width: min(360px, 100%);
          transform: translate(-50%, -120%);
          display: flex;
          align-items: center;
          gap: 10px;
          padding: 10px 12px 10px 10px;
          background: linear-gradient(180deg, #5BC0E8 0%, #2A9DD0 100%);
          color: white;
          border-radius: 14px;
          box-shadow:
            0 18px 42px -12px rgba(56,182,230,0.55),
            0 6px 14px -4px rgba(56,182,230,0.40),
            inset 0 1px 0 rgba(255,255,255,0.30);
          opacity: 0;
          z-index: 10;
          pointer-events: none;
          will-change: transform, opacity;
        }
        .cb-notif-arriving {
          animation: cb-notif-arriving 0.7s cubic-bezier(.2,.8,.2,1) forwards;
        }
        .cb-notif-visible {
          opacity: 1;
          transform: translate(-50%, calc(-100% - 14px));
          animation: cb-notif-bob 1.5s ease-in-out infinite;
        }
        .cb-notif-tucking {
          animation: cb-notif-tuck 0.6s cubic-bezier(.5,0,.75,0) forwards;
        }
        @keyframes cb-notif-arriving {
          0%   { opacity: 0; transform: translate(-50%, -180%); }
          70%  { opacity: 1; transform: translate(-50%, calc(-100% - 6px)); }
          100% { opacity: 1; transform: translate(-50%, calc(-100% - 14px)); }
        }
        @keyframes cb-notif-bob {
          0%, 100% { transform: translate(-50%, calc(-100% - 14px)); }
          50%      { transform: translate(-50%, calc(-100% - 18px)); }
        }
        @keyframes cb-notif-tuck {
          0%   { opacity: 1; transform: translate(-50%, calc(-100% - 14px)) scale(1); }
          60%  { opacity: 0.7; }
          100% { opacity: 0; transform: translate(-50%, 8px) scale(0.86); }
        }
        .cb-notif-icon {
          flex-shrink: 0;
          width: 32px; height: 32px;
          border-radius: 8px;
          background: rgba(255,255,255,0.18);
          display: grid; place-items: center;
          backdrop-filter: blur(8px);
        }
        .cb-notif-text { flex: 1; min-width: 0; }
        .cb-notif-row {
          display: flex;
          align-items: baseline;
          justify-content: space-between;
          gap: 8px;
        }
        .cb-notif-title {
          font-size: 13px;
          font-weight: 700;
          letter-spacing: -0.01em;
          color: white;
        }
        .cb-notif-time {
          font-size: 11px;
          font-weight: 500;
          color: rgba(255,255,255,0.75);
          letter-spacing: -0.005em;
        }
        .cb-notif-body {
          font-size: 12px;
          font-weight: 400;
          color: rgba(255,255,255,0.92);
          line-height: 1.35;
          margin-top: 1px;
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
        }
        @media (prefers-reduced-motion: reduce) {
          .cb-notif-arriving, .cb-notif-visible, .cb-notif-tucking {
            animation: none;
            opacity: 1;
            transform: translate(-50%, calc(-100% - 14px));
          }
        }

        /* Waiting state: strip + button look calm and inactive
           until the notification tucks in and the countdown fires. */
        .cb-strip-waiting { opacity: 0.65; }
        .cb-strip-waiting .cb-bar-fill {
          background: linear-gradient(90deg, #B7C2D0 0%, #8FA0B5 100%);
          opacity: 0.55;
        }
        .cb-strip-waiting .cb-bar-shimmer { display: none; }
        .cb-strip-waiting .cb-strip-dot {
          background: #8FA0B5 !important;
          animation: none !important;
        }
        .cb-btn-waiting {
          opacity: 0.55;
          cursor: not-allowed;
          filter: saturate(0.6);
        }
        .cb-btn-waiting:hover { transform: none; }

        /* ---- Race strip ---- */
        .cb-strip {
          position: relative;
          padding: 12px 16px 10px;
          border-radius: 14px;
          background: var(--surface);
          border: 1px solid var(--line);
          box-shadow: 0 8px 18px -10px rgba(15,30,55,0.10);
          transition: border-color 0.3s ease, box-shadow 0.3s ease;
        }
        .cb-strip-urgent {
          border-color: color-mix(in srgb, #38B6E6 50%, var(--line));
          box-shadow: 0 8px 18px -8px color-mix(in srgb, #38B6E6 30%, transparent);
        }
        .cb-strip-critical {
          border-color: color-mix(in srgb, #1F8AB8 60%, var(--line));
          box-shadow: 0 8px 22px -6px color-mix(in srgb, #1F8AB8 40%, transparent);
          animation: cb-strip-shake 0.5s ease-in-out infinite;
        }
        .cb-strip-success {
          border-color: color-mix(in srgb, #2BBFB0 50%, var(--line));
          box-shadow: 0 8px 18px -8px color-mix(in srgb, #2BBFB0 30%, transparent);
        }
        .cb-strip-failed {
          border-color: color-mix(in srgb, #8A95A5 35%, var(--line));
          opacity: 0.85;
        }
        @keyframes cb-strip-shake {
          0%, 100% { transform: translateX(0); }
          25%      { transform: translateX(-1px); }
          75%      { transform: translateX(1px); }
        }

        .cb-strip-row {
          display: flex;
          align-items: center;
          justify-content: space-between;
          gap: 10px;
          margin-bottom: 8px;
        }
        .cb-strip-label {
          display: inline-flex;
          align-items: center;
          gap: 8px;
          font-size: 12px;
          font-weight: 700;
          letter-spacing: 0.06em;
          text-transform: uppercase;
          color: var(--text);
        }
        .cb-strip-dot {
          width: 7px; height: 7px; border-radius: 50%;
          background: #38B6E6;
          box-shadow: 0 0 0 0 color-mix(in srgb, #38B6E6 50%, transparent);
          animation: cb-dot-pulse 1.4s ease-in-out infinite;
        }
        .cb-strip-success .cb-strip-dot { background: #2BBFB0; animation: none; }
        .cb-strip-failed .cb-strip-dot { background: #8A95A5; animation: none; }
        .cb-strip-critical .cb-strip-dot { animation: cb-dot-pulse 0.5s ease-in-out infinite; }
        @keyframes cb-dot-pulse {
          0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, currentColor 0%, transparent); }
          50%      { box-shadow: 0 0 0 4px color-mix(in srgb, #38B6E6 35%, transparent); }
        }

        .cb-strip-time {
          font-size: 13px;
          font-weight: 600;
          color: var(--text-soft);
          font-variant-numeric: tabular-nums;
          letter-spacing: -0.01em;
        }
        .cb-strip-critical .cb-strip-time { color: #1F8AB8; }
        .cb-strip-success .cb-strip-time { color: #1F8E82; }
        .cb-strip-failed .cb-strip-time { color: var(--muted); }

        /* ---- Bar ---- */
        .cb-bar {
          position: relative;
          height: 6px;
          border-radius: 999px;
          background: var(--bg-alt);
          overflow: hidden;
        }
        .cb-bar-fill {
          height: 100%;
          border-radius: 999px;
          background: linear-gradient(90deg, #5BC0E8 0%, #38B6E6 100%);
          will-change: width;
        }
        .cb-strip-urgent .cb-bar-fill {
          background: linear-gradient(90deg, #38B6E6 0%, #1F8AB8 100%);
        }
        .cb-strip-critical .cb-bar-fill {
          background: linear-gradient(90deg, #1F8AB8 0%, #14638B 100%);
          animation: cb-bar-flash 0.4s ease-in-out infinite;
        }
        .cb-strip-success .cb-bar-fill {
          background: linear-gradient(90deg, #2BBFB0 0%, #1F8E82 100%);
        }
        .cb-strip-failed .cb-bar-fill {
          background: linear-gradient(90deg, #C2C9D2 0%, #8A95A5 100%);
        }
        @keyframes cb-bar-flash {
          0%, 100% { filter: brightness(1); }
          50%      { filter: brightness(1.15); }
        }
        .cb-bar-shimmer {
          position: absolute;
          inset: 0;
          background: linear-gradient(90deg,
            transparent 30%,
            rgba(255,255,255,0.35) 50%,
            transparent 70%);
          mix-blend-mode: overlay;
          pointer-events: none;
          animation: cb-bar-shimmer 2.4s linear infinite;
        }
        @keyframes cb-bar-shimmer {
          from { transform: translateX(-100%); }
          to   { transform: translateX(100%); }
        }

        /* ---- Stats footer ---- */
        .cb-strip-stats {
          margin-top: 8px;
          display: flex;
          align-items: center;
          gap: 8px;
          font-size: 11px;
          font-weight: 600;
          color: var(--muted);
          letter-spacing: 0.02em;
          font-variant-numeric: tabular-nums;
        }
        .cb-stat-win, .cb-stat-miss { display: inline-flex; align-items: center; gap: 5px; }
        .cb-stat-win  { color: #1F8E82; }
        .cb-stat-miss { color: #1F8AB8; }
        .cb-stat-sep  { opacity: 0.5; }

        /* ---- Button stage ---- */
        .cb-btn-stage {
          position: relative;
          display: flex;
          justify-content: center;
          padding: 6px 0;
        }

        .cb-btn {
          position: relative;
          display: inline-flex;
          align-items: center;
          gap: 10px;
          padding: 14px 22px;
          font-family: inherit;
          font-size: 16px;
          font-weight: 600;
          letter-spacing: -0.01em;
          color: white;
          background: linear-gradient(180deg, #5BC0E8 0%, #2A9DD0 100%);
          border: none;
          border-radius: 16px;
          cursor: pointer;
          white-space: nowrap;
          box-shadow:
            0 10px 22px -8px rgba(56, 182, 230, 0.55),
            0 4px 10px -4px rgba(56, 182, 230, 0.40),
            inset 0 1px 0 rgba(255,255,255,0.35),
            inset 0 -2px 0 rgba(0,0,0,0.08);
          transition: transform 0.18s cubic-bezier(.4,0,.2,1), box-shadow 0.18s ease, background 0.3s ease;
          overflow: hidden;
          z-index: 3;
        }
        .cb-btn:hover {
          transform: translateY(-2px) scale(1.02);
          box-shadow:
            0 16px 28px -8px rgba(56, 182, 230, 0.60),
            0 6px 14px -4px rgba(56, 182, 230, 0.45),
            inset 0 1px 0 rgba(255,255,255,0.4),
            inset 0 -2px 0 rgba(0,0,0,0.08);
        }
        .cb-btn.is-pressed {
          transform: translateY(2px) scale(0.97);
          box-shadow:
            0 4px 10px -4px rgba(56, 182, 230, 0.55),
            inset 0 2px 6px rgba(0,0,0,0.15);
          transition-duration: 0.08s;
        }
        .cb-btn-critical {
          animation: cb-btn-wobble 0.45s ease-in-out infinite;
        }
        @keyframes cb-btn-wobble {
          0%, 100% { transform: translateY(0) rotate(0); }
          25%      { transform: translateY(-1px) rotate(-0.6deg); }
          75%      { transform: translateY(-1px) rotate(0.6deg); }
        }
        .cb-btn-failed {
          background: linear-gradient(180deg, #B0B8C2 0%, #8A95A5 100%);
          box-shadow:
            0 8px 18px -8px rgba(15,30,55,0.25),
            inset 0 1px 0 rgba(255,255,255,0.25),
            inset 0 -2px 0 rgba(0,0,0,0.06);
          animation: cb-btn-shake 0.45s cubic-bezier(.4,0,.2,1);
        }
        .cb-btn-success {
          background: linear-gradient(180deg, #4CD2C4 0%, #2BBFB0 100%);
          box-shadow:
            0 14px 26px -8px rgba(43,191,176,0.55),
            inset 0 1px 0 rgba(255,255,255,0.35),
            inset 0 -2px 0 rgba(0,0,0,0.08);
        }
        @keyframes cb-btn-shake {
          0%   { transform: translateX(0); }
          20%  { transform: translateX(-4px); }
          40%  { transform: translateX(4px); }
          60%  { transform: translateX(-3px); }
          80%  { transform: translateX(2px); }
          100% { transform: translateX(0); }
        }

        .cb-btn-label, .cb-btn-icon { position: relative; z-index: 1; }
        .cb-btn-icon { display: inline-flex; transform-origin: 50% 80%; }
        .cb-btn-icon.is-celebrate { animation: cb-icon-shake 0.6s cubic-bezier(.4,0,.2,1); }
        @keyframes cb-icon-shake {
          0%   { transform: rotate(0) scale(1); }
          20%  { transform: rotate(-18deg) scale(1.18); }
          40%  { transform: rotate(14deg)  scale(1.10); }
          60%  { transform: rotate(-8deg)  scale(1.05); }
          80%  { transform: rotate(4deg)   scale(1.02); }
          100% { transform: rotate(0) scale(1); }
        }

        .cb-btn-shine {
          position: absolute;
          top: 0; left: -100%;
          width: 60%; height: 100%;
          background: linear-gradient(110deg, transparent 0%, rgba(255,255,255,0.45) 50%, transparent 100%);
          transform: skewX(-20deg);
          pointer-events: none;
          z-index: 0;
          animation: cb-btn-shine 4.5s cubic-bezier(.6,0,.4,1) infinite;
        }
        @keyframes cb-btn-shine {
          0%   { left: -100%; }
          25%  { left: 200%; }
          100% { left: 200%; }
        }

        /* Pulse rings — only when timer is getting urgent */
        .cb-pulse {
          position: absolute;
          left: 50%; top: 50%;
          transform: translate(-50%, -50%);
          width: 100%; height: 100%;
          max-width: 240px;
          border-radius: 22px;
          border: 2px solid #38B6E6;
          opacity: 0;
          pointer-events: none;
          z-index: 1;
        }
        .cb-pulse-on { animation: cb-pulse 1.2s ease-out infinite; }
        .cb-pulse-2.cb-pulse-on { animation-delay: 0.6s; }
        @keyframes cb-pulse {
          0%   { transform: translate(-50%, -50%) scale(0.96); opacity: 0; }
          15%  { opacity: 0.6; }
          100% { transform: translate(-50%, -50%) scale(1.18); opacity: 0; }
        }

        .cb-ring-flash {
          position: absolute;
          left: 50%; top: 50%;
          transform: translate(-50%, -50%);
          width: 100%; height: 100%;
          max-width: 240px;
          border-radius: 20px;
          border: 3px solid #F2C14E;
          opacity: 0;
          pointer-events: none;
          z-index: 1;
        }
        .cb-ring-flash.is-on { animation: cb-ring-flash 0.7s cubic-bezier(.2,.7,.2,1) forwards; }
        @keyframes cb-ring-flash {
          0%   { transform: translate(-50%, -50%) scale(0.95); opacity: 0; }
          25%  { transform: translate(-50%, -50%) scale(1.04); opacity: 1; }
          100% { transform: translate(-50%, -50%) scale(1.35); opacity: 0; border-width: 1px; }
        }

        .cb-plusone {
          position: absolute;
          top: -2px;
          left: 50%;
          transform: translateX(-50%);
          font-size: 18px;
          font-weight: 700;
          color: #2BBFB0;
          letter-spacing: -0.02em;
          pointer-events: none;
          animation: cb-plusone 0.95s cubic-bezier(.2,.7,.2,1) forwards;
          z-index: 5;
          text-shadow: 0 2px 8px rgba(43,191,176,0.35);
        }
        @keyframes cb-plusone {
          0%   { opacity: 0; transform: translate(-50%, 0) scale(0.6); }
          25%  { opacity: 1; transform: translate(-50%, -10px) scale(1.1); }
          100% { opacity: 0; transform: translate(-50%, -52px) scale(1); }
        }

        /* Confetti */
        .cb-confetti {
          position: absolute;
          left: 50%; top: 50%;
          width: 0; height: 0;
          pointer-events: none;
          z-index: 2;
        }
        .cb-confetti-piece {
          position: absolute;
          left: 0; top: 0;
          transform: translate(-50%, -50%);
          opacity: 0;
          will-change: transform, opacity;
          animation: cb-confetti-burst var(--duration) cubic-bezier(.2,.7,.2,1) var(--delay) forwards;
          box-shadow: 0 1px 2px rgba(15,30,55,0.18);
        }
        @keyframes cb-confetti-burst {
          0% {
            opacity: 0;
            transform: translate(-50%, -50%) rotate(var(--rot)) scale(0.4);
          }
          12% {
            opacity: 1;
            transform:
              translate(calc(-50% + var(--dx) * 0.55), calc(-50% + var(--dy) * 0.55))
              rotate(calc(var(--rot) * 0.5)) scale(1);
          }
          50% {
            opacity: 1;
            transform:
              translate(calc(-50% + var(--dx)), calc(-50% + var(--dy)))
              rotate(var(--rot-end)) scale(1);
          }
          100% {
            opacity: 0;
            transform:
              translate(calc(-50% + var(--dx) * 1.05), calc(-50% + var(--dy) + var(--fall)))
              rotate(calc(var(--rot-end) * 1.4)) scale(0.85);
          }
        }

        @media (prefers-reduced-motion: reduce) {
          .cb-pulse, .cb-pulse-2, .cb-btn-shine, .cb-bar-shimmer,
          .cb-strip-critical, .cb-btn-critical, .cb-strip-dot { animation: none !important; }
          .cb-confetti-piece { animation-duration: 0.6s !important; }
        }
      `}</style>
    </div>
  );
};

window.CelebrationButton = CelebrationButton;
