// chrome.jsx — Header, Footer, Wordmark, primitives

function Wordmark({ size = 19, onClick, variant = 'color' }) {
  // Signature logo image. `size` maps to rendered height for backwards compat.
  const h = Math.round(size * 1.5);
  const R = (typeof window !== 'undefined' && window.__resources) || {};
  const src = variant === 'white'
    ? (R.logoWhite || 'assets/criterionmd-logo-white.png')
    : (R.logoColor || 'assets/criterionmd-logo.png');
  return (
    <a href="/" onClick={(e) => { e.preventDefault(); onClick && onClick(); }}
       className="cmd-wordmark" aria-label="CriterionMD"
       style={{ display: 'inline-flex', alignItems: 'center', lineHeight: 0, flexShrink: 0 }}>
      <img src={src} alt="CriterionMD"
           style={{ height: h, width: 'auto', objectFit: 'contain', display: 'block', flexShrink: 0 }} />
    </a>
  );
}

function ArrowRight({ size = 14 }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none">
      <path d="M3 8h10M9 4l4 4-4 4" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

function Check({ size = 14, color = "currentColor" }) {
  return (
    <svg width={size} height={size} viewBox="0 0 16 16" fill="none">
      <path d="M3.5 8.5l3 3 6-7" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  );
}

// ── Motion primitives ───────────────────────────────────────────────────
// Springs, momentum projection and rubber-banding, after Apple's "Designing
// Fluid Interfaces": motion starts from the CURRENT on-screen value, inherits
// the gesture's velocity, and can be grabbed and reversed at any instant.
// Parameterised the way Apple asks designers to think — damping ratio (how
// much overshoot) and response (how quickly it reaches the target) — not
// mass/stiffness/damping.
function springAnimate(opts) {
  const to = opts.to;
  const damping = opts.damping == null ? 1 : opts.damping;
  const response = opts.response == null ? 0.35 : opts.response;
  const w = 2 * Math.PI / response;
  let x = opts.from, v = opts.velocity || 0;
  let raf = 0, last = performance.now(), stopped = false;
  const step = (now) => {
    if (stopped) return;
    const dt = Math.min((now - last) / 1000, 1 / 30);
    last = now;
    v += (-w * w * (x - to) - 2 * damping * w * v) * dt;
    x += v * dt;
    if (Math.abs(x - to) < 0.35 && Math.abs(v) < 25) {
      opts.onFrame(to);
      if (opts.onDone) opts.onDone();
      return;
    }
    opts.onFrame(x);
    raf = requestAnimationFrame(step);
  };
  raf = requestAnimationFrame(step);
  // Cancelling returns the live value + velocity so the next animation can
  // continue from them instead of hard-cutting (no "brick wall" on reversal).
  return function cancel() {
    stopped = true;
    cancelAnimationFrame(raf);
    return { x: x, v: v };
  };
}

// Apple's projection function (exponential decay, NOT the textbook v²/2a):
// where a flick would come to rest, so we can target that instead of the
// release point.
function projectMomentum(velocity, decelerationRate) {
  const d = decelerationRate == null ? 0.998 : decelerationRate;
  return (velocity / 1000) * d / (1 - d);
}

// Progressive resistance past a boundary — real things slow before they stop.
function rubberband(overshoot, dimension, constant) {
  const c = constant == null ? 0.55 : constant;
  return (overshoot * dimension * c) / (dimension + c * Math.abs(overshoot));
}

// rAF is NOT guaranteed: mobile Safari throttles it for offscreen/background
// content and some embedded contexts never fire it at all. So every spring
// here carries a watchdog — if no frame has been produced shortly after
// starting, snap to the target and run the completion. Motion is a bonus; a
// resting state must never depend on it. (Same standard as the hero demo's
// chained-setTimeout clock.)
function springSafe(opts) {
  let frames = 0, dog = 0, done = false;
  const finish = () => {
    if (done) return;
    done = true;
    clearTimeout(dog);
    if (opts.onDone) opts.onDone();
  };
  const cancel = springAnimate(Object.assign({}, opts, {
    onFrame: (v) => { frames++; opts.onFrame(v); },
    onDone: finish
  }));
  dog = setTimeout(() => {
    if (!done && frames === 0) { cancel(); opts.onFrame(opts.to); finish(); }
  }, 140);
  return function () { clearTimeout(dog); return cancel(); };
}

function prefersReducedMotion() {
  return typeof window !== 'undefined' && window.matchMedia &&
    window.matchMedia('(prefers-reduced-motion: reduce)').matches;
}

function Header({ route, go, authed, onLogout }) {
  const [menuOpen, setMenuOpen] = React.useState(false);
  const menuOpenRef = React.useRef(false);
  menuOpenRef.current = menuOpen;
  // Set true when the sheet closes because the user navigated, so the
  // scroll-restore below is skipped and the new page opens at the top.
  const navigatingRef = React.useRef(false);

  // The header stays pinned (sticky) at all widths — no hide-on-scroll — so
  // the wordmark and menu button are always reachable while scrolling.

  // Sheet open: lock body scroll (restore position on close), close on Esc.
  // useLayoutEffect, not useEffect — the lock has to land before the browser
  // paints, or the page visibly jumps one frame between tap and lock.
  React.useLayoutEffect(() => {
    if (!menuOpen) return;
    const y = window.scrollY;
    const b = document.body;
    b.style.position = 'fixed'; b.style.top = -y + 'px';
    b.style.left = '0'; b.style.right = '0';
    const onKey = (e) => { if (e.key === 'Escape') setMenuOpen(false); };
    window.addEventListener('keydown', onKey);
    return () => {
      b.style.position = ''; b.style.top = ''; b.style.left = ''; b.style.right = '';
      // Only restore the prior scroll position when the menu was simply
      // dismissed. On navigation, go() has already moved to the top (or an
      // anchor) — restoring here would yank the new page back down.
      if (!navigatingRef.current) window.scrollTo(0, y);
      navigatingRef.current = false;
      window.removeEventListener('keydown', onKey);
    };
  }, [menuOpen]);

  const NavLink = ({ to, children }) => (
    <a href={to === 'home' ? '/' : '/' + to} onClick={(e) => { e.preventDefault(); go(to); }}
       className={route === to ? "active" : ""}>{children}</a>
  );
  return (
    <header className="cmd-header" style={{ position: 'sticky' }}>
      <div className="cmd-container cmd-header-inner">
        <Wordmark size={34} variant="white" onClick={() => go(authed ? 'portal' : 'home')} />
        {!authed && (
          <nav className="cmd-nav">
            <NavLink to="home">Home</NavLink>
            <SolutionsNavDropdown route={route} go={go} />
            <NavLink to="insurify">Insurify <span style={{
              fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.08em',
              textTransform: 'uppercase',
              padding: '2px 6px', borderRadius: 4, marginLeft: 4,
              background: 'var(--accent-tint)', color: 'var(--accent-ink)',
              border: '1px solid var(--accent-tint-2)', verticalAlign: 'middle'
            }}>PUBLIC BETA</span></NavLink>
            <AboutNavDropdown route={route} go={go} />
            <ResourcesNavDropdown route={route} go={go} />
            <ContactNavDropdown route={route} go={go} />
          </nav>
        )}
        {authed && (
          <nav className="cmd-nav">
            <NavLink to="portal">Overview</NavLink>
            <a href="/insurify-app" onClick={(e) => { e.preventDefault(); go('insurify-app'); }}
               className={route === 'insurify-app' ? 'active' : ''}>Insurify</a>
            <a href="/portal-billing" onClick={(e) => { e.preventDefault(); go('portal-billing'); }}
               className={route === 'portal-billing' ? 'active' : ''}>Billing</a>
            <a href="/portal-team" onClick={(e) => { e.preventDefault(); go('portal-team'); }}
               className={route === 'portal-team' ? 'active' : ''}>Team</a>
          </nav>
        )}
        <div className="cmd-header-cta">
          {!authed && (
            <>
              <button className="btn btn-ghost" onClick={() => go('login')}>Log in</button>
              <button className="cmd-burger" aria-label="Menu"
                onClick={() => setMenuOpen((o) => !o)}>
                <span></span><span></span><span></span>
              </button>
            </>
          )}
          {authed && (
            <>
              <span style={{ fontSize: 13, color: 'var(--ink-3)' }}>Dr. Marin Okafor</span>
              <div style={{
                width: 32, height: 32, borderRadius: 999,
                background: 'var(--accent)', color: '#fff',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                fontSize: 12, fontWeight: 500, letterSpacing: '.02em'
              }}>MO</div>
              <button className="btn btn-sm btn-ghost" onClick={onLogout}>Log out</button>
            </>
          )}
        </div>
      </div>
      {/* Portaled to <body>: the header's backdrop-filter/transform makes it a
          containing block, which would trap this fixed sheet inside its 56px box */}
      {menuOpen && !authed && ReactDOM.createPortal(
        <MobileMenuSheet go={go} close={() => setMenuOpen(false)} navigatingRef={navigatingRef} />,
        document.body
      )}
    </header>
  );
}

// ── Mobile menu sheet — accordion groups mirroring the desktop dropdowns ────
function MobileMenuSheet({ go, close, navigatingRef }) {
  const [openGroup, setOpenGroup] = React.useState(null);
  const sheetRef = React.useRef(null);
  const yRef = React.useRef(0);            // live presentation value, px
  const springRef = React.useRef(null);
  const dragRef = React.useRef(null);
  const reduced = prefersReducedMotion();

  const setY = React.useCallback((y) => {
    yRef.current = y;
    const el = sheetRef.current;
    if (el) el.style.transform = y ? 'translate3d(0,' + y + 'px,0)' : '';
  }, []);
  // Returns the velocity the cancelled spring was carrying, so a grab
  // mid-flight continues from it rather than starting from zero.
  const stopSpring = React.useCallback(() => {
    if (!springRef.current) return 0;
    const s = springRef.current();
    springRef.current = null;
    return s.v;
  }, []);
  React.useEffect(() => () => { if (springRef.current) springRef.current(); }, []);

  const sheetH = () => {
    const el = sheetRef.current;
    return (el && el.offsetHeight) || window.innerHeight;
  };

  // Tapping ✕ retraces the entry path: the sheet slides back up behind the
  // header it came from. (A swipe instead follows the finger — see below.)
  const closeUp = () => {
    if (reduced) { close(); return; }
    const v0 = stopSpring();
    springRef.current = springSafe({
      from: yRef.current, to: -sheetH(),
      velocity: Math.min(v0, 0), damping: 1, response: 0.3,
      onFrame: setY, onDone: close
    });
  };

  // Drag to dismiss — 1:1 with the finger, reversible at any instant.
  // Only takes over when the sheet is scrolled to the top and the finger is
  // heading down, so content scrolling always wins when there is content.
  const onPointerDown = (e) => {
    if (e.pointerType === 'mouse' && e.button !== 0) return;
    const el = sheetRef.current;
    if (!el) return;
    const v0 = stopSpring();
    dragRef.current = {
      id: e.pointerId,
      // subtracting the live value respects WHERE the sheet was grabbed
      startY: e.clientY - yRef.current,
      lastY: e.clientY, lastT: performance.now(),
      v: v0, committed: false, atTop: el.scrollTop <= 0
    };
  };
  const onPointerMove = (e) => {
    const d = dragRef.current;
    if (!d || e.pointerId !== d.id) return;
    const raw = e.clientY - d.startY;
    if (!d.committed) {
      if (Math.abs(raw - yRef.current) < 10) return;   // hysteresis
      if (raw < yRef.current || !d.atTop) { dragRef.current = null; return; }
      d.committed = true;
      try { sheetRef.current.setPointerCapture(e.pointerId); } catch (err) { /* no capture */ }
      sheetRef.current.style.touchAction = 'none';
    }
    const now = performance.now();
    const dt = now - d.lastT;
    if (dt > 0) d.v = ((e.clientY - d.lastY) / dt) * 1000;   // px/s
    d.lastY = e.clientY; d.lastT = now;
    setY(raw < 0 ? -rubberband(-raw, sheetH()) : raw);
  };
  const onPointerUp = () => {
    const d = dragRef.current;
    dragRef.current = null;
    if (!d || !d.committed) return;
    if (sheetRef.current) sheetRef.current.style.touchAction = '';
    const h = sheetH();
    // Commit on the velocity's SIGN when the flick is decisive; fall back to
    // where the momentum would actually land when it is not.
    const projected = yRef.current + projectMomentum(d.v);
    const commit = d.v > 250 || (d.v > -250 && projected > h * 0.25);
    if (reduced) { setY(commit ? h : 0); if (commit) close(); return; }
    springRef.current = springSafe({
      from: yRef.current, to: commit ? h : 0, velocity: d.v,
      damping: commit ? 1 : 0.8, response: 0.3,   // Apple's drawer values
      onFrame: setY, onDone: commit ? close : null
    });
  };

  // Entry: slide down from behind the header that spawned it (anchored
  // origin), on the same spring the drag uses. springSafe's watchdog is what
  // makes this safe: if rAF never produces a frame, the sheet snaps to its
  // resting position (visible) within 140ms. The CSS resting state is
  // transform:none, so a frozen animation timeline can never hide the menu.
  React.useLayoutEffect(() => {
    if (reduced) return;
    const h = sheetH();
    setY(-h);
    springRef.current = springSafe({
      from: -h, to: 0, velocity: 0, damping: 1, response: 0.3, onFrame: setY
    });
  }, []);

  const nav = (r, anchor) => {
    if (navigatingRef) navigatingRef.current = true;
    close();
    go(r);
    if (anchor) {
      // after the route renders, scroll the anchored section into view
      setTimeout(() => {
        const el = document.getElementById(anchor);
        if (el) {
          const y = el.getBoundingClientRect().top + window.scrollY - 90;
          window.scrollTo({ top: y, behavior: 'smooth' });
        }
      }, 350);
    }
  };
  const chevron = (open) => (
    <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
      style={{ transition: 'transform .2s ease', transform: open ? 'rotate(180deg)' : 'none', opacity: 0.7 }}>
      <path d="M3 4.5l3 3 3-3" stroke="currentColor" strokeWidth="1.4"
        strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
  const GROUPS = [
    { id: 'solutions', label: 'Solutions', targetRoute: 'solutions', sections: SOLUTIONS_NAV_AUDIENCES, footerLabel: 'See all solutions' },
    { id: 'about', label: 'About Us', targetRoute: 'about', sections: ABOUT_NAV_SECTIONS, footerLabel: null },
    { id: 'resources', label: 'Resources', targetRoute: 'press', sections: RESOURCES_NAV_SECTIONS, footerLabel: null },
    { id: 'contact', label: 'Contact Us', targetRoute: 'contact', sections: CONTACT_NAV_SECTIONS, footerLabel: null },
  ];
  return (
    <nav className="cmd-mobile-menu" aria-label="Site menu" ref={sheetRef}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerUp}>
      <div style={{
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        height: 56, marginBottom: 4, flexShrink: 0
      }}>
        <Wordmark size={28} variant="white" onClick={() => nav('home')} />
        <button aria-label="Close menu" onClick={closeUp}
          style={{
            width: 44, height: 44, borderRadius: 10, flexShrink: 0,
            border: '1px solid rgba(255,255,255,0.28)', background: 'rgba(255,255,255,0.06)',
            color: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center'
          }}>
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M3 3l10 10M13 3L3 13" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
          </svg>
        </button>
      </div>

      <a href="/" onClick={(e) => { e.preventDefault(); nav('home'); }}>Home</a>

      <MobileNavGroup group={GROUPS[0]} isOpen={openGroup === 'solutions'}
        onToggle={() => setOpenGroup(openGroup === 'solutions' ? null : 'solutions')}
        nav={nav} chevron={chevron} />

      <a href="/insurify" onClick={(e) => { e.preventDefault(); nav('insurify'); }}>
        Insurify
        <span style={{
          fontFamily: 'var(--font-mono)', fontSize: 10, letterSpacing: '.08em',
          textTransform: 'uppercase',
          padding: '2px 6px', borderRadius: 4, marginLeft: 8,
          background: 'rgba(255,255,255,0.12)', color: '#B9E8CD',
          border: '1px solid rgba(255,255,255,0.24)', verticalAlign: 'middle'
        }}>PUBLIC BETA</span>
      </a>

      {GROUPS.slice(1).map((g) => (
        <MobileNavGroup key={g.id} group={g} isOpen={openGroup === g.id}
          onToggle={() => setOpenGroup(openGroup === g.id ? null : g.id)}
          nav={nav} chevron={chevron} />
      ))}

      <button className="btn btn-lg"
        style={{ background: '#fff', color: 'var(--accent-ink)' }}
        onClick={() => nav('login')}>
        Log in
      </button>
    </nav>
  );
}

// One accordion group in the mobile sheet. The expanded panel is the same
// paper card as the desktop dropdown: identical section rows, sub-item rows,
// product logos, status pills, and footer link.
function MobileNavGroup({ group, isOpen, onToggle, nav, chevron }) {
  const [expanded, setExpanded] = React.useState(null);
  return (
    <div className="cmd-mm-group">
      <a href={'/' + group.targetRoute}
        className={'cmd-mm-toplink' + (isOpen ? ' is-open' : '')}
        aria-expanded={isOpen}
        onClick={(e) => { e.preventDefault(); onToggle(); }}>
        <span>{group.label}</span>
        {chevron(isOpen)}
      </a>
      {isOpen && (
        <div className="cmd-mm-panel">
          {group.sections.map((sec) => {
            if (sec.route && !sec.items) {
              return (
                <button key={sec.id} className="cmd-nav-audience" onClick={() => nav(sec.route)}>
                  <div style={{ minWidth: 0 }}>
                    <div className="cmd-nav-audience-label">{sec.label}</div>
                    {sec.sub && <div className="cmd-nav-audience-sub">{sec.sub}</div>}
                  </div>
                  <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
                    style={{ color: 'var(--ink-4)', flexShrink: 0 }}>
                    <path d="M3 6h6M7 3l3 3-3 3" stroke="currentColor" strokeWidth="1.4"
                      strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </button>
              );
            }
            const secOpen = expanded === sec.id;
            return (
              <div key={sec.id}>
                <button className={'cmd-nav-audience' + (secOpen ? ' is-open' : '')}
                  onClick={() => setExpanded(secOpen ? null : sec.id)}
                  aria-expanded={secOpen}>
                  <div style={{ minWidth: 0 }}>
                    <div className="cmd-nav-audience-label">{sec.label}</div>
                    {sec.sub && <div className="cmd-nav-audience-sub">{sec.sub}</div>}
                  </div>
                  <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
                    style={{
                      transition: 'transform .2s ease',
                      transform: secOpen ? 'rotate(90deg)' : 'rotate(0deg)',
                      color: 'var(--ink-3)', flexShrink: 0
                    }}>
                    <path d="M4.5 3l3 3-3 3" stroke="currentColor" strokeWidth="1.4"
                      strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </button>
                {secOpen && (
                  <div className="cmd-nav-sublist">
                    {sec.items.map((item) => {
                      const clickable = item.status !== 'soon';
                      const Tag = clickable ? 'button' : 'div';
                      return (
                        <Tag key={item.name}
                          className={'cmd-nav-product' + (clickable ? '' : ' is-static')}
                          {...(clickable
                            ? { onClick: () => nav(item.route, item.anchor) }
                            : { 'aria-disabled': true })}>
                          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
                            {item.productKey && <ProductLogo k={item.productKey} />}
                            <span className="cmd-nav-product-name">{item.name}</span>
                          </span>
                          {item.status && (
                            <span className={'cmd-nav-status ' + item.status}>
                              {item.status === 'beta' ? 'Public beta' : 'In development'}
                            </span>
                          )}
                        </Tag>
                      );
                    })}
                  </div>
                )}
              </div>
            );
          })}
          {group.footerLabel && (
            <div className="cmd-nav-footer">
              <a href={'/' + group.targetRoute}
                onClick={(e) => { e.preventDefault(); nav(group.targetRoute); }}>
                {group.footerLabel} <ArrowRight />
              </a>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

function Footer({ go, route }) {
  return (
    <footer className="cmd-footer">
      <div className="cmd-container">
        <div style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          gap: 24, flexWrap: 'wrap'
        }}>
          <Wordmark size={20} onClick={() => go('home')} />
          <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap', fontSize: 13, color: 'var(--ink)' }}>
            {route === 'home' &&
            <span className="footer-disclaimer footer-disc-inline" style={{ fontSize: 11.5, color: 'var(--ink)', whiteSpace: 'nowrap' }}>
              *Where our physician design partners practice; no endorsement implied.
            </span>
            }
            <a href="/terms" onClick={(e) => { e.preventDefault(); go('terms'); }} style={{ color: 'var(--ink)' }}>Terms</a>
            <a href="/privacy" onClick={(e) => { e.preventDefault(); go('privacy'); }} style={{ color: 'var(--ink)' }}>Privacy</a>
            <span className="footer-copyright">© 2026 CriterionMD, Inc.</span>
            <span className="mono footer-city" style={{ fontSize: 12, letterSpacing: '.06em' }}>NEW YORK, NY</span>
          </div>
        </div>
        {route === 'home' &&
        <div className="footer-disclaimer footer-disc-below" style={{ display: 'none' }}>
          *Where our physician design partners practice; no endorsement implied.
        </div>
        }
      </div>
    </footer>
  );
}

// Compact label pair used throughout
function Stat({ label, value, hint }) {
  return (
    <div>
      <div style={{ fontSize: 35, fontWeight: 500, letterSpacing: '-0.03em', lineHeight: 1, color: 'var(--accent)' }}>{value}</div>
      <div className="eyebrow" style={{ marginTop: 14 }}>{label}</div>
      {hint && <div style={{ fontSize: 15, color: 'var(--ink-3)', marginTop: 10, lineHeight: 1.5 }}>{hint}</div>}
    </div>
  );
}

// Small product logo tile for the Solutions nav dropdown. Uses ProductMark and
// PRODUCT_COLORS exported from pages-marketing.jsx (available on window at render
// time even though chrome.jsx loads first).
function ProductLogo({ k, size = 22 }) {
  const colors = (typeof window !== 'undefined' && window.PRODUCT_COLORS) || {};
  const Mark = typeof window !== 'undefined' && window.ProductMark;
  const c = colors[k] || { tile: 'var(--accent)' };
  // ScribeAware's logo is a colored image with transparency — show it on a light
  // tile (and nearly fill it) instead of the dark product tile other glyphs use.
  const isImg = k === 'notes';
  return (
    <span style={{
      width: size, height: size, borderRadius: 6, flexShrink: 0,
      background: isImg ? 'var(--paper-2)' : c.tile,
      border: isImg ? '1px solid var(--line)' : 'none',
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      boxShadow: isImg ? 'none' : '0 1px 2px rgba(15,20,20,.18)',
      overflow: 'hidden'
    }}>
      {Mark ? <Mark k={k} size={isImg ? size - 4 : size - 8} /> : null}
    </span>
  );
}

// ── Nav dropdown (shared by Solutions and About) ─────────────────────────────
// Hover opens a panel; clicking a section either navigates directly (if the
// section has its own `route`) or expands inline to reveal sub-items (if the
// section has `items`). Each sub-item can carry an optional status pill.
function NavDropdown({ label, targetRoute, currentRoute, go, sections, footerLabel, columns = 1 }) {
  const [open, setOpen] = React.useState(false);
  // `closing` keeps the panel mounted long enough to animate OUT along the
  // path it came in on — previously it entered with motion and then simply
  // vanished.
  const [closing, setClosing] = React.useState(false);
  const closingTimer = React.useRef(null);
  const dismiss = React.useCallback(() => {
    setOpen((wasOpen) => {
      if (wasOpen && !prefersReducedMotion()) {
        setClosing(true);
        clearTimeout(closingTimer.current);
        closingTimer.current = setTimeout(() => setClosing(false), 130);
      }
      return false;
    });
  }, []);
  React.useEffect(() => () => clearTimeout(closingTimer.current), []);
  const [expanded, setExpanded] = React.useState(null);
  const closeTimer = React.useRef(null);
  const rootRef = React.useRef(null);
  const touchRef = React.useRef(false);

  const openMenu = () => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    clearTimeout(closingTimer.current);
    setClosing(false);
    setOpen(true);
  };
  const scheduleClose = () => {
    closeTimer.current = setTimeout(() => {
      dismiss();
      setExpanded(null);
    }, 220);
  };
  const navigate = (r) => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    setOpen(false);
    setExpanded(null);
    go(r);
  };

  React.useEffect(() => {
    if (!open) return;
    const onKey = (e) => { if (e.key === 'Escape') { dismiss(); setExpanded(null); } };
    // Tap-away close for touch (no mouseleave on touch devices)
    const onDown = (e) => {
      if (rootRef.current && !rootRef.current.contains(e.target)) {
        dismiss(); setExpanded(null);
      }
    };
    window.addEventListener('keydown', onKey);
    document.addEventListener('pointerdown', onDown);
    return () => {
      window.removeEventListener('keydown', onKey);
      document.removeEventListener('pointerdown', onDown);
    };
  }, [open]);

  return (
    <div
      ref={rootRef}
      onMouseEnter={openMenu}
      onMouseLeave={scheduleClose}
      style={{ position: 'relative', display: 'inline-flex', alignItems: 'center' }}>
      <a href={'/' + targetRoute}
         onTouchStart={() => { touchRef.current = true; }}
         onClick={(e) => {
           e.preventDefault();
           // On touch: first tap opens the menu, second tap (or item tap) navigates
           if (touchRef.current && !open) { openMenu(); return; }
           navigate(targetRoute);
         }}
         className={currentRoute === targetRoute ? 'active' : ''}
         style={{ display: 'inline-flex', alignItems: 'center', gap: 4 }}>
        {label}
        <svg width="10" height="10" viewBox="0 0 12 12" fill="none"
             style={{
               transition: 'transform .2s ease',
               transform: open ? 'rotate(180deg)' : 'rotate(0deg)',
               opacity: 0.7
             }}>
          <path d="M3 4.5l3 3 3-3" stroke="currentColor" strokeWidth="1.4"
                strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </a>

      {(open || closing) && (
        <div role="menu"
             aria-hidden={closing ? 'true' : undefined}
             style={{
               position: 'absolute', top: 'calc(100% + 14px)',
               left: columns === 2 ? 'auto' : -20,
               right: columns === 2 ? -20 : 'auto',
               width: columns === 2 ? 520 : 320,
               background: 'var(--paper)',
               border: '1px solid var(--line)',
               borderRadius: 14,
               padding: 6,
               boxShadow:
                 '0 1px 0 rgba(15,20,20,.04), 0 24px 60px -16px rgba(15,20,20,.22)',
               zIndex: 100,
               // scale from the nav item that spawned it, not from the panel's
               // own centre — the spatial relationship stays legible
               transformOrigin: columns === 2 ? 'top right' : 'top left',
               pointerEvents: closing ? 'none' : undefined,
               animation: closing
                 ? 'cmdNavFadeOut .13s cubic-bezier(.4,0,1,1) both'
                 : 'cmdNavFadeIn .16s cubic-bezier(.2,.7,.2,1) both'
             }}>
          <style>{`
            /* The 0% state deliberately does NOT include opacity: 0. If the
               document animation timeline is frozen, an animation holds its 0%
               values indefinitely — an invisible 0% would mean a permanently
               invisible dropdown. Transform-only keeps it readable either way.
               (The exit is safe because the unmount is timer-driven.) */
            @keyframes cmdNavFadeIn {
              0%   { transform: translateY(-6px) scale(.985); }
              100% { transform: translateY(0) scale(1); }
            }
            /* Exit mirrors the entry path (inverse curve), so the panel
               retreats to the trigger it grew from. */
            @keyframes cmdNavFadeOut {
              0%   { opacity: 1; transform: translateY(0) scale(1); }
              100% { opacity: 0; transform: translateY(-6px) scale(.985); }
            }
            @keyframes cmdNavSubIn {
              0%   { transform: translateY(-2px); }
              100% { transform: translateY(0); }
            }
            .cmd-nav-audience {
              all: unset;
              display: flex; align-items: center; justify-content: space-between;
              width: 100%; box-sizing: border-box;
              padding: 12px 14px; border-radius: 10px;
              cursor: pointer; gap: 12px;
              transition: background .15s ease;
            }
            .cmd-nav-audience:hover { background: var(--paper-2); }
            .cmd-nav-audience.is-open { background: var(--paper-2); }
            .cmd-nav-audience-label {
              font-size: 14px; font-weight: 500; color: var(--ink);
              letter-spacing: -0.005em;
            }
            .cmd-nav-audience-sub {
              font-size: 12px; color: var(--ink-3); margin-top: 2px;
              line-height: 1.35;
            }
            .cmd-nav-product {
              all: unset; box-sizing: border-box;
              display: flex; align-items: center; justify-content: space-between;
              width: 100%;
              padding: 8px 12px 8px 14px;
              border-radius: 8px;
              cursor: pointer; gap: 12px;
              transition: background .15s ease;
              position: relative;
            }
            .cmd-nav-product::before {
              content: ""; position: absolute;
              left: 0; top: 12px; bottom: 12px; width: 2px;
              background: var(--line); border-radius: 2px;
            }
            .cmd-nav-product:hover { background: var(--paper-2); }
            .cmd-nav-product:hover::before { background: var(--accent); }
            .cmd-nav-product.is-static { cursor: default; }
            .cmd-nav-product.is-static:hover { background: transparent; }
            .cmd-nav-product.is-static:hover::before { background: var(--line); }
            .cmd-nav-product.is-static .cmd-nav-product-name { color: var(--ink-3); }
            .cmd-nav-product-name {
              font-size: 13.5px; font-weight: 450; color: var(--ink);
              letter-spacing: -0.005em;
            }
            .cmd-nav-status {
              font-family: var(--font-mono);
              font-size: 10px; letter-spacing: .14em; font-weight: 500;
              padding: 3px 7px; border-radius: 4px; white-space: nowrap;
              text-transform: uppercase;
            }
            .cmd-nav-status.beta {
              background: var(--accent-tint); color: var(--accent-ink);
              border: 1px solid var(--accent-tint-2);
            }
            .cmd-nav-status.soon {
              background: var(--paper-2); color: var(--ink-3);
              border: 1px solid var(--line);
            }
            .cmd-nav-sublist {
              padding: 4px 10px 8px 28px;
              overflow: hidden;
              animation: cmdNavSubIn .22s cubic-bezier(.2,.7,.2,1) both;
            }
            .cmd-nav-footer {
              border-top: 1px solid var(--line);
              margin-top: 4px; padding: 10px 14px 8px;
              display: flex; justify-content: space-between; align-items: center;
            }
            .cmd-nav-footer a {
              font-size: 13px; font-weight: 500; color: var(--accent);
              display: inline-flex; align-items: center; gap: 6px;
            }
          `}</style>

          <div style={columns === 2 ? {
            display: 'grid', gridTemplateColumns: '1fr 1fr',
            gap: 2, alignItems: 'start'
          } : undefined}>
          {sections.map(sec => {
            // Direct-nav section (no sub-items)
            if (sec.route && !sec.items) {
              return (
                <button key={sec.id}
                        className="cmd-nav-audience"
                        onClick={() => navigate(sec.route)}>
                  <div style={{ minWidth: 0 }}>
                    <div className="cmd-nav-audience-label">{sec.label}</div>
                    {sec.sub && <div className="cmd-nav-audience-sub">{sec.sub}</div>}
                  </div>
                  <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
                       style={{ color: 'var(--ink-4)', flexShrink: 0 }}>
                    <path d="M3 6h6M7 3l3 3-3 3" stroke="currentColor" strokeWidth="1.4"
                          strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                </button>
              );
            }
            // Expandable section with sub-items
            const isOpen = expanded === sec.id;
            return (
              <div key={sec.id}>
                <button
                  className={'cmd-nav-audience' + (isOpen ? ' is-open' : '')}
                  onMouseEnter={() => setExpanded(sec.id)}
                  onClick={() => setExpanded(isOpen ? null : sec.id)}
                  aria-expanded={isOpen}>
                  <div style={{ minWidth: 0 }}>
                    <div className="cmd-nav-audience-label">{sec.label}</div>
                    {sec.sub && <div className="cmd-nav-audience-sub">{sec.sub}</div>}
                  </div>
                  <svg width="12" height="12" viewBox="0 0 12 12" fill="none"
                       style={{
                         transition: 'transform .2s ease',
                         transform: isOpen ? 'rotate(90deg)' : 'rotate(0deg)',
                         color: 'var(--ink-3)', flexShrink: 0
                       }}>
                    <path d="M4.5 3l3 3-3 3" stroke="currentColor" strokeWidth="1.4"
                          strokeLinecap="round" strokeLinejoin="round"/>
                  </svg>
                </button>
                {isOpen && (
                  <div className="cmd-nav-sublist">
                    {sec.items.map(item => {
                      const clickable = item.status !== 'soon';
                      const Tag = clickable ? 'button' : 'div';
                      return (
                      <Tag key={item.name}
                              className={'cmd-nav-product' + (clickable ? '' : ' is-static')}
                              {...(clickable
                                ? { onClick: () => navigate(item.route) }
                                : { 'aria-disabled': true })}>
                        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 10, minWidth: 0 }}>
                          {item.productKey && <ProductLogo k={item.productKey} />}
                          <span className="cmd-nav-product-name">{item.name}</span>
                        </span>
                        {item.status && (
                          <span className={'cmd-nav-status ' + item.status}>
                            {item.status === 'beta' ? 'Public beta' : 'In development'}
                          </span>
                        )}
                      </Tag>
                      );
                    })}
                  </div>
                )}
              </div>
            );
          })}
          </div>

          {footerLabel !== null && (
            <div className="cmd-nav-footer">
              <a href={'/' + targetRoute}
                 onClick={(e) => { e.preventDefault(); navigate(targetRoute); }}>
                {footerLabel || `See ${label.toLowerCase()}`} <ArrowRight />
              </a>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

// ── Solutions nav dropdown ───────────────────────────────────────────────────
const SOLUTIONS_NAV_AUDIENCES = [
  {
    id: 'physicians',
    label: 'Physicians',
    sub: 'For individual clinicians and APPs',
    items: [
      { name: 'Insurify',      productKey: 'insurify',     route: 'insurify',  status: 'beta' },
      { name: 'Code QuickRef', productKey: 'codequickref', route: 'solutions', anchor: 'code-quickref', status: 'beta' },
      { name: 'ScribeAware',   productKey: 'notes',        route: 'solutions', status: 'soon' }
    ]
  },
  {
    id: 'practices',
    label: 'Office Practices',
    sub: 'For procedural-specialty practices',
    items: [
      { name: 'Greenlight', productKey: 'authdesk', route: 'solutions', status: 'soon' },
      { name: 'Reckon',     productKey: 'rcmiq',    route: 'solutions', status: 'soon' },
      { name: 'Rebound',    productKey: 'denials',  route: 'solutions', status: 'soon' }
    ]
  },
  {
    id: 'enterprise',
    label: 'Enterprise',
    sub: 'For multi-practice and health system rollouts',
    items: [
      { name: 'Sentinel', productKey: 'policy',   route: 'solutions', status: 'soon' },
      { name: 'Reckon',   productKey: 'rcmiq',    route: 'solutions', status: 'soon' },
      { name: 'Rebound',  productKey: 'denials',  route: 'solutions', status: 'soon' }
    ]
  }
];

function SolutionsNavDropdown({ route, go }) {
  return (
    <NavDropdown
      label="Solutions"
      targetRoute="solutions"
      currentRoute={route}
      go={go}
      sections={SOLUTIONS_NAV_AUDIENCES}
      footerLabel="See all solutions" />
  );
}

// ── About nav dropdown ───────────────────────────────────────────────────────
const ABOUT_NAV_SECTIONS = [
  {
    id: 'leadership',
    label: 'Executive Leadership',
    sub: 'Founders and the team',
    route: 'about'
  },
  {
    id: 'advisory',
    label: 'Advisory Boards',
    sub: 'Physician and general advisors',
    items: [
      { name: 'Physician Advisory Board', route: 'physician-board' },
      { name: 'General Advisory Board',   route: 'general-board' }
    ]
  }
];

function AboutNavDropdown({ route, go }) {
  const ABOUT_ROUTES = ['about', 'physician-board', 'general-board'];
  const currentRoute = ABOUT_ROUTES.includes(route) ? 'about' : route;
  return (
    <NavDropdown
      label="About Us"
      targetRoute="about"
      currentRoute={currentRoute}
      go={go}
      sections={ABOUT_NAV_SECTIONS}
      columns={1}
      footerLabel={null} />
  );
}

// ── Resources nav dropdown ───────────────────────────────────────────────────
const RESOURCES_NAV_SECTIONS = [
  {
    id: 'press',
    label: 'Press Releases',
    sub: 'Company news and announcements',
    route: 'press'
  },
  {
    id: 'legal',
    label: 'Privacy & Security',
    sub: 'Compliance posture and policies',
    items: [
      { name: 'Security overview', route: 'security' },
      { name: 'Privacy',           route: 'privacy'  },
      { name: 'Terms of Use',      route: 'terms'    }
    ]
  }
];

function ResourcesNavDropdown({ route, go }) {
  const RESOURCE_ROUTES = ['press', 'security', 'privacy', 'terms'];
  const currentRoute = RESOURCE_ROUTES.includes(route) ? 'press' : route;
  return (
    <NavDropdown
      label="Resources"
      targetRoute="press"
      currentRoute={currentRoute}
      go={go}
      sections={RESOURCES_NAV_SECTIONS}
      columns={1}
      footerLabel={null} />
  );
}

// ── Contact nav dropdown ─────────────────────────────────────────────────────
const CONTACT_NAV_SECTIONS = [
  {
    id: 'demos',
    label: 'Product Demos & Pilots',
    sub: 'For physicians and practices',
    route: 'contact-demos'
  },
  {
    id: 'press',
    label: 'Media Inquiries',
    sub: 'Editorial and journalist inquiries',
    route: 'contact-media'
  },
  {
    id: 'investors',
    label: 'Investor Relations',
    sub: 'Updates and inquiries',
    route: 'contact-investors'
  },
  {
    id: 'development',
    label: 'Product Development',
    sub: 'Co-develop with our team',
    route: 'contact-development'
  },
  {
    id: 'careers',
    label: 'Careers',
    sub: 'Join the team',
    route: 'contact-careers'
  }
];

function ContactNavDropdown({ route, go }) {
  const CONTACT_ROUTES = ['contact', 'contact-demos', 'contact-media', 'contact-investors', 'contact-development', 'contact-careers'];
  const currentRoute = CONTACT_ROUTES.includes(route) ? 'contact' : route;
  return (
    <NavDropdown
      label="Contact Us"
      targetRoute="contact"
      currentRoute={currentRoute}
      go={go}
      sections={CONTACT_NAV_SECTIONS}
      columns={2} />
  );
}

// ── Sticky bottom CTA bar (mobile, Home + Insurify) ─────────────────────────
// Slides up after the hero, hides on scroll-down, shows on scroll-up, and
// stays away within 600px of the document end so it never covers the footer.
function StickyCtaBar({ go }) {
  const [visible, setVisible] = React.useState(false);
  React.useEffect(() => {
    let lastY = window.scrollY, raf = 0;
    const update = () => {
      const y = window.scrollY;
      const pastHero = y > 560;
      const nearBottom = y + window.innerHeight >
        document.documentElement.scrollHeight - 600;
      if (!pastHero || nearBottom || y > lastY + 4) setVisible(false);
      else if (y < lastY - 4) setVisible(true);
      lastY = y;
    };
    const onScroll = () => { cancelAnimationFrame(raf); raf = requestAnimationFrame(update); };
    window.addEventListener('scroll', onScroll, { passive: true });
    return () => { window.removeEventListener('scroll', onScroll); cancelAnimationFrame(raf); };
  }, []);
  return (
    <div className={'cmd-sticky-cta' + (visible ? ' is-visible' : '')} aria-hidden={!visible}>
      <button className="btn" tabIndex={visible ? 0 : -1} onClick={() => go('access')}>
        Get Insurify free
      </button>
    </div>
  );
}

Object.assign(window, { Wordmark, Header, Footer, ArrowRight, Check, Stat, StickyCtaBar });
