// Walkthrough demo orchestrator — scrubber, narrator, scene host.

const { useState, useEffect, useCallback, useRef } = React;

// Scenes chosen for the preview loop on the project page: hero scenes that
// read well without the demo chrome. Scenes 1/2/4/5/7 either depend on chrome
// to make sense (the loading state, the modal-on-blur) or have too much
// whitespace at small scale. 3 = extraction review (hero), 6 = client form
// telling the cross-actor story, 8 = signature (most visceral), 9 = audit log
// (closes the auditability pitch).
const PREVIEW_SCENES = [
  { component: Scene3Review,    title: 'Adviser reviews · confidence dots draw the eye' },
  { component: Scene6Form,      title: 'Client confirms · modified fields tint amber' },
  { component: Scene8Signature, title: 'E-signature · pulsing target on unsigned field' },
  { component: Scene9Audit,     title: 'Immutable audit log · one vertical thread' },
];

const SCENES = [
  { id: 1, title: 'Adviser uploads documents',         narrate: 'Daniel & Anna Bennett are due for their annual review. Lachlan uploads four source documents to begin extraction.', component: Scene1Upload },
  { id: 2, title: 'Claude extracts into structured fields', narrate: 'One batched call sends all four documents to Claude. The model returns JSON conforming to a fixed schema, with a confidence label on every leaf.', component: Scene2Extracting },
  { id: 3, title: 'Adviser reviews the extraction',    narrate: 'The hero screen. Red and amber dots draw the eye. The model flagged Daniel\'s salary as low-confidence — Lachlan corrects it to $215k using the PAYG summary.', component: Scene3Review },
  { id: 4, title: 'Adviser confirms the extraction',   narrate: 'Confirmation creates a draft submission and a magic link. Three fields changed in review — every change is in the audit log.', component: Scene4Confirm },
  { id: 5, title: 'Client opens the link & enters OTP', narrate: 'No password, no app. Magic link from email, six-digit code from SMS. The link is POST-only on entry so safe-link scanners can\'t auto-trigger.', component: Scene5OTP },
  { id: 6, title: 'Client confirms their details',     narrate: 'A pre-filled form. Modified fields tint amber, brand-new information tints green. The client\'s submission becomes the canonical record.', component: Scene6Form },
  { id: 7, title: 'Adviser cockpit & money audit',     narrate: 'Before signature, Lachlan walks the 12-section agenda, ticks scope, acknowledges general warnings. Confirming snapshots a pinned view model.', component: Scene7Cockpit },
  { id: 8, title: 'E-signature with audit page',       narrate: 'Daniel has signed. Anna\'s field is pulsing for attention. The submitted bundle gets an audit page appended with SHA-256 of every component.', component: Scene8Signature },
  { id: 9, title: 'Immutable audit log',               narrate: 'Filter by the Bennett client_id and the whole story shows up in order, on one page. This is what auditability by design feels like.', component: Scene9Audit },
];

function App() {
  // Preview mode — chrome-free auto-cycling loop for embed on the project page.
  const isPreview = (() => {
    try { return new URL(window.location.href).searchParams.get('preview') === '1'; }
    catch { return false; }
  })();
  if (isPreview) return <PreviewLoop/>;

  const bp = useBreakpoint();
  const isMobile = bp === 'mobile';
  const isTablet = bp === 'tablet';
  const isCompact = isMobile || isTablet;

  const [idx, setIdx] = useState(() => {
    const u = new URL(window.location.href);
    const p = parseInt(u.searchParams.get('s') || '1', 10);
    return Math.max(0, Math.min(SCENES.length - 1, p - 1));
  });
  const [showNarrator, setShowNarrator] = useState(true);

  // Cover screen — only on a fresh first visit (no ?s= deep link).
  // Auto-dismisses after 2.5s or on any user interaction.
  const [showCover, setShowCover] = useState(() => {
    const u = new URL(window.location.href);
    if (u.searchParams.has('s')) return false;
    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    return !reducedMotion;
  });
  useEffect(() => {
    if (!showCover) return;
    const dismiss = () => setShowCover(false);
    const timer = setTimeout(dismiss, 2500);
    window.addEventListener('keydown', dismiss, { once: true });
    window.addEventListener('click', dismiss, { once: true });
    return () => {
      clearTimeout(timer);
      window.removeEventListener('keydown', dismiss);
      window.removeEventListener('click', dismiss);
    };
  }, [showCover]);

  const go = useCallback((next) => {
    const n = Math.max(0, Math.min(SCENES.length - 1, next));
    setIdx(n);
    const u = new URL(window.location.href);
    u.searchParams.set('s', String(n + 1));
    window.history.replaceState({}, '', u.toString());
  }, []);

  useEffect(() => {
    const onKey = (e) => {
      if (e.key === 'ArrowRight' || e.key === ' ') { e.preventDefault(); go(idx + 1); }
      if (e.key === 'ArrowLeft')  { e.preventDefault(); go(idx - 1); }
      if (e.key === 'Escape')     { setShowNarrator(s => !s); }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [idx, go]);

  // Swipe gesture handling — only enabled on touch-capable narrow viewports.
  // Requires |dx| > |dy| and |dx| > 50 so vertical scrolling inside scenes
  // (esp. the audit table on Scene 9) isn't misread as a swipe.
  const touchRef = useRef({ x: 0, y: 0, active: false });
  const onTouchStart = useCallback((e) => {
    const t = e.touches[0];
    touchRef.current = { x: t.clientX, y: t.clientY, active: true };
  }, []);
  const onTouchEnd = useCallback((e) => {
    if (!touchRef.current.active) return;
    touchRef.current.active = false;
    const t = e.changedTouches[0];
    const dx = t.clientX - touchRef.current.x;
    const dy = t.clientY - touchRef.current.y;
    if (Math.abs(dx) > 50 && Math.abs(dx) > Math.abs(dy)) {
      if (dx < 0) go(idx + 1); else go(idx - 1);
    }
  }, [idx, go]);

  const scene = SCENES[idx];
  const Scene = scene.component;

  return (
    <div style={{
      width: '100%', height: '100%',
      background: '#0b1417',
      display: 'flex', flexDirection: 'column',
      overflow: 'hidden',
      fontFamily: EPG.sans,
      position: 'relative',
    }}>
      {/* Cover screen — fades out after 2.5s on fresh first visits */}
      {showCover && <DemoCover/>}

      {/* Demo chrome — top */}
      <header style={{
        height: isMobile ? 44 : 52, flexShrink: 0,
        background: '#0b1417',
        borderBottom: `1px solid rgba(255,255,255,0.06)`,
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        padding: `0 ${isMobile ? 12 : 20}px`,
        color: 'rgba(255,255,255,0.7)',
        gap: 10,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 10 : 14, minWidth: 0 }}>
          <a href="/building#epg" target="_top" style={{ display: 'flex', alignItems: 'center', gap: 6, color: 'rgba(255,255,255,0.7)', textDecoration: 'none', flexShrink: 0 }}>
            <Icon d={ICONS.chevL} size={14}/>
            <span style={{ fontSize: 12 }}>{isMobile ? 'Back' : 'Back to case study'}</span>
          </a>
          {!isMobile && <div style={{ width: 1, height: 18, background: 'rgba(255,255,255,0.12)', flexShrink: 0 }}/>}
          <span style={{ fontFamily: EPG.serif, fontSize: isMobile ? 13 : 14, color: '#fff', letterSpacing: '-0.005em', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>Financial Advisory Firm</span>
          {!isCompact && (
            <span style={{ fontFamily: EPG.mono, fontSize: 10, color: 'rgba(255,255,255,0.4)', border: `1px solid rgba(255,255,255,0.15)`, padding: '2px 6px', borderRadius: 4, letterSpacing: '0.08em', textTransform: 'uppercase', flexShrink: 0 }}>walkthrough · demo</span>
          )}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 10 : 14, fontSize: 11.5, flexShrink: 0 }}>
          {!isCompact && (
            <span style={{ color: 'rgba(255,255,255,0.5)' }}>
              All names and data synthetic. Real product, recreated as a static walkthrough.
            </span>
          )}
          <a href="https://cameroncarmody.com/contact" style={{ color: 'rgba(255,255,255,0.7)', textDecoration: 'none', fontSize: 11.5 }}>Contact ↗</a>
        </div>
      </header>

      {/* Scene viewport */}
      <div
        onTouchStart={isCompact ? onTouchStart : undefined}
        onTouchEnd={isCompact ? onTouchEnd : undefined}
        style={{
          flex: 1, minHeight: 0,
          background: '#0b1417',
          padding: isMobile ? 0 : isTablet ? 10 : 18,
          display: 'flex',
          alignItems: 'stretch',
          justifyContent: 'center',
          position: 'relative',
        }}>
        <div style={{
          flex: 1, maxWidth: 1440,
          background: EPG.card,
          borderRadius: isMobile ? 0 : 10,
          overflow: 'hidden',
          boxShadow: isMobile ? 'none' : '0 20px 60px rgba(0,0,0,0.4)',
          display: 'flex', flexDirection: 'column',
          position: 'relative',
        }}>
          <div key={idx} style={{
            flex: 1, minHeight: 0, overflow: 'auto',
            animation: 'epgFadeIn 220ms cubic-bezier(0.25, 0.46, 0.45, 0.94)',
          }}>
            <Scene/>
          </div>
        </div>

        {/* Big side-arrow buttons — desktop only; mobile/tablet use swipe */}
        {!isCompact && (
          <>
            <SideArrow direction="left"  onClick={() => go(idx - 1)} disabled={idx === 0}/>
            <SideArrow direction="right" onClick={() => go(idx + 1)} disabled={idx === SCENES.length - 1}/>
          </>
        )}
      </div>

      {/* Bottom chrome — light cream control bar */}
      <footer style={{
        flexShrink: 0,
        background: '#F5F8F9',
        borderTop: `4px solid ${EPG.brand}`,
        boxShadow: '0 -2px 16px rgba(0,0,0,0.35)',
        padding: isMobile ? '10px 12px 12px' : '16px 24px 18px',
        display: 'flex', flexDirection: 'column', gap: isMobile ? 10 : 14,
        position: 'relative',
      }}>
        {/* Subtle teal glow line just under the border */}
        <div style={{
          position: 'absolute', top: 0, left: 0, right: 0, height: 2,
          background: `linear-gradient(90deg, transparent, ${EPG.brand2}88, transparent)`,
          pointerEvents: 'none',
        }}/>

        {showNarrator && (
          <div style={{
            display: 'flex',
            flexDirection: isMobile ? 'column' : 'row',
            alignItems: isMobile ? 'stretch' : 'center',
            gap: isMobile ? 8 : 14,
            position: isMobile ? 'relative' : 'static',
            paddingRight: isMobile ? 28 : 0,
          }}>
            <div style={{
              display: 'flex', alignItems: 'center', gap: isMobile ? 10 : 14, minWidth: 0,
            }}>
              <div style={{
                flexShrink: 0,
                width: isMobile ? 36 : 44, height: isMobile ? 36 : 44,
                background: EPG.brand,
                border: `2px solid ${EPG.brand2}`,
                borderRadius: '50%',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontFamily: EPG.serif, fontSize: isMobile ? 16 : 19, fontWeight: 700, color: '#fff',
                boxShadow: '0 3px 12px rgba(29,85,99,0.35)',
              }}>{scene.id}</div>
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{
                  fontFamily: EPG.mono, fontSize: 10.5, color: EPG.brand,
                  letterSpacing: '0.14em', textTransform: 'uppercase',
                  marginBottom: isMobile ? 0 : 3, fontWeight: 700,
                }}>
                  Step {scene.id} of {SCENES.length}{isMobile ? '' : ` · ${scene.title}`}
                </div>
                {isMobile && (
                  <div style={{ fontFamily: EPG.serif, fontSize: 13, color: EPG.ink, lineHeight: 1.25, marginTop: 2, fontWeight: 600 }}>
                    {scene.title}
                  </div>
                )}
              </div>
            </div>
            <div style={{ fontFamily: EPG.sans, fontSize: isMobile ? 12.5 : 14, color: EPG.ink, lineHeight: 1.45, fontWeight: 500, flex: isMobile ? 'none' : 1 }}>
              {scene.narrate}
            </div>
            <button onClick={() => setShowNarrator(false)} style={{
              background: 'transparent', border: 'none', cursor: 'pointer',
              color: EPG.muted, padding: 6, flexShrink: 0,
              position: isMobile ? 'absolute' : 'static',
              top: isMobile ? -2 : undefined, right: isMobile ? -2 : undefined,
              minWidth: 32, minHeight: 32,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }} title="Hide narration (Esc)" aria-label="Hide narration">
              <Icon d={ICONS.x} size={14}/>
            </button>
          </div>
        )}

        {/* Scrubber row */}
        <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 10 : 16 }}>
          {!showNarrator && (
            <button onClick={() => setShowNarrator(true)} title="Show narration (Esc)"
              style={{
                display: 'inline-flex', alignItems: 'center', gap: 6,
                padding: '5px 10px',
                background: '#fff', border: `1px solid ${EPG.line2}`,
                borderRadius: 99,
                fontFamily: EPG.mono, fontSize: 10.5,
                color: EPG.ink2,
                letterSpacing: '0.08em', textTransform: 'uppercase',
                cursor: 'pointer',
                flexShrink: 0,
                transition: 'all 160ms',
              }}>
              <Icon d={ICONS.info} size={11}/>
              {!isMobile && 'Notes'}
            </button>
          )}
          {/* Scrubber track */}
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: isCompact ? 0 : 7, minWidth: 0 }}>
            {/* Markers */}
            <div style={{ display: 'flex', gap: 4, position: 'relative' }}>
              {SCENES.map((s, i) => {
                const active = i === idx;
                const past = i < idx;
                return (
                  <button key={s.id} onClick={() => go(i)}
                    title={`Step ${s.id} — ${s.title}`}
                    aria-label={`Step ${s.id} — ${s.title}`}
                    style={{
                      flex: 1, height: isMobile ? 6 : 8,
                      background: active ? EPG.brand2 : past ? EPG.brand : '#D6D2C9',
                      border: 'none',
                      borderRadius: 99,
                      cursor: 'pointer',
                      transition: 'all 160ms',
                      boxShadow: active ? `0 0 0 3px rgba(43,122,140,0.25)` : 'none',
                      padding: 0, minWidth: 0,
                    }}/>
                );
              })}
            </div>
            {/* Step labels — desktop only; too small to read below desktop */}
            {!isCompact && (
              <div style={{ display: 'flex', gap: 4 }}>
                {SCENES.map((s, i) => {
                  const active = i === idx;
                  const past = i < idx;
                  return (
                    <button key={s.id} onClick={() => go(i)}
                      style={{
                        flex: 1, minWidth: 0,
                        background: 'transparent', border: 'none',
                        color: active ? EPG.brand : past ? EPG.ink2 : EPG.muted,
                        fontFamily: EPG.mono, fontSize: 10.5,
                        letterSpacing: '0.06em', textTransform: 'uppercase',
                        padding: '3px 4px 0',
                        cursor: 'pointer',
                        textAlign: 'left',
                        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                        fontWeight: active ? 700 : 500,
                      }}>
                      {String(s.id).padStart(2, '0')} {s.title}
                    </button>
                  );
                })}
              </div>
            )}
          </div>

          {/* Hint + counter */}
          <div style={{ display: 'flex', alignItems: 'center', gap: isMobile ? 8 : 12, flexShrink: 0 }}>
            {!isCompact && (
              <span style={{
                fontFamily: EPG.mono, fontSize: 10.5,
                color: EPG.muted,
                letterSpacing: '0.06em',
                display: 'flex', alignItems: 'center', gap: 6,
              }}>
                <kbd style={kbdStyle}>←</kbd>
                <kbd style={kbdStyle}>→</kbd>
                <span>navigate</span>
              </span>
            )}
            {isCompact && (
              <span style={{ fontFamily: EPG.mono, fontSize: 10.5, color: EPG.muted, letterSpacing: '0.08em', textTransform: 'uppercase' }}>
                {isMobile ? 'Swipe' : 'Swipe / ← →'}
              </span>
            )}
            <div style={{
              fontFamily: EPG.mono, fontSize: isMobile ? 12 : 14,
              color: '#fff',
              padding: isMobile ? '5px 10px' : '7px 14px',
              background: EPG.brand,
              border: `1px solid ${EPG.brandInk}`,
              borderRadius: 8, letterSpacing: '0.08em',
              whiteSpace: 'nowrap',
              boxShadow: '0 2px 8px rgba(29,85,99,0.25)',
              display: 'inline-flex', alignItems: 'baseline', gap: 0,
            }}>
              <span style={{ fontWeight: 700 }}>{String(idx + 1).padStart(2, '0')}</span>
              <span style={{ fontWeight: 400, color: 'rgba(255,255,255,0.55)', margin: '0 4px' }}>/</span>
              <span style={{ fontWeight: 500, color: 'rgba(255,255,255,0.75)' }}>{String(SCENES.length).padStart(2, '0')}</span>
            </div>
          </div>
        </div>
      </footer>
    </div>
  );
}

const kbdStyle = {
  display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
  minWidth: 20, height: 20,
  background: '#fff',
  border: `1px solid ${EPG.line2 || '#CBD5D8'}`,
  borderRadius: 4,
  fontSize: 11, fontWeight: 600, color: EPG.ink2,
  padding: '0 5px',
  boxShadow: '0 1px 0 rgba(0,0,0,0.06)',
};

// ---------- Preview loop (rendered when ?preview=1) -------------------------
// Renders selected scenes at their natural desktop size (1280×960, 4:3) and
// scales them down to fit the host iframe. Cross-fades every 6 seconds.
// Respects prefers-reduced-motion. The whole frame is meant to be wrapped
// in a click-through anchor by the host page (FeatureBlock.astro), so we
// don't handle the click here.
const PREVIEW_NATURAL_W = 1280;
const PREVIEW_NATURAL_H = 960;   // 4:3 of 1280
const PREVIEW_INTERVAL  = 6000;
const PREVIEW_FADE_MS   = 600;

function PreviewLoop() {
  const containerRef = useRef(null);
  const [scale, setScale] = useState(1);
  const [idx, setIdx] = useState(0);

  // Track container width → scale so the inner 1280px-wide scene fits.
  useEffect(() => {
    const el = containerRef.current;
    if (!el) return;
    const update = () => {
      const w = el.clientWidth;
      const h = el.clientHeight;
      // Use whichever dimension is the limiting factor — usually width on 4:3
      // hosts but doesn't hurt to be defensive.
      const s = Math.min(w / PREVIEW_NATURAL_W, h / PREVIEW_NATURAL_H);
      setScale(s || 1);
    };
    update();
    const obs = new ResizeObserver(update);
    obs.observe(el);
    return () => obs.disconnect();
  }, []);

  // Auto-advance unless the user prefers reduced motion.
  useEffect(() => {
    const mq = window.matchMedia('(prefers-reduced-motion: reduce)');
    if (mq.matches) return;
    const id = setInterval(() => {
      setIdx(i => (i + 1) % PREVIEW_SCENES.length);
    }, PREVIEW_INTERVAL);
    return () => clearInterval(id);
  }, []);

  return (
    <div ref={containerRef} style={{
      position: 'relative',
      width: '100%', height: '100%',
      background: '#0b1417',
      overflow: 'hidden',
      fontFamily: EPG.sans,
    }}>
      {/* Stacked scene layers — only opacity changes for cross-fade */}
      {PREVIEW_SCENES.map((s, i) => {
        const Scene = s.component;
        const visible = i === idx;
        return (
          <div key={i} aria-hidden={!visible} style={{
            position: 'absolute', inset: 0,
            opacity: visible ? 1 : 0,
            transition: `opacity ${PREVIEW_FADE_MS}ms ease`,
            pointerEvents: 'none',
          }}>
            <div style={{
              width: PREVIEW_NATURAL_W,
              height: PREVIEW_NATURAL_H,
              transform: `scale(${scale})`,
              transformOrigin: 'top left',
              overflow: 'hidden',
              background: EPG.page,
            }}>
              <Scene/>
            </div>
          </div>
        );
      })}

      {/* CTA overlay — tagline + pill button, gradient strip at bottom */}
      <div style={{
        position: 'absolute', left: 0, right: 0, bottom: 0,
        padding: '64px 14px 14px',
        background: 'linear-gradient(to bottom, transparent 0%, rgba(11,20,23,0) 20%, rgba(11,20,23,0.85) 100%)',
        display: 'flex', flexDirection: 'column', alignItems: 'center',
        gap: 8,
        pointerEvents: 'none',
      }}>
        <div style={{
          fontFamily: EPG.mono, fontSize: 9.5,
          color: 'rgba(255,255,255,0.7)',
          letterSpacing: '0.18em', textTransform: 'uppercase',
        }}>
          Live · 9-step adviser portal demo
        </div>
        <div style={{
          display: 'inline-flex', alignItems: 'center', gap: 8,
          padding: '8px 16px',
          borderRadius: 999,
          background: '#fff',
          color: '#0b1417',
          fontFamily: EPG.sans, fontSize: 12.5, fontWeight: 600,
          letterSpacing: '0.01em',
          boxShadow: '0 6px 20px rgba(0,0,0,0.35)',
        }}>
          Try the walkthrough demo
          <span style={{ fontFamily: EPG.serif, fontSize: 14 }}>→</span>
        </div>
        {/* Progress dots */}
        <div style={{ display: 'flex', gap: 5, marginTop: 4 }}>
          {PREVIEW_SCENES.map((_, i) => (
            <div key={i} style={{
              width: 5, height: 5, borderRadius: '50%',
              background: i === idx ? '#fff' : 'rgba(255,255,255,0.3)',
              transition: 'all 250ms ease',
            }}/>
          ))}
        </div>
      </div>

      {/* Top-corner watermark — subtle "walkthrough preview" tag */}
      <div style={{
        position: 'absolute', top: 10, left: 12,
        fontFamily: EPG.mono, fontSize: 9, color: 'rgba(255,255,255,0.55)',
        letterSpacing: '0.16em', textTransform: 'uppercase',
        background: 'rgba(11,20,23,0.5)',
        padding: '3px 7px', borderRadius: 4,
        border: '1px solid rgba(255,255,255,0.08)',
        backdropFilter: 'blur(2px)',
        pointerEvents: 'none',
      }}>
        Preview
      </div>
    </div>
  );
}

function DemoCover() {
  // Fades itself out via opacity transition once the parent unmounts it.
  // (Stays mounted for 2.5s, then parent flips showCover=false.)
  return (
    <div style={{
      position: 'absolute', inset: 0, zIndex: 100,
      background: '#0b1417',
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      gap: 18,
      animation: 'coverFade 2500ms cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards',
      pointerEvents: 'none',
    }}>
      <div style={{
        width: 64, height: 64, borderRadius: 14,
        background: EPG.brand,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: EPG.serif, fontSize: 32, fontWeight: 600, color: '#fff',
        boxShadow: '0 8px 32px rgba(29,85,99,0.45)',
        letterSpacing: '-0.02em',
      }}>E</div>
      <div style={{ textAlign: 'center' }}>
        <div style={{ fontFamily: EPG.serif, fontSize: 22, fontWeight: 600, color: '#fff', letterSpacing: '-0.005em', marginBottom: 6 }}>
          Adviser Portal Walkthrough
        </div>
        <div style={{ fontFamily: EPG.mono, fontSize: 11, color: 'rgba(255,255,255,0.55)', letterSpacing: '0.16em', textTransform: 'uppercase' }}>
          9 steps · synthetic data · recreation of a real product
        </div>
      </div>
      <div style={{ marginTop: 8, fontFamily: EPG.sans, fontSize: 11.5, color: 'rgba(255,255,255,0.4)' }}>
        Press <span style={{ ...kbdStyle, color: 'rgba(255,255,255,0.7)', background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.18)' }}>→</span> or click anywhere to begin
      </div>
      <style>{`
        @keyframes coverFade {
          0%, 70%   { opacity: 1; transform: scale(1); }
          100%      { opacity: 0; transform: scale(1.04); }
        }
      `}</style>
    </div>
  );
}

function SideArrow({ direction, onClick, disabled }) {
  const isLeft = direction === 'left';
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      aria-label={isLeft ? 'Previous step' : 'Next step'}
      style={{
        position: 'absolute',
        top: '50%',
        [isLeft ? 'left' : 'right']: 24,
        transform: 'translateY(-50%)',
        width: 56, height: 56,
        borderRadius: '50%',
        background: disabled ? 'rgba(255,255,255,0.04)' : EPG.brand,
        border: `2px solid ${disabled ? 'rgba(255,255,255,0.08)' : EPG.brand2}`,
        color: disabled ? 'rgba(255,255,255,0.18)' : '#fff',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: disabled ? 'not-allowed' : 'pointer',
        boxShadow: disabled ? 'none' : '0 6px 24px rgba(0,0,0,0.45), 0 0 0 4px rgba(43,122,140,0.15)',
        transition: 'all 180ms ease',
        zIndex: 10,
      }}
      onMouseEnter={(e) => { if (!disabled) { e.currentTarget.style.transform = 'translateY(-50%) scale(1.08)'; e.currentTarget.style.boxShadow = '0 8px 32px rgba(0,0,0,0.5), 0 0 0 6px rgba(43,122,140,0.25)'; } }}
      onMouseLeave={(e) => { if (!disabled) { e.currentTarget.style.transform = 'translateY(-50%) scale(1)'; e.currentTarget.style.boxShadow = '0 6px 24px rgba(0,0,0,0.45), 0 0 0 4px rgba(43,122,140,0.15)'; } }}
    >
      <Icon d={ICONS[isLeft ? 'chevL' : 'chevR']} size={26} stroke="currentColor"/>
    </button>
  );
}

// Boot
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App/>);
