// HeroAnimated v4 — clean enterprise "liquid glass" hero
// Brand: ABGS blue (#1E57D6 primary, #1846B0 dark)
// White background, blue ambient glows, glass orb (mix-blend-screen + CSS filter),
// large translucent orbit card that auto-flips to quote (pauses on hover).
const { useState: useStateH, useRef: useRefH, useEffect: useEffectH } = React;

const ORB_URL = 'uploads/optimized/im-orb.mp4';

// Step 2 — who is shipping. Used to tailor the quote (B2B vs personal flow).
const AUDIENCES = [
  { key: 'business', icon: 'warehouse', name: 'Business',   tag: 'For companies',
    desc: 'Cargo, inventory and product shipments. Volume rates and credit terms.' },
  { key: 'personal', icon: 'users',     name: 'Individual', tag: 'For people',
    desc: 'Household goods, personal effects and one-off shipments. Door-to-door.' },
];

// Step 3 — service catalog (transport modes we currently support).
const SERVICES = [
  { key: 'FCL',     icon: 'ship',      name: 'Ocean FCL',   tag: 'Full container',      desc: 'Direct carrier contracts on every lane.' },
  { key: 'LCL',     icon: 'container', name: 'Ocean LCL',   tag: 'Less than container', desc: 'Consolidated shipments of any volume.' },
  { key: 'Air',     icon: 'plane',     name: 'Air Freight', tag: 'Time-critical',       desc: 'Expedited and standard air worldwide.' },
  { key: 'Ground',  icon: 'truck',     name: 'Ground',      tag: 'Road network',        desc: 'FTL, LTL and intermodal drayage.' },
  { key: 'Courier', icon: 'box',       name: 'Courier',     tag: 'Door-to-door',        desc: 'DHL, FedEx, UPS and USPS rates.' },
];

function HeroAnimated({ tweaks }) {
  const [mode, setMode]         = useStateH('FCL');
  const [audience, setAudience] = useStateH(null);
  const [step, setStep]         = useStateH('audience'); // 'audience' | 'service' | 'quote' | 'results'
  const [flipped, setFlipped] = useStateH(false);
  const [paused, setPaused] = useStateH(false);
  const [pulseTick, setPulseTick] = useStateH(0);   // re-triggers halo on click
  // Lifted from QuoteGlass so the dedicated Step-5 ResultsGlass can render the
  // rates and the user can round-trip "Edit search" without losing form state.
  const [results, setResults] = useStateH(null);
  const [searchSummary, setSearchSummary] = useStateH(null);
  // Once the user clicks "Get instant quote" we kill the auto-flip preview
  // permanently for the session. Flipping back to the OrbitTrigger pill mid-
  // flow (or worse, mid-results) wipes their progress.
  const [engaged, setEngaged] = useStateH(false);
  const orbCardRef = useRefH(null);
  const openQuote = () => {
    // Always start a fresh flow at step 2 (audience).
    setStep('audience');
    setAudience(null);
    setMode('FCL');
    setResults(null);
    setSearchSummary(null);
    setFlipped(true);
    setPaused(true);
    setEngaged(true);
    setPulseTick(t => t + 1);
    if (orbCardRef.current) {
      const r = orbCardRef.current.getBoundingClientRect();
      // Only scroll if the card is partially off-screen
      if (r.top < 80 || r.bottom > window.innerHeight) {
        window.scrollTo({ top: window.scrollY + r.top - 120, behavior: 'smooth' });
      }
    }
  };
  const closeFlow = () => {
    setFlipped(false); setPaused(true);
    setStep('audience'); setResults(null); setSearchSummary(null);
    setEngaged(false);
  };

  // Auto-flip preview every 6s — but only while the user hasn't engaged yet.
  // Once they click the CTA (engaged=true), we never auto-flip again, so the
  // flow card / results page can't collapse back to the pill.
  useEffectH(() => {
    if (paused || engaged) return;
    const t = setInterval(() => setFlipped(f => !f), 6500);
    return () => clearInterval(t);
  }, [paused, engaged]);

  // Expose a small global handle so CTAs anywhere on Homepage (FinalCTA,
  // SiteNav, mobile nav) can open the quote widget without prop-drilling.
  // Cross-page CTAs use the URL-param effect below instead.
  useEffectH(() => {
    window.__abgsHomepageQuote = {
      open: (opts) => {
        opts = opts || {};
        if (opts.source) window.__abgsHomepageQuoteAttribution = opts.source;
        if (opts.audience && AUDIENCES.find(a => a.key === opts.audience)) {
          setAudience(opts.audience);
        }
        if (opts.svc) {
          const map = (window.__abgsCTA && window.__abgsCTA.SVC_FROM_SLUG) || {};
          const mode = map[opts.svc] || (SERVICES.find(s => s.key === opts.svc)
            ? opts.svc : null);
          if (mode) setMode(mode);
        }
        openQuote();
      },
    };
    return () => {
      if (window.__abgsHomepageQuote && window.__abgsHomepageQuote.open === undefined) return;
      delete window.__abgsHomepageQuote;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  // On mount, if the URL has ?openQuote=1 (set by cross-page CTAs via
  // cta-actions.js), auto-open the widget — preserving any svc/audience
  // hints, then strip the params so refresh doesn't re-open.
  useEffectH(() => {
    const p = new URLSearchParams(location.search);
    if (p.get('openQuote') !== '1') return;
    const src = p.get('src');
    const aud = p.get('aud');
    const svcParam = p.get('svc');
    if (src) window.__abgsHomepageQuoteAttribution = src;

    // Wait for the orb card to mount before scrolling.
    requestAnimationFrame(() => {
      if (aud && AUDIENCES.find(a => a.key === aud)) setAudience(aud);
      if (svcParam) {
        const map = (window.__abgsCTA && window.__abgsCTA.SVC_FROM_SLUG) || {};
        const mode = map[svcParam] || (SERVICES.find(s => s.key === svcParam)
          ? svcParam : null);
        if (mode) setMode(mode);
      }
      openQuote();
    });

    // Strip the open-trigger params so a refresh stays put.
    try {
      const url = new URL(location.href);
      ['openQuote', 'src', 'svc', 'aud'].forEach(k => url.searchParams.delete(k));
      // Keep #hero-quote so the section anchor still works.
      history.replaceState({}, '', url.pathname + (url.search ? url.search : '') + '#hero-quote');
    } catch (e) { /* harmless */ }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return (
    <section id="hero-quote" className="abgs-hero-section"
      style={{ position: 'relative', overflow: 'hidden',
      background: '#ffffff', borderBottom: '1px solid #eef0f3', scrollMarginTop: 80 }}>
      {/* Responsive overrides — at ≤768px the desktop 2-col hero collapses
          to a single column so the quote widget stacks below the headline
          and stays usable on phones. The animated globe + glass card still
          render but at a friendlier height. */}
      <style>{`
        @media (max-width: 768px) {
          .abgs-hero-inner { padding: 12px 16px 48px !important; }
          .abgs-hero-grid {
            grid-template-columns: minmax(0, 1fr) !important;
            gap: 24px !important;
            min-height: 0 !important;
          }
          .abgs-hero-grid > * { min-width: 0 !important; }
          .abgs-hero-orb { max-width: 100% !important; }
          /* Center-align the LEFT column content on mobile so the
             headline, description, CTA row, rating pill and trust
             badges all read as a polished hero stack. */
          .abgs-hero-grid > div:first-child { text-align: center !important; }
          .abgs-hero-grid > div:first-child > h1 { text-align: center !important; }
          .abgs-hero-grid > div:first-child > p {
            margin-left: auto !important;
            margin-right: auto !important;
            text-align: center !important;
          }
          .abgs-hero-grid > div:first-child > div {
            justify-content: center !important;
            flex-wrap: wrap !important;
          }
          .abgs-hero-grid > div:first-child > div[class*="reveal delay-3"] {
            margin-left: auto !important;
            margin-right: auto !important;
          }
          .abgs-hero-grid > div:first-child > div[class*="reveal delay-4"] {
            justify-content: center !important;
            gap: 16px !important;
            row-gap: 10px !important;
          }
          .abgs-hero-grid h1 { font-size: 44px !important; line-height: 1.05 !important; }
          .abgs-hero-grid p  { font-size: 16px !important; }
          .abgs-hero-orb {
            min-height: 0 !important;
            aspect-ratio: auto !important;
            height: auto !important;
          }
          /* Hide the floating-earth video on mobile — with the form
             flowing in normal layout below the headline, the orb has
             no role as a backdrop and would just leave an orphan video
             with empty space underneath the form. */
          .abgs-hero-orb > video { display: none !important; }
          /* On mobile let the glass card flow in normal layout so the
             section grows with form content (service list, multi-item
             quote rows, etc.) instead of getting clipped by the orb's
             fixed height. */
          .abgs-hero-card {
            position: relative !important;
            inset: auto !important;
            transform: none !important;
            padding: 0 !important;
            width: 100% !important;
          }
          /* Inner card wrapper: cap to viewport width minus the section's
             horizontal padding so the form never overflows the screen. */
          .abgs-hero-card > div {
            max-width: 100% !important;
            width: 100% !important;
          }
          /* Disable the 3D flip on mobile and let the back face render
             in normal flow so the form (service list, multi-item quote,
             pickup/delivery rows) can grow the section naturally
             instead of being clipped by the orb's fixed height.
             Transitions are killed so the flip can't render an
             interpolated mirrored state mid-animation on smaller devices. */
          .abgs-hero-flipper,
          .abgs-hero-flipper * {
            transform: none !important;
            transform-style: flat !important;
            perspective: none !important;
            transition: none !important;
            backface-visibility: visible !important;
            -webkit-backface-visibility: visible !important;
          }
          .abgs-hero-flipper[data-flipped="true"] .abgs-hero-front {
            display: none !important;
          }
          .abgs-hero-flipper[data-flipped="false"] .abgs-hero-back {
            display: none !important;
          }
          .abgs-hero-flipper[data-flipped="true"] .abgs-hero-back {
            position: relative !important;
            top: auto !important;
            left: auto !important;
            right: auto !important;
          }
          /* Quote-widget grids stay multi-column on mobile (mobile.css's
             generic [style*="grid-template-columns"] rule would otherwise
             collapse them to 1-col). */
          /* Lane grid: collapse to a single column and reorder so each
             column "stacks" — From → Pickup, then To → Delivery. Hide
             the swap button (no adjacent gap to overlay anymore). */
          [data-quote-row="lane-grid"] {
            grid-template-columns: 1fr !important;
            grid-template-areas: "from" "to" !important;
            gap: 8px !important;
          }
          [data-quote-row="lane-grid"][data-has-transport="true"] {
            grid-template-areas:
              "from"
              "pickup"
              "to"
              "delivery" !important;
          }
          [data-swap-btn] { display: none !important; }
          [data-quote-row="zip"] {
            grid-template-columns: 1fr 1fr !important;
            gap: 6px !important;
          }
          [data-quote-row="zip-quote"] {
            gap: 6px !important;
          }
          /* Items header on mobile: title centers above the toggles,
             toggles sit on their own row in an evenly-split 2-column
             grid so they read as a balanced pair. */
          [data-items-header] {
            flex-direction: column !important;
            align-items: stretch !important;
            gap: 8px !important;
            padding: 8px 10px !important;
          }
          [data-items-title] {
            justify-content: center !important;
            display: flex !important;
            width: 100% !important;
          }
          /* Stack DIM and WT toggles vertically on mobile — at 375px
             both wouldn't fit side-by-side without clipping the LBS
             segment. Each pill takes a full row, with labels left and
             segmented pills right so they align cleanly with one another. */
          [data-unit-toggles] {
            display: flex !important;
            flex-direction: column !important;
            gap: 4px !important;
            width: 100% !important;
          }
          [data-unit-toggles] > * {
            width: 100% !important;
            justify-content: space-between !important;
          }
          /* Equalize the DIM/WT label widths so the segmented pills on
             both rows start at the same x-coordinate. */
          [data-unit-toggles] > * > span:first-child {
            min-width: 22px !important;
          }
          [data-quote-row="config-3"] {
            grid-template-columns: 1.1fr 1fr 1fr !important;
            gap: 6px !important;
          }
          [data-quote-row="config-2"] {
            grid-template-columns: 1.1fr 1fr !important;
            gap: 6px !important;
          }
          /* Each item gets a 2-row layout on mobile so the inputs have
             room to breathe: row 1 = qty + length + width; row 2 =
             height + weight + delete. grid-template-areas wires children
             in DOM order to those slots. */
          [data-quote-row="packages-row"] {
            grid-template-columns: minmax(0,1fr) minmax(0,1fr) minmax(0,1fr) !important;
            grid-template-areas:
              "qty length width"
              "height weight remove" !important;
            gap: 6px !important;
          }
          [data-quote-row="packages-row"] > *:nth-child(1) { grid-area: qty; }
          [data-quote-row="packages-row"] > *:nth-child(2) { grid-area: length; }
          [data-quote-row="packages-row"] > *:nth-child(3) { grid-area: width; }
          [data-quote-row="packages-row"] > *:nth-child(4) { grid-area: height; }
          [data-quote-row="packages-row"] > *:nth-child(5) { grid-area: weight; }
          [data-quote-row="packages-row"] > *:nth-child(6) { grid-area: remove; }
        }
        @media (max-width: 480px) {
          .abgs-hero-grid h1 { font-size: 36px !important; }
        }
      `}</style>
      {/* ——— Ambient glows ——— */}
      <div aria-hidden style={{ position: 'absolute', top: -260, left: -200, width: 820, height: 820,
        borderRadius: '50%', filter: 'blur(140px)',
        background: 'radial-gradient(circle, rgba(96,165,250,0.55) 0%, rgba(37,99,235,0.22) 40%, transparent 70%)',
        pointerEvents: 'none' }}/>
      <div aria-hidden style={{ position: 'absolute', top: -180, right: -180, width: 720, height: 720,
        borderRadius: '50%', filter: 'blur(130px)',
        background: 'radial-gradient(circle, rgba(37,99,235,0.28) 0%, rgba(96,165,250,0.14) 45%, transparent 70%)',
        pointerEvents: 'none' }}/>
      <div aria-hidden style={{ position: 'absolute', bottom: -260, left: '30%', width: 700, height: 500,
        borderRadius: '50%', filter: 'blur(130px)',
        background: 'radial-gradient(ellipse, rgba(96,165,250,0.18) 0%, transparent 70%)',
        pointerEvents: 'none' }}/>

      {/* Grid */}
      <svg width="100%" height="100%" style={{ position: 'absolute', inset: 0, opacity: 0.4, pointerEvents: 'none' }}>
        <defs>
          <pattern id="hgrid" width="72" height="72" patternUnits="userSpaceOnUse">
            <path d="M 72 0 L 0 0 0 72" fill="none" stroke="#eef0f3" strokeWidth="1"/>
          </pattern>
          <linearGradient id="hgridFade" x1="0" y1="0" x2="0" y2="1">
            <stop offset="0%" stopColor="#fff" stopOpacity="0"/>
            <stop offset="100%" stopColor="#fff" stopOpacity="1"/>
          </linearGradient>
        </defs>
        <rect width="100%" height="100%" fill="url(#hgrid)"/>
        <rect width="100%" height="100%" fill="url(#hgridFade)"/>
      </svg>

      <div className="abgs-hero-inner" style={{ position: 'relative',
        maxWidth: step === 'results' ? 'none' : 1600,
        margin: '0 auto',
        padding: step === 'results' ? '16px 24px 80px' : '16px 64px 80px',
        zIndex: 2,
        transition: 'padding 500ms ease' }}>
        <div className="abgs-hero-grid" style={{ display: 'grid',
          gridTemplateColumns: step === 'results' ? '1fr' : '1.05fr 0.95fr',
          alignItems: 'center', gap: step === 'results' ? 0 : 48,
          minHeight: 560,
          transition: 'grid-template-columns 600ms cubic-bezier(0.4,0,0.2,1), gap 600ms cubic-bezier(0.4,0,0.2,1)' }}>

          {/* LEFT */}
          <div style={{ position: 'relative',
            opacity: step === 'results' ? 0 : 1,
            pointerEvents: step === 'results' ? 'none' : 'auto',
            maxHeight: step === 'results' ? 0 : 'none',
            overflow: 'hidden',
            transition: 'opacity 400ms ease, max-height 500ms ease' }}>
            <h1 className="reveal in" style={{ margin: '0 0 20px', fontSize: 84,
              lineHeight: 1.02, fontWeight: 700, letterSpacing: '-0.035em', color: '#0B1220' }}>
              Logistics,<br/>
              <span style={{
                background: 'linear-gradient(90deg, #1E57D6 0%, #2563EB 50%, #60A5FA 100%)',
                WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
                backgroundClip: 'text' }}>reimagined.</span>
            </h1>
            <p className="reveal delay-2" style={{ margin: 0, fontSize: 19, lineHeight: 1.55,
              color: '#475467', maxWidth: 540, letterSpacing: '-0.005em' }}>
              Effortlessly quote, manage, and move freight globally. One platform for air, ocean,
              ground and courier — connected to 40+ carriers, customs-ready.
            </p>

            <div className="reveal delay-3" style={{ display: 'flex', gap: 12, marginTop: 36, alignItems: 'center' }}>
              <button onClick={openQuote} className="lift hero-cta-pulse"
                key={pulseTick}
                style={{ position: 'relative',
                  padding: '14px 18px 14px 22px',
                  background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
                  color: '#fff', border: 0, borderRadius: 14, fontFamily: 'inherit', fontSize: 15, fontWeight: 600,
                  cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 10,
                  overflow: 'hidden',
                  boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.35), 0 12px 28px -8px rgba(30,87,214,0.5)' }}>
                {/* Pulsing halo */}
                <span aria-hidden style={{ position: 'absolute', inset: -2, borderRadius: 16,
                  border: '2px solid rgba(46,106,232,0.55)',
                  animation: 'hero-cta-halo 2.4s ease-out infinite',
                  pointerEvents: 'none' }}/>
                {/* Shimmer sweep */}
                <span aria-hidden style={{ position: 'absolute', inset: 0,
                  background: 'linear-gradient(110deg, transparent 30%, rgba(255,255,255,0.22) 50%, transparent 70%)',
                  transform: 'translateX(-100%)',
                  animation: 'hero-cta-shimmer 3.2s ease-in-out infinite',
                  pointerEvents: 'none' }}/>
                <span style={{ position: 'relative', display: 'inline-flex',
                  alignItems: 'center', gap: 10 }}>
                  Get instant quote
                  <span style={{ width: 26, height: 26, borderRadius: 9999,
                    background: 'rgba(255,255,255,0.22)', display: 'inline-grid', placeItems: 'center',
                    boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.35)' }}>
                    <Icon name="arrowRight" size={13}/>
                  </span>
                </span>
              </button>
              <a href="services/ab-group-platform.html" className="lift" style={{ padding: '14px 22px',
                background: 'rgba(255,255,255,0.5)',
                backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
                color: '#101828', border: '1px solid rgba(0,0,0,0.08)', borderRadius: 14,
                fontFamily: 'inherit', fontSize: 15, fontWeight: 600, cursor: 'pointer',
                textDecoration: 'none', display: 'inline-flex', alignItems: 'center', gap: 8,
                boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.6)' }}>
                Explore platform
                <Icon name="arrowRight" size={14} style={{ opacity: 0.6 }}/>
              </a>
            </div>

            <div className="reveal delay-3" style={{ display: 'inline-flex', alignItems: 'center', gap: 10,
              marginTop: 24,
              padding: '8px 14px', background: 'rgba(255,255,255,0.6)',
              backdropFilter: 'blur(20px) saturate(1.4)', WebkitBackdropFilter: 'blur(20px) saturate(1.4)',
              border: '1px solid rgba(0,0,0,0.06)', borderRadius: 9999,
              boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.6), 0 1px 2px rgba(16,24,40,0.04)',
              fontSize: 13, color: '#101828' }}>
              <div style={{ display: 'inline-flex', gap: 2 }}>
                {[0,1,2,3,4].map(i => (
                  <svg key={i} width="14" height="14" viewBox="0 0 24 24" fill="#FF801E">
                    <path d="M12 2l2.9 6.9 7.5.6-5.7 4.9 1.7 7.3L12 17.8 5.6 21.7l1.7-7.3L1.6 9.5l7.5-.6L12 2z"/>
                  </svg>
                ))}
              </div>
              <span style={{ fontWeight: 600 }}>Rated 4.9/5</span>
              <span style={{ color: '#667085' }}>· 2,000+ shippers</span>
            </div>

            <div className="reveal delay-4" style={{ display: 'flex', gap: 28, marginTop: 36, alignItems: 'center' }}>
              {['NVOCC licensed','C-TPAT certified','SOC 2 Type II'].map(c => (
                <div key={c} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, color: '#475467' }}>
                  <Icon name="check" size={16} style={{ color: '#12b76a' }}/> {c}
                </div>
              ))}
            </div>
          </div>

          {/* RIGHT — Orb + Glass card */}
          <div ref={orbCardRef} className="abgs-hero-orb abgs-hero-orb-wrap"
            style={{ position: 'relative', width: '100%',
            aspectRatio: step === 'results' ? 'auto' : '1 / 1',
            minHeight: step === 'results' ? 0 : 540,
            margin: step === 'results' ? '0 auto' : 0,
            transition: 'min-height 500ms ease' }}
            onMouseEnter={() => setPaused(true)}
            onMouseLeave={() => setPaused(false)}>
            {/* Floating Earth video */}
            <video
              autoPlay muted loop playsInline
              style={{ position: 'absolute', inset: 0, width: '140%', height: '140%',
                top: '-10%', left: '-20%',
                objectFit: 'cover',
                pointerEvents: 'none',
                opacity: step === 'results' ? 0 : 1,
                transition: 'opacity 400ms ease',
                WebkitMaskImage: 'radial-gradient(circle at 50% 50%, #000 28%, rgba(0,0,0,0.75) 44%, rgba(0,0,0,0.35) 58%, rgba(0,0,0,0.1) 70%, transparent 82%)',
                maskImage: 'radial-gradient(circle at 50% 50%, #000 28%, rgba(0,0,0,0.75) 44%, rgba(0,0,0,0.35) 58%, rgba(0,0,0,0.1) 70%, transparent 82%)',
                zIndex: 1 }} poster="assets/posters/im-orb.jpg">
              <source src={ORB_URL} type="video/mp4"/>
            </video>

            {/* Floating glass card */}
            <div className="reveal delay-2 abgs-hero-card" style={{
              position: step === 'results' ? 'relative' : 'absolute',
              inset: step === 'results' ? 'auto' : 0,
              display: 'flex',
              flexDirection: 'column',
              justifyContent: 'center',
              alignItems: 'center',
              zIndex: 2,
              padding: step === 'results' ? '40px 0 24px' : 24,
              transform: step === 'results' ? 'none' : 'translateY(4%)',
              transition: 'padding 500ms ease' }}>
              <div style={{ width: '100%',
                maxWidth: flipped
                  ? (step === 'audience' ? 480
                    : step === 'service' ? 580
                    : step === 'quote'   ? 580
                    : /* results */        'min(1800px, calc(100vw - 48px))')
                  : 340,
                perspective: 1600,
                transition: 'max-width 600ms cubic-bezier(0.4,0,0.2,1)' }}>
                <div className="abgs-hero-flipper" data-flipped={flipped ? 'true' : 'false'} style={{ position: 'relative',
                  transformStyle: step === 'results' ? 'flat' : 'preserve-3d',
                  transition: 'transform 900ms cubic-bezier(0.4, 0, 0.2, 1)',
                  transform: step === 'results'
                    ? 'none'
                    : (flipped ? 'rotateY(180deg)' : 'rotateY(0deg)') }}>
                  {step !== 'results' && (
                    <div className="abgs-hero-front" style={{ backfaceVisibility: 'hidden', WebkitBackfaceVisibility: 'hidden' }}>
                      <OrbitTrigger onOpen={openQuote}/>
                    </div>
                  )}
                  {/* For Steps 2-4 the back face overlays the front via
                      absolute positioning so the 3D flip works. Step 5
                      content is much taller — render in normal flow so
                      the page extends naturally. */}
                  <div className="abgs-hero-back" style={step === 'results' ? {
                      position: 'relative'
                    } : {
                      position: 'absolute', left: 0, right: 0, top: '50%',
                      backfaceVisibility: 'hidden',
                      WebkitBackfaceVisibility: 'hidden',
                      transform: 'translateY(-50%) rotateY(180deg)' }}>
                    {step === 'audience' && (
                      <AudienceSelectGlass
                        onPick={(a) => { setAudience(a); setStep('service'); }}
                        onClose={closeFlow}/>
                    )}
                    {step === 'service' && (
                      <ServiceSelectGlass
                        audience={audience}
                        onPick={(svcKey) => { setMode(svcKey); setStep('quote'); }}
                        onBack={() => setStep('audience')}
                        onClose={closeFlow}/>
                    )}
                    {step === 'quote' && (
                      <QuoteGlass mode={mode} audience={audience}
                        initialSummary={searchSummary}
                        onChangeService={() => setStep('service')}
                        onBack={() => setStep('service')}
                        onClose={closeFlow}
                        onResults={(rates, summary) => {
                          setResults(rates);
                          setSearchSummary(summary);
                          setStep('results');
                        }}/>
                    )}
                    {step === 'results' && (
                      <ResultsGlass
                        rates={results}
                        summary={searchSummary}
                        onEdit={() => setStep('quote')}
                        onChangeService={() => setStep('service')}
                        onClose={closeFlow}/>
                    )}
                  </div>
                </div>
              </div>
              {/* trailing space filler to replace removed indicator dots */}
              <div style={{ marginTop: 12 }}/>
            </div>
          </div>
        </div>

      </div>
    </section>
  );
}

// Liquid-glass gradient hairline border
function LiquidBorder({ radius = 20, alpha = [0.8, 0.3] }) {
  const [a, b] = alpha;
  return (
    <div aria-hidden style={{ position: 'absolute', inset: 0, borderRadius: radius,
      padding: 1.4, pointerEvents: 'none',
      background: `linear-gradient(180deg, rgba(255,255,255,${a}) 0%, rgba(255,255,255,${b}) 20%, transparent 40%, transparent 60%, rgba(255,255,255,${b}) 80%, rgba(255,255,255,${a}) 100%)`,
      WebkitMask: 'linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0)',
      WebkitMaskComposite: 'xor', maskComposite: 'exclude' }}/>
  );
}

// ——— Orbit trigger — compact pill that reveals the Quote form ———
function OrbitTrigger({ onOpen }) {
  return (
    <button
      onClick={onOpen}
      style={{ position: 'relative', cursor: 'pointer',
        width: '100%', display: 'inline-flex', alignItems: 'center', gap: 12,
        padding: '12px 14px 12px 16px',
        border: '1px solid rgba(255,255,255,0.6)',
        borderRadius: 9999,
        background: 'rgba(255,255,255,0.92)',
        backdropFilter: 'blur(32px) saturate(1.6)', WebkitBackdropFilter: 'blur(32px) saturate(1.6)',
        color: '#0B1220', fontFamily: 'inherit', fontWeight: 600, fontSize: 14,
        letterSpacing: '-0.005em', textAlign: 'left',
        boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.95), 0 20px 44px -12px rgba(16,24,40,0.28), 0 4px 12px rgba(16,24,40,0.08)',
        transition: 'transform 220ms cubic-bezier(0.16,1,0.3,1), box-shadow 220ms' }}
      onMouseEnter={(e) => {
        e.currentTarget.style.transform = 'translateY(-2px)';
        e.currentTarget.style.boxShadow = 'inset 0 1px 0 rgba(255,255,255,0.95), 0 26px 52px -14px rgba(30,87,214,0.32), 0 4px 12px rgba(16,24,40,0.08)';
      }}
      onMouseLeave={(e) => {
        e.currentTarget.style.transform = 'translateY(0)';
        e.currentTarget.style.boxShadow = 'inset 0 1px 0 rgba(255,255,255,0.95), 0 20px 44px -12px rgba(16,24,40,0.28), 0 4px 12px rgba(16,24,40,0.08)';
      }}
    >
      <span style={{ width: 34, height: 34, borderRadius: 9999, flexShrink: 0,
        background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
        display: 'inline-grid', placeItems: 'center', color: '#fff',
        boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 4px 10px -2px rgba(30,87,214,0.45)' }}>
        <Icon name="zap" size={15}/>
      </span>
      <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.15, flex: 1, minWidth: 0 }}>
        <span style={{ fontSize: 13.5, fontWeight: 700, color: '#0B1220',
          letterSpacing: '-0.01em', whiteSpace: 'nowrap' }}>Get instant quote</span>
        <span style={{ fontSize: 11.5, fontWeight: 500, color: '#667085',
          letterSpacing: '0.005em', marginTop: 1, whiteSpace: 'nowrap' }}>Air · Ocean · Ground · Courier</span>
      </span>
      <span style={{ width: 28, height: 28, borderRadius: 9999, flexShrink: 0,
        background: '#0B1220', display: 'inline-grid', placeItems: 'center', color: '#fff' }}>
        <Icon name="arrowRight" size={13}/>
      </span>
    </button>
  );
}

// ——— Orbit face — corporate, tight, data-forward ———
function OrbitGlass({ onOpen }) {
  const ports = [
    { n: 'FCL',    deg: 270, icon: 'box',       city: 'Full container' },   // top
    { n: 'LCL',    deg: 342, icon: 'warehouse', city: 'Less than container' },
    { n: 'AIR',    deg: 54,  icon: 'plane',     city: 'Air freight' },
    { n: 'REEFER', deg: 126, icon: 'zap',       city: 'Refrigerated' },
    { n: 'LAND',   deg: 198, icon: 'truck',     city: 'Ground & trucking' },
  ];
  return (
    <div onClick={onOpen}
      style={{ position: 'relative', cursor: 'pointer',
        background: 'rgba(255,255,255,0.55)',
        backdropFilter: 'blur(50px) saturate(1.6)', WebkitBackdropFilter: 'blur(50px) saturate(1.6)',
        border: '1px solid rgba(255,255,255,0.7)', borderRadius: 24,
        boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85), 0 24px 48px -12px rgba(16,24,40,0.18), 0 2px 6px rgba(16,24,40,0.06)',
        transition: 'transform 240ms cubic-bezier(0.16,1,0.3,1)',
        overflow: 'hidden' }}
      onMouseEnter={e => e.currentTarget.style.transform = 'translateY(-4px)'}
      onMouseLeave={e => e.currentTarget.style.transform = 'translateY(0)'}
    >
      <LiquidBorder radius={24} alpha={[0.9, 0.35]}/>

      {/* Header strip */}
      <div style={{ position: 'relative', padding: '16px 22px',
        borderBottom: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <Icon name="globe" size={14} style={{ color: '#0B1220', opacity: 0.55 }}/>
          <span style={{ fontSize: 13, fontWeight: 600, color: '#0B1220',
            letterSpacing: '-0.005em' }}>Global network</span>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 12,
          color: '#667085', fontVariantNumeric: 'tabular-nums' }}>
          <span><span style={{ color: '#0B1220', fontWeight: 600 }}>40</span> carriers</span>
          <span style={{ width: 3, height: 3, borderRadius: 9999, background: '#D0D5DD' }}/>
          <span><span style={{ color: '#0B1220', fontWeight: 600 }}>140</span> countries</span>
        </div>
      </div>

      {/* Center orbit */}
      <div style={{ position: 'relative', padding: '18px 24px 14px' }}>
        <div style={{ position: 'relative', width: '100%', aspectRatio: '1.15', margin: '0 auto',
          maxWidth: 340 }}>
          {/* Rings */}
          <svg viewBox="0 0 500 500" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
            <defs>
              <radialGradient id="centerGlow" cx="50%" cy="50%" r="30%">
                <stop offset="0%" stopColor="rgba(30,87,214,0.15)"/>
                <stop offset="100%" stopColor="rgba(30,87,214,0)"/>
              </radialGradient>
            </defs>
            <circle cx="250" cy="250" r="140" fill="url(#centerGlow)"/>
            <circle cx="250" cy="250" r="200" fill="none" stroke="rgba(30,87,214,0.2)" strokeWidth="1" strokeDasharray="3 6"/>
            <circle cx="250" cy="250" r="130" fill="none" stroke="rgba(30,87,214,0.12)" strokeWidth="1"/>
            {/* connecting spokes */}
            {[270, 342, 54, 126, 198].map(d => {
              const rad = d * Math.PI / 180;
              const x1 = 250 + 80 * Math.cos(rad);
              const y1 = 250 + 80 * Math.sin(rad);
              const x2 = 250 + 200 * Math.cos(rad);
              const y2 = 250 + 200 * Math.sin(rad);
              return <line key={d} x1={x1} y1={y1} x2={x2} y2={y2}
                stroke="rgba(30,87,214,0.15)" strokeWidth="1" strokeDasharray="1 3"/>;
            })}
          </svg>

          {/* Orbiting ports */}
          <div style={{ position: 'absolute', inset: 0, animation: 'counter-orbit 50s linear infinite' }}>
            {ports.map((p) => {
              const rad = (p.deg * Math.PI) / 180;
              const x = 50 + (200/500)*100 * Math.cos(rad);
              const y = 50 + (200/500)*100 * Math.sin(rad);
              return (
                <div key={p.n} style={{ position: 'absolute', left: `${x}%`, top: `${y}%`,
                  transform: 'translate(-50%,-50%)' }}>
                  <div style={{ animation: 'orbit 50s linear infinite' }}>
                  <div style={{ position: 'relative', padding: '6px 11px',
                    background: '#ffffff',
                    border: '1px solid rgba(16,24,40,0.08)',
                    borderRadius: 9999,
                    boxShadow: '0 4px 10px -2px rgba(16,24,40,0.12), inset 0 1px 0 rgba(255,255,255,0.9)',
                    display: 'flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
                    <span style={{ width: 5, height: 5, borderRadius: 9999, background: '#12b76a' }}/>
                    <Icon name={p.icon} size={11} style={{ color: '#1E57D6' }}/>
                    <span style={{ fontSize: 10, fontWeight: 700, fontFamily: 'JetBrains Mono, monospace',
                      color: '#0B1220', letterSpacing: '0.06em' }}>{p.n}</span>
                  </div>
                  </div>
                </div>
              );
            })}
          </div>

          {/* Center CTA — glass button, matching pill language */}
          <div style={{ position: 'absolute', left: '50%', top: '50%', transform: 'translate(-50%,-50%)' }}>
            <div style={{ animation: 'float 6s ease-in-out infinite' }}>
            <button
              onClick={(e) => { e.stopPropagation(); onOpen(); }}
              style={{ position: 'relative', cursor: 'pointer',
                display: 'inline-flex', alignItems: 'center', gap: 10,
                padding: '11px 14px 11px 18px',
                border: '1px solid rgba(16,24,40,0.08)',
                borderRadius: 9999,
                background: '#ffffff',
                color: '#0B1220', fontFamily: 'inherit', fontWeight: 700, fontSize: 13,
                letterSpacing: '0.02em',
                boxShadow: '0 6px 14px -4px rgba(16,24,40,0.14), inset 0 1px 0 rgba(255,255,255,0.9)',
                transition: 'transform 200ms cubic-bezier(0.16,1,0.3,1), box-shadow 200ms' }}
              onMouseEnter={(e) => {
                e.currentTarget.style.transform = 'translateY(-1px)';
                e.currentTarget.style.boxShadow = '0 10px 20px -4px rgba(30,87,214,0.18), inset 0 1px 0 rgba(255,255,255,0.9)';
              }}
              onMouseLeave={(e) => {
                e.currentTarget.style.transform = 'translateY(0)';
                e.currentTarget.style.boxShadow = '0 6px 14px -4px rgba(16,24,40,0.14), inset 0 1px 0 rgba(255,255,255,0.9)';
              }}
            >
              <span style={{ width: 6, height: 6, borderRadius: 9999, background: '#12b76a',
                boxShadow: '0 0 0 3px rgba(18,183,106,0.18)' }}/>
              <span style={{ fontFamily: 'JetBrains Mono, monospace', color: '#1E57D6',
                fontSize: 11, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase' }}>
                Quote now
              </span>
              <Icon name="arrowRight" size={13} style={{ color: '#1E57D6' }}/>
            </button>
            </div>
          </div>
        </div>
      </div>

      {/* Footer — mini stats */}
      <div style={{ position: 'relative', padding: '14px 24px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 0,
        background: 'linear-gradient(0deg, rgba(255,255,255,0.5) 0%, rgba(255,255,255,0) 100%)' }}>
        {[
          ['12,480', 'today'],
          ['< 2 s', 'avg quote'],
          ['99.8%', 'on-time'],
        ].map(([v, l], i) => (
          <div key={l} style={{ padding: '0 12px', textAlign: 'center',
            borderLeft: i>0 ? '1px solid rgba(16,24,40,0.08)' : 'none' }}>
            <div style={{ fontSize: 16, fontWeight: 700, color: '#0B1220',
              letterSpacing: '-0.02em', fontVariantNumeric: 'tabular-nums' }}>{v}</div>
            <div style={{ fontSize: 10, color: '#667085', letterSpacing: '0.06em',
              textTransform: 'uppercase', fontWeight: 600, marginTop: 2 }}>{l}</div>
          </div>
        ))}
      </div>

    </div>
  );
}

// ——— Step indicator — 4 dots, current dot is the gradient blue pill ———
function StepDots({ step, total = 5 }) {
  const dots = Array.from({ length: total }, (_, i) => i + 1);
  return (
    <div style={{ position: 'relative', padding: '10px 18px 0',
      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
      <div style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
        {dots.map(n => {
          const active = n === step;
          const done   = n < step;
          return (
            <span key={n} style={{
              width: active ? 22 : 6, height: 6, borderRadius: 9999,
              background: active
                ? 'linear-gradient(90deg, #2E6AE8 0%, #1E57D6 100%)'
                : done ? 'rgba(30,87,214,0.45)' : 'rgba(16,24,40,0.12)',
              transition: 'width 280ms cubic-bezier(0.4,0,0.2,1), background 200ms' }}/>
          );
        })}
      </div>
      <div style={{ fontSize: 10, color: '#98A2B3', fontWeight: 600,
        fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.06em',
        textTransform: 'uppercase' }}>
        Step {step} of {total}
      </div>
    </div>
  );
}

// ——— Step 2 — Business vs Individual ————————————————————————
function AudienceSelectGlass({ onPick, onClose }) {
  return (
    <div style={{ position: 'relative',
      background: 'rgba(255,255,255,0.62)',
      backdropFilter: 'blur(50px) saturate(1.6)', WebkitBackdropFilter: 'blur(50px) saturate(1.6)',
      border: '1px solid rgba(255,255,255,0.7)', borderRadius: 24,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85), 0 24px 48px -12px rgba(16,24,40,0.18)',
      overflow: 'hidden' }}>
      <LiquidBorder radius={24} alpha={[0.9, 0.35]}/>

      {/* Header */}
      <div style={{ position: 'relative', padding: '12px 14px 12px 14px',
        borderBottom: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
        background: 'linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 9, minWidth: 0 }}>
          <span style={{ width: 26, height: 26, borderRadius: 7, flexShrink: 0,
            background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
            display: 'inline-grid', placeItems: 'center', color: '#fff',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' }}>
            <Icon name="users" size={13}/>
          </span>
          <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.15, minWidth: 0 }}>
            <span style={{ fontSize: 13, fontWeight: 700, color: '#0B1220',
              letterSpacing: '-0.005em' }}>Who is shipping?</span>
            <span style={{ fontSize: 10.5, color: '#667085',
              fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em' }}>
              Step 2 of 5 · helps tailor the quote
            </span>
          </div>
        </div>
        <button type="button" onClick={onClose} style={{ padding: '5px 11px', flexShrink: 0,
          background: 'rgba(255,255,255,0.7)',
          border: '1px solid rgba(16,24,40,0.1)', borderRadius: 9999, cursor: 'pointer',
          color: '#475467', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600 }}>
          Close
        </button>
      </div>

      <StepDots step={2}/>

      {/* Two large picker cards */}
      <div style={{ position: 'relative', padding: '14px 18px 16px',
        display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
        {AUDIENCES.map(a => (
          <PickerCard key={a.key}
            icon={a.icon} title={a.name} tag={a.tag} desc={a.desc}
            onClick={() => onPick(a.key)}/>
        ))}
      </div>

      {/* Footer — same trust row as the quote face */}
      <div style={{ position: 'relative', padding: '9px 18px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        background: 'linear-gradient(0deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 10.5, color: '#667085' }}>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="check" size={11} style={{ color: '#12b76a' }}/> No signup
          </span>
          <span style={{ width:1, height:10, background:'rgba(16,24,40,0.1)' }}/>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="lock" size={11} style={{ color: '#1E57D6' }}/> Quote stays private
          </span>
        </div>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6,
          fontSize: 10, color: '#667085', fontFamily: 'JetBrains Mono, monospace',
          letterSpacing: '0.06em' }}>
          <Icon name="shield" size={11} style={{ color: '#1E57D6' }}/>
          SOC 2 · C-TPAT · NVOCC
        </div>
      </div>
    </div>
  );
}

// ——— Step 3 — Service / transport mode ————————————————————————
function ServiceSelectGlass({ audience, onPick, onBack, onClose }) {
  const aud = AUDIENCES.find(a => a.key === audience);
  return (
    <div style={{ position: 'relative',
      background: 'rgba(255,255,255,0.62)',
      backdropFilter: 'blur(50px) saturate(1.6)', WebkitBackdropFilter: 'blur(50px) saturate(1.6)',
      border: '1px solid rgba(255,255,255,0.7)', borderRadius: 24,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85), 0 24px 48px -12px rgba(16,24,40,0.18)',
      overflow: 'hidden' }}>
      <LiquidBorder radius={24} alpha={[0.9, 0.35]}/>

      {/* Header */}
      <div style={{ position: 'relative', padding: '12px 14px 12px 12px',
        borderBottom: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
        background: 'linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
          <button type="button" onClick={onBack} aria-label="Back"
            style={{ width: 28, height: 28, borderRadius: 9999, flexShrink: 0,
              background: '#ffffff', border: '1px solid rgba(16,24,40,0.1)',
              boxShadow: '0 1px 2px rgba(16,24,40,0.06), inset 0 1px 0 rgba(255,255,255,0.9)',
              display: 'inline-grid', placeItems: 'center', cursor: 'pointer', color: '#475467' }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
              stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 18l-6-6 6-6"/>
            </svg>
          </button>
          <span style={{ width: 26, height: 26, borderRadius: 7, flexShrink: 0,
            background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
            display: 'inline-grid', placeItems: 'center', color: '#fff',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' }}>
            <Icon name="zap" size={13}/>
          </span>
          <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.15, minWidth: 0 }}>
            <span style={{ fontSize: 13, fontWeight: 700, color: '#0B1220',
              letterSpacing: '-0.005em' }}>Pick a service</span>
            <span style={{ fontSize: 10.5, color: '#667085',
              fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em',
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {aud ? `${aud.name} · ` : ''}Step 3 of 5
            </span>
          </div>
        </div>
        <button type="button" onClick={onClose} style={{ padding: '5px 11px', flexShrink: 0,
          background: 'rgba(255,255,255,0.7)',
          border: '1px solid rgba(16,24,40,0.1)', borderRadius: 9999, cursor: 'pointer',
          color: '#475467', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600 }}>
          Close
        </button>
      </div>

      <StepDots step={3}/>

      {/* 5-up service grid */}
      <div style={{ position: 'relative', padding: '14px 18px 16px',
        display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 8 }}>
        {SERVICES.map(s => (
          <ServiceTile key={s.key}
            icon={s.icon} title={s.name} tag={s.tag}
            onClick={() => onPick(s.key)}/>
        ))}
      </div>

      {/* Footer */}
      <div style={{ position: 'relative', padding: '9px 18px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        background: 'linear-gradient(0deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 10.5, color: '#667085' }}>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="check" size={11} style={{ color: '#12b76a' }}/> Live rates
          </span>
          <span style={{ width:1, height:10, background:'rgba(16,24,40,0.1)' }}/>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="globe" size={11} style={{ color: '#1E57D6' }}/> 40+ carriers
          </span>
        </div>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6,
          fontSize: 10, color: '#667085', fontFamily: 'JetBrains Mono, monospace',
          letterSpacing: '0.06em' }}>
          <Icon name="shield" size={11} style={{ color: '#1E57D6' }}/>
          SOC 2 · C-TPAT · NVOCC
        </div>
      </div>
    </div>
  );
}

// Large picker card used on step 2 (audience).
function PickerCard({ icon, title, tag, desc, onClick }) {
  const [hover, setHover] = useStateH(false);
  return (
    <button type="button" onClick={onClick}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{ position: 'relative', cursor: 'pointer', textAlign: 'left',
        padding: '14px 14px 14px 14px',
        background: hover ? '#ffffff' : 'rgba(255,255,255,0.85)',
        border: hover ? '1px solid rgba(30,87,214,0.5)' : '1px solid rgba(16,24,40,0.1)',
        borderRadius: 14,
        boxShadow: hover
          ? 'inset 0 1px 0 rgba(255,255,255,0.95), 0 14px 28px -10px rgba(30,87,214,0.28), 0 0 0 4px rgba(30,87,214,0.08)'
          : 'inset 0 1px 0 rgba(255,255,255,0.85), 0 4px 10px -2px rgba(16,24,40,0.06)',
        transition: 'transform 200ms cubic-bezier(0.16,1,0.3,1), box-shadow 200ms, border-color 200ms, background 200ms',
        transform: hover ? 'translateY(-2px)' : 'translateY(0)',
        fontFamily: 'inherit' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
        <span style={{ width: 32, height: 32, borderRadius: 9,
          background: hover
            ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
            : '#EEF4FF',
          display: 'inline-grid', placeItems: 'center',
          color: hover ? '#ffffff' : '#1E57D6',
          boxShadow: hover ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 4px 10px -2px rgba(30,87,214,0.4)' : 'inset 0 1px 0 rgba(255,255,255,0.9)',
          transition: 'background 200ms, color 200ms, box-shadow 200ms' }}>
          <Icon name={icon} size={15}/>
        </span>
        <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.15 }}>
          <span style={{ fontSize: 14, fontWeight: 700, color: '#0B1220',
            letterSpacing: '-0.01em' }}>{title}</span>
          <span style={{ fontSize: 10, color: '#667085', fontWeight: 600,
            fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.06em',
            textTransform: 'uppercase', marginTop: 2 }}>{tag}</span>
        </div>
      </div>
      <div style={{ fontSize: 11.5, color: '#475467', lineHeight: 1.45,
        letterSpacing: '-0.005em' }}>
        {desc}
      </div>
      <div style={{ position: 'absolute', top: 14, right: 14,
        width: 22, height: 22, borderRadius: 9999,
        background: hover ? '#0B1220' : 'transparent',
        color: hover ? '#fff' : 'transparent',
        display: 'inline-grid', placeItems: 'center',
        transition: 'background 200ms, color 200ms' }}>
        <Icon name="arrowRight" size={11}/>
      </div>
    </button>
  );
}

// Compact tile used on step 3 (service / transport mode).
function ServiceTile({ icon, title, tag, onClick }) {
  const [hover, setHover] = useStateH(false);
  return (
    <button type="button" onClick={onClick}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{ position: 'relative', cursor: 'pointer', textAlign: 'center',
        padding: '14px 8px 12px',
        background: hover ? '#ffffff' : 'rgba(255,255,255,0.85)',
        border: hover ? '1px solid rgba(30,87,214,0.5)' : '1px solid rgba(16,24,40,0.1)',
        borderRadius: 12,
        boxShadow: hover
          ? 'inset 0 1px 0 rgba(255,255,255,0.95), 0 12px 22px -10px rgba(30,87,214,0.28), 0 0 0 3px rgba(30,87,214,0.08)'
          : 'inset 0 1px 0 rgba(255,255,255,0.85), 0 2px 6px -1px rgba(16,24,40,0.06)',
        transition: 'transform 200ms cubic-bezier(0.16,1,0.3,1), box-shadow 200ms, border-color 200ms, background 200ms',
        transform: hover ? 'translateY(-2px)' : 'translateY(0)',
        fontFamily: 'inherit',
        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
      <span style={{ width: 36, height: 36, borderRadius: 10,
        background: hover
          ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
          : '#EEF4FF',
        display: 'inline-grid', placeItems: 'center',
        color: hover ? '#ffffff' : '#1E57D6',
        boxShadow: hover ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 4px 10px -2px rgba(30,87,214,0.4)' : 'inset 0 1px 0 rgba(255,255,255,0.9)',
        transition: 'background 200ms, color 200ms, box-shadow 200ms' }}>
        <Icon name={icon} size={16}/>
      </span>
      <span style={{ fontSize: 12.5, fontWeight: 700, color: '#0B1220',
        letterSpacing: '-0.01em', lineHeight: 1.15, marginTop: 2 }}>{title}</span>
      <span style={{ fontSize: 9.5, color: '#667085', fontWeight: 600,
        fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.04em',
        textTransform: 'uppercase', lineHeight: 1.2 }}>{tag}</span>
    </button>
  );
}

// Mirror the booking-form incoterm derivations so the homepage widget sends
// the same Incoterm the customer-portal would compute for the same toggle
// combo. Source of truth: AIRBookingForm.jsx and LCLBookingForm.jsx in the
// abgroupshipping-login customer portal.
function calculateAirIncoterm(originTransport, destinationTransport) {
  if (originTransport === 'customer_deliver' && destinationTransport === 'customer_pickup') return 'FCA';
  if (originTransport === 'customer_deliver' && destinationTransport === 'abgroup_deliver') return 'CPT';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'customer_pickup') return 'EXW';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'abgroup_deliver') return 'DDP';
  return 'FCA';
}
function calculateLCLIncoterm(originTransport, destinationTransport) {
  if (originTransport === 'customer_deliver' && destinationTransport === 'customer_pickup') return 'FCA';
  if (originTransport === 'customer_deliver' && destinationTransport === 'abgroup_deliver') return 'CIF';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'customer_pickup') return 'EXW';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'abgroup_deliver') return 'DAP';
  return 'FCA';
}
function calculateFCLIncoterm(originTransport, destinationTransport) {
  if (originTransport === 'customer_deliver' && destinationTransport === 'customer_pickup') return 'FOB';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'customer_pickup') return 'EXW';
  if (originTransport === 'customer_deliver' && destinationTransport === 'abgroup_deliver') return 'CIF';
  if (originTransport === 'abgroup_pickup'   && destinationTransport === 'abgroup_deliver') return 'DDP';
  return 'FOB';
}
const FCL_INCOTERM_DESC = {
  FOB: 'Port-to-Port: You arrange delivery to origin port and pickup from destination port',
  EXW: 'Door-to-Port: AB Group picks up at your facility; you arrange pickup at destination port',
  CIF: 'Port-to-Door: You arrange delivery to origin port; AB Group delivers to your facility',
  DDP: 'Door-to-Door: AB Group handles pickup at origin and delivery at destination',
};
// ECU Worldwide proxy — same Firebase Cloud Function the customer portal
// hits (`abgroupshipping-login/src/services/ecuWorldwideAPI.js`). Open CORS,
// no auth, returns both ports and door locations. Used by the FCL flow to
// surface ECU-formatted UN/LOCODEs (USHOU, COCTG, …) and door-location
// codes so the rate request mirrors the booking form.
const ECU_PROXY_URL = 'https://us-central1-abgs-calculadora.cloudfunctions.net/ecuProxy/locations/search';
async function ecuSearchLocations(name, dep, countryCode) {
  const place = (name || '').trim();
  if (place.length < 3) return { ports: [], doors: [] };
  const params = new URLSearchParams({
    place, productType: 'FCL', dep: dep || 'E', isPartialName: 'true',
  });
  try {
    const res = await fetch(`${ECU_PROXY_URL}?${params.toString()}`);
    if (!res.ok) return { ports: [], doors: [] };
    const json = await res.json();
    const all = (json && json.success && Array.isArray(json.data)) ? json.data : [];
    const filtered = countryCode
      ? all.filter(loc => (loc.countryCode || '').toUpperCase() === countryCode.toUpperCase())
      : all;
    const ports = filtered
      .filter(loc => loc.isPort === true && loc.isDoorLocation !== true)
      .map(loc => ({ unCode: loc.unCode, portName: loc.name || loc.unCode,
        countryCode: loc.countryCode || '', zipCode: loc.zipCode || '' }));
    const doors = filtered
      .filter(loc => loc.isDoorLocation === true || (!!loc.zipCode && !loc.isPort))
      .map(loc => {
        const display = loc.name || loc.unCode || '';
        const zipMatch = String(loc.zipCode || display).match(/\b(\d{5})\b/);
        return {
          code: loc.unCode || display,
          displayName: display,
          countryCode: loc.countryCode || '',
          zipCode: zipMatch ? zipMatch[1] : '',
        };
      });
    return { ports, doors };
  } catch {
    return { ports: [], doors: [] };
  }
}

// Compact selector card used for both port (CY) and door-location (SD)
// pickers in the FCL flow. Shows the currently selected option as a
// pill-style row; clicking opens a dropdown of alternates. Both pickers
// share styling so origin / destination columns line up cleanly.
function FCLLocationSelector({ kind, label, loading, options, selected,
                               onSelect, onSearch, searchValue, error,
                               placeholder }) {
  const [open, setOpen] = useStateH(false);
  const wrapRef = useRefH(null);
  useEffectH(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);
  const isDoor = kind === 'door';
  const summary = selected
    ? (isDoor ? selected.displayName : `${selected.unCode} · ${(selected.portName || '').replace(/^[A-Z]{5}\s*,\s*/, '')}`)
    : '';
  return (
    <div ref={wrapRef} style={{ position: 'relative', minWidth: 0 }}>
      <div style={{ fontSize: 9.5, fontWeight: 700, color: '#475467',
        letterSpacing: '0.1em', textTransform: 'uppercase',
        marginBottom: 4, paddingLeft: 2 }}>{label}</div>
      {isDoor ? (
        <input type="text" value={searchValue || ''}
          onChange={(e) => { onSearch(e.target.value); setOpen(true); }}
          onFocus={() => setOpen(true)}
          placeholder={placeholder || 'Search door location'}
          style={{ width: '100%', padding: '9px 11px',
            background: 'rgba(255,255,255,0.92)',
            border: '1px solid rgba(16,24,40,0.12)', borderRadius: 9,
            fontFamily: 'inherit', fontSize: 12, color: '#0B1220',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
            outline: 'none' }}/>
      ) : (
        <button type="button" onClick={() => setOpen(o => !o)}
          style={{ width: '100%', padding: '9px 11px', textAlign: 'left',
            background: 'rgba(255,255,255,0.92)',
            border: '1px solid rgba(16,24,40,0.12)', borderRadius: 9,
            fontFamily: 'inherit', fontSize: 12, color: summary ? '#0B1220' : '#98A2B3',
            cursor: 'pointer',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between',
            gap: 8 }}>
          <span style={{ overflow: 'hidden', textOverflow: 'ellipsis',
            whiteSpace: 'nowrap', flex: 1, minWidth: 0 }}>
            {summary || (loading ? 'Loading ports…' : (placeholder || 'Pick a port'))}
          </span>
          <span style={{ color: '#98A2B3', flexShrink: 0 }}>▾</span>
        </button>
      )}
      {error && !loading && options.length === 0 && (
        <div style={{ marginTop: 4, fontSize: 10, color: '#B54708',
          fontFamily: 'JetBrains Mono, monospace' }}>{error}</div>
      )}
      {open && (loading || options.length > 0) && (
        <div style={{ position: 'absolute', top: '100%', left: 0, right: 0,
          marginTop: 4, zIndex: 20,
          background: '#fff', borderRadius: 10,
          border: '1px solid rgba(16,24,40,0.12)',
          boxShadow: '0 12px 28px -8px rgba(16,24,40,0.22)',
          maxHeight: 220, overflowY: 'auto' }}>
          {loading && (
            <div style={{ padding: '10px 12px', fontSize: 11.5,
              color: '#667085' }}>Searching…</div>
          )}
          {!loading && options.map((opt, i) => {
            const key = isDoor ? opt.code + i : opt.unCode + i;
            const isSel = selected && (isDoor
              ? selected.code === opt.code
              : selected.unCode === opt.unCode);
            return (
              <button type="button" key={key}
                onClick={() => { onSelect(opt); setOpen(false); }}
                style={{ width: '100%', textAlign: 'left',
                  padding: '9px 12px',
                  background: isSel ? 'rgba(30,87,214,0.08)' : 'transparent',
                  border: 0, cursor: 'pointer', fontFamily: 'inherit',
                  borderBottom: i < options.length - 1 ? '1px solid rgba(16,24,40,0.06)' : 'none',
                  display: 'block' }}>
                {isDoor ? (
                  <span style={{ display: 'block', fontSize: 11.5, fontWeight: 600,
                    color: '#0B1220', lineHeight: 1.35 }}>{opt.displayName}</span>
                ) : (
                  <React.Fragment>
                    <span style={{ display: 'inline-block',
                      padding: '1px 6px', borderRadius: 5,
                      background: 'rgba(30,87,214,0.1)',
                      color: '#1846B0',
                      fontSize: 10, fontWeight: 800,
                      fontFamily: 'JetBrains Mono, monospace',
                      letterSpacing: '0.04em', marginRight: 6 }}>{opt.unCode}</span>
                    <span style={{ fontSize: 11.5, fontWeight: 600,
                      color: '#0B1220' }}>
                      {(opt.portName || '').replace(/^[A-Z]{5}\s*,\s*/, '')}
                    </span>
                  </React.Fragment>
                )}
              </button>
            );
          })}
        </div>
      )}
    </div>
  );
}

// Container specs mirror FCLBookingForm.jsx in the customer portal so the
// quote widget surfaces the exact same dims / payload limits the booking
// stage will display later.
const FCL_CONTAINERS = [
  { code: '20',   title: "20' STANDARD",  dims: "20' × 8' × 8.6'", cbm: 33, kg: 28200 },
  { code: '40',   title: "40' STANDARD",  dims: "40' × 8' × 8.6'", cbm: 67, kg: 26700 },
  { code: '40HC', title: "40' HIGH CUBE", dims: "40' × 8' × 9.6'", cbm: 76, kg: 26420 },
  { code: '45',   title: "45' HIGH CUBE", dims: "45' × 8' × 9.6'", cbm: 86, kg: 27600 },
];

// Per-side inland transportation picker for the FCL flow. Two stacked radio
// cards (CY = port/inland — customer arranges, SD = store door — AB Group
// arranges). Together both sides drive the displayed Incoterm.
function InlandTransportColumn({ label, value, onChange, options }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
      <div style={{ fontSize: 10, fontWeight: 800, color: '#475467',
        letterSpacing: '0.12em', textTransform: 'uppercase', paddingLeft: 2 }}>{label}</div>
      {options.map(opt => {
        const active = value === opt.value;
        return (
          <button type="button" key={opt.value} onClick={() => onChange(opt.value)}
            style={{ textAlign: 'left',
              padding: '10px 12px', borderRadius: 10,
              background: active ? 'rgba(30,87,214,0.08)' : 'rgba(255,255,255,0.85)',
              border: '1px solid ' + (active ? 'rgba(30,87,214,0.55)' : 'rgba(16,24,40,0.1)'),
              boxShadow: active
                ? '0 0 0 1px rgba(30,87,214,0.3)'
                : 'inset 0 1px 0 rgba(255,255,255,0.85)',
              cursor: 'pointer', fontFamily: 'inherit',
              display: 'flex', alignItems: 'flex-start', gap: 10 }}>
            <span style={{ marginTop: 2, width: 14, height: 14, borderRadius: 9999,
              border: '2px solid ' + (active ? '#1E57D6' : 'rgba(16,24,40,0.25)'),
              flexShrink: 0, display: 'inline-grid', placeItems: 'center', background: '#fff' }}>
              {active && <span style={{ width: 6, height: 6, borderRadius: 9999,
                background: '#1E57D6' }}/>}
            </span>
            <span style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
              padding: '2px 7px', borderRadius: 6, flexShrink: 0, marginTop: 1,
              background: active
                ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
                : 'rgba(30,87,214,0.1)',
              color: active ? '#fff' : '#1846B0',
              fontSize: 10, fontWeight: 800, letterSpacing: '0.05em',
              fontFamily: 'JetBrains Mono, monospace' }}>{opt.code}</span>
            <span style={{ minWidth: 0, flex: 1 }}>
              <span style={{ display: 'block', fontSize: 11.5, fontWeight: 700,
                color: '#0B1220', lineHeight: 1.3 }}>{opt.title}</span>
              <span style={{ display: 'block', fontSize: 10.5, color: '#667085',
                marginTop: 2, lineHeight: 1.35 }}>{opt.desc}</span>
            </span>
          </button>
        );
      })}
    </div>
  );
}

// Container quantity card used in the FCL container grid. Stepper floors at
// 0 and ceilings at 20; cards highlight green once at least one is added.
function FCLContainerCard({ spec, count, onChange }) {
  const active = count > 0;
  const dec = () => onChange(Math.max(0, count - 1));
  const inc = () => onChange(Math.min(20, count + 1));
  return (
    <div style={{ padding: '12px 10px',
      background: active ? 'rgba(30,87,214,0.08)' : 'rgba(255,255,255,0.85)',
      border: '1px solid ' + (active ? 'rgba(30,87,214,0.55)' : 'rgba(16,24,40,0.1)'),
      borderRadius: 12,
      boxShadow: active
        ? '0 0 0 1px rgba(30,87,214,0.3)'
        : 'inset 0 1px 0 rgba(255,255,255,0.85)',
      display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6 }}>
      <span style={{ width: 36, height: 36, borderRadius: 9,
        background: active
          ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
          : 'rgba(30,87,214,0.12)',
        color: active ? '#fff' : '#1846B0',
        display: 'inline-grid', placeItems: 'center' }}>
        <Icon name="container" size={20}/>
      </span>
      <div style={{ fontSize: 10.5, fontWeight: 800, color: '#0B1220',
        letterSpacing: '0.06em', textAlign: 'center' }}>{spec.title}</div>
      <div style={{ fontSize: 9.5, color: '#667085', fontFamily: 'JetBrains Mono, monospace',
        textAlign: 'center', lineHeight: 1.4 }}>
        {spec.dims}<br/>{spec.cbm} CBM / {spec.kg.toLocaleString()} kg
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 4,
        paddingTop: 6, borderTop: '1px dashed rgba(16,24,40,0.12)', width: '100%',
        justifyContent: 'center' }}>
        <button type="button" onClick={dec} disabled={count === 0}
          aria-label={`Decrease ${spec.title}`}
          style={{ width: 24, height: 24, borderRadius: 9999,
            border: '1px solid rgba(16,24,40,0.15)', background: '#fff',
            color: count === 0 ? '#98A2B3' : '#475467',
            cursor: count === 0 ? 'not-allowed' : 'pointer',
            fontSize: 14, fontWeight: 700, lineHeight: 1, fontFamily: 'inherit' }}>−</button>
        <span style={{ minWidth: 18, textAlign: 'center', fontSize: 13, fontWeight: 800,
          color: active ? '#1846B0' : '#0B1220',
          fontFamily: 'JetBrains Mono, monospace' }}>{count}</span>
        <button type="button" onClick={inc} disabled={count >= 20}
          aria-label={`Increase ${spec.title}`}
          style={{ width: 24, height: 24, borderRadius: 9999,
            border: '1px solid rgba(16,24,40,0.15)', background: '#fff',
            color: count >= 20 ? '#98A2B3' : '#475467',
            cursor: count >= 20 ? 'not-allowed' : 'pointer',
            fontSize: 14, fontWeight: 700, lineHeight: 1, fontFamily: 'inherit' }}>+</button>
      </div>
    </div>
  );
}

// Two-option segmented toggle used for origin/destination transport, kg/lbs,
// cm/in. Mirrors the Walio AIR/LCL location-selector pattern.
function QuoteSegmented({ label, value, onChange, options }) {
  // Label now sits INSIDE the white card so it's always readable, even
  // when the widget overlays the globe scene on mobile (where there's
  // no separate side-card to host it).
  return (
    <div style={{ display: 'block', padding: '6px 6px 4px',
      background: 'rgba(255,255,255,0.85)',
      border: '1px solid rgba(16,24,40,0.1)', borderRadius: 10,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)' }}>
      {label && (
        <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
          letterSpacing: '0.1em', textTransform: 'uppercase',
          marginBottom: 4, paddingLeft: 4 }}>
          {label}
        </div>
      )}
      <div style={{ display: 'grid', gridTemplateColumns: `repeat(${options.length}, 1fr)`,
        gap: 4 }}>
        {options.map(o => {
          const active = o.value === value;
          return (
            <button key={o.value} type="button" onClick={() => onChange(o.value)}
              style={{ padding: '6px 8px',
                background: active ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)' : 'transparent',
                color: active ? '#fff' : '#475467',
                border: 0, borderRadius: 7,
                fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600,
                letterSpacing: '-0.005em', cursor: 'pointer',
                boxShadow: active ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' : 'none',
                transition: 'background 150ms, color 150ms' }}>
              {o.label}
            </button>
          );
        })}
      </div>
    </div>
  );
}

// Compact checkbox-chip used for hazmat / refrigerated / bonded flags.
function QuoteChip({ label, value, onChange }) {
  return (
    <button type="button" onClick={() => onChange(!value)}
      style={{ padding: '8px 10px',
        background: value ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)' : 'rgba(255,255,255,0.85)',
        color: value ? '#fff' : '#475467',
        border: '1px solid ' + (value ? 'rgba(30,87,214,0.6)' : 'rgba(16,24,40,0.1)'),
        borderRadius: 10,
        fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600,
        letterSpacing: '-0.005em', cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 5,
        boxShadow: value ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.25)' : 'inset 0 1px 0 rgba(255,255,255,0.85)',
        transition: 'background 150ms, color 150ms, border-color 150ms' }}>
      {value && <Icon name="check" size={11}/>}
      {label}
    </button>
  );
}

// ——— Quote face (step 4) — route + cargo details ———
// The user has already picked audience (step 2) and service mode (step 3),
// so this screen is mode-locked: no segmented tabs, just a breadcrumb header
// with a back button to step 3.
function QuoteGlass({ mode, audience, onChangeService, onBack, onClose,
                       initialSummary, onResults }) {
  // When the user clicks "Edit search" from Step 5, ResultsGlass remounts
  // QuoteGlass with the previous summary. Seed every form field from it so
  // the round-trip preserves user input.
  const seed = initialSummary || {};
  // Per-mode default placeholders for what the user enters
  const modeDefaults = {
    'FCL':     { fromHint: 'e.g. Shanghai (CNSHA)', toHint: 'e.g. Long Beach (USLGB)',
                 unitLabel: 'Container', unitOpts: ['1×20GP','1×40GP','1×40HC','2×40HC'] },
    'LCL':     { fromHint: 'e.g. Shanghai (CNSHA)', toHint: 'e.g. Long Beach (USLGB)',
                 unitLabel: 'Cargo',     unitOpts: ['Loose pieces','Palletized','Crated'] },
    'Air':     { fromHint: 'e.g. Shanghai (PVG)',    toHint: 'e.g. Los Angeles (LAX)',
                 unitLabel: 'Cargo',     unitOpts: ['Loose','Palletized','ULD'] },
    'Ground':  { fromHint: 'e.g. Laredo, TX',        toHint: 'e.g. Chicago, IL',
                 unitLabel: 'Equipment', unitOpts: ['Dry van','Reefer','Flatbed','LTL'] },
    'Courier': { fromHint: 'Origin ZIP / city',      toHint: 'Destination ZIP / city',
                 unitLabel: 'Service',   unitOpts: ['Standard','Express','Priority'] },
  };
  const cfg = modeDefaults[mode] || modeDefaults['FCL'];
  const svc = SERVICES.find(s => s.key === mode) || SERVICES[0];
  const aud = AUDIENCES.find(a => a.key === audience);

  const [from, setFrom]         = useStateH(seed.fromLabel || '');
  const [to, setTo]             = useStateH(seed.toLabel   || '');
  const [fromPlace, setFromPlace] = useStateH(seed.fromPlace || null);
  const [toPlace, setToPlace]     = useStateH(seed.toPlace   || null);
  const [weight, setWeight]     = useStateH(seed.weight || '');
  const [ready, setReady]       = useStateH(seed.ready  || '');
  const [unit, setUnit]         = useStateH(
    (seed.mode === mode && seed.unit) ? seed.unit : cfg.unitOpts[0]);
  // Per-mode optional inputs. Default to '' so they don't override the smart
  // fallbacks in buildRequest unless the user explicitly fills them.
  const [originZip, setOriginZip] = useStateH(seed.originZip || ''); // Ground / Courier
  const [destZip,   setDestZip]   = useStateH(seed.destZip   || ''); // Ground / Courier
  const [cbm,       setCbm]       = useStateH(seed.cbm  || ''); // LCL / Air
  const [dimL,      setDimL]      = useStateH(seed.dimL || ''); // Courier
  const [dimW,      setDimW]      = useStateH(seed.dimW || ''); // Courier
  const [dimH,      setDimH]      = useStateH(seed.dimH || ''); // Courier
  const [submitting, setSubmitting] = useStateH(false);
  const [results, setResults]   = useStateH(null); // null = not yet searched
  const [errorMsg, setErrorMsg] = useStateH(null);
  // Lead capture: gate every rate request behind a contact form so we
  // never burn rates-API quota without a captured prospect.
  const [leadModalOpen, setLeadModalOpen] = useStateH(false);
  const [pendingReq, setPendingReq] = useStateH(null);
  const [leadFields, setLeadFields] = useStateH({ name: '', email: '', company: '', phone: '' });
  const [leadSaving, setLeadSaving] = useStateH(false);
  const [leadError, setLeadError] = useStateH(null);

  // ——— Air & LCL capture parity with customer-portal booking forms ———
  // These mirror the AIR/LCL booking-form payload shape so leads carry the
  // same qualifying data a sales rep needs to actually price and follow up.
  // The two transport toggles also drive the derived Incoterm.
  const [originTransport, setOriginTransport]           = useStateH(seed.originTransport      || 'customer_deliver');
  const [destinationTransport, setDestinationTransport] = useStateH(seed.destinationTransport || 'customer_pickup');
  const [originZipQuote, setOriginZipQuote] = useStateH(seed.originZipQuote || ''); // Air/LCL door pickup
  const [destZipQuote,   setDestZipQuote]   = useStateH(seed.destZipQuote   || ''); // LCL door delivery
  const [commodity,      setCommodity]      = useStateH(seed.commodity      || '');
  const [cargoValue,     setCargoValue]     = useStateH(seed.cargoValue     || '');
  const [pieces,         setPieces]         = useStateH(seed.pieces         || '1');
  const [hazmat,         setHazmat]         = useStateH(!!seed.hazmat);
  const [tempControlled, setTempControlled] = useStateH(!!seed.tempControlled);
  const [bonded,         setBonded]         = useStateH(!!seed.bonded);
  // FCL container quantities — keyed by API container code. Replaces the
  // old single `unit` string ("1×40HC") so the form can submit mixed-
  // container quotes ({ '20': 1, '40HC': 2 }) directly to /rates/fcl.
  const [fclContainers, setFclContainers] = useStateH(() => {
    const blank = { '20': 0, '40': 0, '40HC': 0, '45': 0 };
    if (seed.mode === 'FCL' && seed.fclContainers) {
      return { ...blank, ...seed.fclContainers };
    }
    return blank;
  });

  // FCL port + door selectors — mirror the customer-portal FCLBookingForm
  // so the quote request carries an explicit ECU UN/LOCODE and (when SD
  // is chosen) a door-location code. The lists are populated by querying
  // the same ECU proxy the booking form uses; the user can adjust the
  // auto-selected pick from the dropdown.
  const [originPorts, setOriginPorts] = useStateH({
    loading: false, list: [], selected: seed.originPort || null, error: null });
  const [destPorts, setDestPorts] = useStateH({
    loading: false, list: [], selected: seed.destinationPort || null, error: null });
  const [originDoor, setOriginDoor] = useStateH({
    loading: false, list: [], selected: seed.originDoor || null,
    search: (seed.originDoor && seed.originDoor.displayName) || '', error: null });
  const [destDoor, setDestDoor] = useStateH({
    loading: false, list: [], selected: seed.destinationDoor || null,
    search: (seed.destinationDoor && seed.destinationDoor.displayName) || '', error: null });
  const originDoorTimerRef = useRefH(null);
  const destDoorTimerRef   = useRefH(null);
  const [requiredDeliveryDate, setRequiredDeliveryDate] = useStateH(seed.requiredDeliveryDate || '');
  const [notes,          setNotes]          = useStateH(seed.notes || '');
  const [weightUnit,     setWeightUnit]     = useStateH(seed.weightUnit    || 'kg');
  const [dimensionUnit,  setDimensionUnit]  = useStateH(seed.dimensionUnit || 'cm');
  const [moreDetailsOpen, setMoreDetailsOpen] = useStateH(false);

  // ——— Multi-item cargo capture (Air / LCL / Courier) ——————————————
  // Mirrors the customer-portal LCL/AIR booking-form `packages` array so a
  // single quote can describe several different items. Old single-package
  // seeds (top-level weight/dim/pieces) are migrated into a one-row array
  // so "Edit search" still round-trips. Weight & dim units stay global to
  // keep the row short — every package shares the same kg/lbs and cm/in.
  const seededPackages = (() => {
    if (Array.isArray(seed.packages) && seed.packages.length > 0) {
      return seed.packages.map((p, i) => ({
        id: p.id || (Date.now() + i),
        pieces: p.pieces != null ? String(p.pieces) : '1',
        length: p.length != null ? String(p.length) : '',
        width:  p.width  != null ? String(p.width)  : '',
        height: p.height != null ? String(p.height) : '',
        weight: p.weight != null ? String(p.weight) : '',
      }));
    }
    if (seed.weight || seed.dimL || seed.dimW || seed.dimH) {
      return [{ id: 1,
        pieces: seed.pieces || '1',
        length: seed.dimL || '', width: seed.dimW || '', height: seed.dimH || '',
        weight: seed.weight || '' }];
    }
    return [{ id: 1, pieces: '1', length: '', width: '', height: '', weight: '' }];
  })();
  const [packages, setPackages] = useStateH(seededPackages);
  const addPackage = () => setPackages(p => [...p, {
    id: Date.now() + p.length,
    pieces: '1', length: '', width: '', height: '', weight: '' }]);
  const removePackage = (id) => setPackages(p => p.length === 1 ? p : p.filter(x => x.id !== id));
  const updatePackage = (id, field, value) => setPackages(p =>
    p.map(x => x.id === id ? { ...x, [field]: value } : x));

  // Aggregate totals across every package row. Dimensions are always
  // converted to cm before the CBM math so the customer can mix in/cm at
  // their convenience (the unit toggle is global).
  const totals = packages.reduce((acc, pkg) => {
    const qty = Math.max(1, parseInt(pkg.pieces, 10) || 1);
    const w   = Number(pkg.weight) || 0;
    const L   = Number(pkg.length) || 0;
    const W   = Number(pkg.width)  || 0;
    const H   = Number(pkg.height) || 0;
    if (w > 0)               acc.weight += w * qty;
    if (Number(pkg.pieces))  acc.pieces += qty;
    if (L > 0 && W > 0 && H > 0) {
      const lcm = dimensionUnit === 'in' ? L * 2.54 : L;
      const wcm = dimensionUnit === 'in' ? W * 2.54 : W;
      const hcm = dimensionUnit === 'in' ? H * 2.54 : H;
      acc.cbm += (lcm * wcm * hcm / 1_000_000) * qty;
    }
    return acc;
  }, { weight: 0, pieces: 0, cbm: 0 });
  // Convert aggregated weight to kg for the API regardless of the user's
  // chosen unit (the booking forms store both raw + canonical).
  const totalWeightKg = weightUnit === 'lbs'
    ? Math.round(totals.weight * 0.45359237 * 100) / 100
    : Math.round(totals.weight * 100) / 100;
  const totalCbm = Math.round(totals.cbm * 1000) / 1000;
  const totalPieces = Math.max(0, totals.pieces);
  // The form must send something — if the user filled nothing yet, fall
  // back to 1 piece so downstream defaults still kick in.
  const piecesForRequest = totalPieces > 0 ? totalPieces : 1;
  const usesPackagesUI = mode === 'Air' || mode === 'LCL' || mode === 'Courier';

  const incoterm = mode === 'Air'
    ? calculateAirIncoterm(originTransport, destinationTransport)
    : mode === 'LCL'
      ? calculateLCLIncoterm(originTransport, destinationTransport)
      : mode === 'FCL'
        ? calculateFCLIncoterm(originTransport, destinationTransport)
        : 'FOB';

  // Reset unit when mode changes so it's always valid for the new mode
  useEffectH(() => { setUnit(cfg.unitOpts[0]); }, [mode]);
  // Clear results when mode changes — the lane / cargo shape is different.
  useEffectH(() => { setResults(null); setErrorMsg(null); }, [mode]);
  // When a Place is picked and Google returned a postal code, prefill the
  // matching zip input. Don't overwrite if the user has already typed one.
  useEffectH(() => {
    if (fromPlace && fromPlace.zip && !originZip) setOriginZip(fromPlace.zip);
  }, [fromPlace]);
  useEffectH(() => {
    if (toPlace && toPlace.zip && !destZip) setDestZip(toPlace.zip);
  }, [toPlace]);

  // Auto-search ECU ports when the FCL flow has a resolved origin Place.
  // Picks the first match by default (closest to user intent for that city
  // name) — the user can swap via the dropdown.
  useEffectH(() => {
    if (mode !== 'FCL') return;
    if (!fromPlace || !fromPlace.city) {
      setOriginPorts({ loading: false, list: [], selected: null, error: null });
      return;
    }
    let cancelled = false;
    setOriginPorts(p => ({ ...p, loading: true, error: null }));
    ecuSearchLocations(fromPlace.city, 'E', fromPlace.country).then(({ ports }) => {
      if (cancelled) return;
      setOriginPorts(prev => ({
        loading: false,
        list: ports.slice(0, 8),
        selected: prev.selected && ports.some(p => p.unCode === prev.selected.unCode)
          ? prev.selected
          : (ports[0] || null),
        error: ports.length === 0 ? 'No ports matched this city' : null,
      }));
    });
    return () => { cancelled = true; };
  }, [mode, fromPlace && fromPlace.city, fromPlace && fromPlace.country]);

  useEffectH(() => {
    if (mode !== 'FCL') return;
    if (!toPlace || !toPlace.city) {
      setDestPorts({ loading: false, list: [], selected: null, error: null });
      return;
    }
    let cancelled = false;
    setDestPorts(p => ({ ...p, loading: true, error: null }));
    ecuSearchLocations(toPlace.city, 'I', toPlace.country).then(({ ports }) => {
      if (cancelled) return;
      setDestPorts(prev => ({
        loading: false,
        list: ports.slice(0, 8),
        selected: prev.selected && ports.some(p => p.unCode === prev.selected.unCode)
          ? prev.selected
          : (ports[0] || null),
        error: ports.length === 0 ? 'No ports matched this city' : null,
      }));
    });
    return () => { cancelled = true; };
  }, [mode, toPlace && toPlace.city, toPlace && toPlace.country]);

  // Auto-search ECU door locations when SD is chosen. Auto-fills the
  // search input from the resolved Place's city so the user usually
  // doesn't need to type anything; they can refine the search and pick
  // the exact door location from the dropdown.
  useEffectH(() => {
    if (mode !== 'FCL') return;
    if (originTransport !== 'abgroup_pickup') {
      if (originDoorTimerRef.current) clearTimeout(originDoorTimerRef.current);
      return;
    }
    const term = (originDoor.search || (fromPlace && fromPlace.city) || '').trim();
    if (term.length < 2) return;
    if (originDoorTimerRef.current) clearTimeout(originDoorTimerRef.current);
    setOriginDoor(p => ({ ...p, loading: true, error: null }));
    originDoorTimerRef.current = setTimeout(async () => {
      const { doors } = await ecuSearchLocations(term, 'E',
        (fromPlace && fromPlace.country) || null);
      setOriginDoor(prev => ({
        ...prev,
        loading: false,
        list: doors.slice(0, 10),
        selected: prev.selected && doors.some(d => d.code === prev.selected.code)
          ? prev.selected
          : (doors[0] || prev.selected),
        error: doors.length === 0 ? 'No door locations found' : null,
      }));
    }, 400);
    return () => { if (originDoorTimerRef.current) clearTimeout(originDoorTimerRef.current); };
  }, [mode, originTransport, originDoor.search,
      fromPlace && fromPlace.city, fromPlace && fromPlace.country]);

  useEffectH(() => {
    if (mode !== 'FCL') return;
    if (destinationTransport !== 'abgroup_deliver') {
      if (destDoorTimerRef.current) clearTimeout(destDoorTimerRef.current);
      return;
    }
    const term = (destDoor.search || (toPlace && toPlace.city) || '').trim();
    if (term.length < 2) return;
    if (destDoorTimerRef.current) clearTimeout(destDoorTimerRef.current);
    setDestDoor(p => ({ ...p, loading: true, error: null }));
    destDoorTimerRef.current = setTimeout(async () => {
      const { doors } = await ecuSearchLocations(term, 'I',
        (toPlace && toPlace.country) || null);
      setDestDoor(prev => ({
        ...prev,
        loading: false,
        list: doors.slice(0, 10),
        selected: prev.selected && doors.some(d => d.code === prev.selected.code)
          ? prev.selected
          : (doors[0] || prev.selected),
        error: doors.length === 0 ? 'No door locations found' : null,
      }));
    }, 400);
    return () => { if (destDoorTimerRef.current) clearTimeout(destDoorTimerRef.current); };
  }, [mode, destinationTransport, destDoor.search,
      toPlace && toPlace.city, toPlace && toPlace.country]);

  const isAirOrLcl = mode === 'Air' || mode === 'LCL';
  const usesContainersUI = mode === 'FCL';
  const totalContainers = Object.values(fclContainers).reduce((a, b) => a + (Number(b) || 0), 0);
  const zipOk = (z) => /^\d{5}$/.test((z || '').trim());
  // Air / LCL / Courier capture cargo as a packages array — the form is
  // valid as long as at least one row has either a weight or a complete
  // L+W+H. FCL submits container counts; Ground keeps the single weight
  // field.
  const hasValidPackage = packages.some(p =>
    Number(p.weight) > 0
    || (Number(p.length) > 0 && Number(p.width) > 0 && Number(p.height) > 0));
  const baseReady = from.trim().length > 1 && to.trim().length > 1
    && (usesPackagesUI ? hasValidPackage
        : usesContainersUI ? totalContainers > 0
        : weight.trim().length > 0)
    && (isAirOrLcl || usesContainersUI || ready.trim().length > 0);
  // Air / LCL / FCL all expose origin & destination transport pickers —
  // require both, plus a valid 5-digit ZIP whenever a "door" service is
  // chosen on either side. Air still skips the destination zip (no door
  // delivery toggle).
  const needsTransport = isAirOrLcl || usesContainersUI;
  // FCL door pickup/delivery accepts an ECU door-location pick in lieu of a
  // hand-typed 5-digit zip — the selected door object carries the zip the
  // upstream API needs.
  const fclOriginDoorOk = mode === 'FCL'
    && (zipOk(originZipQuote) || !!(originDoor && originDoor.selected));
  const fclDestDoorOk = mode === 'FCL'
    && (zipOk(destZipQuote) || !!(destDoor && destDoor.selected));
  const transportReady = !needsTransport || (
    !!originTransport && !!destinationTransport
    && (originTransport !== 'abgroup_pickup'
        || (mode === 'FCL' ? fclOriginDoorOk : zipOk(originZipQuote)))
    && ((mode === 'LCL' || mode === 'FCL')
        ? (destinationTransport !== 'abgroup_deliver'
           || (mode === 'FCL' ? fclDestDoorOk : zipOk(destZipQuote)))
        : true)
  );
  const ready_ok = baseReady && transportReady;

  // Parse a unit option like "1×40HC" into { code: "40HC", count: 1 }.
  // Falls back to { code: "40", count: 1 } if the format is unfamiliar.
  const parseFCLUnit = (u) => {
    const m = (u || '').match(/^(\d+)\s*[x×]\s*(20GP|40HC|40GP|45)$/i);
    if (!m) return { code: '40', count: 1 };
    const sizeMap = { '20GP': '20', '40GP': '40', '40HC': '40HC', '45': '45' };
    return { code: sizeMap[m[2].toUpperCase()] || '40', count: parseInt(m[1], 10) || 1 };
  };

  // Build the (url, body) pair for each transport mode. Uses the form fields
  // the user already filled (from / to / unit / weight / ready) plus sensible
  // defaults for fields the form doesn't yet collect (dimensions, freight
  // class, package counts). Replace the defaults with explicit form fields
  // once the UI grows.
  const buildRequest = () => {
    const BASE = 'https://us-central1-abgs-calculadora.cloudfunctions.net/walioPublicAPI/v1/rates/';
    const wRaw = Number(weight) || 0;
    // Coerce to kg/cm if the user entered imperial units (mirrors the
    // booking form, which stores weight/dim units alongside the values).
    const wKg = weightUnit === 'lbs' ? Math.round(wRaw * 0.45359237 * 100) / 100 : wRaw;
    const toCm = (v) => {
      const n = Number(v);
      if (!(n > 0)) return 0;
      return dimensionUnit === 'in' ? Math.round(n * 2.54 * 100) / 100 : n;
    };
    const piecesNum    = Math.max(1, parseInt(pieces, 10) || 1);
    const cargoValueNum = Number(cargoValue) || 0;
    const commodityStr  = commodity.trim() || 'General Cargo';
    // Normalize the multi-package state into the canonical kg/cm payload
    // shape used by AIRBookingForm/LCLBookingForm (weightUnit: 'KG',
    // dimensionUnit: 'CM' on every row). Only rows with at least a weight
    // or a complete L+W+H are kept.
    const apiPackages = packages
      .filter(p => Number(p.weight) > 0
        || (Number(p.length) > 0 && Number(p.width) > 0 && Number(p.height) > 0))
      .map(p => {
        const qty = Math.max(1, parseInt(p.pieces, 10) || 1);
        const wRow = Number(p.weight) || 0;
        const wRowKg = weightUnit === 'lbs'
          ? Math.round(wRow * 0.45359237 * 100) / 100
          : wRow;
        return {
          weight: wRowKg,
          weightUnit: 'KG',
          length: toCm(p.length),
          width:  toCm(p.width),
          height: toCm(p.height),
          dimensionUnit: 'CM',
          pieces: qty,
          description: commodityStr,
        };
      });
    const oZipQuote     = (originZipQuote || '').trim();
    const dZipQuote     = (destZipQuote   || '').trim();
    // Manual zip override → resolved Place zip → empty.
    const oZip = (originZip || (fromPlace && fromPlace.zip) || '').trim();
    const dZip = (destZip   || (toPlace   && toPlace.zip)   || '').trim();
    const placeOrigin = {
      country: fromPlace.country || '', city: fromPlace.city || '',
      state:   fromPlace.state || '',   zip:  oZip,
      lat: fromPlace.lat, lng: fromPlace.lng,
    };
    const placeDest = {
      country: toPlace.country || '', city: toPlace.city || '',
      state:   toPlace.state || '',   zip:  dZip,
      lat: toPlace.lat, lng: toPlace.lng,
    };
    const placeWithCoords = (p) => ({ ...p, coordinates: { lat: p.lat, lng: p.lng } });
    // User-provided dimension/volume overrides → fallback to estimates.
    // Volume is in cbm regardless of dimensionUnit (it's already metric).
    const userCbm = Number(cbm);
    const userL   = toCm(dimL);
    const userW   = toCm(dimW);
    const userH   = toCm(dimH);

    if (mode === 'FCL') {
      // Drop zero-quantity containers so the API only sees what the user
      // actually wants priced.
      const containers = {};
      Object.keys(fclContainers).forEach((k) => {
        const n = Number(fclContainers[k]) || 0;
        if (n > 0) containers[k] = n;
      });
      // For door pickup/delivery, prefer the door-location's zip; fall
      // back to the hand-typed zip; finally to the Place's postal code
      // for port-to-port moves.
      const originDoorZip = (originDoor.selected && originDoor.selected.zipCode) || '';
      const destDoorZip   = (destDoor.selected   && destDoor.selected.zipCode)   || '';
      const fclOriginZip = originTransport === 'abgroup_pickup'
        ? (oZipQuote || originDoorZip)
        : ((fromPlace && fromPlace.zip) || '');
      const fclDestZip = destinationTransport === 'abgroup_deliver'
        ? (dZipQuote || destDoorZip)
        : ((toPlace && toPlace.zip) || '');
      return { url: BASE + 'fcl', body: {
        serviceType: 'OCEAN_FCL',
        origin: { ...placeOrigin, zip: fclOriginZip },
        destination: { ...placeDest, zip: fclDestZip },
        cargo: { containers,
                 commodity: 'General Cargo',
                 cargoValue: 50000,
                 hazmat,
                 refrigerated: tempControlled,
                 bonded },
        options: { incoterms: incoterm,
                   originTransport,
                   destinationTransport,
                   isVehicleShipping: false,
                   readyDate: ready || undefined },
        // ECU UN/LOCODE port + door-location pickers mirror the customer-
        // portal FCLBookingForm; included on the rate request so the
        // upstream Cloud Function can route to ECU with the same codes
        // the booking stage will use.
        ecuOriginPort:        (originPorts.selected && originPorts.selected.unCode)   || null,
        ecuOriginPortName:    (originPorts.selected && originPorts.selected.portName) || null,
        ecuDestinationPort:     (destPorts.selected && destPorts.selected.unCode)     || null,
        ecuDestinationPortName: (destPorts.selected && destPorts.selected.portName)   || null,
        ecuOriginDoorCode:        originTransport === 'abgroup_pickup'
          ? ((originDoor.selected && originDoor.selected.code) || (originDoor.list[0] && originDoor.list[0].code) || null) : null,
        ecuOriginDoorName:        originTransport === 'abgroup_pickup'
          ? ((originDoor.selected && originDoor.selected.displayName) || (originDoor.list[0] && originDoor.list[0].displayName) || null) : null,
        ecuDestinationDoorCode:   destinationTransport === 'abgroup_deliver'
          ? ((destDoor.selected && destDoor.selected.code) || (destDoor.list[0] && destDoor.list[0].code) || null) : null,
        ecuDestinationDoorName:   destinationTransport === 'abgroup_deliver'
          ? ((destDoor.selected && destDoor.selected.displayName) || (destDoor.list[0] && destDoor.list[0].displayName) || null) : null,
      }};
    }
    if (mode === 'LCL') {
      // Multi-row totals: prefer the dimension-derived CBM, fall back to a
      // weight-based stowage estimate (~1 cbm per 250 kg, min 1 cbm) when
      // dimensions weren't filled. totalWeightKg / totalPieces come from
      // the same packages array we send in the body.
      const wTotalKg = totalWeightKg > 0 ? totalWeightKg : wKg;
      const volCbm = totalCbm > 0 ? Math.max(0.1, totalCbm)
        : Math.max(1, Math.round((wTotalKg / 250) * 10) / 10);
      const apiPkgs = apiPackages.length > 0 ? apiPackages
        : [{ weight: wTotalKg, weightUnit: 'KG',
             length: 120, width: 100, height: 80, dimensionUnit: 'CM',
             pieces: piecesForRequest, description: commodityStr }];
      return { url: BASE + 'lcl', body: {
        origin: placeWithCoords(placeOrigin), destination: placeWithCoords(placeDest),
        cargo: { weight: wTotalKg, cbm: volCbm, pieces: piecesForRequest,
                 cargoValue: cargoValueNum || 50000,
                 commodity: commodityStr,
                 hazmat, refrigerated: tempControlled, bonded,
                 packages: apiPkgs },
        incoterm,
        originTransport, destinationTransport,
        originZip:      originTransport === 'abgroup_pickup'   ? oZipQuote : null,
        destinationZip: destinationTransport === 'abgroup_deliver' ? dZipQuote : null,
        readyDate: ready || null,
        requiredDeliveryDate: requiredDeliveryDate || null,
        notes: notes.trim() || null,
        weightUnit, dimensionUnit,
      }};
    }
    if (mode === 'Air') {
      // Air chargeable weight uses 167 kg/cbm. Use the multi-row totals;
      // when dimensions are missing, estimate volume from total weight.
      const wTotalKg = totalWeightKg > 0 ? totalWeightKg : wKg;
      const volCbm = totalCbm > 0 ? Math.max(0.01, totalCbm)
        : Math.max(0.1, Math.round((wTotalKg / 250) * 100) / 100);
      const apiPkgs = apiPackages.length > 0 ? apiPackages
        : [{ weight: wTotalKg, weightUnit: 'KG',
             length: 120, width: 100, height: 80, dimensionUnit: 'CM',
             pieces: piecesForRequest, description: commodityStr }];
      return { url: BASE + 'air', body: {
        serviceType: 'AIR',
        origin: placeWithCoords(placeOrigin), destination: placeWithCoords(placeDest),
        cargo: { weight: wTotalKg, cbm: volCbm, pieces: piecesForRequest,
          cargoValue: cargoValueNum || 50000,
          commodity: commodityStr,
          hazmat, temperatureControlled: tempControlled, bonded,
          packages: apiPkgs },
        incoterm,
        originTransport, destinationTransport,
        originZip: originTransport === 'abgroup_pickup' ? oZipQuote : null,
        readyDate: ready || null,
        requiredDeliveryDate: requiredDeliveryDate || null,
        notes: notes.trim() || null,
        weightUnit, dimensionUnit,
      }};
    }
    if (mode === 'Ground') {
      // 'LTL' option → mode=LTL; everything else (Dry van / Reefer / Flatbed)
      // → FTL. Honour custom L/W/H if filled; otherwise default pallet.
      const isLTL = /^LTL$/i.test(unit || '');
      const itemL = userL > 0 ? userL : 120;
      const itemW = userW > 0 ? userW : 100;
      const itemH = userH > 0 ? userH : 150;
      return { url: BASE + 'land', body: {
        origin: placeOrigin, destination: placeDest,
        cargo: { mode: isLTL ? 'LTL' : 'FTL',
          items: [{ weight: wKg, weightUnit: 'KG', length: itemL, width: itemW, height: itemH,
                    dimensionUnit: 'CM', pieces: 1, freightClass: '50' }],
          totalWeight: wKg, weightUnit: 'KG', commodity: 'General Cargo' },
        options: {},
      }};
    }
    if (mode === 'Courier') {
      // Courier rates are very dimension-sensitive. Honour the user's
      // multi-row entry; if no packages have weight or full dimensions,
      // fall back to a single medium-box (30×25×20 cm).
      const courierPkgs = packages
        .filter(p => Number(p.weight) > 0
          || (Number(p.length) > 0 && Number(p.width) > 0 && Number(p.height) > 0))
        .map(p => {
          const wRow = Number(p.weight) || 0;
          const wRowKg = weightUnit === 'lbs'
            ? Math.round(wRow * 0.45359237 * 100) / 100
            : wRow;
          return {
            weight: wRowKg || 1,
            length: toCm(p.length) || 30,
            width:  toCm(p.width)  || 25,
            height: toCm(p.height) || 20,
            quantity: Math.max(1, parseInt(p.pieces, 10) || 1),
          };
        });
      const finalPkgs = courierPkgs.length > 0 ? courierPkgs
        : [{ weight: 1, length: 30, width: 25, height: 20, quantity: 1 }];
      return { url: BASE + 'courier', body: {
        origin: placeOrigin, destination: placeDest,
        packages: finalPkgs,
        batteryType: 'none', cargoValue: 200, commodity: 'General Cargo',
      }};
    }
    return null;
  };

  // Step 1: validate the form, build the request, then open the lead-capture
  // modal. Nothing is sent to the rates API until the contact form is filled.
  const handleSubmit = (e) => {
    e.preventDefault();
    if (!ready_ok || submitting || leadModalOpen) return;
    setErrorMsg(null);

    if (!fromPlace || !fromPlace.lat || !toPlace || !toPlace.lat) {
      setErrorMsg('Pick locations from the suggestions so we can match carrier lanes.');
      return;
    }
    const req = buildRequest();
    if (!req) {
      setErrorMsg('Quotes for ' + svc.name + ' are coming soon — a specialist will follow up.');
      return;
    }
    setPendingReq(req);
    setLeadError(null);
    setLeadModalOpen(true);
  };

  const cancelLeadCapture = () => {
    if (leadSaving) return;
    setLeadModalOpen(false);
    setPendingReq(null);
    setLeadError(null);
  };

  // Step 2: save the lead, then call the rates API. The lead document
  // includes the rate request payload so the admin can see what they
  // asked for, even if the rates fetch fails afterwards.
  const submitLeadAndFetch = async (e) => {
    e.preventDefault();
    if (leadSaving) return;
    const f = leadFields;
    const name    = (f.name || '').trim();
    const email   = (f.email || '').trim();
    const company = (f.company || '').trim();
    const phone   = (f.phone || '').trim();
    const validEmail = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
    if (!name || !email || !company || !phone) {
      setLeadError('Please complete all fields.');
      return;
    }
    if (!validEmail) {
      setLeadError('Please enter a valid email address.');
      return;
    }
    if (!pendingReq) {
      setLeadError('Something went wrong. Close and try again.');
      return;
    }

    setLeadSaving(true);
    setLeadError(null);

    const req = pendingReq;
    const modeKey = (req.url.split('/').pop() || '').toUpperCase();
    // Hoist a small Air/LCL summary onto the top-level lead doc so the admin
    // can see pickup/delivery/incoterm/commodity at a glance without parsing
    // the full requestBody snapshot.
    const portTerm = mode === 'Air' ? 'airport' : 'port';
    const airLclSummary = isAirOrLcl ? {
      pickup: originTransport === 'abgroup_pickup'
        ? { type: 'door', zip: (originZipQuote || '').trim() || null }
        : { type: portTerm },
      delivery: destinationTransport === 'abgroup_deliver'
        ? { type: 'door', zip: (destZipQuote || '').trim() || null }
        : { type: portTerm },
      incoterm,
      commodity: commodity.trim() || null,
      cargoValue: Number(cargoValue) || null,
      pieces: Math.max(1, parseInt(pieces, 10) || 1),
      flags: { hazmat, tempControlled, bonded },
      requiredDeliveryDate: requiredDeliveryDate || null,
      notes: notes.trim() || null,
      weightUnit, dimensionUnit,
    } : {};
    const leadDoc = {
      name, email, company, phone,
      audience: aud ? aud.name : null,
      service: svc.name,
      mode: modeKey,
      lane: { from: from.trim(), to: to.trim() },
      origin: req.body.origin || null,
      destination: req.body.destination || null,
      cargo: req.body.cargo || null,
      readyDate: ready || null,
      ...airLclSummary,
      requestUrl: req.url,
      requestBody: req.body,
      source: 'homepage-quote-widget',
      attribution: window.__abgsHomepageQuoteAttribution || 'homepage-hero',
      pageUrl: typeof location !== 'undefined' ? location.href : null,
      userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : null,
    };

    try {
      if (window.__abgsLeads && window.__abgsLeads.saveLead) {
        await window.__abgsLeads.saveLead(leadDoc);
      } else {
        // Firebase didn't load — fail fast so we don't quietly bypass the gate.
        throw new Error('Lead service unavailable. Please refresh and try again.');
      }
    } catch (err) {
      setLeadSaving(false);
      setLeadError(err && err.message ? err.message : 'Could not save your details. Please try again.');
      return;
    }

    setLeadSaving(false);
    setLeadModalOpen(false);
    setPendingReq(null);
    // Now run the original rate fetch flow.
    setSubmitting(true);
    setResults(null);
    try {
      const res = await fetch(req.url, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(req.body),
      });
      if (!res.ok) {
        if (res.status === 429) throw new Error('Too many requests — try again in a moment.');
        throw new Error('Quote service is unavailable. Please try again.');
      }
      const json = await res.json();
      const rates = Array.isArray(json.rates) ? json.rates : [];
      rates.sort((a, b) => (rateTotalOf(a) || Infinity) - (rateTotalOf(b) || Infinity));
      // Empty: keep the user in Step 4 with the inline "no live rates" message.
      // Otherwise hand off to ResultsGlass via the parent so it can render
      // dedicated Step 5 with a wider canvas.
      if (rates.length > 0 && typeof onResults === 'function') {
        onResults(rates, {
          mode, modeName: svc.name, modeIcon: svc.icon,
          audience, audienceName: aud ? aud.name : null,
          fromLabel: from, toLabel: to,
          fromPlace, toPlace,
          unit, weight, ready,
          cbm, dimL, dimW, dimH, originZip, destZip,
          fclContainers,
          originPort: originPorts.selected || null,
          destinationPort: destPorts.selected || null,
          originDoor: originDoor.selected || null,
          destinationDoor: destDoor.selected || null,
          // Air/LCL capture so "Edit search" preserves user input
          originTransport, destinationTransport,
          originZipQuote, destZipQuote,
          commodity, cargoValue, pieces,
          hazmat, tempControlled, bonded,
          requiredDeliveryDate, notes,
          weightUnit, dimensionUnit,
          packages,
        });
      } else {
        setResults(rates);
      }
    } catch (err) {
      setErrorMsg(err.message || 'Unable to reach the quote service.');
    } finally {
      setSubmitting(false);
    }
  };

  return (
    <React.Fragment>
    <form onSubmit={handleSubmit} style={{ position: 'relative',
      background: 'rgba(255,255,255,0.62)',
      backdropFilter: 'blur(50px) saturate(1.6)', WebkitBackdropFilter: 'blur(50px) saturate(1.6)',
      border: '1px solid rgba(255,255,255,0.7)', borderRadius: 24,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85), 0 24px 48px -12px rgba(16,24,40,0.18)',
      overflow: 'hidden' }}>
      <LiquidBorder radius={24} alpha={[0.9, 0.35]}/>

      {/* Header — breadcrumb + back/close */}
      <div style={{ position: 'relative', padding: '12px 14px 12px 12px',
        borderBottom: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10,
        background: 'linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>
          <button type="button" onClick={onBack} aria-label="Back to service selection"
            style={{ width: 28, height: 28, borderRadius: 9999, flexShrink: 0,
              background: '#ffffff', border: '1px solid rgba(16,24,40,0.1)',
              boxShadow: '0 1px 2px rgba(16,24,40,0.06), inset 0 1px 0 rgba(255,255,255,0.9)',
              display: 'inline-grid', placeItems: 'center', cursor: 'pointer', color: '#475467' }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
              stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 18l-6-6 6-6"/>
            </svg>
          </button>
          <span style={{ width: 26, height: 26, borderRadius: 7, flexShrink: 0,
            background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
            display: 'inline-grid', placeItems: 'center', color: '#fff',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' }}>
            <Icon name={svc.icon} size={13}/>
          </span>
          <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.15, minWidth: 0 }}>
            <span style={{ fontSize: 13, fontWeight: 700, color: '#0B1220',
              letterSpacing: '-0.005em', whiteSpace: 'nowrap', overflow: 'hidden',
              textOverflow: 'ellipsis' }}>{svc.name} quote</span>
            <span style={{ fontSize: 10.5, color: '#667085',
              fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em',
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {aud ? `${aud.name} · ` : ''}Step 4 of 5 · route &amp; cargo
            </span>
          </div>
        </div>
        <button type="button" onClick={onClose} style={{ padding: '5px 11px', flexShrink: 0,
          background: 'rgba(255,255,255,0.7)',
          border: '1px solid rgba(16,24,40,0.1)', borderRadius: 9999, cursor: 'pointer',
          color: '#475467', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600 }}>
          Close
        </button>
      </div>

      {/* Step indicator */}
      <StepDots step={4}/>

      {/* Inputs */}
      <div style={{ position: 'relative', padding: '0 18px 8px' }}>
        {/* Lane + transport — From and To always render; on Air/LCL the
            Pickup and Delivery segmented selectors join the same grid so
            each column reads as a logical "origin" / "destination" pair.
            Mobile collapses the grid to a single column and reorders to
            [From → Pickup → To → Delivery] for breathing room (handled
            in the hero <style> block via grid-template-areas). */}
        <div style={{ position: 'relative', marginBottom: 10 }}>
          <div data-quote-row="lane-grid"
            data-has-transport={isAirOrLcl ? 'true' : 'false'}
            style={{ display: 'grid', gridTemplateColumns: '1fr 1fr',
            gridTemplateAreas: isAirOrLcl
              ? '"from to" "pickup delivery"'
              : '"from to"',
            gap: 8 }}>
            <div style={{ gridArea: 'from' }}>
              <QuotePlacesInput
                icon="globe"
                label="From"
                placeholder={cfg.fromHint}
                value={from}
                onChange={setFrom}
                onSelect={setFromPlace}
              />
            </div>
            <div style={{ gridArea: 'to' }}>
              <QuotePlacesInput
                icon="track"
                label="To"
                placeholder={cfg.toHint}
                value={to}
                onChange={setTo}
                onSelect={setToPlace}
              />
            </div>
            {isAirOrLcl && (
              <div style={{ gridArea: 'pickup' }}>
                <QuoteSegmented label="Pickup" value={originTransport} onChange={setOriginTransport}
                  options={[
                    { value: 'customer_deliver', label: mode === 'Air' ? 'Drop at airport' : 'Drop at port' },
                    { value: 'abgroup_pickup',   label: 'Door pickup' },
                  ]}/>
              </div>
            )}
            {isAirOrLcl && (
              <div style={{ gridArea: 'delivery' }}>
                <QuoteSegmented label="Delivery" value={destinationTransport} onChange={setDestinationTransport}
                  options={[
                    { value: 'customer_pickup',  label: mode === 'Air' ? 'Pickup at airport' : 'Pickup at port' },
                    { value: 'abgroup_deliver',  label: 'Door delivery' },
                  ]}/>
              </div>
            )}
          </div>
          {/* Swap button — overlays the From/To gap on desktop. Hidden
              on mobile (each column stacks, so the swap target is no
              longer adjacent). */}
          <button type="button" aria-label="Swap origin and destination"
            data-swap-btn
            onClick={() => {
              const f = from, fp = fromPlace;
              setFrom(to); setTo(f);
              setFromPlace(toPlace); setToPlace(fp);
            }}
            style={{ position: 'absolute', left: '50%',
              top: 'calc(50% / ' + (isAirOrLcl ? '2' : '1') + ' + 2px)',
              transform: 'translate(-50%, -50%)',
              width: 28, height: 28, borderRadius: 9999,
              background: '#fff', border: '1px solid rgba(16,24,40,0.1)',
              boxShadow: '0 2px 6px rgba(16,24,40,0.1)',
              display: 'grid', placeItems: 'center', cursor: 'pointer',
              color: '#1E57D6', zIndex: 2 }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
              stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M7 10l-4 4 4 4 M3 14h14 M17 14l4-4-4-4 M21 10H7"/>
            </svg>
          </button>
        </div>

        {/* Origin / destination ZIP — Ground & Courier need it for routing.
            Auto-populated from Google address_components when available. */}
        {(mode === 'Ground' || mode === 'Courier') && (
          <div data-quote-row="zip"
            style={{ display: 'grid', gridTemplateColumns: '1fr 1fr',
            gap: 8, marginBottom: 10 }}>
            <QuoteSmallInput label="Origin ZIP" type="text"
              value={originZip} onChange={setOriginZip} placeholder="33101"/>
            <QuoteSmallInput label="Dest. ZIP" type="text"
              value={destZip} onChange={setDestZip} placeholder="30301"/>
          </div>
        )}

        {/* AIR/LCL — conditional door-pickup / door-delivery ZIP fields.
            Rendered separately from the lane grid so they only appear
            when the user actually selected door service. (FCL renders
            its own zip inputs inside the FCL block below.) */}
        {isAirOrLcl && (originTransport === 'abgroup_pickup'
          || (mode === 'LCL' && destinationTransport === 'abgroup_deliver')) && (
          <div data-quote-row="zip-quote"
            style={{ display: 'grid',
            gridTemplateColumns: (originTransport === 'abgroup_pickup'
              && mode === 'LCL' && destinationTransport === 'abgroup_deliver')
              ? '1fr 1fr' : '1fr',
            gap: 8, marginBottom: 10 }}>
            {originTransport === 'abgroup_pickup' && (
              <QuoteSmallInput label="Pickup ZIP" type="text"
                value={originZipQuote} onChange={setOriginZipQuote} placeholder="33101"/>
            )}
            {mode === 'LCL' && destinationTransport === 'abgroup_deliver' && (
              <QuoteSmallInput label="Delivery ZIP" type="text"
                value={destZipQuote} onChange={setDestZipQuote} placeholder="30301"/>
            )}
          </div>
        )}

        {/* Mode-specific row above the items table. Air/LCL: nothing (cargo
            type, weight, ready and commodity were intentionally removed —
            those go to the booking stage). Courier: keep service select +
            ready, but hand weight off to the per-row table below. FCL/
            Ground: container/equipment + single weight + ready as before. */}
        {!usesPackagesUI && !usesContainersUI && (
          <div data-quote-row="config-3"
            style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr 1fr',
            gap: 8, marginBottom: 10 }}>
            <QuoteSelect label={cfg.unitLabel} value={unit} onChange={setUnit}
              options={cfg.unitOpts}/>
            <QuoteSmallInput
              label="Weight"
              suffix='kg'
              type="number" placeholder="0" value={weight} onChange={setWeight}/>
            <QuoteSmallInput label="Ready" type="date"
              value={ready} onChange={setReady} placeholder="—"/>
          </div>
        )}

        {/* FCL — full booking-form parity. Inland transportation per side
            (CY/SD radio cards) drives the derived Shipping Terms badge.
            Cargo flag chips (Temperature Control / Hazardous Materials /
            Bonded Cargo) toggle the matching booleans the API consumes.
            Container grid lets the user mix 20'/40'/40HC/45' quantities
            in a single quote. */}
        {usesContainersUI && (
          <div data-quote-row="fcl-block">
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr',
              gap: 10, marginBottom: 10 }}>
              <InlandTransportColumn
                label="Origin · Inland transportation"
                value={originTransport}
                onChange={setOriginTransport}
                options={[
                  { value: 'customer_deliver', code: 'CY',
                    title: 'I will arrange to deliver the container to the port/inland location',
                    desc: 'We will match it with the nearest port of departure if this option is selected' },
                  { value: 'abgroup_pickup', code: 'SD',
                    title: 'I want AB Group Shipping to pick up the container at my facility',
                    desc: 'AB Group Shipping arranges pickup from your location' },
                ]}/>
              <InlandTransportColumn
                label="Destination · Inland transportation"
                value={destinationTransport}
                onChange={setDestinationTransport}
                options={[
                  { value: 'customer_pickup', code: 'CY',
                    title: 'I will arrange for pick up of the container from the port/inland location',
                    desc: 'We will match it with the nearest port of arrival if this option is selected' },
                  { value: 'abgroup_deliver', code: 'SD',
                    title: 'I want AB Group Shipping to deliver the container at my facility',
                    desc: 'AB Group Shipping arranges delivery to your final destination' },
                ]}/>
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr',
              gap: 10, marginBottom: 10 }}>
              <div>
                {originTransport === 'customer_deliver' ? (
                  <FCLLocationSelector kind="port"
                    label="Origin port"
                    loading={originPorts.loading}
                    options={originPorts.list}
                    selected={originPorts.selected}
                    onSelect={(p) => setOriginPorts(prev => ({ ...prev, selected: p }))}
                    error={originPorts.error}
                    placeholder={fromPlace ? 'Pick a port' : 'Select origin city first'}
                  />
                ) : (
                  <FCLLocationSelector kind="door"
                    label="Origin door location"
                    loading={originDoor.loading}
                    options={originDoor.list}
                    selected={originDoor.selected}
                    onSelect={(d) => {
                      setOriginDoor(prev => ({ ...prev, selected: d, search: d.displayName }));
                      if (d.zipCode) setOriginZipQuote(d.zipCode);
                    }}
                    onSearch={(v) => setOriginDoor(prev => ({ ...prev, search: v }))}
                    searchValue={originDoor.search}
                    error={originDoor.error}
                    placeholder={fromPlace ? 'Search door location' : 'Select origin city first'}
                  />
                )}
              </div>
              <div>
                {destinationTransport === 'customer_pickup' ? (
                  <FCLLocationSelector kind="port"
                    label="Destination port"
                    loading={destPorts.loading}
                    options={destPorts.list}
                    selected={destPorts.selected}
                    onSelect={(p) => setDestPorts(prev => ({ ...prev, selected: p }))}
                    error={destPorts.error}
                    placeholder={toPlace ? 'Pick a port' : 'Select destination city first'}
                  />
                ) : (
                  <FCLLocationSelector kind="door"
                    label="Destination door location"
                    loading={destDoor.loading}
                    options={destDoor.list}
                    selected={destDoor.selected}
                    onSelect={(d) => {
                      setDestDoor(prev => ({ ...prev, selected: d, search: d.displayName }));
                      if (d.zipCode) setDestZipQuote(d.zipCode);
                    }}
                    onSearch={(v) => setDestDoor(prev => ({ ...prev, search: v }))}
                    searchValue={destDoor.search}
                    error={destDoor.error}
                    placeholder={toPlace ? 'Search door location' : 'Select destination city first'}
                  />
                )}
              </div>
            </div>

            <div style={{ marginBottom: 10, padding: '8px 12px',
              background: 'rgba(30,87,214,0.08)',
              border: '1px solid rgba(30,87,214,0.25)',
              borderRadius: 10,
              display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
              <span style={{ padding: '3px 8px', borderRadius: 6,
                background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
                color: '#fff', fontSize: 10.5, fontWeight: 800, letterSpacing: '0.05em',
                fontFamily: 'JetBrains Mono, monospace' }}>{incoterm}</span>
              <span style={{ fontSize: 11, fontWeight: 700, color: '#1846B0' }}>
                Shipping Terms
              </span>
              <span style={{ fontSize: 10.5, color: '#475467', flex: 1, minWidth: 0 }}>
                {FCL_INCOTERM_DESC[incoterm] || ''}
              </span>
            </div>

            <div style={{ marginBottom: 10, display: 'flex', alignItems: 'center',
              gap: 6, flexWrap: 'wrap' }}>
              <span style={{ fontSize: 10, fontWeight: 800, color: '#475467',
                letterSpacing: '0.12em', textTransform: 'uppercase', marginRight: 4 }}>
                Cargo
              </span>
              <QuoteChip label="Temperature Control"  value={tempControlled} onChange={setTempControlled}/>
              <QuoteChip label="Hazardous Materials" value={hazmat}         onChange={setHazmat}/>
              <QuoteChip label="Bonded Cargo"        value={bonded}         onChange={setBonded}/>
            </div>

            <div data-fcl-grid
              style={{ marginBottom: 10, display: 'grid',
              gridTemplateColumns: 'repeat(4, minmax(0,1fr))', gap: 8 }}>
              {FCL_CONTAINERS.map(spec => (
                <FCLContainerCard
                  key={spec.code}
                  spec={spec}
                  count={fclContainers[spec.code] || 0}
                  onChange={(next) => setFclContainers({ ...fclContainers, [spec.code]: next })}
                />
              ))}
            </div>
          </div>
        )}
        {mode === 'Courier' && (
          <div data-quote-row="config-2"
            style={{ display: 'grid', gridTemplateColumns: '1.1fr 1fr',
            gap: 8, marginBottom: 10 }}>
            <QuoteSelect label={cfg.unitLabel} value={unit} onChange={setUnit}
              options={cfg.unitOpts}/>
            <QuoteSmallInput label="Ready" type="date"
              value={ready} onChange={setReady} placeholder="—"/>
          </div>
        )}

        {/* Items / dimensions — multi-row capture for Air, LCL and
            Courier. Each row is `qty × L × W × H × weight` followed by a
            delete button. cm/in and kg/lbs are global toggles applied to
            every row. Totals (weight, CBM, pieces) recompute live. The
            grid uses minmax(0, …) so cells shrink cleanly on mobile.  */}
        {usesPackagesUI && (
          <div style={{ marginBottom: 10 }}>
            {/* Items header — solid white card so the title stays legible
                over the globe scene. Desktop: title left, unit toggles
                right, single line. Mobile (CSS-driven): title row on top
                centered; toggles row below, side-by-side and centered as
                an even 2-col grid (each toggle equal width, perfectly
                aligned with the other). */}
            <div data-items-header
              style={{ display: 'flex', alignItems: 'center',
              justifyContent: 'space-between', gap: 8, marginBottom: 6,
              flexWrap: 'wrap',
              padding: '6px 10px 6px 12px',
              background: 'linear-gradient(180deg, rgba(255,255,255,0.96) 0%, rgba(247,249,252,0.92) 100%)',
              border: '1px solid rgba(16,24,40,0.1)',
              borderRadius: 9,
              boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85), 0 1px 2px rgba(16,24,40,0.04)' }}>
              <div data-items-title
                style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
                <span style={{ width: 18, height: 18, borderRadius: 5,
                  background: 'linear-gradient(180deg, #2E6AE8 0%, #1846B0 100%)',
                  display: 'inline-grid', placeItems: 'center', color: '#fff',
                  flexShrink: 0,
                  boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 1px 2px rgba(30,87,214,0.35)' }}>
                  <Icon name="container" size={11}/>
                </span>
                <span style={{ fontSize: 10, fontWeight: 800, color: '#0B1220',
                  letterSpacing: '0.12em', textTransform: 'uppercase' }}>
                  Items
                </span>
                {totalPieces > 0 && (
                  <span style={{ fontSize: 9.5, color: '#475467', fontWeight: 600,
                    fontFamily: 'JetBrains Mono, monospace',
                    letterSpacing: '0.04em', marginLeft: 2 }}>
                    · {totalPieces} pcs
                  </span>
                )}
              </div>
              <div data-unit-toggles
                style={{ display: 'inline-flex', gap: 6, alignItems: 'center' }}>
                <UnitToggle label="Dim" value={dimensionUnit} onChange={setDimensionUnit} options={['cm','in']}/>
                <UnitToggle label="Wt"  value={weightUnit}    onChange={setWeightUnit}    options={['kg','lbs']}/>
              </div>
            </div>
            {packages.map((pkg) => (
              <div key={pkg.id} data-quote-row="packages-row"
                style={{ display: 'grid',
                gridTemplateColumns: 'minmax(0,0.6fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1fr) minmax(0,1.1fr) 36px',
                gap: 6, alignItems: 'stretch',
                marginBottom: 8,
                padding: '8px 10px',
                background: 'linear-gradient(180deg, rgba(255,255,255,0.92) 0%, rgba(247,249,252,0.85) 100%)',
                border: '1px solid rgba(16,24,40,0.1)',
                borderRadius: 10,
                boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85), 0 1px 2px rgba(16,24,40,0.04)' }}>
                <PackageInput label="Qty" value={pkg.pieces}
                  onChange={(v) => updatePackage(pkg.id, 'pieces', v)} placeholder="1"/>
                <PackageInput label="Length" suffix={dimensionUnit} value={pkg.length}
                  onChange={(v) => updatePackage(pkg.id, 'length', v)}
                  placeholder={dimensionUnit === 'cm' ? '120' : '47'}/>
                <PackageInput label="Width" suffix={dimensionUnit} value={pkg.width}
                  onChange={(v) => updatePackage(pkg.id, 'width', v)}
                  placeholder={dimensionUnit === 'cm' ? '100' : '39'}/>
                <PackageInput label="Height" suffix={dimensionUnit} value={pkg.height}
                  onChange={(v) => updatePackage(pkg.id, 'height', v)}
                  placeholder={dimensionUnit === 'cm' ? '80' : '31'}/>
                <PackageInput label="Weight" suffix={weightUnit} value={pkg.weight}
                  onChange={(v) => updatePackage(pkg.id, 'weight', v)} placeholder="0"/>
                <button type="button" onClick={() => removePackage(pkg.id)}
                  disabled={packages.length === 1}
                  aria-label="Remove item"
                  style={{ alignSelf: 'stretch', minHeight: 36,
                    padding: 0,
                    border: '1px solid ' + (packages.length === 1 ? 'rgba(16,24,40,0.08)' : 'rgba(180,35,24,0.25)'),
                    borderRadius: 8,
                    background: packages.length === 1 ? 'rgba(16,24,40,0.03)' : 'rgba(220,38,38,0.06)',
                    color: packages.length === 1 ? '#98A2B3' : '#B42318',
                    cursor: packages.length === 1 ? 'not-allowed' : 'pointer',
                    fontSize: 15, fontWeight: 800, lineHeight: 1,
                    display: 'inline-grid', placeItems: 'center',
                    fontFamily: 'inherit' }}>
                  ×
                </button>
              </div>
            ))}
            <div style={{ display: 'flex', alignItems: 'center',
              justifyContent: 'space-between', gap: 8, flexWrap: 'wrap',
              marginTop: 4, paddingTop: 6,
              borderTop: '1px dashed rgba(16,24,40,0.12)' }}>
              <button type="button" onClick={addPackage}
                style={{ display: 'inline-flex', alignItems: 'center', gap: 4,
                  padding: '5px 12px', borderRadius: 9999,
                  background: 'rgba(30,87,214,0.08)',
                  border: '1px solid rgba(30,87,214,0.2)',
                  color: '#1846B0', fontSize: 11, fontWeight: 700,
                  cursor: 'pointer', fontFamily: 'inherit',
                  letterSpacing: '0.01em' }}>
                + Add item
              </button>
              <div style={{ fontSize: 10.5, color: '#475467',
                fontFamily: 'JetBrains Mono, monospace',
                letterSpacing: '0.02em' }}>
                {totalPieces > 0 && <span>{totalPieces} pcs</span>}
                {totalWeightKg > 0 && (
                  <span> · {Math.round(totalWeightKg * 100) / 100} kg</span>
                )}
                {totalCbm > 0 && <span> · {totalCbm} cbm</span>}
                {totalPieces === 0 && totalWeightKg === 0 && totalCbm === 0 && (
                  <span style={{ color: '#98A2B3' }}>Add weight or dimensions</span>
                )}
              </div>
            </div>
          </div>
        )}

        {/* AIR/LCL — accessorials. Sits below the items table so the
            user adds packages first, then opens the dropdown to flag
            anything special (Hazmat / Refrigerated / Bonded). */}
        {isAirOrLcl && (
          <div style={{ marginBottom: 10 }}>
            <AccessorialsSelect
              hazmat={hazmat} onHazmatChange={setHazmat}
              refrigerated={tempControlled} onRefrigeratedChange={setTempControlled}
              bonded={bonded} onBondedChange={setBonded}
            />
          </div>
        )}

      </div>

      {/* Result panel — idle preview / loading / error / live rates */}
      <div style={{ position: 'relative', padding: '0 18px 12px' }}>
        {submitting && (
          <div style={{ padding: '12px 14px',
            background: 'rgba(247,249,252,0.7)',
            border: '1px dashed rgba(16,24,40,0.14)', borderRadius: 10,
            display: 'flex', alignItems: 'center', gap: 11 }}>
            <span style={{ position: 'relative', width: 22, height: 22, flexShrink: 0 }}>
              <span style={{ position: 'absolute', inset: 0, borderRadius: 9999,
                border: '2px solid rgba(30,87,214,0.18)',
                borderTopColor: '#1E57D6',
                animation: 'qg-spin 0.9s linear infinite' }}/>
            </span>
            <div style={{ minWidth: 0 }}>
              <div style={{ fontSize: 12.5, fontWeight: 700, color: '#0B1220',
                letterSpacing: '-0.005em' }}>Fetching live rates…</div>
              <div style={{ fontSize: 10.5, color: '#667085', marginTop: 2,
                fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em' }}>
                Polling 40+ carrier APIs
              </div>
            </div>
          </div>
        )}

        {!submitting && errorMsg && (
          <div style={{ padding: '10px 12px',
            background: 'rgba(254,243,234,0.85)',
            border: '1px solid rgba(247,144,9,0.3)', borderRadius: 10,
            display: 'flex', alignItems: 'flex-start', gap: 10 }}>
            <span style={{ width: 22, height: 22, borderRadius: 9999, flexShrink: 0,
              background: 'rgba(247,144,9,0.18)', color: '#B54708',
              display: 'inline-grid', placeItems: 'center' }}>
              <Icon name="shield" size={12}/>
            </span>
            <div style={{ fontSize: 12, color: '#7A2E0E', lineHeight: 1.4 }}>
              {errorMsg}
            </div>
          </div>
        )}

        {!submitting && !errorMsg && results && results.length === 0 && (
          <div style={{ padding: '12px 14px',
            background: 'rgba(247,249,252,0.7)',
            border: '1px dashed rgba(16,24,40,0.14)', borderRadius: 10,
            display: 'flex', alignItems: 'center', gap: 11 }}>
            <span style={{ width: 22, height: 22, borderRadius: 7,
              background: '#fff', border: '1px solid rgba(16,24,40,0.1)',
              display: 'inline-grid', placeItems: 'center', color: '#1E57D6',
              flexShrink: 0 }}>
              <Icon name="globe" size={12}/>
            </span>
            <div>
              <div style={{ fontSize: 12, fontWeight: 700, color: '#0B1220' }}>
                No live rates for this exact lane.
              </div>
              <div style={{ fontSize: 10.5, color: '#667085', marginTop: 2,
                fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em' }}>
                A specialist can build a custom quote in under 1 business day.
              </div>
            </div>
          </div>
        )}

        {/* Non-empty rates are handed up to ResultsGlass via onResults (Step 5).
            QuoteGlass only renders the empty / loading / error / idle states. */}

        {!submitting && !errorMsg && !results && !usesContainersUI && (
          <div style={{ padding: '12px 14px',
            background: 'rgba(247,249,252,0.7)',
            border: '1px dashed rgba(16,24,40,0.14)', borderRadius: 10,
            display: 'flex', alignItems: 'center', gap: 11 }}>
            <span style={{ width: 22, height: 22, borderRadius: 7,
              background: '#fff', border: '1px solid rgba(16,24,40,0.1)',
              display: 'inline-grid', placeItems: 'center', color: '#1E57D6',
              flexShrink: 0,
              boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.9)' }}>
              <Icon name="sparkles" size={12}/>
            </span>
            <div style={{ minWidth: 0, flex: 1 }}>
              <div style={{ fontSize: 12, fontWeight: 600, color: '#0B1220',
                letterSpacing: '-0.005em' }}>
                {ready_ok ? 'Ready to compare 40+ carriers' : 'Fill the lane to see rates'}
              </div>
              <div style={{ fontSize: 10.5, color: '#667085', marginTop: 2,
                fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em' }}>
                All-in pricing · transit days · ETD
              </div>
            </div>
            <span style={{ display: 'inline-flex', gap: 3, flexShrink: 0 }}>
              {[0,1,2].map(i => (
                <span key={i} style={{ width: 18, height: 4, borderRadius: 9999,
                  background: ready_ok ? 'rgba(30,87,214,0.4)' : 'rgba(16,24,40,0.08)' }}/>
              ))}
            </span>
          </div>
        )}
      </div>

      {/* CTA */}
      <div style={{ position: 'relative', padding: '0 18px 14px' }}>
        <button type="submit" disabled={!ready_ok || submitting}
          className={ready_ok && !submitting ? 'lift' : ''}
          style={{ width:'100%', padding: '12px',
            background: ready_ok && !submitting
              ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
              : 'rgba(16,24,40,0.08)',
            color: ready_ok && !submitting ? '#fff' : '#98A2B3',
            border: 0, borderRadius: 12, fontFamily: 'inherit', fontSize: 13.5, fontWeight: 600,
            cursor: ready_ok && !submitting ? 'pointer' : 'not-allowed',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 10,
            boxShadow: ready_ok && !submitting
              ? 'inset 0 1px 1px rgba(255,255,255,0.35), 0 12px 28px -8px rgba(30,87,214,0.5)'
              : 'none',
            transition: 'background 200ms, color 200ms, box-shadow 200ms' }}>
          {submitting ? 'Requesting rates…' : 'Get live rates'}
          {!submitting && (
            <span style={{ width: 22, height: 22, borderRadius: 9999,
              background: ready_ok ? 'rgba(255,255,255,0.22)' : 'rgba(16,24,40,0.04)',
              display: 'inline-grid', placeItems: 'center',
              boxShadow: ready_ok ? 'inset 0 1px 1px rgba(255,255,255,0.35)' : 'none' }}>
              <Icon name="arrowRight" size={12}/>
            </span>
          )}
        </button>
      </div>

      {/* Footer */}
      <div style={{ position: 'relative', padding: '9px 18px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        background: 'linear-gradient(0deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 10.5, color: '#667085' }}>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="check" size={11} style={{ color: '#12b76a' }}/> No signup
          </span>
          <span style={{ width:1, height:10, background:'rgba(16,24,40,0.1)' }}/>
          <span style={{ display:'inline-flex', alignItems:'center', gap:5 }}>
            <Icon name="lock" size={11} style={{ color: '#1E57D6' }}/> Quote stays private
          </span>
        </div>
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6,
          fontSize: 10, color: '#667085', fontFamily: 'JetBrains Mono, monospace',
          letterSpacing: '0.06em' }}>
          <Icon name="shield" size={11} style={{ color: '#1E57D6' }}/>
          SOC 2 · C-TPAT · NVOCC
        </div>
      </div>

      <style>{`
        @keyframes qg-spin { to { transform: rotate(360deg); } }
        .qg-input::placeholder { color: #98A2B3; }
        .qg-input:focus { outline: none; }
        .qg-field:focus-within {
          border-color: rgba(30,87,214,0.45) !important;
          box-shadow: inset 0 1px 0 rgba(255,255,255,0.85), 0 0 0 3px rgba(30,87,214,0.12) !important;
        }
      `}</style>
    </form>

    {leadModalOpen && (
      <div role="dialog" aria-modal="true" aria-label="Tell us who you are"
        onClick={cancelLeadCapture}
        style={{ position: 'fixed', inset: 0, zIndex: 9999,
          background: 'rgba(11,18,32,0.55)',
          backdropFilter: 'blur(6px)', WebkitBackdropFilter: 'blur(6px)',
          display: 'grid', placeItems: 'center', padding: 16 }}>
        <form onSubmit={submitLeadAndFetch} onClick={(e) => e.stopPropagation()}
          style={{ width: '100%', maxWidth: 440,
            background: '#ffffff', borderRadius: 16,
            border: '1px solid rgba(16,24,40,0.08)',
            boxShadow: '0 24px 48px -12px rgba(16,24,40,0.35)',
            padding: '28px 26px', position: 'relative',
            fontFamily: 'Inter, system-ui, sans-serif', color: '#0B1220' }}>
          <button type="button" onClick={cancelLeadCapture} aria-label="Close"
            disabled={leadSaving}
            style={{ position: 'absolute', top: 12, right: 12, width: 28, height: 28,
              borderRadius: 9999, border: '1px solid rgba(16,24,40,0.1)',
              background: '#ffffff', color: '#475467', cursor: leadSaving ? 'not-allowed' : 'pointer',
              display: 'inline-grid', placeItems: 'center' }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
              stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 6L6 18M6 6l12 12"/>
            </svg>
          </button>

          <div style={{ fontSize: 11.5, fontWeight: 700, letterSpacing: '0.06em',
            textTransform: 'uppercase', color: '#1E57D6', marginBottom: 6 }}>
            Almost there
          </div>
          <h3 style={{ margin: 0, fontSize: 20, fontWeight: 700, letterSpacing: '-0.015em',
            color: '#0B1220' }}>Tell us who you are</h3>
          <p style={{ margin: '6px 0 18px', fontSize: 13.5, lineHeight: 1.5, color: '#475467' }}>
            We'll send your live {svc.name} rates and your dedicated specialist's contact in seconds.
          </p>

          <div style={{ display: 'grid', gap: 10 }}>
            <LeadField label="Full name" name="name" value={leadFields.name}
              onChange={(v) => setLeadFields({ ...leadFields, name: v })}
              autoComplete="name" disabled={leadSaving} autoFocus/>
            <LeadField label="Work email" name="email" type="email" value={leadFields.email}
              onChange={(v) => setLeadFields({ ...leadFields, email: v })}
              autoComplete="email" disabled={leadSaving}/>
            <LeadField label="Company" name="company" value={leadFields.company}
              onChange={(v) => setLeadFields({ ...leadFields, company: v })}
              autoComplete="organization" disabled={leadSaving}/>
            <LeadField label="Phone" name="phone" type="tel" value={leadFields.phone}
              onChange={(v) => setLeadFields({ ...leadFields, phone: v })}
              autoComplete="tel" disabled={leadSaving}/>
          </div>

          {leadError && (
            <div role="alert" style={{ marginTop: 12, padding: '10px 12px',
              background: '#FEF3F2', border: '1px solid #F04438', borderRadius: 8,
              color: '#B42318', fontSize: 13 }}>
              {leadError}
            </div>
          )}

          <button type="submit" disabled={leadSaving}
            style={{ marginTop: 16, width: '100%', height: 44,
              border: 'none', borderRadius: 10,
              background: leadSaving ? '#1846B0' : '#1E57D6',
              color: '#ffffff', fontFamily: 'inherit', fontSize: 14.5, fontWeight: 600,
              cursor: leadSaving ? 'not-allowed' : 'pointer',
              transition: 'background 120ms ease' }}>
            {leadSaving ? 'Submitting…' : 'Get my live rates'}
          </button>

          <p style={{ margin: '14px 0 0', fontSize: 11.5, color: '#98A2B3',
            textAlign: 'center', lineHeight: 1.5 }}>
            By submitting you agree we may contact you about your shipment.
          </p>
        </form>
      </div>
    )}
    </React.Fragment>
  );
}

// ——— Step 5 — Dedicated results page ————————————————————————————————
// Renders the rates that QuoteGlass handed up via onResults. Reuses the
// liquid-glass shell tokens of the other steps and gets a wider canvas
// (up to 960px) so RichRateCards can lay out in 2 columns with breathing
// room. No internal scroll — the page extends naturally.
function ResultsGlass({ rates, summary, onEdit, onChangeService, onClose }) {
  const wrapRef = useRefH(null);
  const [wide, setWide] = useStateH(true);
  const [veryWide, setVeryWide] = useStateH(true);
  const [ultraWide, setUltraWide] = useStateH(false);
  const [narrowHeader, setNarrowHeader] = useStateH(false);
  // Watch the card itself, not the viewport — the card resizes mid-flow.
  // narrowHeader ≤ 820 → header switches Edit-search pill to icon-only.
  // wide ≥ 780px → 2-column rate grid.
  // veryWide ≥ 1100px → unlocks the BookingJourney sidebar on the right.
  // ultraWide ≥ 1500px → 3-column rate grid (rates area still wide enough).
  useEffectH(() => {
    if (!wrapRef.current || typeof ResizeObserver === 'undefined') return;
    const ro = new ResizeObserver(([e]) => {
      const w = e.contentRect.width;
      setWide(w >= 780);
      setVeryWide(w >= 1100);
      setUltraWide(w >= 1500);
      setNarrowHeader(w <= 820);
    });
    ro.observe(wrapRef.current);
    return () => ro.disconnect();
  }, []);

  const safeRates = Array.isArray(rates) ? rates : [];
  const s = summary || {};
  const svc = SERVICES.find(x => x.key === s.mode) || null;
  const modeIcon = s.modeIcon || (svc && svc.icon) || 'box';
  const modeName = s.modeName || (svc && svc.name) || 'Quote';
  const fromLbl  = s.fromLabel || '—';
  const toLbl    = s.toLabel || '—';
  const readyLbl = fmtShortDate(s.ready);

  // Compose the mono summary chip ("Ocean FCL · 1×20GP · 500 kg · Apr 29 · 6 rates")
  const chips = [];
  if (modeName) chips.push(modeName);
  if (s.unit)   chips.push(s.unit);
  if (s.weight) chips.push(`${s.weight} kg`);
  if (readyLbl) chips.push(readyLbl);
  chips.push(`${safeRates.length} rate${safeRates.length === 1 ? '' : 's'}`);

  const gridCols = safeRates.length === 1 ? '1fr'
                  : ultraWide ? 'repeat(3, minmax(0, 1fr))'
                  : wide      ? 'repeat(2, minmax(0, 1fr))'
                  : '1fr';

  // Set-wide stats so each card can decide which selling badges it's earned.
  // We compute these once per render rather than inside each card so the
  // "Fastest", "Longest free time" etc. comparisons stay consistent.
  const setStats = (() => {
    const transitOf = (r) => (r.transit && r.transit.days) || r.transitDays || r.oceanTransitTime;
    const freeOf = (r) => r.freeDays;
    const transits = safeRates.map(transitOf).filter(v => typeof v === 'number');
    const frees = safeRates.map(freeOf).filter(v => typeof v === 'number');
    const minTransit = transits.length ? Math.min(...transits) : null;
    const maxFree = frees.length ? Math.max(...frees) : null;
    return { minTransit, maxFree, count: safeRates.length };
  })();

  return (
    <div ref={wrapRef} style={{ position: 'relative',
      background: 'rgba(255,255,255,0.62)',
      backdropFilter: 'blur(50px) saturate(1.6)', WebkitBackdropFilter: 'blur(50px) saturate(1.6)',
      border: '1px solid rgba(255,255,255,0.7)', borderRadius: 24,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85), 0 24px 48px -12px rgba(16,24,40,0.18)',
      overflow: 'hidden' }}>
      <LiquidBorder radius={24} alpha={[0.9, 0.35]}/>

      {/* Header — back, mode badge, route summary, edit / close */}
      <div style={{ position: 'relative', padding: '14px 16px',
        borderBottom: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
        background: 'linear-gradient(180deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, minWidth: 0, flex: 1 }}>
          <button type="button" onClick={onEdit} aria-label="Edit search"
            style={{ width: 30, height: 30, borderRadius: 9999, flexShrink: 0,
              background: '#ffffff', border: '1px solid rgba(16,24,40,0.1)',
              boxShadow: '0 1px 2px rgba(16,24,40,0.06), inset 0 1px 0 rgba(255,255,255,0.9)',
              display: 'inline-grid', placeItems: 'center', cursor: 'pointer', color: '#475467' }}>
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
              stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 18l-6-6 6-6"/>
            </svg>
          </button>
          <span style={{ width: 30, height: 30, borderRadius: 8, flexShrink: 0,
            background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
            display: 'inline-grid', placeItems: 'center', color: '#fff',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' }}>
            <Icon name={modeIcon} size={14}/>
          </span>
          <div style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.2, minWidth: 0 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0,
              fontSize: 14, fontWeight: 700, color: '#0B1220', letterSpacing: '-0.01em' }}>
              <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                minWidth: 0 }}>{fromLbl}</span>
              <svg width="12" height="12" viewBox="0 0 24 24" fill="none"
                stroke="#1E57D6" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"
                style={{ flexShrink: 0 }}>
                <path d="M5 12h14M13 6l6 6-6 6"/>
              </svg>
              <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                minWidth: 0 }}>{toLbl}</span>
            </div>
            <span style={{ fontSize: 10.5, color: '#667085', marginTop: 2,
              fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em',
              whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
              {chips.join(' · ')}
            </span>
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
          {narrowHeader ? (
            <button type="button" onClick={onEdit} aria-label="Edit search"
              title="Edit search"
              style={{ width: 30, height: 30, borderRadius: 9999, flexShrink: 0,
                background: 'rgba(255,255,255,0.85)',
                border: '1px solid rgba(16,24,40,0.12)', cursor: 'pointer',
                color: '#0B1220',
                display: 'inline-grid', placeItems: 'center',
                boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.9)' }}>
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none"
                stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z"/>
              </svg>
            </button>
          ) : (
            <button type="button" onClick={onEdit}
              style={{ padding: '6px 12px',
                background: 'rgba(255,255,255,0.85)',
                border: '1px solid rgba(16,24,40,0.12)', borderRadius: 9999, cursor: 'pointer',
                color: '#0B1220', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600,
                display: 'inline-flex', alignItems: 'center', gap: 6,
                boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.9)' }}>
              <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
                stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M12 20h9M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4z"/>
              </svg>
              Edit search
            </button>
          )}
          <button type="button" onClick={onClose}
            style={{ padding: '6px 12px',
              background: 'rgba(255,255,255,0.7)',
              border: '1px solid rgba(16,24,40,0.1)', borderRadius: 9999, cursor: 'pointer',
              color: '#475467', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600 }}>
            Close
          </button>
        </div>
      </div>

      <StepDots step={5}/>

      {/* Body — rate grid + optional booking-journey sidebar (very-wide cards only) */}
      <div style={{ position: 'relative', padding: '14px 18px 18px',
        display: 'grid',
        gridTemplateColumns: veryWide ? '1fr 280px' : '1fr',
        gap: veryWide ? 18 : 0,
        alignItems: 'start' }}>
        <div style={{ display: 'grid', gridTemplateColumns: gridCols, gap: 12,
          minWidth: 0 }}>
          {safeRates.map((r, i) => (
            <RichRateCard key={r.rateId || r.id || i} r={r}
              highlight={i === 0} bestPrice={i === 0}
              setStats={setStats}/>
          ))}
        </div>
        {veryWide && (
          <BookingJourney mode={s.mode} modeName={modeName} onTalk={() => {
            const el = document.getElementById('contact-section') || document.getElementById('contact');
            if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
          }}/>
        )}
      </div>

      {/* Footer — trust row + specialist CTA */}
      <div style={{ position: 'relative', padding: '12px 18px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12,
        flexWrap: 'wrap',
        background: 'linear-gradient(0deg, rgba(255,255,255,0.55) 0%, rgba(255,255,255,0) 100%)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, fontSize: 10.5, color: '#667085' }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
            <Icon name="check" size={11} style={{ color: '#12b76a' }}/> All-in pricing
          </span>
          <span style={{ width: 1, height: 10, background: 'rgba(16,24,40,0.1)' }}/>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5 }}>
            <Icon name="lock" size={11} style={{ color: '#1E57D6' }}/> Quote stays private
          </span>
          <span style={{ width: 1, height: 10, background: 'rgba(16,24,40,0.1)' }}/>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5,
            fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.06em' }}>
            <Icon name="shield" size={11} style={{ color: '#1E57D6' }}/>
            SOC 2 · C-TPAT · NVOCC
          </span>
        </div>
        <a href="#contact" onClick={(e) => { e.preventDefault();
          const el = document.getElementById('contact-section') || document.getElementById('contact');
          if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }
        }}
          style={{ padding: '9px 14px',
            background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
            color: '#fff', border: 0, borderRadius: 9999,
            fontFamily: 'inherit', fontSize: 12, fontWeight: 600,
            cursor: 'pointer', textDecoration: 'none',
            display: 'inline-flex', alignItems: 'center', gap: 8,
            boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.35), 0 8px 18px -6px rgba(30,87,214,0.45)' }}>
          Talk to a specialist
          <span style={{ width: 18, height: 18, borderRadius: 9999,
            background: 'rgba(255,255,255,0.22)', display: 'inline-grid', placeItems: 'center',
            boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.35)' }}>
            <Icon name="arrowRight" size={10}/>
          </span>
        </a>
      </div>
    </div>
  );
}

// ——— BookingJourney sidebar — Step-5 right rail ———————————————————
// Mirrors the customer-portal "FCL Ocean Export Process" stepper but lit
// for our white liquid-glass aesthetic. Tells the user where they are in
// the wider booking journey: Search lane (done) → Compare rates (current)
// → Book & confirm (next) → Track shipment.
const JOURNEY_BY_MODE = {
  FCL: [
    { title: 'Search lane',     desc: 'Origin, destination, container, weight' },
    { title: 'Compare rates',   desc: 'Live quotes from 40+ ocean carriers' },
    { title: 'Book & confirm',  desc: 'Reserve slots and receive your B/L' },
    { title: 'Track container', desc: 'Real-time vessel and milestone updates' },
  ],
  LCL: [
    { title: 'Search lane',     desc: 'Origin, destination, volume, weight' },
    { title: 'Compare rates',   desc: 'Consolidated sailings, weekly schedules' },
    { title: 'Book & confirm',  desc: 'Cargo cut-off, B/L issued on departure' },
    { title: 'Track shipment',  desc: 'Vessel ETA + arrival notifications' },
  ],
  Air: [
    { title: 'Search lane',     desc: 'Origin, destination, dims, weight' },
    { title: 'Compare rates',   desc: 'Live air freight from 13 airlines' },
    { title: 'Book & confirm',  desc: 'Tender to airline and receive AWB' },
    { title: 'Track shipment',  desc: 'Per-leg flight status and POD' },
  ],
  Ground: [
    { title: 'Search lane',     desc: 'Origin and destination ZIPs, equipment' },
    { title: 'Compare rates',   desc: 'LTL & FTL across 70+ carriers' },
    { title: 'Book & confirm',  desc: 'Pickup window and BOL generation' },
    { title: 'Track shipment',  desc: 'Driver ETA + signed delivery proof' },
  ],
  Courier: [
    { title: 'Search lane',     desc: 'Origin, destination, parcel dims' },
    { title: 'Compare rates',   desc: 'DHL, FedEx, UPS and 30+ couriers' },
    { title: 'Book & confirm',  desc: 'Print label and schedule pickup' },
    { title: 'Track shipment',  desc: 'Per-scan status and POD signature' },
  ],
};
function BookingJourney({ mode, modeName, onTalk }) {
  const steps = JOURNEY_BY_MODE[mode] || JOURNEY_BY_MODE.FCL;
  // The user is in Step 5 of the widget — that maps to journey step 2
  // (compare rates). Steps 0 is done, 1 is current, 2-3 are upcoming.
  const currentIdx = 1;

  return (
    <aside style={{ position: 'sticky', top: 16,
      background: 'rgba(255,255,255,0.78)',
      backdropFilter: 'blur(20px) saturate(1.4)',
      WebkitBackdropFilter: 'blur(20px) saturate(1.4)',
      border: '1px solid rgba(16,24,40,0.06)',
      borderRadius: 16,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.95), 0 4px 12px -4px rgba(16,24,40,0.06)',
      padding: '16px 16px 14px',
      fontFamily: 'Inter, system-ui, sans-serif' }}>
      {/* Header */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 8,
        marginBottom: 14 }}>
        <span style={{ width: 24, height: 24, borderRadius: 7,
          background: 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)',
          display: 'inline-grid', placeItems: 'center', color: '#fff',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)' }}>
          <Icon name="sparkles" size={11}/>
        </span>
        <span style={{ fontSize: 9.5, fontWeight: 700, color: '#475467',
          letterSpacing: '0.12em', textTransform: 'uppercase' }}>
          {modeName ? `${modeName} journey` : 'Booking journey'}
        </span>
      </div>

      {/* Steps */}
      <div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
        {steps.map((step, i) => {
          const done    = i < currentIdx;
          const current = i === currentIdx;
          const last    = i === steps.length - 1;
          // Circle
          const bg = done
            ? 'linear-gradient(180deg, #18B66B 0%, #058F4D 100%)'
            : current
              ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
              : '#fff';
          const fg = (done || current) ? '#fff' : '#98A2B3';
          const bd = (done || current) ? 'transparent' : 'rgba(16,24,40,0.12)';
          // Connector line color (only between steps that exist)
          const lineColor = done
            ? 'rgba(18,183,106,0.45)'
            : current
              ? 'rgba(30,87,214,0.25)'
              : 'rgba(16,24,40,0.1)';
          return (
            <div key={i} style={{ display: 'grid',
              gridTemplateColumns: '24px 1fr',
              gap: 10, alignItems: 'flex-start' }}>
              {/* Circle + connector column */}
              <div style={{ display: 'flex', flexDirection: 'column',
                alignItems: 'center', minHeight: last ? 0 : 56 }}>
                <span style={{ width: 24, height: 24, borderRadius: 9999,
                  background: bg, color: fg,
                  border: bd === 'transparent' ? 'none' : `1px solid ${bd}`,
                  display: 'inline-grid', placeItems: 'center',
                  fontSize: 10, fontWeight: 700, fontFamily: 'JetBrains Mono, monospace',
                  letterSpacing: '0.02em', flexShrink: 0,
                  boxShadow: (done || current)
                    ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 4px rgba(16,24,40,0.12)'
                    : 'inset 0 1px 0 rgba(255,255,255,0.9)',
                  position: 'relative' }}>
                  {done ? (
                    <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
                      stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
                      <path d="M5 12l5 5L20 7"/>
                    </svg>
                  ) : (
                    <span>{i + 1}</span>
                  )}
                  {current && (
                    <span aria-hidden style={{ position: 'absolute', inset: -4,
                      borderRadius: 9999, border: '2px solid rgba(30,87,214,0.35)',
                      animation: 'bj-pulse 2s ease-out infinite',
                      pointerEvents: 'none' }}/>
                  )}
                </span>
                {!last && (
                  <span style={{ flex: 1, width: 2, marginTop: 4,
                    background: lineColor, borderRadius: 1, minHeight: 22 }}/>
                )}
              </div>
              {/* Title + description */}
              <div style={{ paddingBottom: last ? 0 : 14, minWidth: 0 }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6,
                  flexWrap: 'wrap' }}>
                  <span style={{ fontSize: 13, fontWeight: 700,
                    color: current ? '#1846B0' : (done ? '#0B1220' : '#475467'),
                    letterSpacing: '-0.005em' }}>
                    {step.title}
                  </span>
                  {current && (
                    <span style={{ padding: '1px 6px', borderRadius: 9999,
                      background: 'rgba(30,87,214,0.1)', color: '#1846B0',
                      border: '1px solid rgba(30,87,214,0.25)',
                      fontSize: 9, fontWeight: 700, letterSpacing: '0.06em',
                      textTransform: 'uppercase' }}>You're here</span>
                  )}
                  {done && (
                    <span style={{ fontSize: 10, fontWeight: 600, color: '#067647',
                      letterSpacing: '0.04em', textTransform: 'uppercase' }}>Done</span>
                  )}
                </div>
                <div style={{ fontSize: 11.5, color: current ? '#475467' : '#98A2B3',
                  lineHeight: 1.4, marginTop: 3 }}>
                  {step.desc}
                </div>
              </div>
            </div>
          );
        })}
      </div>

      {/* Specialist callout */}
      <div style={{ marginTop: 12, padding: '12px 12px',
        background: 'linear-gradient(180deg, rgba(239,246,255,0.85) 0%, rgba(239,246,255,0.5) 100%)',
        border: '1px solid rgba(30,87,214,0.18)', borderRadius: 12 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 7, marginBottom: 5 }}>
          <Icon name="users" size={12} style={{ color: '#1E57D6' }}/>
          <span style={{ fontSize: 11.5, fontWeight: 700, color: '#0B1220',
            letterSpacing: '-0.005em' }}>Need a hand?</span>
        </div>
        <p style={{ margin: '0 0 8px', fontSize: 11, color: '#475467', lineHeight: 1.4 }}>
          A logistics specialist can review your lane and validate the right
          carrier for your cargo.
        </p>
        <button type="button" onClick={onTalk}
          style={{ width: '100%', padding: '7px 10px',
            background: '#fff', border: '1px solid rgba(30,87,214,0.25)',
            borderRadius: 8, cursor: 'pointer',
            color: '#1E57D6', fontFamily: 'inherit', fontSize: 11.5, fontWeight: 600,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
            transition: 'background 200ms, border-color 200ms' }}
          onMouseEnter={(e) => { e.currentTarget.style.background = 'rgba(30,87,214,0.06)';
            e.currentTarget.style.borderColor = 'rgba(30,87,214,0.4)'; }}
          onMouseLeave={(e) => { e.currentTarget.style.background = '#fff';
            e.currentTarget.style.borderColor = 'rgba(30,87,214,0.25)'; }}>
          Talk to a specialist
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
            stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 12h14M13 6l6 6-6 6"/>
          </svg>
        </button>
      </div>

      <style>{`
        @keyframes bj-pulse {
          0%   { transform: scale(1);   opacity: 0.6; }
          70%  { transform: scale(1.4); opacity: 0;   }
          100% { transform: scale(1.4); opacity: 0;   }
        }
      `}</style>
    </aside>
  );
}

function LeadField({ label, name, type, value, onChange, autoComplete, disabled, autoFocus }) {
  return (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 6,
      fontSize: 12, fontWeight: 600, color: '#344054', letterSpacing: '0.01em' }}>
      {label}
      <input
        name={name}
        type={type || 'text'}
        value={value}
        onChange={(e) => onChange(e.target.value)}
        autoComplete={autoComplete}
        disabled={disabled}
        autoFocus={autoFocus}
        required
        style={{ height: 40, padding: '0 12px',
          border: '1px solid #D0D5DD', borderRadius: 8,
          background: '#ffffff', color: '#0B1220',
          fontFamily: 'inherit', fontSize: 14, fontWeight: 400, letterSpacing: 0,
          outline: 'none', transition: 'border-color 120ms, box-shadow 120ms' }}
        onFocus={(e) => {
          e.target.style.borderColor = '#1E57D6';
          e.target.style.boxShadow = '0 0 0 4px rgba(30, 87, 214, 0.18)';
        }}
        onBlur={(e) => {
          e.target.style.borderColor = '#D0D5DD';
          e.target.style.boxShadow = 'none';
        }}
      />
    </label>
  );
}

function QuoteInput({ icon, label, placeholder, value, onChange }) {
  return (
    <div className="qg-field" style={{ display: 'flex', alignItems: 'center', gap: 10,
      padding: '10px 12px',
      background: 'rgba(255,255,255,0.85)',
      border: '1px solid rgba(16,24,40,0.1)', borderRadius: 10,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
      transition: 'border-color 150ms, box-shadow 150ms' }}>
      <Icon name={icon} size={14} style={{ color: '#1E57D6', flexShrink: 0 }}/>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
          letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 1 }}>
          {label}
        </div>
        <input
          className="qg-input"
          type="text"
          value={value}
          placeholder={placeholder}
          onChange={(e) => onChange(e.target.value)}
          style={{ width: '100%', border: 0, padding: 0,
            background: 'transparent', color: '#0B1220',
            fontSize: 13.5, fontWeight: 600, fontFamily: 'inherit',
            letterSpacing: '-0.005em' }}/>
      </div>
    </div>
  );
}

// Google Places-powered variant of QuoteInput. Uses AutocompleteService
// (programmatic API) — same approach the abgroupshipping-login portal uses,
// since the legacy Autocomplete widget is unreliable for new API keys.
// `onSelect` (optional) fires once the user picks a suggestion, with the
// resolved Place details (lat/lng + address components) — needed by the
// Walio rates API. If `onSelect` is omitted the component still works as
// a plain text input.
function QuotePlacesInput({ icon, label, placeholder, value, onChange, onSelect }) {
  const inputRef = useRefH(null);
  const wrapRef = useRefH(null);
  const svcRef = useRefH(null);
  const detailsSvcRef = useRefH(null);
  const tokenRef = useRefH(null);
  const debounceRef = useRefH(null);
  const [ready, setReady] = useStateH(!!(window.google && window.google.maps && window.google.maps.places));
  const [suggestions, setSuggestions] = useStateH([]);
  const [open, setOpen] = useStateH(false);
  const [active, setActive] = useStateH(-1);

  // Wait for the global Maps loader to fire its callback.
  useEffectH(() => {
    if (ready) return;
    if (window.__abgsGooglePlacesReady) { setReady(true); return; }
    const onReady = () => setReady(true);
    window.__abgsGooglePlacesListeners = window.__abgsGooglePlacesListeners || [];
    window.__abgsGooglePlacesListeners.push(onReady);
  }, [ready]);

  // Initialize the AutocompleteService and a session token (for billing).
  // PlacesService is also created so we can resolve a chosen prediction to
  // lat/lng + address components via getDetails().
  useEffectH(() => {
    if (!ready || svcRef.current) return;
    try {
      svcRef.current = new window.google.maps.places.AutocompleteService();
      tokenRef.current = new window.google.maps.places.AutocompleteSessionToken();
      // PlacesService needs an HTMLElement; an off-screen div is fine.
      const host = document.createElement('div');
      detailsSvcRef.current = new window.google.maps.places.PlacesService(host);
    } catch (e) { /* ignore — input degrades to plain text */ }
  }, [ready]);

  // Resolve a prediction to a structured place (lat/lng + address parts),
  // then call onSelect. Falls back to the description text if details fail.
  const resolvePlace = (placeId, fallbackText) => {
    const svc = detailsSvcRef.current;
    if (!onSelect) return;
    if (!svc || !placeId) {
      onSelect({ description: fallbackText, place_id: null });
      return;
    }
    svc.getDetails(
      { placeId, fields: ['geometry', 'address_components', 'name', 'formatted_address'],
        sessionToken: tokenRef.current },
      (place, status) => {
        const ok = status === window.google.maps.places.PlacesServiceStatus.OK;
        if (!ok || !place) {
          onSelect({ description: fallbackText, place_id: placeId });
          return;
        }
        const ac = place.address_components || [];
        const find = (type) => ac.find(c => c.types.includes(type));
        const country = (find('country') || {}).short_name || '';
        const state   = (find('administrative_area_level_1') || {}).short_name || '';
        const city    = (find('locality') || find('postal_town')
                         || find('administrative_area_level_2') || {}).long_name || '';
        const zip     = (find('postal_code') || {}).short_name || '';
        const loc = place.geometry && place.geometry.location;
        onSelect({
          description: fallbackText,
          place_id: placeId,
          country, state, city, zip,
          lat: loc ? loc.lat() : null,
          lng: loc ? loc.lng() : null,
        });
      }
    );
  };

  // Cleanup any in-flight debounce when the field unmounts.
  useEffectH(() => () => {
    if (debounceRef.current) clearTimeout(debounceRef.current);
  }, []);

  // Close the dropdown when the user clicks outside the field.
  useEffectH(() => {
    const onDocDown = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    document.addEventListener('mousedown', onDocDown);
    return () => document.removeEventListener('mousedown', onDocDown);
  }, []);

  const fetchSuggestions = (input) => {
    const svc = svcRef.current;
    if (!svc || !input || input.length < 2) {
      setSuggestions([]); setOpen(false); return;
    }
    svc.getPlacePredictions(
      // (regions) covers cities + postal codes + administrative areas, so users
      // can type either "Los Angeles" or "90210" and get matching predictions.
      { input, types: ['(regions)'], sessionToken: tokenRef.current },
      (preds, status) => {
        const ok = status === window.google.maps.places.PlacesServiceStatus.OK;
        if (ok && preds && preds.length) {
          setSuggestions(preds); setOpen(true); setActive(-1);
        } else {
          setSuggestions([]); setOpen(false);
        }
      }
    );
  };

  const handleChange = (e) => {
    const v = e.target.value;
    onChange(v);
    // Editing the text invalidates any previously resolved place — null it
    // so the parent only holds a place once the user picks a suggestion.
    if (onSelect) onSelect(null);
    if (debounceRef.current) clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => fetchSuggestions(v), 220);
  };

  const selectSuggestion = (pred) => {
    const text = pred.description || '';
    onChange(text);
    setSuggestions([]); setOpen(false); setActive(-1);
    resolvePlace(pred.place_id, text);
    // Refresh session token after a selection (billing best practice).
    try { tokenRef.current = new window.google.maps.places.AutocompleteSessionToken(); } catch (e) {}
  };

  const handleKeyDown = (e) => {
    if (!open || !suggestions.length) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setActive(p => Math.min(p + 1, suggestions.length - 1)); }
    else if (e.key === 'ArrowUp')   { e.preventDefault(); setActive(p => Math.max(p - 1, 0)); }
    else if (e.key === 'Enter' && active >= 0) { e.preventDefault(); selectSuggestion(suggestions[active]); }
    else if (e.key === 'Escape') { setOpen(false); }
  };

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <div className="qg-field" style={{ display: 'flex', alignItems: 'center', gap: 10,
        padding: '10px 12px',
        background: 'rgba(255,255,255,0.85)',
        border: '1px solid rgba(16,24,40,0.1)', borderRadius: 10,
        boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
        transition: 'border-color 150ms, box-shadow 150ms' }}>
        <Icon name={icon} size={14} style={{ color: '#1E57D6', flexShrink: 0 }}/>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
            letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 1 }}>
            {label}
          </div>
          <input
            ref={inputRef}
            className="qg-input"
            type="text"
            value={value}
            placeholder={placeholder}
            onChange={handleChange}
            onKeyDown={handleKeyDown}
            onFocus={() => { if (suggestions.length) setOpen(true); }}
            autoComplete="off"
            spellCheck="false"
            style={{ width: '100%', border: 0, padding: 0,
              background: 'transparent', color: '#0B1220',
              fontSize: 13.5, fontWeight: 600, fontFamily: 'inherit',
              letterSpacing: '-0.005em' }}/>
        </div>
      </div>

      {open && suggestions.length > 0 && (
        <div className="qg-places-dropdown" style={{ position: 'absolute',
          top: 'calc(100% + 4px)', left: 0, right: 0, zIndex: 50,
          background: '#ffffff', border: '1px solid rgba(16,24,40,0.1)',
          borderRadius: 10, boxShadow: '0 12px 28px -8px rgba(16,24,40,0.18)',
          maxHeight: 224, overflowY: 'auto' }}>
          {suggestions.map((p, idx) => {
            const main = (p.structured_formatting && p.structured_formatting.main_text) || p.description;
            const secondary = p.structured_formatting && p.structured_formatting.secondary_text;
            const isActive = idx === active;
            return (
              <div key={p.place_id}
                onMouseDown={(e) => { e.preventDefault(); selectSuggestion(p); }}
                onMouseEnter={() => setActive(idx)}
                style={{ display: 'flex', alignItems: 'center', gap: 10,
                  padding: '8px 12px', cursor: 'pointer',
                  background: isActive ? '#F0F6FF' : 'transparent',
                  borderBottom: idx === suggestions.length - 1 ? 'none' : '1px solid #F3F4F6' }}>
                <span style={{ width: 24, height: 24, borderRadius: 9999,
                  background: '#EEF4FF', display: 'inline-grid', placeItems: 'center',
                  flexShrink: 0, color: '#1E57D6' }}>
                  <Icon name="globe" size={11}/>
                </span>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, color: '#0B1220',
                    whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                    {main}
                  </div>
                  {secondary && (
                    <div style={{ fontSize: 11, color: '#667085', marginTop: 1,
                      whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {secondary}
                    </div>
                  )}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

function QuoteSelect({ label, value, onChange, options }) {
  return (
    <label className="qg-field" style={{ display: 'block', position: 'relative',
      padding: '8px 12px',
      background: 'rgba(255,255,255,0.85)',
      border: '1px solid rgba(16,24,40,0.1)', borderRadius: 10,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)', cursor: 'pointer',
      transition: 'border-color 150ms, box-shadow 150ms' }}>
      <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
        letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 1 }}>
        {label}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 6 }}>
        <select value={value} onChange={(e) => onChange(e.target.value)}
          style={{ flex: 1, minWidth: 0, appearance: 'none', WebkitAppearance: 'none',
            border: 0, background: 'transparent', padding: 0,
            color: '#0B1220', fontSize: 13, fontWeight: 600,
            fontFamily: 'inherit', letterSpacing: '-0.005em',
            cursor: 'pointer', outline: 'none' }}>
          {options.map(o => <option key={o} value={o}>{o}</option>)}
        </select>
        <Icon name="chevronDown" size={12} style={{ color: '#667085', flexShrink: 0 }}/>
      </div>
    </label>
  );
}

function QuoteSmallInput({ label, suffix, value, onChange, placeholder, type = 'text' }) {
  const isJsxSuffix = suffix && typeof suffix !== 'string';
  return (
    <label className="qg-field" style={{ display: 'block', position: 'relative',
      padding: '8px 12px',
      background: 'rgba(255,255,255,0.85)',
      border: '1px solid rgba(16,24,40,0.1)', borderRadius: 10,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
      transition: 'border-color 150ms, box-shadow 150ms' }}>
      <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
        letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 1 }}>
        {label}
      </div>
      <div style={{ display: 'flex', alignItems: isJsxSuffix ? 'center' : 'baseline', gap: 4 }}>
        <input
          className="qg-input"
          type={type}
          value={value}
          placeholder={placeholder}
          onChange={(e) => onChange(e.target.value)}
          style={{ flex: 1, minWidth: 0, border: 0, padding: 0,
            background: 'transparent', color: '#0B1220',
            fontSize: 13, fontWeight: 600, fontFamily: 'inherit',
            letterSpacing: '-0.005em' }}/>
        {suffix && (isJsxSuffix ? suffix : (
          <span style={{ fontSize: 10.5, color: '#98A2B3', fontWeight: 600,
            fontFamily: 'JetBrains Mono, monospace' }}>{suffix}</span>
        ))}
      </div>
    </label>
  );
}

// Accessorials dropdown — one trigger that opens a small panel with
// three checkboxes (Hazmat / Refrigerated / Bonded). Acts like a
// standard "select multiple" field: closed state shows a summary
// ("None", "Hazmat", "Hazmat · Bonded"); clicking opens the panel
// where each option toggles independently. Closes on outside click.
function AccessorialsSelect({ hazmat, onHazmatChange,
  refrigerated, onRefrigeratedChange,
  bonded, onBondedChange }) {
  const [open, setOpen] = useStateH(false);
  const wrapRef = useRefH(null);
  useEffectH(() => {
    if (!open) return;
    const onDoc = (e) => {
      if (wrapRef.current && !wrapRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    document.addEventListener('keydown', onKey);
    return () => {
      document.removeEventListener('mousedown', onDoc);
      document.removeEventListener('keydown', onKey);
    };
  }, [open]);

  const items = [
    { key: 'hazmat',       label: 'Hazmat',       value: hazmat,       onChange: onHazmatChange },
    { key: 'refrigerated', label: 'Refrigerated', value: refrigerated, onChange: onRefrigeratedChange },
    { key: 'bonded',       label: 'Bonded',       value: bonded,       onChange: onBondedChange },
  ];
  const selected = items.filter(i => i.value);
  const summary = selected.length === 0
    ? 'None'
    : selected.length <= 2
      ? selected.map(i => i.label).join(' · ')
      : selected[0].label + ' · +' + (selected.length - 1) + ' more';

  return (
    <div ref={wrapRef} style={{ position: 'relative' }}>
      <button type="button"
        onClick={() => setOpen(v => !v)}
        aria-haspopup="listbox" aria-expanded={open}
        className="qg-field"
        style={{ width: '100%', display: 'block', textAlign: 'left',
          padding: '8px 12px',
          background: 'rgba(255,255,255,0.85)',
          border: '1px solid ' + (open ? 'rgba(30,87,214,0.45)' : 'rgba(16,24,40,0.1)'),
          borderRadius: 10, cursor: 'pointer',
          boxShadow: open
            ? 'inset 0 1px 0 rgba(255,255,255,0.85), 0 0 0 3px rgba(30,87,214,0.12)'
            : 'inset 0 1px 0 rgba(255,255,255,0.85)',
          transition: 'border-color 150ms, box-shadow 150ms',
          fontFamily: 'inherit' }}>
        <div style={{ fontSize: 9.5, fontWeight: 700, color: '#667085',
          letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 1 }}>
          Accessorials
        </div>
        <div style={{ display: 'flex', alignItems: 'center',
          justifyContent: 'space-between', gap: 6 }}>
          <span style={{ flex: 1, minWidth: 0,
            color: selected.length === 0 ? '#98A2B3' : '#0B1220',
            fontSize: 13, fontWeight: 600,
            letterSpacing: '-0.005em',
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {summary}
          </span>
          {selected.length > 0 && (
            <span style={{ fontSize: 10, fontWeight: 700, color: '#1846B0',
              padding: '1px 6px', borderRadius: 9999,
              background: 'rgba(30,87,214,0.1)',
              fontFamily: 'JetBrains Mono, monospace',
              letterSpacing: '0.04em' }}>
              {selected.length}
            </span>
          )}
          <Icon name="chevronDown" size={12}
            style={{ color: '#667085', flexShrink: 0,
              transform: open ? 'rotate(180deg)' : 'none',
              transition: 'transform 160ms' }}/>
        </div>
      </button>
      {open && (
        <div role="listbox"
          style={{ position: 'absolute', left: 0, right: 0, top: 'calc(100% + 6px)',
            zIndex: 30,
            padding: 6,
            background: '#ffffff',
            border: '1px solid rgba(16,24,40,0.1)',
            borderRadius: 10,
            boxShadow: '0 14px 28px -8px rgba(16,24,40,0.18), 0 4px 8px rgba(16,24,40,0.06)' }}>
          {items.map(it => (
            <button key={it.key} type="button"
              role="option" aria-selected={it.value}
              onClick={() => it.onChange(!it.value)}
              style={{ width: '100%', display: 'flex', alignItems: 'center',
                gap: 10, padding: '8px 10px', borderRadius: 7,
                cursor: 'pointer', textAlign: 'left',
                border: 0, fontFamily: 'inherit',
                background: it.value ? 'rgba(30,87,214,0.06)' : 'transparent',
                transition: 'background 120ms' }}
              onMouseEnter={(e) => { if (!it.value) e.currentTarget.style.background = 'rgba(16,24,40,0.04)'; }}
              onMouseLeave={(e) => { if (!it.value) e.currentTarget.style.background = 'transparent'; }}>
              <span aria-hidden style={{ width: 16, height: 16, flexShrink: 0,
                borderRadius: 4,
                border: '1.5px solid ' + (it.value ? '#1E57D6' : 'rgba(16,24,40,0.25)'),
                background: it.value
                  ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
                  : '#fff',
                display: 'inline-grid', placeItems: 'center',
                color: '#fff',
                boxShadow: it.value
                  ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 1px 2px rgba(30,87,214,0.35)'
                  : 'inset 0 1px 0 rgba(255,255,255,0.85)',
                transition: 'background 120ms, border-color 120ms' }}>
                {it.value && (
                  <svg width="10" height="10" viewBox="0 0 24 24" fill="none"
                    stroke="currentColor" strokeWidth="3.5"
                    strokeLinecap="round" strokeLinejoin="round">
                    <path d="M20 6 9 17l-5-5"/>
                  </svg>
                )}
              </span>
              <span style={{ fontSize: 13, fontWeight: 600, color: '#0B1220',
                letterSpacing: '-0.005em' }}>
                {it.label}
              </span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// Compact number input used inside the multi-row package table on the
// Air/LCL/Courier flows. Each cell carries its own label inside the
// rounded card (matching QuoteSmallInput) so labels stay readable even
// when the widget overlays the busy globe scene on mobile, and so the
// 2-row mobile layout doesn't lose the column-header context.
function PackageInput({ label, suffix, value, onChange, placeholder, ariaLabel }) {
  return (
    <label className="qg-field" style={{ display: 'block', position: 'relative',
      padding: '5px 8px',
      background: 'rgba(255,255,255,0.85)',
      border: '1px solid rgba(16,24,40,0.1)', borderRadius: 8,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
      transition: 'border-color 150ms, box-shadow 150ms' }}>
      {label && (
        <div style={{ fontSize: 8.5, fontWeight: 700, color: '#667085',
          letterSpacing: '0.1em', textTransform: 'uppercase',
          lineHeight: 1, marginBottom: 1 }}>
          {label}
        </div>
      )}
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 3 }}>
        <input
          type="number"
          inputMode="decimal"
          value={value}
          onChange={(e) => onChange(e.target.value)}
          placeholder={placeholder}
          aria-label={ariaLabel || label}
          min="0"
          style={{ width: '100%', minWidth: 0,
            border: 0, padding: 0, background: 'transparent',
            color: '#0B1220', fontSize: 13, fontWeight: 600,
            fontFamily: 'inherit', letterSpacing: '-0.005em',
            outline: 'none' }}/>
        {suffix && (
          <span style={{ fontSize: 9, color: '#98A2B3', fontWeight: 600,
            fontFamily: 'JetBrains Mono, monospace' }}>{suffix}</span>
        )}
      </div>
    </label>
  );
}

// Two-state unit toggle with a more refined "corporate" look — a
// hairline-bordered pill with an inset track, soft inactive label, and
// a deep-navy active capsule with a subtle gradient + ring. Optional
// `label` ("Dim", "Wt") prefixes the control so the user knows what
// the toggle controls without having to guess from context.
function UnitToggle({ label, value, onChange, options }) {
  return (
    <div style={{ display: 'inline-flex', alignItems: 'center', gap: 5,
      padding: '2px 3px 2px 7px',
      background: 'rgba(255,255,255,0.92)',
      border: '1px solid rgba(16,24,40,0.12)',
      borderRadius: 9999,
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85), 0 1px 1px rgba(16,24,40,0.04)' }}>
      {label && (
        <span style={{ fontSize: 8.5, fontWeight: 800, color: '#98A2B3',
          letterSpacing: '0.14em', textTransform: 'uppercase',
          lineHeight: 1 }}>
          {label}
        </span>
      )}
      <div style={{ display: 'inline-flex', padding: 1.5,
        background: 'linear-gradient(180deg, rgba(16,24,40,0.07) 0%, rgba(16,24,40,0.04) 100%)',
        borderRadius: 9999, border: '1px solid rgba(16,24,40,0.06)',
        fontFamily: 'JetBrains Mono, monospace' }}>
        {options.map(o => {
          const active = o === value;
          return (
            <button key={o} type="button"
              onClick={(e) => { e.preventDefault(); onChange(o); }}
              style={{ padding: '2px 8px',
                background: active
                  ? 'linear-gradient(180deg, #2E6AE8 0%, #1E57D6 60%, #1846B0 100%)'
                  : 'transparent',
                color: active ? '#fff' : '#667085',
                border: 0, borderRadius: 9999,
                fontFamily: 'inherit', fontSize: 9.5,
                fontWeight: 800, letterSpacing: '0.06em',
                cursor: 'pointer', textTransform: 'uppercase',
                boxShadow: active
                  ? 'inset 0 1px 0 rgba(255,255,255,0.35), 0 2px 6px rgba(30,87,214,0.35)'
                  : 'none',
                transition: 'background 120ms, color 120ms, box-shadow 120ms' }}>
              {o}
            </button>
          );
        })}
      </div>
    </div>
  );
}

// Origin → arc → Destination strip with port codes, transit time, distance
function RouteStrip({ mode }) {
  const lanes = {
    'Ocean FCL': { from: 'Shanghai', fromCode: 'CNSHA', fromCountry: 'CN',
                   to: 'Long Beach', toCode: 'USLGB', toCountry: 'US',
                   transit: '28 days', distance: '6,431 nmi', fromIcon: 'ship', toIcon: 'ship' },
    'Air':       { from: 'Shanghai', fromCode: 'PVG',   fromCountry: 'CN',
                   to: 'Los Angeles', toCode: 'LAX',    toCountry: 'US',
                   transit: '3 days',  distance: '6,484 mi', fromIcon: 'plane', toIcon: 'plane' },
    'Ground':    { from: 'Laredo',   fromCode: 'LRD',   fromCountry: 'MX/US',
                   to: 'Chicago',   toCode: 'CHI',     toCountry: 'US',
                   transit: '5 days',  distance: '1,374 mi', fromIcon: 'truck', toIcon: 'truck' },
    'Courier':   { from: 'Shenzhen', fromCode: 'SZX',   fromCountry: 'CN',
                   to: 'New York',  toCode: 'JFK',     toCountry: 'US',
                   transit: '2 days',  distance: '8,055 mi', fromIcon: 'box', toIcon: 'box' },
  };
  const L = lanes[mode] || lanes['Ocean FCL'];

  return (
    <div style={{ position: 'relative',
      background: 'linear-gradient(180deg, rgba(239,246,255,0.7) 0%, rgba(255,255,255,0.3) 100%)',
      border: '1px solid rgba(16,24,40,0.07)', borderRadius: 12,
      padding: '12px 14px',
      boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr',
        alignItems: 'center', gap: 12 }}>
        <RouteEndpoint align="left"  city={L.from} code={L.fromCode} country={L.fromCountry} icon={L.fromIcon}/>
        <RouteArc transit={L.transit}/>
        <RouteEndpoint align="right" city={L.to}   code={L.toCode}   country={L.toCountry}   icon={L.toIcon}/>
      </div>
      <div style={{ display: 'flex', justifyContent: 'space-between',
        marginTop: 10, paddingTop: 10, borderTop: '1px dashed rgba(16,24,40,0.1)',
        fontSize: 10.5, color: '#475467', fontFamily: 'JetBrains Mono, monospace',
        letterSpacing: '0.02em' }}>
        <span>1×40HC · 12,400 kg</span>
        <span>{L.distance}</span>
        <span>ETD <span style={{ color: '#0B1220', fontWeight: 700 }}>Apr 30</span></span>
      </div>
    </div>
  );
}

function RouteEndpoint({ align, city, code, country, icon }) {
  return (
    <div style={{ textAlign: align === 'right' ? 'right' : 'left' }}>
      <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6,
        flexDirection: align === 'right' ? 'row-reverse' : 'row' }}>
        <span style={{ width: 22, height: 22, borderRadius: 7,
          background: '#fff', border: '1px solid rgba(16,24,40,0.1)',
          display: 'inline-grid', placeItems: 'center', color: '#1E57D6',
          boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.9), 0 1px 2px rgba(16,24,40,0.06)' }}>
          <Icon name={icon} size={12}/>
        </span>
        <div style={{ fontSize: 10, fontWeight: 700, color: '#0B1220',
          fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.06em' }}>
          {code}
        </div>
      </div>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#0B1220',
        letterSpacing: '-0.01em', marginTop: 4, lineHeight: 1.1 }}>
        {city}
      </div>
      <div style={{ fontSize: 10.5, color: '#667085', marginTop: 2,
        letterSpacing: '0.02em' }}>
        {country}
      </div>
    </div>
  );
}

function RouteArc({ transit }) {
  return (
    <div style={{ width: 84, position: 'relative', display: 'flex',
      flexDirection: 'column', alignItems: 'center' }}>
      <svg width="84" height="28" viewBox="0 0 84 28" style={{ display: 'block' }}>
        <defs>
          <linearGradient id="arcGrad" x1="0" y1="0" x2="1" y2="0">
            <stop offset="0%"  stopColor="#1E57D6" stopOpacity="0.2"/>
            <stop offset="50%" stopColor="#1E57D6" stopOpacity="0.95"/>
            <stop offset="100%" stopColor="#1E57D6" stopOpacity="0.2"/>
          </linearGradient>
        </defs>
        <path d="M 4 22 Q 42 -6 80 22" fill="none"
          stroke="url(#arcGrad)" strokeWidth="1.5" strokeDasharray="2 3"/>
        <circle cx="4"  cy="22" r="2.5" fill="#1E57D6"/>
        <circle cx="80" cy="22" r="2.5" fill="#1E57D6"/>
        {/* Moving pulse along path */}
        <circle r="3" fill="#1E57D6">
          <animateMotion dur="3.2s" repeatCount="indefinite"
            path="M 4 22 Q 42 -6 80 22"/>
          <animate attributeName="opacity" values="0;1;1;0"
            keyTimes="0;0.1;0.9;1" dur="3.2s" repeatCount="indefinite"/>
        </circle>
      </svg>
      <div style={{ fontSize: 10, fontWeight: 700, color: '#1E57D6',
        fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.06em',
        marginTop: 2 }}>
        {transit}
      </div>
    </div>
  );
}

// Pull a usable total out of a Walio rate. Different endpoints expose the
// total under different keys: completeQuote.totalCost (FCL/LCL/Air new
// schema), pricing.total (FCL/Land/Courier), totalPrice (Air/Vehicle).
function rateTotalOf(r) {
  if (!r) return 0;
  if (r.completeQuote && typeof r.completeQuote.totalCost === 'number') return r.completeQuote.totalCost;
  if (r.pricing && typeof r.pricing.total === 'number') return r.pricing.total;
  if (typeof r.totalPrice === 'number') return r.totalPrice;
  if (typeof r.totalCost === 'number') return r.totalCost;
  return 0;
}

const fmtMoney = (v, ccy) => {
  if (typeof v !== 'number' || !isFinite(v)) return null;
  try { return v.toLocaleString('en-US', { style: 'currency', currency: ccy || 'USD',
    maximumFractionDigits: v >= 100 ? 0 : 2 }); }
  catch (e) { return '$' + v.toLocaleString('en-US'); }
};
const fmtDate = (iso) => {
  if (!iso) return null;
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return iso;
    return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
  } catch (e) { return iso; }
};
const fmtShortDate = (iso) => {
  if (!iso) return null;
  try {
    const d = new Date(iso);
    if (isNaN(d.getTime())) return iso;
    return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
  } catch (e) { return iso; }
};
const titleCase = (s) => (s || '').toString()
  .replace(/[_-]+/g, ' ')
  // split camelCase: "oceanFreight" → "ocean Freight"
  .replace(/([a-z])([A-Z])/g, '$1 $2')
  // collapse multiple spaces and uppercase first letter of each word
  .replace(/\s+/g, ' ')
  .replace(/\b\w/g, c => c.toUpperCase());
const carrierCode = (c) => (c || '?').toString().toUpperCase().replace(/[^A-Z0-9]/g, '').slice(0, 4) || '?';

// Fetch the Walio carriers catalog once and reuse for every rate card. The
// admin portal at abgroupshipping.com pulls logos and brand colors from this
// same endpoint — using it here gives us real carrier marks (COSCO red,
// MAERSK blue, etc.) instead of hand-coded brand chips.
const WALIO_CARRIERS_URL =
  'https://us-central1-abgs-calculadora.cloudfunctions.net/walioPublicAPI/v1/carriers';
let __walioCarrierIndex = null;     // built lookup map (normalized name → entry)
let __walioCarrierPromise = null;   // dedupes concurrent fetches
const __walioNorm = (s) => (s || '').toString().toLowerCase().replace(/[^a-z0-9]/g, '');
function loadWalioCarriers() {
  if (__walioCarrierIndex) return Promise.resolve(__walioCarrierIndex);
  if (__walioCarrierPromise) return __walioCarrierPromise;
  __walioCarrierPromise = fetch(WALIO_CARRIERS_URL)
    .then(r => r.ok ? r.json() : null)
    .then(json => {
      const idx = new Map();
      const tokens = []; // [{ key, entry }] for fuzzy fallback
      const data = json && json.data ? json.data : {};
      // shippingLines, airlines, courier, land — flatten and index by name
      Object.keys(data).forEach(group => {
        const arr = Array.isArray(data[group]) ? data[group] : [];
        arr.forEach(c => {
          if (!c || !c.name || !c.logoUrl) return;
          const entry = { name: c.name, logoUrl: c.logoUrl,
            brandColor: c.brandColor || null, group };
          const k = __walioNorm(c.name);
          if (k && !idx.has(k)) idx.set(k, entry);
          tokens.push({ key: k, entry });
        });
      });
      __walioCarrierIndex = { exact: idx, tokens };
      return __walioCarrierIndex;
    })
    .catch(() => { __walioCarrierIndex = { exact: new Map(), tokens: [] };
      return __walioCarrierIndex; });
  return __walioCarrierPromise;
}
// React hook: returns the index when available, triggers a single fetch.
function useWalioCarriers() {
  const [idx, setIdx] = useStateH(__walioCarrierIndex);
  useEffectH(() => {
    if (__walioCarrierIndex) { setIdx(__walioCarrierIndex); return; }
    let alive = true;
    loadWalioCarriers().then(v => { if (alive) setIdx(v); });
    return () => { alive = false; };
  }, []);
  return idx;
}
// Resolve a rate-response carrier name to a Walio catalog entry. Tries an
// exact normalized match first ("COSCO Shipping" → "coscoshipping" → "cosco"),
// then a token containment fuzzy match for names that diverge slightly.
function resolveCarrierLogo(idx, name) {
  if (!idx || !name) return null;
  const n = __walioNorm(name);
  if (!n) return null;
  if (idx.exact.has(n)) return idx.exact.get(n);
  // Try progressively shorter prefixes
  for (let len = n.length - 1; len >= 3; len--) {
    if (idx.exact.has(n.slice(0, len))) return idx.exact.get(n.slice(0, len));
  }
  // Fuzzy: rate name contains catalog key, or vice versa (longest first)
  let best = null;
  for (const t of idx.tokens) {
    if (!t.key || t.key.length < 3) continue;
    if (n.includes(t.key) || t.key.includes(n)) {
      if (!best || t.key.length > best.key.length) best = t;
    }
  }
  return best ? best.entry : null;
}

// Carrier brand registry — primary brand color + initials for the logo badge.
// Walio responses don't carry logo URLs, so we recognise carriers by name and
// render an Airbnb-style branded chip. Unknown carriers fall back to the dark
// monochrome badge defined in carrierBrand() below.
const CARRIER_BRANDS = [
  // Ocean liners
  { match: /\bcosco\b/i,                bg: '#E60012', fg: '#fff', label: 'COSCO' },
  { match: /\bmaersk\b/i,                bg: '#42B0D5', fg: '#0B1220', label: 'MAERSK' },
  { match: /\bmsc\b|mediterranean shipping/i, bg: '#FFCB00', fg: '#0B1220', label: 'MSC' },
  { match: /\bcma[\s-]*cgm\b/i,          bg: '#0B66A8', fg: '#fff', label: 'CMA' },
  { match: /\bevergreen\b/i,             bg: '#00853E', fg: '#fff', label: 'EVER' },
  { match: /hapag[-\s]*lloyd/i,          bg: '#FF6600', fg: '#fff', label: 'HAPAG' },
  { match: /\bone\b|ocean network express/i, bg: '#DE0084', fg: '#fff', label: 'ONE' },
  { match: /\bhmm\b|hyundai merchant/i,  bg: '#003C7E', fg: '#fff', label: 'HMM' },
  { match: /yang[\s-]*ming/i,            bg: '#00468C', fg: '#fff', label: 'YML' },
  { match: /\bzim\b/i,                   bg: '#1F3864', fg: '#fff', label: 'ZIM' },
  { match: /\boocl\b/i,                  bg: '#003876', fg: '#fff', label: 'OOCL' },
  { match: /\bpil\b|pacific international/i, bg: '#003E7E', fg: '#fff', label: 'PIL' },
  { match: /\bwan\s*hai\b/i,             bg: '#003B71', fg: '#fff', label: 'WHL' },
  { match: /\bsealand\b/i,               bg: '#005DAA', fg: '#fff', label: 'SEAL' },
  // Air carriers
  { match: /lufthansa/i,                 bg: '#FFD600', fg: '#0B1220', label: 'LH' },
  { match: /emirates/i,                  bg: '#D71921', fg: '#fff', label: 'EK' },
  { match: /qatar\s*airways/i,           bg: '#5C0632', fg: '#fff', label: 'QR' },
  { match: /cathay\s*pacific/i,          bg: '#006564', fg: '#fff', label: 'CX' },
  { match: /korean\s*air/i,              bg: '#00256C', fg: '#fff', label: 'KE' },
  { match: /singapore\s*airlines/i,      bg: '#19457E', fg: '#fff', label: 'SQ' },
  { match: /turkish\s*airlines/i,        bg: '#C70A0C', fg: '#fff', label: 'TK' },
  { match: /china\s*southern/i,          bg: '#0067B1', fg: '#fff', label: 'CZ' },
  { match: /china\s*airlines/i,          bg: '#C8102E', fg: '#fff', label: 'CI' },
  { match: /etihad/i,                    bg: '#BA9869', fg: '#fff', label: 'EY' },
  { match: /atlas\s*air/i,               bg: '#00467F', fg: '#fff', label: 'ATL' },
  { match: /\bana\b|all nippon/i,        bg: '#13448F', fg: '#fff', label: 'ANA' },
  { match: /jal|japan\s*airlines/i,      bg: '#C8102E', fg: '#fff', label: 'JAL' },
  // Couriers
  { match: /\bdhl\b/i,                   bg: '#FFCC00', fg: '#D40511', label: 'DHL' },
  { match: /\bfedex\b/i,                 bg: '#4D148C', fg: '#FF6600', label: 'FX' },
  { match: /\bups\b/i,                   bg: '#351C15', fg: '#FFB500', label: 'UPS' },
  { match: /\busps\b/i,                  bg: '#004B87', fg: '#fff', label: 'USPS' },
  // Ground / trucking
  { match: /\bxpo\b/i,                   bg: '#E03C31', fg: '#fff', label: 'XPO' },
  { match: /old\s*dominion|\bodfl\b/i,   bg: '#76232F', fg: '#fff', label: 'ODFL' },
  { match: /estes\b/i,                   bg: '#E03C31', fg: '#fff', label: 'EXLA' },
  { match: /yellow|\byrc\b/i,            bg: '#FFCB00', fg: '#0B1220', label: 'YRC' },
];
const carrierBrand = (name) => {
  const s = (name || '').toString();
  for (const b of CARRIER_BRANDS) if (b.match.test(s)) return b;
  return { bg: 'linear-gradient(180deg, #0B1220 0%, #1f2937 100%)', fg: '#fff',
    label: carrierCode(s) };
};

// Renders all rates returned by the API, each as a RichRateCard. Shows
// every field the response contains (price breakdown, transit, route,
// distances, drayage, pickup/delivery, sailings, free time, features,
// restrictions, validity). Cheapest first; the top one is highlighted.
function RatesList({ rates }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8,
      maxHeight: 460, overflowY: 'auto', overflowX: 'hidden',
      paddingRight: 4, marginRight: -4 }}>
      {rates.map((r, idx) => (
        <RichRateCard key={r.rateId || r.id || idx} r={r} highlight={idx === 0} bestPrice={idx === 0}/>
      ))}
    </div>
  );
}

// Airbnb-style quick stat — bold value, small caps label below.
// Used in the always-visible strip on each rate card.
function QuickStat({ label, value }) {
  return (
    <div style={{ minWidth: 0, textAlign: 'center', padding: '0 4px' }}>
      <div style={{ fontSize: 14, fontWeight: 700, color: '#0B1220',
        letterSpacing: '-0.005em', fontVariantNumeric: 'tabular-nums',
        whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
        fontFamily: 'JetBrains Mono, monospace' }}>
        {value}
      </div>
      <div style={{ fontSize: 9, fontWeight: 700, color: '#98A2B3',
        letterSpacing: '0.08em', textTransform: 'uppercase', marginTop: 3 }}>
        {label}
      </div>
    </div>
  );
}

// Compact "label · value" row used inside the rich rate card.
function StatRow({ label, value, mono }) {
  if (value == null || value === '') return null;
  return (
    <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
      gap: 10, padding: '3px 0', fontSize: 11.5, lineHeight: 1.3 }}>
      <span style={{ color: '#667085', fontWeight: 500, letterSpacing: '0.02em',
        fontSize: 10.5, textTransform: 'uppercase' }}>{label}</span>
      <span style={{ color: '#0B1220', fontWeight: 600, textAlign: 'right',
        fontFamily: mono ? 'JetBrains Mono, monospace' : 'inherit',
        fontVariantNumeric: 'tabular-nums' }}>
        {value}
      </span>
    </div>
  );
}

// Section heading used inside the rich rate card. `noBorderTop` lets a section
// sit inside an inner grid (where dividing lines come from the grid, not the
// section itself) without doubling up on hairlines.
function CardSection({ label, children, noBorderTop }) {
  return (
    <div style={{ borderTop: noBorderTop ? 0 : '1px solid rgba(16,24,40,0.06)',
      padding: '8px 12px 6px' }}>
      <div style={{ fontSize: 9.5, fontWeight: 700, color: '#98A2B3',
        letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 4 }}>
        {label}
      </div>
      {children}
    </div>
  );
}

// Pulls every line item out of a rate's various breakdown shapes. Endpoints
// expose the same totals in multiple shapes (pricing.breakdown +
// completeQuote.* + pricingBreakdown[] + top-level airFreight) which would
// double-count if we just concatenated them all. Strategy: prefer the
// richest source first and only fall back to others when nothing is found.
function collectBreakdown(r) {
  const items = [];
  const cq = r.completeQuote;

  // 1. Preferred — completeQuote (newer schema, already itemised by Walio).
  if (cq) {
    if (typeof cq.oceanFreight === 'number')
      items.push({ label: 'Ocean freight', amount: cq.oceanFreight });
    if (typeof cq.airFreight === 'number')
      items.push({ label: 'Air freight', amount: cq.airFreight });
    if (Array.isArray(cq.additionalServices)) {
      cq.additionalServices.forEach(s => {
        if (s && typeof s.amount === 'number')
          items.push({ label: s.name || 'Service', amount: s.amount });
      });
    }
    if (typeof cq.drayageCost === 'number' && cq.drayageCost > 0)
      items.push({ label: 'Drayage', amount: cq.drayageCost });
    if (typeof cq.servicesCost === 'number' && cq.servicesCost > 0
        && !Array.isArray(cq.additionalServices))
      items.push({ label: 'Services', amount: cq.servicesCost });
  }

  // 2. Fallback — pricingBreakdown array (FCL pre-completeQuote schema).
  if (items.length === 0 && Array.isArray(r.pricingBreakdown)) {
    r.pricingBreakdown.forEach(item => {
      if (item && typeof item.amount === 'number')
        items.push({ label: item.name || item.code || 'Charge', amount: item.amount });
    });
  }

  // 3. Fallback — pricing.breakdown object (Land / Courier).
  if (items.length === 0 && r.pricing && r.pricing.breakdown
      && typeof r.pricing.breakdown === 'object'
      && !Array.isArray(r.pricing.breakdown)) {
    Object.entries(r.pricing.breakdown).forEach(([k, v]) => {
      if (typeof v === 'number' && k !== 'total')
        items.push({ label: titleCase(k), amount: v });
    });
  }

  // 4. Fallback — additionalServices array at the rate root (Air schema).
  if (items.length === 0 && Array.isArray(r.additionalServices)) {
    r.additionalServices.forEach(s => {
      if (s && typeof s.amount === 'number')
        items.push({ label: s.name || 'Service', amount: s.amount });
    });
  }

  // 5. Catch-all — root-level airFreight if nothing surfaced a freight line.
  if (typeof r.airFreight === 'number' && !items.find(i => /freight/i.test(i.label)))
    items.push({ label: 'Air freight', amount: r.airFreight });

  // Dedupe identical lines (label + amount) — defensive, in case a single
  // source repeats itself. Whitespace and case insensitive.
  const seen = new Set();
  return items.filter(it => {
    const lbl = it.label.toLowerCase().replace(/\s+/g, '');
    const amt = Math.round(it.amount * 100) / 100;
    const key = lbl + ':' + amt;
    if (seen.has(key)) return false;
    seen.add(key);
    return true;
  });
}

function RichRateCard({ r, highlight, bestPrice, setStats }) {
  const [expanded, setExpanded] = useStateH(false);
  const [logoFailed, setLogoFailed] = useStateH(false);
  const carrierIdx = useWalioCarriers();
  // Derive normalized values from whichever shape the endpoint used.
  const ccy        = (r.pricing && r.pricing.currency) || r.currency || 'USD';
  const total      = rateTotalOf(r);
  const carrierLbl = r.displayCarrier || r.shippingLine || r.carrier || r.airline || 'Carrier';
  const service    = r.service || 'Standard';
  const serviceDes = r.serviceDescription;
  const transit    = (r.transit && r.transit.days) || r.transitDays || r.oceanTransitTime;
  const transitMin = r.transit && r.transit.minDays;
  const transitMax = r.transit && r.transit.maxDays;
  const etd        = (r.transit && r.transit.estimatedDelivery) || r.estimatedDelivery;
  const validUntil = r.validUntil || r.validTo || r.validTill;
  const validFrom  = r.validFrom;
  const originPort = (r.route && r.route.originPort) || r.originAirport || r.originPort;
  const destPort   = (r.route && r.route.destinationPort) || r.destinationAirport || r.destinationPort;
  const oCountry   = (r.route && r.route.originCountry) || r.originCountry;
  const dCountry   = (r.route && r.route.destinationCountry) || r.destinationCountry;
  const oDist      = r.originPortDistance != null ? r.originPortDistance
                     : (r.distanceFromOrigin != null ? r.distanceFromOrigin : null);
  const dDist      = r.destinationPortDistance != null ? r.destinationPortDistance
                     : (r.distanceToDestination != null ? r.distanceToDestination : null);
  const distUnit   = r.distanceUnit || 'mi';
  const breakdown  = collectBreakdown(r);
  const features   = Array.isArray(r.features) ? r.features.filter(Boolean) : [];
  const restrictions = Array.isArray(r.restrictions) ? r.restrictions.filter(Boolean) : [];
  const ratePerKg   = r.ratePerKg;
  const ratePerCBM  = r.ratePerCBM;
  const actCBM      = r.actualCBM;
  const volCBM      = r.volumetricCBM;
  const chgCBM      = r.chargeableCBM;
  const minChargeC  = r.minCharge;
  const actW        = r.actualWeightKg;
  const volW        = r.volumetricWeightKg;
  const chgW        = r.chargeableWeightKg;
  const minAir      = r.minAir;

  // Resolve the real carrier logo from Walio's catalog. Match against any
  // of the names the rate response might carry. If we find a logo URL, we
  // render it as an <img> with onError → branded fallback chip.
  const walioCarrier =
    resolveCarrierLogo(carrierIdx, r.displayCarrier) ||
    resolveCarrierLogo(carrierIdx, r.shippingLine) ||
    resolveCarrierLogo(carrierIdx, r.carrier) ||
    resolveCarrierLogo(carrierIdx, r.airline);
  const fallbackBrand = carrierBrand(carrierLbl);
  // Walio's brandColor wins over the hand-coded registry when available.
  const brand = walioCarrier && walioCarrier.brandColor
    ? { bg: walioCarrier.brandColor, fg: '#fff', label: fallbackBrand.label }
    : fallbackBrand;
  const showRealLogo = walioCarrier && walioCarrier.logoUrl && !logoFailed;
  const transitDisplay = transit != null
    ? (transitMin && transitMax && transitMin !== transitMax
        ? `${transitMin}–${transitMax} days` : `${transit} days`)
    : null;
  const freeTimeDisplay = (r.freeDays != null)
    ? `${r.freeDays} days`
    : (r.freeTime != null ? String(r.freeTime) : null);
  const hasChargeableBasis = (actCBM != null || volCBM != null || chgCBM != null
    || ratePerCBM != null || actW != null || volW != null || chgW != null
    || ratePerKg != null || minAir != null || minChargeC != null);

  // Selling badges — every chip is data-driven, never marketing fluff.
  // Order matters: badges render in the order returned, top-priority first.
  // We cap at 3 visible chips per card to keep the hero band uncluttered.
  const badges = [];
  if (bestPrice) {
    badges.push({ key: 'best-price', label: 'Best price',
      bg: 'rgba(18,183,106,0.12)', fg: '#067647',
      bd: 'rgba(18,183,106,0.3)' });
  }
  // Fastest = lowest transit days in the result set (and not a tie with itself only)
  if (setStats && setStats.minTransit != null && transit === setStats.minTransit
      && setStats.count > 1) {
    badges.push({ key: 'fastest', label: 'Fastest',
      bg: 'rgba(30,87,214,0.1)', fg: '#1846B0',
      bd: 'rgba(30,87,214,0.28)' });
  }
  // Direct = no rail leg, no transshipment via different ocean port
  const isDirect = !r.hasRail
    && (!r.originalOceanPort || !originPort || r.originalOceanPort === originPort);
  if (isDirect && (originPort || destPort)) {
    badges.push({ key: 'direct', label: 'Direct service',
      bg: 'rgba(124,58,237,0.1)', fg: '#5B21B6',
      bd: 'rgba(124,58,237,0.28)' });
  }
  // Door-to-door = pickup AND delivery handled by carrier
  if (r.hasPickup && r.hasDelivery) {
    badges.push({ key: 'd2d', label: 'Door to door',
      bg: 'rgba(2,132,199,0.1)', fg: '#075985',
      bd: 'rgba(2,132,199,0.3)' });
  }
  // Longest free time = max free days in the set (and meaningful: ≥ 7 days)
  if (setStats && setStats.maxFree != null && r.freeDays != null
      && r.freeDays === setStats.maxFree && r.freeDays >= 7
      && setStats.count > 1) {
    badges.push({ key: 'free-time', label: `${r.freeDays} days free`,
      bg: 'rgba(245,158,11,0.12)', fg: '#92400E',
      bd: 'rgba(245,158,11,0.3)' });
  }
  // Expires soon = validUntil within next 3 days (urgency, not fake scarcity)
  if (validUntil) {
    const ms = new Date(validUntil).getTime() - Date.now();
    if (ms > 0 && ms < 3 * 24 * 60 * 60 * 1000) {
      badges.push({ key: 'urgent', label: 'Expires soon',
        bg: 'rgba(220,38,38,0.1)', fg: '#991B1B',
        bd: 'rgba(220,38,38,0.28)' });
    }
  }
  // Customs included = carrier handles clearance (data signal in features)
  const hasCustoms = features.some(f => /customs/i.test(f));
  if (hasCustoms) {
    badges.push({ key: 'customs', label: 'Customs included',
      bg: 'rgba(16,24,40,0.06)', fg: '#0B1220',
      bd: 'rgba(16,24,40,0.14)' });
  }
  // Cap visible badges so the header stays calm. Best price always wins
  // first slot; the next two slots are filled in priority order above.
  const visibleBadges = badges.slice(0, 3);

  return (
    <div style={{ position: 'relative', flexShrink: 0,
      background: '#ffffff',
      border: highlight ? '1px solid rgba(30,87,214,0.32)' : '1px solid rgba(16,24,40,0.08)',
      borderRadius: 16,
      boxShadow: highlight
        ? '0 10px 24px -8px rgba(30,87,214,0.22), 0 2px 4px rgba(16,24,40,0.04), inset 0 1px 0 rgba(255,255,255,0.95)'
        : '0 4px 12px -4px rgba(16,24,40,0.08), 0 1px 2px rgba(16,24,40,0.04), inset 0 1px 0 rgba(255,255,255,0.85)',
      overflow: 'hidden',
      transition: 'transform 220ms cubic-bezier(0.16,1,0.3,1), box-shadow 220ms' }}
      onMouseEnter={(e) => { e.currentTarget.style.transform = 'translateY(-2px)';
        e.currentTarget.style.boxShadow = highlight
          ? '0 16px 32px -8px rgba(30,87,214,0.3), 0 4px 8px rgba(16,24,40,0.06), inset 0 1px 0 rgba(255,255,255,0.95)'
          : '0 12px 24px -8px rgba(16,24,40,0.14), 0 2px 4px rgba(16,24,40,0.06), inset 0 1px 0 rgba(255,255,255,0.85)';
      }}
      onMouseLeave={(e) => { e.currentTarget.style.transform = 'translateY(0)';
        e.currentTarget.style.boxShadow = highlight
          ? '0 10px 24px -8px rgba(30,87,214,0.22), 0 2px 4px rgba(16,24,40,0.04), inset 0 1px 0 rgba(255,255,255,0.95)'
          : '0 4px 12px -4px rgba(16,24,40,0.08), 0 1px 2px rgba(16,24,40,0.04), inset 0 1px 0 rgba(255,255,255,0.85)';
      }}>

      {/* Hero band — carrier brand chip, name, big price */}
      <div style={{ padding: '14px 16px 12px',
        display: 'grid', gridTemplateColumns: 'auto 1fr auto',
        alignItems: 'center', gap: 14 }}>
        {/* Brand chip — Airbnb-style logo block. Real carrier logos come
            from Walio's /v1/carriers catalog; we fall back to a brand-color
            chip with the carrier code if the image fails or isn't found. */}
        {showRealLogo ? (
          <div style={{ width: 56, height: 56, borderRadius: 14,
            background: '#fff',
            border: '1px solid rgba(16,24,40,0.06)',
            display: 'grid', placeItems: 'center',
            padding: 6, flexShrink: 0,
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.95), 0 1px 2px rgba(16,24,40,0.06)' }}>
            <img src={walioCarrier.logoUrl}
              alt={`${walioCarrier.name} logo`}
              onError={() => setLogoFailed(true)}
              style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain',
                display: 'block' }}/>
          </div>
        ) : (
          <div style={{ width: 56, height: 56, borderRadius: 14,
            background: brand.bg, color: brand.fg,
            display: 'grid', placeItems: 'center',
            fontFamily: 'Inter, system-ui, sans-serif', fontSize: 12,
            fontWeight: 800, letterSpacing: '0.02em',
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.18), 0 2px 4px rgba(16,24,40,0.1)',
            flexShrink: 0 }}>
            {brand.label}
          </div>
        )}
        <div style={{ minWidth: 0 }}>
          <span style={{ display: 'block', fontSize: 15, fontWeight: 700, color: '#0B1220',
            letterSpacing: '-0.01em', whiteSpace: 'nowrap', overflow: 'hidden',
            textOverflow: 'ellipsis', maxWidth: '100%' }}>{carrierLbl}</span>
          <div style={{ fontSize: 12, color: '#667085', marginTop: 3, lineHeight: 1.3,
            whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {titleCase(service)}{serviceDes ? ` · ${serviceDes}` : ''}
          </div>
          {/* Selling badges row — all data-driven. Sits beneath the carrier
              service line so it doesn't fight with the brand name for space.
              Mono caps tracking matches the rest of the system. */}
          {visibleBadges.length > 0 && (
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 7 }}>
              {visibleBadges.map(b => (
                <span key={b.key} style={{
                  padding: '2px 8px', borderRadius: 9999,
                  background: b.bg, color: b.fg,
                  border: `1px solid ${b.bd}`,
                  fontSize: 9.5, fontWeight: 700, letterSpacing: '0.06em',
                  textTransform: 'uppercase', whiteSpace: 'nowrap',
                  fontFamily: 'Inter, system-ui, sans-serif' }}>
                  {b.label}
                </span>
              ))}
            </div>
          )}
        </div>
        <div style={{ textAlign: 'right' }}>
          <div style={{ fontSize: 22, fontWeight: 800, color: '#0B1220',
            letterSpacing: '-0.025em', fontVariantNumeric: 'tabular-nums', lineHeight: 1 }}>
            {fmtMoney(total, ccy) || '—'}
          </div>
          <div style={{ fontSize: 9, color: '#98A2B3', marginTop: 4,
            letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 700 }}>
            All-in {ccy}
          </div>
        </div>
      </div>

      {/* Quick stats strip — transit / ETD / free time, prominently displayed */}
      <div style={{ padding: '0 16px 12px',
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(0, 1fr))',
        gap: 0 }}>
        <div style={{ display: 'grid',
          gridTemplateColumns: `repeat(${[transitDisplay, fmtShortDate(etd), freeTimeDisplay].filter(Boolean).length || 1}, minmax(0, 1fr))`,
          gap: 8, padding: '10px 12px',
          background: 'linear-gradient(180deg, rgba(247,249,252,0.7) 0%, rgba(247,249,252,0.4) 100%)',
          borderRadius: 10, border: '1px solid rgba(16,24,40,0.05)' }}>
          {transitDisplay && (
            <QuickStat label="Transit" value={transitDisplay}/>
          )}
          {fmtShortDate(etd) && (
            <QuickStat label="ETD" value={fmtShortDate(etd)}/>
          )}
          {freeTimeDisplay && (
            <QuickStat label="Free time" value={freeTimeDisplay}/>
          )}
        </div>
      </div>

      {/* Route line — single visual indicator, no clutter */}
      {(originPort || destPort) && (
        <div style={{ padding: '0 16px 12px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8,
            padding: '8px 12px',
            background: '#fff', border: '1px solid rgba(16,24,40,0.06)',
            borderRadius: 10,
            fontFamily: 'JetBrains Mono, monospace', fontSize: 11,
            color: '#0B1220', fontWeight: 600, letterSpacing: '0.02em',
            minWidth: 0 }}>
            <span style={{ overflow: 'hidden', textOverflow: 'ellipsis',
              whiteSpace: 'nowrap', minWidth: 0 }}>
              {originPort || '—'}{oCountry ? ` (${oCountry})` : ''}
            </span>
            <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
              stroke="#1E57D6" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"
              style={{ flexShrink: 0 }}>
              <path d="M5 12h14M13 6l6 6-6 6"/>
            </svg>
            <span style={{ overflow: 'hidden', textOverflow: 'ellipsis',
              whiteSpace: 'nowrap', minWidth: 0 }}>
              {destPort || '—'}{dCountry ? ` (${dCountry})` : ''}
            </span>
          </div>
        </div>
      )}

      {/* Disclosure trigger — Airbnb-style "Show breakdown" pill. The
          breakdown is the most-asked-for detail, so it leads the trigger
          copy and the expanded content. */}
      <div style={{ padding: '0 16px 12px' }}>
        <button type="button" onClick={() => setExpanded(e => !e)}
          aria-expanded={expanded}
          style={{ width: '100%', padding: '10px 14px',
            background: expanded ? 'rgba(30,87,214,0.06)' : '#fff',
            border: '1px solid rgba(16,24,40,0.1)',
            borderRadius: 10, cursor: 'pointer',
            fontFamily: 'inherit', fontSize: 12, fontWeight: 600, color: '#0B1220',
            letterSpacing: '-0.005em',
            display: 'inline-flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
            boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.85)',
            transition: 'background 200ms, border-color 200ms' }}
          onMouseEnter={(e) => {
            e.currentTarget.style.background = expanded ? 'rgba(30,87,214,0.1)' : 'rgba(247,249,252,0.85)';
            e.currentTarget.style.borderColor = 'rgba(30,87,214,0.3)';
          }}
          onMouseLeave={(e) => {
            e.currentTarget.style.background = expanded ? 'rgba(30,87,214,0.06)' : '#fff';
            e.currentTarget.style.borderColor = 'rgba(16,24,40,0.1)';
          }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8 }}>
            <Icon name="sparkles" size={12} style={{ color: '#1E57D6' }}/>
            {expanded ? 'Hide breakdown' : 'Show breakdown'}
          </span>
          <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
            stroke="#475467" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round"
            style={{ transform: expanded ? 'rotate(180deg)' : 'rotate(0deg)',
              transition: 'transform 220ms cubic-bezier(0.4,0,0.2,1)' }}>
            <path d="M6 9l6 6 6-6"/>
          </svg>
        </button>
      </div>

      {expanded && (
        <div style={{ background: 'rgba(247,249,252,0.55)',
          borderTop: '1px solid rgba(16,24,40,0.06)' }}>

          {/* Price breakdown leads — the "summary in the desglose" the user wants */}
          {breakdown.length > 0 && (
            <CardSection label="Price breakdown" noBorderTop>
              {breakdown.map((it, i) => (
                <StatRow key={`${it.label}-${i}`} label={it.label}
                  value={fmtMoney(it.amount, ccy)} mono/>
              ))}
              <div style={{ borderTop: '1px dashed rgba(16,24,40,0.12)',
                margin: '6px 0 0', paddingTop: 6 }}>
                <StatRow label="Total all-in" value={fmtMoney(total, ccy)} mono/>
              </div>
            </CardSection>
          )}

          {hasChargeableBasis && (
            <CardSection label="Chargeable basis">
              {actCBM != null  && <StatRow label="Actual volume"     value={`${actCBM} cbm`}      mono/>}
              {volCBM != null  && <StatRow label="Volumetric volume" value={`${volCBM} cbm`}      mono/>}
              {chgCBM != null  && <StatRow label="Chargeable volume" value={`${chgCBM} cbm`}      mono/>}
              {ratePerCBM != null && <StatRow label="Rate / cbm" value={fmtMoney(ratePerCBM, ccy)} mono/>}
              {minChargeC != null && <StatRow label="Minimum charge" value={fmtMoney(minChargeC, ccy)} mono/>}
              {actW != null  && <StatRow label="Actual weight"     value={`${actW} kg`} mono/>}
              {volW != null  && <StatRow label="Volumetric weight" value={`${volW} kg`} mono/>}
              {chgW != null  && <StatRow label="Chargeable weight" value={`${chgW} kg`} mono/>}
              {ratePerKg != null && <StatRow label="Rate / kg" value={fmtMoney(ratePerKg, ccy)} mono/>}
              {minAir != null && <StatRow label="Minimum air" value={fmtMoney(minAir, ccy)} mono/>}
              {r.minApplied != null && <StatRow label="Minimum applied" value={r.minApplied ? 'Yes' : 'No'} mono/>}
            </CardSection>
          )}

          {/* Route extras — distances, rail leg, original port (the lane
              summary above is enough at a glance; details belong here) */}
          {(oDist != null || dDist != null || (r.hasRail && (r.railOrigin || r.railDestination))
            || r.originalOceanPort || (validFrom || validUntil)) && (
            <CardSection label="Route & validity">
              {oDist != null && (
                <StatRow label="Origin port dist." value={`${oDist} ${distUnit}`} mono/>
              )}
              {dDist != null && (
                <StatRow label="Dest. port dist." value={`${dDist} ${distUnit}`} mono/>
              )}
              {r.hasRail && (r.railOrigin || r.railDestination) && (
                <StatRow label="Rail leg"
                  value={`${r.railOrigin || '—'} → ${r.railDestination || '—'}`} mono/>
              )}
              {r.originalOceanPort && (
                <StatRow label="Original port" value={r.originalOceanPort} mono/>
              )}
              {r.oceanTransitTime != null && (
                <StatRow label="Ocean transit" value={`${r.oceanTransitTime} days`} mono/>
              )}
              {r.railTransitTime != null && (
                <StatRow label="Rail transit"
                  value={`${r.railTransitTime} days${r.railTransitPending ? ' (est.)' : ''}`} mono/>
              )}
              {(validFrom || validUntil) && (
                <StatRow label="Valid"
                  value={validFrom && validUntil
                    ? `${fmtShortDate(validFrom)} → ${fmtDate(validUntil)}`
                    : `through ${fmtDate(validUntil || validFrom)}`} mono/>
              )}
            </CardSection>
          )}

          {(r.closingDate || r.sailingDate || r.nextSailing
            || (Array.isArray(r.sailingSchedules) && r.sailingSchedules.length > 0)) && (
            <CardSection label="Sailings">
              {r.closingDate && <StatRow label="Booking cutoff" value={fmtDate(r.closingDate)} mono/>}
              {r.sailingDate && <StatRow label="Sailing date" value={fmtDate(r.sailingDate)} mono/>}
              {r.nextSailing && <StatRow label="Next sailing" value={fmtDate(r.nextSailing)} mono/>}
              {Array.isArray(r.sailingSchedules) && r.sailingSchedules.length > 0 && (
                <StatRow label="Schedules"
                  value={`${r.sailingSchedules.length} weekly`} mono/>
              )}
            </CardSection>
          )}

          {(r.hasPickup || r.hasDelivery
            || (r.hasDrayage && (r.drayageCost > 0 || r.drayageWarning))
            || r.drayageWarning || r.deliveryWarning
            || (r.hasPortFees && r.portFeesCost > 0)) && (
            <CardSection label="Pickup, delivery & drayage">
              {r.hasPickup && (
                <StatRow label="Pickup"
                  value={[r.pickupLocation,
                    fmtMoney(r.pickupCost, ccy),
                    r.pickupCarrier,
                    r.pickupTransitDays != null ? `${r.pickupTransitDays}d` : null
                  ].filter(Boolean).join(' · ')} mono/>
              )}
              {r.hasDelivery && (
                <StatRow label="Delivery"
                  value={[r.deliveryLocation, fmtMoney(r.deliveryCost, ccy)].filter(Boolean).join(' · ')} mono/>
              )}
              {r.hasDrayage && (
                <StatRow label="Drayage"
                  value={fmtMoney(r.drayageCost, ccy) || 'Included'} mono/>
              )}
              {r.drayageWarning && (
                <div style={{ marginTop: 4, padding: '5px 8px',
                  background: 'rgba(247,144,9,0.1)',
                  border: '1px solid rgba(247,144,9,0.25)', borderRadius: 6,
                  fontSize: 10.5, color: '#7A2E0E', lineHeight: 1.35 }}>
                  <strong style={{ fontWeight: 700 }}>Note:</strong> {r.drayageWarning}
                </div>
              )}
              {r.deliveryWarning && (
                <div style={{ marginTop: 4, padding: '5px 8px',
                  background: 'rgba(247,144,9,0.1)',
                  border: '1px solid rgba(247,144,9,0.25)', borderRadius: 6,
                  fontSize: 10.5, color: '#7A2E0E', lineHeight: 1.35 }}>
                  <strong style={{ fontWeight: 700 }}>Note:</strong> {r.deliveryWarning}
                </div>
              )}
              {r.hasPortFees && (
                <StatRow label="Port fees" value={fmtMoney(r.portFeesCost, ccy) || 'Included'} mono/>
              )}
            </CardSection>
          )}

          {(features.length > 0 || restrictions.length > 0) && (
            <CardSection label="Inclusions">
              {features.length > 0 && (
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 2 }}>
                  {features.map((f, i) => (
                    <span key={i} style={{ padding: '2px 7px', borderRadius: 9999,
                      background: 'rgba(18,183,106,0.1)', color: '#067647',
                      border: '1px solid rgba(18,183,106,0.25)',
                      fontSize: 10, fontWeight: 600 }}>
                      ✓ {f}
                    </span>
                  ))}
                </div>
              )}
              {restrictions.length > 0 && (
                <div style={{ marginTop: 6 }}>
                  <div style={{ fontSize: 9.5, fontWeight: 700, color: '#98A2B3',
                    letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 2 }}>
                    Restrictions
                  </div>
                  {restrictions.map((x, i) => (
                    <div key={i} style={{ fontSize: 10.5, color: '#475467',
                      lineHeight: 1.4 }}>• {x}</div>
                  ))}
                </div>
              )}
            </CardSection>
          )}
        </div>
      )}

      {/* Footer — provider / bookable / mode / id */}
      <div style={{ padding: '7px 12px 9px',
        borderTop: '1px solid rgba(16,24,40,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        gap: 10, fontSize: 10, color: '#667085',
        fontFamily: 'JetBrains Mono, monospace', letterSpacing: '0.02em' }}>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          {r.provider && <span>via {r.provider}</span>}
          {r.transportMode && r.provider && <span style={{ opacity: 0.4 }}>·</span>}
          {r.transportMode && <span>{r.transportMode}</span>}
        </span>
        <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6 }}>
          {r.bookable !== undefined && (
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4,
              color: r.bookable ? '#067647' : '#98A2B3' }}>
              <span style={{ width: 6, height: 6, borderRadius: 9999,
                background: r.bookable ? '#12b76a' : '#98A2B3' }}/>
              {r.bookable ? 'Bookable' : 'Quote only'}
            </span>
          )}
        </span>
      </div>
    </div>
  );
}

function LightGlassInputFull({ label, value, sub }) {
  return (
    <div style={{ padding: '10px 12px',
      background: 'rgba(255,255,255,0.85)',
      backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
      border: '1px solid rgba(16,24,40,0.08)', borderRadius: 12,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.85)' }}>
      <div style={{ fontSize: 10, fontWeight: 600, color: '#667085',
        letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 4 }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <Icon name="globe" size={14} style={{ color: '#1E57D6' }}/>
        <div>
          <div style={{ fontSize: 14, fontWeight: 600, color: '#0B1220',
            letterSpacing: '-0.01em' }}>{value}</div>
          <div style={{ fontSize: 10.5, color: '#98a2b3', fontFamily: 'JetBrains Mono, monospace',
            marginTop: 1, letterSpacing: '0.02em' }}>{sub}</div>
        </div>
      </div>
    </div>
  );
}

function MetaChip({ label, value }) {
  return (
    <div style={{ padding: '8px 12px',
      background: 'rgba(242,244,247,0.6)',
      border: '1px solid rgba(16,24,40,0.06)', borderRadius: 10,
      display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
      <span style={{ fontSize: 11, color: '#667085', fontWeight: 500,
        letterSpacing: '0.06em', textTransform: 'uppercase' }}>{label}</span>
      <span style={{ fontSize: 13, color: '#0B1220', fontWeight: 600,
        fontVariantNumeric: 'tabular-nums' }}>{value}</span>
    </div>
  );
}

function LightGlassInput({ value }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 14px',
      background: 'rgba(255,255,255,0.8)',
      backdropFilter: 'blur(12px)', WebkitBackdropFilter: 'blur(12px)',
      border: '1px solid rgba(0,0,0,0.08)', borderRadius: 10, color: '#0B1220',
      fontSize: 13, fontWeight: 500,
      boxShadow: 'inset 0 1px 1px rgba(255,255,255,0.8)' }}>
      <Icon name="globe" size={14} style={{ color: '#667085' }}/>
      {value}
    </div>
  );
}

window.HeroAnimated = HeroAnimated;
