/* global React */
// Keyboard shortcuts overlay — hidden from the UI (no visible button).
// `?` opens the help menu, leader keys (F / R / G) start a chord, single
// utility keys (/, ⌘K, ,) trigger actions, `Esc` closes whatever's open.
// Power-user only; not surfaced in the chrome on purpose.
//
// Leader-key model — maps to the VF—X.Y / VR—X.Y / VG—X.Y page eyebrows.
// Main-page chords only (sub-page anchor chords were removed to keep
// the menu surface tight).
//
//   F        → Films home (VF—1.0)            R       → Rentals home
//   F1       → Films home (same)              R1      → Rentals home (same)
//   F2       → Services                       R2      → Equipment
//   F3       → About                          R3      → About (rentals)
//   F4       → Contact                        R4      → Open Account
//   F5       → Crew onboard (hidden)          R5      → Careers (hidden)
//   F6       → Privacy (hidden)
//
//   G        → Games landing (VG—1.0)         G2      → Clapperboard Chaos
//   G1       → Games landing (same)           G3      → Focus Fever
//                                              G4      → The Greenlight
//
// Chord timing:
//   Leader pressed, no digit within 800ms → fire side-home
//   First digit pressed, no second digit within 350ms → fire 1-digit target
//   Second digit pressed → fire 2-digit target if defined, otherwise fall
//     back to the 1-digit destination (e.g. F19 isn't defined → Films home).
//
// Games is a separate static site at /games/, not part of the SPA, so the
// G-leader does a full page navigation (window.location.href) rather than
// pushState.
const { useState: useShortcutState, useEffect: useShortcutEffect, useRef: useShortcutRef } = React;

const LEADER_MAP = {
  // Only main-page destinations now — sub-section chords (F11, F12,
  // F21, F31, F32, R11–R14) were removed to keep the keyboard menu
  // surface tight. Pressing one of the removed chords falls back to
  // the 1-digit parent page via the "not in map" branch of the chord
  // handler.
  f: {
    '':   '/',
    '1':  '/',
    '2':  '/services',
    '3':  '/about',
    '4':  '/contact',
    '5':  '/crew/onboard',
    '6':  '/privacy',
  },
  r: {
    '':   '/rentals',
    '1':  '/rentals',
    '2':  '/rentals/equipment',
    '3':  '/rentals/about',
    '4':  '/rentals/open-account',
    '5':  '/rentals/careers',
    // R6 Reference is token-based — no useful shortcut, intentionally omitted.
  },
  g: {
    '':   '/games/',
    '1':  '/games/',
    '2':  '/games/clapperboardchaos/',
    '3':  '/games/focusfever/',
    '4':  '/games/thegreenlight/',
  },
};

const LEADER_TIMEOUT_MS = 800;       // After leader, wait for first digit
const SECOND_DIGIT_TIMEOUT_MS = 350; // After first digit, wait for second

// Leaders that route OUT of the SPA — these use full-page navigation
// rather than pushState because the target is a separate static site.
const EXTERNAL_LEADERS = new Set(['g']);

const SHORTCUT_GROUPS = [
  // Staff Tools — sits at the very top so the global review-mode
  // toggle is the first thing a power user sees when they open the
  // menu. The single entry renders as a sliding switch (mirrors
  // .twk-toggle in tweaks-panel.jsx) rather than a chord row.
  { title: 'Staff Tools', items: [
    {
      // `switch: true` flips the row's renderer to a sliding-pill
      // toggle. `stateKey` names a localStorage entry the menu reads
      // for the initial value; click runs `action()` which dispatches
      // the toggle event consumed by EquipmentPage + CartDrawer.
      switch: true,
      stateKey: 'vf-rentals-review-mode',
      label: 'Equipment Revision Mode',
      action: () => {
        if (typeof window === 'undefined') return;
        const onPath = window.location.pathname.startsWith('/rentals/equipment');
        if (onPath) {
          window.dispatchEvent(new CustomEvent('vf-rentals-review-toggle'));
        } else {
          // Going TO equipment from elsewhere: flip the stored flag
          // before navigating so the page mounts in the new mode.
          let current = '0';
          try { current = localStorage.getItem('vf-rentals-review-mode') || '0'; } catch (e) { /* */ }
          try { localStorage.setItem('vf-rentals-review-mode', current === '1' ? '0' : '1'); } catch (e) { /* */ }
          window.history.pushState(null, '', '/rentals/equipment');
          window.dispatchEvent(new PopStateEvent('popstate'));
        }
      },
    },
    {
      // Custom quote mode — gates the cart-drawer's staff features:
      // the "+ Add custom item" form (with ownership picker), the
      // drag-to-reorder handles on each line, the editable per-line
      // discount %, and the "Custom order" reset banner. Pure
      // localStorage toggle — no navigation needed because the cart
      // drawer listens for the toggle event live.
      switch: true,
      stateKey: 'vf-rentals-quote-staff-mode',
      label: 'Custom quote mode',
      action: () => {
        if (typeof window === 'undefined') return;
        let current = '0';
        try { current = localStorage.getItem('vf-rentals-quote-staff-mode') || '0'; } catch (e) { /* */ }
        try { localStorage.setItem('vf-rentals-quote-staff-mode', current === '1' ? '0' : '1'); } catch (e) { /* */ }
        window.dispatchEvent(new CustomEvent('vf-rentals-quote-staff-toggle'));
      },
    },
    {
      // PDF Comet mode — when on, the cart's "Download quote" appends
      // an extra page with Claude-Comet-readable instructions for
      // importing the quote into Booqable (customer match, booking
      // number, items, discounts, dates). Off by default — the
      // customer-facing quote stays clean unless a staff member
      // explicitly enables it for an internal handoff PDF.
      switch: true,
      stateKey: 'vf-rentals-pdf-comet-mode',
      label: 'PDF Comet instructions',
      action: () => {
        if (typeof window === 'undefined') return;
        // Pure-toggle entry — no navigation. Flip localStorage then
        // dispatch so CartDrawer + this menu's local mirror update.
        let current = '0';
        try { current = localStorage.getItem('vf-rentals-pdf-comet-mode') || '0'; } catch (e) { /* */ }
        try { localStorage.setItem('vf-rentals-pdf-comet-mode', current === '1' ? '0' : '1'); } catch (e) { /* */ }
        window.dispatchEvent(new CustomEvent('vf-rentals-pdf-comet-toggle'));
      },
    },
    {
      // Ownership codes — when on, surfaces the admin-only ownership
      // tag (O-VR / C-XX / X-XX) as a top-left badge on every grid
      // card AND in the drawer. Off by default — the codes are useful
      // for staff but visually noisy for customers. Per-product:
      //   • owned       → O-VR (Owned by Valley Rentals)
      //   • consignment → C-XX (Consignor initials, e.g. C-PK)
      //   • cross-hire  → X-XX (Cross-hire initials, e.g. X-SB)
      switch: true,
      stateKey: 'vf-rentals-show-ownership-codes',
      label: 'Show ownership codes',
      action: () => {
        if (typeof window === 'undefined') return;
        let current = '0';
        try { current = localStorage.getItem('vf-rentals-show-ownership-codes') || '0'; } catch (e) { /* */ }
        try { localStorage.setItem('vf-rentals-show-ownership-codes', current === '1' ? '0' : '1'); } catch (e) { /* */ }
        window.dispatchEvent(new CustomEvent('vf-rentals-show-ownership-codes-toggle'));
      },
    },
    {
      // Owned kit only — hides every consignment + cross-hire line
      // from the rentals catalog so staff can scroll a clean
      // VF-owned-kit-only view. Doesn't lock the rest of the UI: search,
      // category chips, drawer all keep working. Independent of "Custom
      // quote mode" (which uses the same backing flag but also surfaces
      // the chip as a visible indicator inside the chips row). Pure
      // localStorage toggle — the EquipmentPage's filter useMemo reads
      // ownedOnly state and re-renders live.
      switch: true,
      stateKey: 'vf-rentals-owned-only',
      label: 'Owned kit only',
      action: () => {
        if (typeof window === 'undefined') return;
        let current = '0';
        try { current = localStorage.getItem('vf-rentals-owned-only') || '0'; } catch (e) { /* */ }
        try { localStorage.setItem('vf-rentals-owned-only', current === '1' ? '0' : '1'); } catch (e) { /* */ }
        window.dispatchEvent(new CustomEvent('vf-rentals-owned-only-toggle'));
      },
    },
    {
      // Internal order mode — when on, the cart morphs into an Internal
      // Order builder: header swaps to "Internal Order", an account
      // picker appears (Max Paterson / Peter Dundas / Valley Films),
      // customer fields hide, and the bottom CTAs collapse to a single
      // "Generate Comet instruction" button. Output is a one-shot
      // prompt for Claude Comet to import the order into Booqable —
      // applies the WETHIRE coupon for the 30% discount. Off by default.
      switch: true,
      stateKey: 'vf-rentals-internal-order-mode',
      label: 'Internal order mode',
      action: () => {
        if (typeof window === 'undefined') return;
        let current = '0';
        try { current = localStorage.getItem('vf-rentals-internal-order-mode') || '0'; } catch (e) { /* */ }
        try { localStorage.setItem('vf-rentals-internal-order-mode', current === '1' ? '0' : '1'); } catch (e) { /* */ }
        window.dispatchEvent(new CustomEvent('vf-rentals-internal-order-mode-toggle'));
      },
    },
  ]},
  { title: 'Films', items: [
    { keys: ['F'],            label: 'Home' },
    { keys: ['F', '2'],       chord: true, label: 'Services' },
    { keys: ['F', '3'],       chord: true, label: 'About' },
    { keys: ['F', '4'],       chord: true, label: 'Contact' },
    { keys: ['F', '5'],       chord: true, label: 'Crew' },
    { keys: ['F', '6'],       chord: true, label: 'Privacy' },
  ]},
  { title: 'Rentals', items: [
    { keys: ['R'],            label: 'Rentals home' },
    { keys: ['R', '2'],       chord: true, label: 'Equipment' },
    { keys: ['R', '3'],       chord: true, label: 'About (rentals)' },
    { keys: ['R', '4'],       chord: true, label: 'Open Account' },
    { keys: ['R', '5'],       chord: true, label: 'Careers' },
  ]},
  { title: 'Games', items: [
    { keys: ['G'],            label: 'Games landing' },
    { keys: ['G', '2'],       chord: true, label: 'Clapperboard Chaos' },
    { keys: ['G', '3'],       chord: true, label: 'Focus Fever' },
    { keys: ['G', '4'],       chord: true, label: 'Greenlight' },
  ]},
  { title: 'Actions', items: [
    { keys: ['/'],              label: 'Focus search' },
    { keys: ['⌘ K', 'Ctrl K'],  label: 'Focus search' },
    { keys: [','],              label: 'Toggle cart' },
  ]},
  { title: 'Help', items: [
    { keys: ['⌘ /', 'Ctrl /'], label: 'Show this menu' },
    { keys: ['Esc'], label: 'Close menu / drawer / modal' },
  ]},
];

// True when the user is typing into a field — we should not steal
// single-letter keystrokes from form inputs, search boxes, etc.
function isTypingTarget(target) {
  if (!target) return false;
  const tag = (target.tagName || '').toLowerCase();
  if (tag === 'input' || tag === 'textarea' || tag === 'select') return true;
  if (target.isContentEditable) return true;
  return false;
}

function ShortcutsMenu({ open, onClose }) {
  // Collect every stateKey referenced by a `switch: true` item so we
  // can mirror their current values in local state — that gives the
  // toggle pill instant visual feedback instead of waiting on a
  // localStorage round-trip from another component.
  const switchKeys = React.useMemo(() => {
    const ks = [];
    for (const g of SHORTCUT_GROUPS) for (const it of g.items) {
      if (it.switch && it.stateKey) ks.push(it.stateKey);
    }
    return ks;
  }, []);
  const readSwitches = React.useCallback(() => {
    const out = {};
    if (typeof window !== 'undefined') {
      for (const k of switchKeys) {
        try { out[k] = localStorage.getItem(k) === '1'; } catch (e) { out[k] = false; }
      }
    }
    return out;
  }, [switchKeys]);
  const [switchState, setSwitchState] = useShortcutState(readSwitches);
  // Re-sync when the menu opens — covers the case where another tab
  // or component flipped the flag while the menu was closed.
  useShortcutEffect(() => {
    if (!open) return;
    setSwitchState(readSwitches());
  }, [open, readSwitches]);
  // Listen for the same toggle event the rest of the app dispatches
  // so the switch reflects flips made from anywhere (cart toolbar
  // Exit button, Esc key in review mode, etc.). Flip the matching
  // key locally so we don't re-read localStorage mid-cycle (which
  // can lose a race against EquipmentPage's persistence effect).
  useShortcutEffect(() => {
    const onReviewToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-review-mode': !s['vf-rentals-review-mode'],
    }));
    const onQuoteStaffToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-quote-staff-mode': !s['vf-rentals-quote-staff-mode'],
    }));
    const onCometToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-pdf-comet-mode': !s['vf-rentals-pdf-comet-mode'],
    }));
    const onOwnershipCodesToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-show-ownership-codes': !s['vf-rentals-show-ownership-codes'],
    }));
    const onOwnedOnlyToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-owned-only': !s['vf-rentals-owned-only'],
    }));
    const onInternalOrderToggle = () => setSwitchState((s) => ({
      ...s,
      'vf-rentals-internal-order-mode': !s['vf-rentals-internal-order-mode'],
    }));
    window.addEventListener('vf-rentals-review-toggle', onReviewToggle);
    window.addEventListener('vf-rentals-quote-staff-toggle', onQuoteStaffToggle);
    window.addEventListener('vf-rentals-pdf-comet-toggle', onCometToggle);
    window.addEventListener('vf-rentals-show-ownership-codes-toggle', onOwnershipCodesToggle);
    window.addEventListener('vf-rentals-owned-only-toggle', onOwnedOnlyToggle);
    window.addEventListener('vf-rentals-internal-order-mode-toggle', onInternalOrderToggle);
    return () => {
      window.removeEventListener('vf-rentals-review-toggle', onReviewToggle);
      window.removeEventListener('vf-rentals-quote-staff-toggle', onQuoteStaffToggle);
      window.removeEventListener('vf-rentals-pdf-comet-toggle', onCometToggle);
      window.removeEventListener('vf-rentals-show-ownership-codes-toggle', onOwnershipCodesToggle);
      window.removeEventListener('vf-rentals-owned-only-toggle', onOwnedOnlyToggle);
      window.removeEventListener('vf-rentals-internal-order-mode-toggle', onInternalOrderToggle);
    };
  }, []);
  return (
    <div
      className={`vf-shortcuts-overlay ${open ? 'is-open' : ''}`}
      role="dialog"
      aria-modal="true"
      aria-label="Keyboard shortcuts"
      aria-hidden={!open}
      onClick={onClose}>
      <div className="vf-shortcuts-panel" onClick={(e) => e.stopPropagation()}>
        <header className="vf-shortcuts-head">
          <div className="vf-shortcuts-eyebrow">Keyboard shortcuts</div>
          <button
            type="button"
            className="vf-shortcuts-close"
            onClick={onClose}
            aria-label="Close shortcuts">×</button>
        </header>
        <div className="vf-shortcuts-grid">
          {SHORTCUT_GROUPS.map((grp) => (
            <section key={grp.title} className="vf-shortcuts-group">
              <h3>{grp.title}</h3>
              <dl>
                {grp.items.map((it) => {
                  // ─── Switch row ─────────────────────────────────
                  // Slim sliding pill (style mirrors .twk-toggle in
                  // tweaks-panel.jsx). Click anywhere on the row —
                  // pill OR label — flips it. Menu stays open after
                  // the click so consecutive toggles don't require
                  // re-opening with Cmd+/.
                  if (it.switch) {
                    const on = !!switchState[it.stateKey];
                    const handleSwitchClick = () => { if (it.action) it.action(); };
                    return (
                      <React.Fragment key={it.label}>
                        <dt className="vf-shortcuts-switch-cell">
                          <button
                            type="button"
                            className="vf-shortcuts-switch"
                            data-on={on ? '1' : '0'}
                            role="switch"
                            aria-checked={on}
                            aria-label={`${it.label} — ${on ? 'on' : 'off'}`}
                            onClick={handleSwitchClick}>
                            <i />
                          </button>
                        </dt>
                        <dd className="vf-shortcuts-switch-label" onClick={handleSwitchClick}>
                          {it.label}
                          <span className={`vf-shortcuts-state vf-shortcuts-state-${on ? 'on' : 'off'}`}>{on ? 'ON' : 'OFF'}</span>
                        </dd>
                      </React.Fragment>);
                  }
                  // ─── Action / chord row (existing behaviour) ────
                  const clickable = typeof it.action === 'function';
                  const handleClick = clickable ? () => { onClose(); it.action(); } : undefined;
                  const rowProps = clickable
                    ? { className: 'is-clickable', onClick: handleClick, role: 'button', tabIndex: 0,
                        onKeyDown: (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); handleClick(); } } }
                    : {};
                  const buttonOnly = !it.keys || it.keys.length === 0;
                  const stateText = typeof it.getState === 'function' ? it.getState() : null;
                  return (
                    <React.Fragment key={it.label + (it.keys || []).join()}>
                      <dt {...rowProps}>
                        {buttonOnly ? (
                          <span className="vf-shortcuts-run-pill">{it.pillLabel || 'Run ↗'}</span>
                        ) : (
                          it.keys.map((k, i) => (
                            <React.Fragment key={i}>
                              {i > 0 && !it.chord && <span className="vf-shortcuts-or"> or </span>}
                              <kbd>{k}</kbd>
                            </React.Fragment>)))}
                      </dt>
                      <dd {...(clickable ? { className: 'is-clickable', onClick: handleClick } : {})}>
                        {it.label}
                        {stateText && (
                          <span className={`vf-shortcuts-state vf-shortcuts-state-${stateText.toLowerCase()}`}>{stateText}</span>)}
                        {clickable && !buttonOnly && !stateText && <span className="vf-shortcuts-run" aria-hidden="true"> ↗</span>}
                      </dd>
                    </React.Fragment>);
                })}
              </dl>
            </section>))}
        </div>
        <footer className="vf-shortcuts-foot">
          Press <kbd>Esc</kbd> to close · Hidden help (<kbd>?</kbd> from anywhere)
        </footer>
      </div>
    </div>);
}

function KeyboardShortcuts({ goto }) {
  const [open, setOpen] = useShortcutState(false);
  const openRef = useShortcutRef(open);
  openRef.current = open;

  // Chord state — leader letter + buffered digits + pending timeout. Refs
  // because the keydown handler is bound once and mustn't see stale state.
  const leaderRef = useShortcutRef(null);
  const digitsRef = useShortcutRef('');
  const timerRef = useShortcutRef(null);

  const clearLeader = () => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
      timerRef.current = null;
    }
    leaderRef.current = null;
    digitsRef.current = '';
  };

  // Navigate to a target. SPA leaders (F, R) use the in-app router and
  // smooth-scroll to any hash anchor. External leaders (G) do a full
  // page load since /games/ isn't part of the React app.
  const navigateTo = (target, external) => {
    if (external) {
      window.location.href = target;
      return;
    }
    const hashIdx = target.indexOf('#');
    const path = hashIdx === -1 ? target : target.slice(0, hashIdx);
    const hash = hashIdx === -1 ? '' : target.slice(hashIdx + 1);
    const samePath = window.location.pathname === path;

    const scrollToHash = () => {
      if (!hash) return;
      const el = document.getElementById(hash);
      if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
    };

    if (samePath) {
      if (hash) scrollToHash();
      else window.scrollTo({ top: 0, behavior: 'smooth' });
    } else {
      goto(path);
      if (hash) setTimeout(scrollToHash, 80);
    }
  };

  // Resolve a chord against the leader map, with fall-back: F19 (no such
  // section) drops the trailing digit and goes to F1.
  const resolveChord = (leader, digits) => {
    const map = LEADER_MAP[leader];
    if (!map) return null;
    if (digits in map) return map[digits];
    if (digits.length > 1 && digits[0] in map) return map[digits[0]];
    return map[''] || null;
  };

  const fireChord = (leader, digits) => {
    const target = resolveChord(leader, digits);
    clearLeader();
    if (target) navigateTo(target, EXTERNAL_LEADERS.has(leader));
  };

  const startLeader = (side) => {
    // Same leader pressed twice → commit to side-home immediately.
    if (leaderRef.current === side && digitsRef.current === '') {
      fireChord(side, '');
      return;
    }
    // Switching leader mid-window → cancel old, start new.
    if (timerRef.current) clearTimeout(timerRef.current);
    leaderRef.current = side;
    digitsRef.current = '';
    timerRef.current = setTimeout(() => {
      timerRef.current = null;
      if (leaderRef.current === side) fireChord(side, '');
    }, LEADER_TIMEOUT_MS);
  };

  const addDigit = (digit) => {
    const leader = leaderRef.current;
    if (!leader) return;
    if (timerRef.current) clearTimeout(timerRef.current);

    const next = digitsRef.current + digit;
    digitsRef.current = next;

    if (next.length >= 2) {
      // Second digit completes the chord — fire immediately.
      fireChord(leader, next);
      return;
    }
    // First digit: wait briefly for a possible second digit.
    timerRef.current = setTimeout(() => {
      timerRef.current = null;
      if (leaderRef.current === leader && digitsRef.current === next) {
        fireChord(leader, next);
      }
    }, SECOND_DIGIT_TIMEOUT_MS);
  };

  // DOM-driven actions for the rentals-only widgets. Silent no-op on
  // Films pages where those elements don't exist.
  const focusSearch = () => {
    const input = document.querySelector('.rfs-bar input');
    if (input) input.focus();
  };
  const toggleCart = () => {
    const btn = document.querySelector('.rfs-cart');
    if (btn) btn.click();
  };

  // Esc closes whatever is open: shortcuts menu → leader window →
  // cart drawer → catalog drawer → video modal.
  const closeAll = () => {
    if (openRef.current) { setOpen(false); return; }
    if (leaderRef.current) { clearLeader(); return; }
    const cartClose = document.querySelector('.rcart-drawer .rcart-close, .rcart-drawer [aria-label*="lose"]');
    if (cartClose && cartClose.offsetParent !== null) { cartClose.click(); return; }
    const catalogClose = document.querySelector('.rcat-drawer-close, .rcat-drawer [aria-label*="lose"]');
    if (catalogClose && catalogClose.offsetParent !== null) { catalogClose.click(); return; }
    const videoClose = document.querySelector('.vf-modal-close, .video-modal [aria-label*="lose"]');
    if (videoClose && videoClose.offsetParent !== null) { videoClose.click(); return; }
  };

  useShortcutEffect(() => {
    const onKey = (e) => {
      // Cmd/Ctrl + `/` opens the help menu — moved off bare `?`
      // because that conflicted with typing the punctuation. The
      // modifier ensures it never fires accidentally during input.
      if ((e.metaKey || e.ctrlKey) && (e.key === '/' || e.code === 'Slash')) {
        e.preventDefault();
        clearLeader();
        setOpen((v) => !v);
        return;
      }
      // Esc closes whatever's open (including a pending leader).
      if (e.key === 'Escape') {
        closeAll();
        return;
      }
      // Cmd/Ctrl + K focuses the search.
      if ((e.metaKey || e.ctrlKey) && (e.key === 'k' || e.key === 'K')) {
        e.preventDefault();
        focusSearch();
        return;
      }
      // Never steal modifier-key combos (Cmd+R reload, Cmd+S save…).
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      // Don't steal keystrokes from form inputs.
      if (isTypingTarget(e.target)) return;
      // If the help menu is open, only the close handlers above run.
      if (openRef.current) return;

      const key = e.key.toLowerCase();

      // Active leader — consume digits, cancel on other keys.
      if (leaderRef.current) {
        if (/^[0-9]$/.test(e.key)) {
          e.preventDefault();
          addDigit(e.key);
          return;
        }
        if (key !== 'f' && key !== 'r' && key !== 'g') {
          // Non-leader, non-digit while leader is active → fire whatever
          // we have (page-only if any digits buffered, otherwise side-home)
          // and fall through so the key can still trigger its own action.
          fireChord(leaderRef.current, digitsRef.current);
        }
      }

      // Leader keys.
      if (key === 'f' || key === 'r' || key === 'g') {
        e.preventDefault();
        startLeader(key);
        return;
      }

      // Single-key utility actions.
      if (e.key === '/') {
        e.preventDefault();
        focusSearch();
        return;
      }
      if (e.key === ',') {
        e.preventDefault();
        toggleCart();
        return;
      }
    };
    window.addEventListener('keydown', onKey);
    return () => {
      window.removeEventListener('keydown', onKey);
      if (timerRef.current) clearTimeout(timerRef.current);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return <ShortcutsMenu open={open} onClose={() => setOpen(false)} />;
}

Object.assign(window, { KeyboardShortcuts });
