{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "freight-quote-calculator",
  "title": "Freight Quote Calculator",
  "type": "registry:block",
  "files": [
    {
      "path": "packages/registry-angular/blocks/freight-quote-calculator/freight-quote-calculator.component.ts",
      "content": "// Multimodal Air, Ocean, and Ground freight rate estimator with CBM volumetric weight\n// calculation, dynamic route pricing, value-added services, and live comparison quotes.\n// Port of the React block 1:1.\nimport { ChangeDetectionStrategy, Component, Input, computed, signal } from '@angular/core'\nimport {\n  ArrowLeftRight,\n  ArrowRight,\n  Boxes,\n  Check,\n  CheckCircle2,\n  Clock,\n  Download,\n  FileText,\n  Info,\n  Leaf,\n  LucideAngularModule,\n  Package,\n  Plane,\n  Scale,\n  Ship,\n  Truck,\n  Zap,\n} from 'lucide-angular'\nimport { cn } from '@/lib/utils'\nimport { UiBadgeComponent } from '@/ui/badge/badge.component'\nimport { UiButtonComponent } from '@/ui/button/button.component'\nimport {\n  UiCardComponent,\n  UiCardContentComponent,\n  UiCardDescriptionComponent,\n  UiCardFooterComponent,\n  UiCardHeaderComponent,\n  UiCardTitleComponent,\n} from '@/ui/card/card.component'\nimport { UiCheckboxComponent } from '@/ui/checkbox/checkbox.component'\nimport { UiInputComponent } from '@/ui/input/input.component'\nimport {\n  UiSelectComponent,\n  UiSelectContentComponent,\n  UiSelectItemComponent,\n  UiSelectTriggerComponent,\n  UiSelectValueComponent,\n} from '@/ui/select/select.component'\nimport { UiSeparatorComponent } from '@/ui/separator/separator.component'\n\nexport type FreightMode = 'ocean-fcl' | 'ocean-lcl' | 'air' | 'ground'\n\ninterface PortOption {\n  code: string\n  name: string\n}\n\ninterface CurrencyConfig {\n  code: string\n  symbol: string\n  label: string\n  rate: number\n}\n\nconst ORIGIN_PORTS: PortOption[] = [\n  { code: 'CNSHA', name: 'Shanghai Port (CNSHA)' },\n  { code: 'CNNGB', name: 'Ningbo-Zhoushan Port (CNNGB)' },\n  { code: 'SGSIN', name: 'Port of Singapore (SGSIN)' },\n  { code: 'DEHAM', name: 'Port of Hamburg (DEHAM)' },\n  { code: 'NLRTM', name: 'Port of Rotterdam (NLRTM)' },\n  { code: 'JPTYO', name: 'Port of Tokyo (JPTYO)' },\n]\n\nconst DESTINATION_PORTS: PortOption[] = [\n  { code: 'USLAX', name: 'Port of Los Angeles (USLAX)' },\n  { code: 'USNYC', name: 'Port of New York & New Jersey (USNYC)' },\n  { code: 'USORD', name: 'Chicago O’Hare Logistics Hub (USORD)' },\n  { code: 'GBFXT', name: 'Port of Felixstowe (GBFXT)' },\n  { code: 'AUMEL', name: 'Port of Melbourne (AUMEL)' },\n  { code: 'AEJEA', name: 'Jebel Ali Port (AEJEA)' },\n]\n\nconst CURRENCIES: Record<string, CurrencyConfig> = {\n  USD: { code: 'USD', symbol: '$', label: 'USD ($)', rate: 1.0 },\n  EUR: { code: 'EUR', symbol: '€', label: 'EUR (€)', rate: 0.92 },\n  GBP: { code: 'GBP', symbol: '£', label: 'GBP (£)', rate: 0.79 },\n  CNY: { code: 'CNY', symbol: '¥', label: 'CNY (¥)', rate: 7.23 },\n  SGD: { code: 'SGD', symbol: 'S$', label: 'SGD (S$)', rate: 1.34 },\n}\n\nconst CURRENCY_LIST = [\n  CURRENCIES['USD']!,\n  CURRENCIES['EUR']!,\n  CURRENCIES['GBP']!,\n  CURRENCIES['CNY']!,\n  CURRENCIES['SGD']!,\n]\n\nconst CUSTOMS_USD = 150\nconst INSURANCE_USD = 85\nconst LIFTGATE_USD = 50\n\n@Component({\n  changeDetection: ChangeDetectionStrategy.Eager,\n  selector: 'ui-freight-quote-calculator, [ui-freight-quote-calculator]',\n  standalone: true,\n  // React renders the root <div> itself: the host stays out of layout and `class` goes to the root.\n  host: { '[attr.class]': '\"contents\"' },\n  imports: [\n    LucideAngularModule,\n    UiBadgeComponent,\n    UiButtonComponent,\n    UiCardComponent,\n    UiCardContentComponent,\n    UiCardDescriptionComponent,\n    UiCardFooterComponent,\n    UiCardHeaderComponent,\n    UiCardTitleComponent,\n    UiCheckboxComponent,\n    UiInputComponent,\n    UiSelectComponent,\n    UiSelectContentComponent,\n    UiSelectItemComponent,\n    UiSelectTriggerComponent,\n    UiSelectValueComponent,\n    UiSeparatorComponent,\n  ],\n  template: `\n    <div data-slot=\"freight-quote-calculator\" [class]=\"rootClass\">\n      <!-- Header Section -->\n      <div class=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n        <div class=\"space-y-1.5\">\n          <div class=\"flex items-center gap-2\">\n            <span ui-badge variant=\"outline\" class=\"gap-1.5 px-2.5 py-0.5 text-xs font-medium\">\n              <lucide-icon [img]=\"Boxes\" class=\"text-primary size-3.5\" />\n              Multimodal Logistics\n            </span>\n            <span ui-badge variant=\"secondary\" class=\"text-xs font-normal\">Spot Rates Live</span>\n          </div>\n          <h2 class=\"text-foreground text-2xl font-bold tracking-tight sm:text-3xl\">\n            Freight Rate &amp; Cargo Quote Calculator\n          </h2>\n          <p class=\"text-muted-foreground text-sm\">\n            Compare spot rates across Ocean FCL/LCL, Air Freight, and Ground Trucking with instant CBM volumetric\n            analysis.\n          </p>\n        </div>\n        <div class=\"flex items-center gap-2 sm:self-start\">\n          <span class=\"text-muted-foreground text-xs font-medium\">Currency:</span>\n          <ui-select [value]=\"selectedCurrency()\" (valueChange)=\"selectedCurrency.set($event)\">\n            <button ui-select-trigger class=\"w-[125px] text-xs font-medium\">\n              <ui-select-value [placeholder]=\"selectedCurrency()\" />\n            </button>\n            <ui-select-content align=\"end\">\n              @for (curr of currencyList; track curr.code) {\n                <ui-select-item [value]=\"curr.code\" class=\"text-xs\">{{ curr.label }}</ui-select-item>\n              }\n            </ui-select-content>\n          </ui-select>\n        </div>\n      </div>\n\n      <div class=\"grid grid-cols-1 gap-8 lg:grid-cols-12 lg:items-start\">\n        <!-- Left Column -->\n        <div class=\"space-y-6 lg:col-span-7\">\n          <!-- 1. Route -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-4\">\n              <div class=\"flex flex-wrap items-center justify-between\">\n                <h3 ui-card-title class=\"text-base font-semibold\">1. Shipping Route</h3>\n                <span class=\"text-muted-foreground text-xs font-medium\">International Corridors</span>\n              </div>\n              <p ui-card-description class=\"text-xs\">Select origin loading port and destination discharge terminal.</p>\n            </div>\n            <div ui-card-content class=\"space-y-4\">\n              <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-[1fr,auto,1fr] sm:items-end\">\n                <div class=\"space-y-1.5\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-origin\">Origin (Port / Hub)</label>\n                  <ui-select [value]=\"origin()\" (valueChange)=\"origin.set($event)\">\n                    <button ui-select-trigger id=\"fq-origin\" class=\"text-xs\">\n                      <ui-select-value />\n                    </button>\n                    <ui-select-content>\n                      @for (p of originPorts; track p.code) {\n                        <ui-select-item [value]=\"p.code\" class=\"text-xs\">{{ p.name }}</ui-select-item>\n                      }\n                    </ui-select-content>\n                  </ui-select>\n                </div>\n                <div class=\"flex justify-center pb-0.5 sm:pb-0\">\n                  <button\n                    ui-button\n                    type=\"button\"\n                    variant=\"outline\"\n                    size=\"icon\"\n                    class=\"size-9 shrink-0\"\n                    aria-label=\"Swap Origin and Destination\"\n                    (click)=\"swapRoute()\"\n                  >\n                    <lucide-icon [img]=\"ArrowLeftRight\" class=\"size-4\" />\n                  </button>\n                </div>\n                <div class=\"space-y-1.5\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-destination\">\n                    Destination (Port / Hub)\n                  </label>\n                  <ui-select [value]=\"destination()\" (valueChange)=\"destination.set($event)\">\n                    <button ui-select-trigger id=\"fq-destination\" class=\"text-xs\">\n                      <ui-select-value />\n                    </button>\n                    <ui-select-content>\n                      @for (p of destinationPorts; track p.code) {\n                        <ui-select-item [value]=\"p.code\" class=\"text-xs\">{{ p.name }}</ui-select-item>\n                      }\n                    </ui-select-content>\n                  </ui-select>\n                </div>\n              </div>\n              <div\n                class=\"bg-muted/40 border-border/80 flex flex-wrap items-center justify-between rounded-lg border px-3 py-2 text-xs\"\n              >\n                <div class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Ship\" class=\"text-primary size-4 shrink-0\" />\n                  <span class=\"text-foreground font-medium\">Transpacific Direct Corridor</span>\n                </div>\n                <span class=\"text-muted-foreground tabular-nums\">Distance: ~5,800 NM (10,740 km)</span>\n              </div>\n            </div>\n          </div>\n\n          <!-- 2. Mode -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-4\">\n              <div class=\"flex flex-wrap items-center justify-between\">\n                <h3 ui-card-title class=\"text-base font-semibold\">2. Cargo Freight Mode</h3>\n                <span class=\"text-muted-foreground text-xs\">Select primary transport</span>\n              </div>\n              <p ui-card-description class=\"text-xs\">\n                Choose your preferred multimodal transit mode to evaluate rates.\n              </p>\n            </div>\n            <div ui-card-content>\n              <div class=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n                <button type=\"button\" [class]=\"modeCardClass('ocean-fcl')\" (click)=\"selectedMode.set('ocean-fcl')\">\n                  <div class=\"flex w-full items-start justify-between\">\n                    <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n                      <lucide-icon [img]=\"Ship\" class=\"size-4\" />\n                    </div>\n                    <span ui-badge variant=\"default\" class=\"text-xs\">Recommended</span>\n                  </div>\n                  <div>\n                    <div class=\"text-foreground text-sm font-semibold\">Ocean FCL (Full Container)</div>\n                    <div class=\"text-muted-foreground mt-0.5 text-xs\">20ft / 40ft dedicated sea freight container</div>\n                  </div>\n                  <div class=\"border-border/50 flex w-full items-center justify-between border-t pt-1 text-xs\">\n                    <span class=\"text-muted-foreground\">Est. Transit: 14-18 days</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">{{ formatMoney(oceanFclBaseUsd()) }}</span>\n                  </div>\n                </button>\n                <button type=\"button\" [class]=\"modeCardClass('ocean-lcl')\" (click)=\"selectedMode.set('ocean-lcl')\">\n                  <div class=\"flex w-full items-start justify-between\">\n                    <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n                      <lucide-icon [img]=\"Boxes\" class=\"size-4\" />\n                    </div>\n                    <span ui-badge variant=\"outline\" class=\"text-xs font-normal\">Economy</span>\n                  </div>\n                  <div>\n                    <div class=\"text-foreground text-sm font-semibold\">Ocean LCL (Shared)</div>\n                    <div class=\"text-muted-foreground mt-0.5 text-xs\">\n                      Consolidated sea freight priced by CBM volume\n                    </div>\n                  </div>\n                  <div class=\"border-border/50 flex w-full items-center justify-between border-t pt-1 text-xs\">\n                    <span class=\"text-muted-foreground\">Est. Transit: 18-22 days</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">{{ formatMoney(oceanLclBaseUsd()) }}</span>\n                  </div>\n                </button>\n                <button type=\"button\" [class]=\"modeCardClass('air')\" (click)=\"selectedMode.set('air')\">\n                  <div class=\"flex w-full items-start justify-between\">\n                    <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n                      <lucide-icon [img]=\"Plane\" class=\"size-4\" />\n                    </div>\n                    <span ui-badge variant=\"secondary\" class=\"text-xs\">Fastest</span>\n                  </div>\n                  <div>\n                    <div class=\"text-foreground text-sm font-semibold\">Express Air Freight</div>\n                    <div class=\"text-muted-foreground mt-0.5 text-xs\">Priority belly/freighter aircraft dispatch</div>\n                  </div>\n                  <div class=\"border-border/50 flex w-full items-center justify-between border-t pt-1 text-xs\">\n                    <span class=\"text-muted-foreground\">Est. Transit: 3-5 days</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">{{\n                      formatMoney(airFreightBaseUsd())\n                    }}</span>\n                  </div>\n                </button>\n                <button type=\"button\" [class]=\"modeCardClass('ground')\" (click)=\"selectedMode.set('ground')\">\n                  <div class=\"flex w-full items-start justify-between\">\n                    <div class=\"bg-primary/10 text-primary flex size-8 items-center justify-center rounded-md\">\n                      <lucide-icon [img]=\"Truck\" class=\"size-4\" />\n                    </div>\n                    <span ui-badge variant=\"outline\" class=\"text-xs font-normal\">Overland</span>\n                  </div>\n                  <div>\n                    <div class=\"text-foreground text-sm font-semibold\">Ground Freight</div>\n                    <div class=\"text-muted-foreground mt-0.5 text-xs\">\n                      Regional linehaul, FTL &amp; pallet LTL trucking\n                    </div>\n                  </div>\n                  <div class=\"border-border/50 flex w-full items-center justify-between border-t pt-1 text-xs\">\n                    <span class=\"text-muted-foreground\">Est. Transit: 5-7 days</span>\n                    <span class=\"text-foreground font-semibold tabular-nums\">{{\n                      formatMoney(groundFreightBaseUsd())\n                    }}</span>\n                  </div>\n                </button>\n              </div>\n            </div>\n          </div>\n\n          <!-- 3. Dimensions -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-4\">\n              <div class=\"flex flex-wrap items-center justify-between\">\n                <h3 ui-card-title class=\"text-base font-semibold\">3. Cargo Dimensions &amp; Volumetric Weight</h3>\n                <span ui-badge variant=\"outline\" class=\"gap-1 text-xs font-normal\">\n                  <lucide-icon [img]=\"Scale\" class=\"text-primary size-3\" />\n                  IATA 1:6 Standard\n                </span>\n              </div>\n              <p ui-card-description class=\"text-xs\">\n                Enter pallet quantity, unit dimensions, and individual gross weight.\n              </p>\n            </div>\n            <div ui-card-content class=\"space-y-5\">\n              <div class=\"grid grid-cols-2 gap-3 sm:grid-cols-5\">\n                <div class=\"col-span-2 space-y-1.5 sm:col-span-1\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-pallets\">Quantity</label>\n                  <div class=\"relative\">\n                    <ui-input\n                      id=\"fq-pallets\"\n                      type=\"number\"\n                      [value]=\"pallets()\"\n                      (valueChange)=\"setPallets($event)\"\n                      class=\"pr-8 text-xs tabular-nums\"\n                    />\n                    <span class=\"text-muted-foreground absolute top-1/2 right-2.5 -translate-y-1/2 text-xs\">pal</span>\n                  </div>\n                </div>\n                <div class=\"space-y-1.5\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-length\">Length (L)</label>\n                  <div class=\"relative\">\n                    <ui-input\n                      id=\"fq-length\"\n                      type=\"number\"\n                      [value]=\"lengthCm()\"\n                      (valueChange)=\"setLengthCm($event)\"\n                      class=\"pr-8 text-xs tabular-nums\"\n                    />\n                    <span class=\"text-muted-foreground absolute top-1/2 right-2.5 -translate-y-1/2 text-xs\">cm</span>\n                  </div>\n                </div>\n                <div class=\"space-y-1.5\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-width\">Width (W)</label>\n                  <div class=\"relative\">\n                    <ui-input\n                      id=\"fq-width\"\n                      type=\"number\"\n                      [value]=\"widthCm()\"\n                      (valueChange)=\"setWidthCm($event)\"\n                      class=\"pr-8 text-xs tabular-nums\"\n                    />\n                    <span class=\"text-muted-foreground absolute top-1/2 right-2.5 -translate-y-1/2 text-xs\">cm</span>\n                  </div>\n                </div>\n                <div class=\"space-y-1.5\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-height\">Height (H)</label>\n                  <div class=\"relative\">\n                    <ui-input\n                      id=\"fq-height\"\n                      type=\"number\"\n                      [value]=\"heightCm()\"\n                      (valueChange)=\"setHeightCm($event)\"\n                      class=\"pr-8 text-xs tabular-nums\"\n                    />\n                    <span class=\"text-muted-foreground absolute top-1/2 right-2.5 -translate-y-1/2 text-xs\">cm</span>\n                  </div>\n                </div>\n                <div class=\"col-span-2 space-y-1.5 sm:col-span-1\">\n                  <label class=\"text-foreground text-xs font-medium\" for=\"fq-weight\">Weight/Pallet</label>\n                  <div class=\"relative\">\n                    <ui-input\n                      id=\"fq-weight\"\n                      type=\"number\"\n                      [value]=\"grossWeightKg()\"\n                      (valueChange)=\"setGrossWeightKg($event)\"\n                      class=\"pr-8 text-xs tabular-nums\"\n                    />\n                    <span class=\"text-muted-foreground absolute top-1/2 right-2.5 -translate-y-1/2 text-xs\">kg</span>\n                  </div>\n                </div>\n              </div>\n\n              <div class=\"border-border/80 bg-muted/30 space-y-3 rounded-lg border p-4\">\n                <div class=\"flex flex-wrap items-center justify-between text-xs\">\n                  <span class=\"text-foreground flex items-center gap-1.5 font-semibold\">\n                    <lucide-icon [img]=\"Package\" class=\"text-primary size-3.5\" />\n                    Cargo Metric Computations\n                  </span>\n                  <span class=\"text-muted-foreground\">Standard Euro/US Pallet Basis</span>\n                </div>\n                <div class=\"grid grid-cols-2 gap-3 sm:grid-cols-4\">\n                  <div class=\"bg-card border-border/80 rounded-md border p-2.5\">\n                    <div class=\"text-muted-foreground text-xs\">Total Volume</div>\n                    <div class=\"text-foreground mt-0.5 text-lg font-bold tabular-nums\">\n                      {{ totalVolumeCbm().toFixed(2) }}\n                      <span class=\"text-muted-foreground text-xs font-normal\">CBM</span>\n                    </div>\n                    <div class=\"text-muted-foreground mt-1 text-xs\">\n                      {{ singlePalletVolumeCbm().toFixed(2) }} m³ / unit\n                    </div>\n                  </div>\n                  <div class=\"bg-card border-border/80 rounded-md border p-2.5\">\n                    <div class=\"text-muted-foreground text-xs\">Actual Gross Wt.</div>\n                    <div class=\"text-foreground mt-0.5 text-lg font-bold tabular-nums\">\n                      {{ totalGrossWeightKg().toLocaleString('en-US') }}\n                      <span class=\"text-muted-foreground text-xs font-normal\">kg</span>\n                    </div>\n                    <div class=\"text-muted-foreground mt-1 text-xs\">{{ pallets() }} × {{ grossWeightKg() }} kg</div>\n                  </div>\n                  <div class=\"bg-card border-border/80 rounded-md border p-2.5\">\n                    <div class=\"text-muted-foreground text-xs\">Chargeable Wt. (Air)</div>\n                    <div class=\"text-foreground mt-0.5 text-lg font-bold tabular-nums\">\n                      {{ airVolumetricWeightKg().toLocaleString('en-US') }}\n                      <span class=\"text-muted-foreground text-xs font-normal\">kg</span>\n                    </div>\n                    <div class=\"text-muted-foreground mt-1 text-xs\">Volumetric (1:6000)</div>\n                  </div>\n                  <div class=\"bg-card border-border/80 rounded-md border p-2.5\">\n                    <div class=\"text-muted-foreground text-xs\">Ocean Revenue Ton</div>\n                    <div class=\"text-foreground mt-0.5 text-lg font-bold tabular-nums\">\n                      {{ oceanRevenueTon().toFixed(2) }}\n                      <span class=\"text-muted-foreground text-xs font-normal\">RT</span>\n                    </div>\n                    <div class=\"text-muted-foreground mt-1 text-xs\">Max(CBM, Weight/T)</div>\n                  </div>\n                </div>\n                <p class=\"text-muted-foreground flex items-center gap-1.5 pt-1 text-xs\">\n                  <lucide-icon [img]=\"Info\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>\n                    Computed Total Volume ({{ totalVolumeCbm().toFixed(2) }} CBM) &amp; Chargeable Weight ({{\n                      airVolumetricWeightKg().toLocaleString('en-US')\n                    }}\n                    kg) apply dynamically to quotes.\n                  </span>\n                </p>\n              </div>\n            </div>\n          </div>\n\n          <!-- 4. Services -->\n          <div ui-card class=\"border-border shadow-xs\">\n            <div ui-card-header class=\"pb-3\">\n              <div class=\"flex flex-wrap items-center justify-between\">\n                <h3 ui-card-title class=\"text-base font-semibold\">4. Value-Added Freight Services</h3>\n                <span class=\"text-muted-foreground text-xs font-medium\">Optional Add-ons</span>\n              </div>\n              <p ui-card-description class=\"text-xs\">\n                Enhance shipment handling with customs, all-risk insurance, and destination equipment.\n              </p>\n            </div>\n            <div ui-card-content class=\"space-y-3\">\n              <label\n                class=\"border-border hover:border-primary/40 bg-card flex cursor-pointer items-start justify-between gap-3 rounded-lg border p-3.5 transition-colors\"\n              >\n                <div class=\"flex items-start gap-3\">\n                  <ui-checkbox [checked]=\"addCustoms()\" (checkedChange)=\"addCustoms.set(!!$event)\" />\n                  <div class=\"space-y-0.5\">\n                    <div class=\"flex items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-medium\">Customs Clearance</span>\n                      <span ui-badge variant=\"outline\" class=\"text-xs font-normal tabular-nums\"\n                        >+{{ formatMoney(customsUsd) }}</span\n                      >\n                    </div>\n                    <p class=\"text-muted-foreground text-xs\">\n                      Export filing, ISF 10+2 documentation, automated customs broker import clearance.\n                    </p>\n                  </div>\n                </div>\n              </label>\n              <label\n                class=\"border-border hover:border-primary/40 bg-card flex cursor-pointer items-start justify-between gap-3 rounded-lg border p-3.5 transition-colors\"\n              >\n                <div class=\"flex items-start gap-3\">\n                  <ui-checkbox [checked]=\"addInsurance()\" (checkedChange)=\"addInsurance.set(!!$event)\" />\n                  <div class=\"space-y-0.5\">\n                    <div class=\"flex items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-medium\">All-Risk Cargo Insurance</span>\n                      <span ui-badge variant=\"outline\" class=\"text-xs font-normal tabular-nums\"\n                        >+{{ formatMoney(insuranceUsd) }}</span\n                      >\n                    </div>\n                    <p class=\"text-muted-foreground text-xs\">\n                      Comprehensive door-to-door insurance coverage up to $100,000 against damage or loss.\n                    </p>\n                  </div>\n                </div>\n              </label>\n              <label\n                class=\"border-border hover:border-primary/40 bg-card flex cursor-pointer items-start justify-between gap-3 rounded-lg border p-3.5 transition-colors\"\n              >\n                <div class=\"flex items-start gap-3\">\n                  <ui-checkbox [checked]=\"addLiftgate()\" (checkedChange)=\"addLiftgate.set(!!$event)\" />\n                  <div class=\"space-y-0.5\">\n                    <div class=\"flex items-center gap-2\">\n                      <span class=\"text-foreground text-sm font-medium\">Liftgate at Delivery</span>\n                      <span ui-badge variant=\"outline\" class=\"text-xs font-normal tabular-nums\"\n                        >+{{ formatMoney(liftgateUsd) }}</span\n                      >\n                    </div>\n                    <p class=\"text-muted-foreground text-xs\">\n                      Hydraulic liftgate truck delivery for destinations without a dedicated loading dock.\n                    </p>\n                  </div>\n                </div>\n              </label>\n            </div>\n          </div>\n        </div>\n\n        <!-- Right Column -->\n        <div class=\"space-y-6 lg:sticky lg:top-8 lg:col-span-5\">\n          <div ui-card class=\"border-border bg-card shadow-xs\">\n            <div ui-card-header class=\"pb-4\">\n              <div class=\"flex flex-wrap items-center justify-between\">\n                <span class=\"text-muted-foreground text-xs font-semibold tracking-wider uppercase\"\n                  >Instant Spot Rates</span\n                >\n                <span ui-badge variant=\"default\" class=\"gap-1 text-xs\">\n                  <lucide-icon [img]=\"Zap\" class=\"size-3\" />\n                  Live Quote\n                </span>\n              </div>\n              <div class=\"border-border/60 mt-2 flex flex-wrap items-center justify-between border-b pb-3 text-xs\">\n                <div class=\"text-foreground flex items-center gap-1.5 font-medium\">\n                  <span>{{ origin() }}</span>\n                  <lucide-icon [img]=\"ArrowRight\" class=\"text-muted-foreground size-3\" />\n                  <span>{{ destination() }}</span>\n                </div>\n                <span class=\"text-muted-foreground tabular-nums\"\n                  >{{ pallets() }} Pallets · {{ totalVolumeCbm().toFixed(2) }} CBM</span\n                >\n              </div>\n              <div class=\"mt-4 space-y-1\">\n                <div class=\"text-muted-foreground text-xs font-medium\">Total Estimated Landed Freight Cost</div>\n                <div class=\"flex items-baseline gap-2\">\n                  <span class=\"text-foreground text-4xl font-bold tracking-tight tabular-nums\">{{\n                    formatMoney(activeTotalPriceUsd())\n                  }}</span>\n                  <span class=\"text-muted-foreground text-xs font-medium\">({{ selectedCurrency() }})</span>\n                </div>\n                <div class=\"text-muted-foreground flex items-center gap-3 pt-1 text-xs\">\n                  <span class=\"flex items-center gap-1\">\n                    <lucide-icon [img]=\"Clock\" class=\"text-primary size-3\" />\n                    Transit: {{ activeTransitTime() }}\n                  </span>\n                  <span>·</span>\n                  <span class=\"flex items-center gap-1\">\n                    <lucide-icon [img]=\"Leaf\" class=\"text-success size-3\" />\n                    Est. {{ activeCarbonEmission() }}\n                  </span>\n                </div>\n              </div>\n            </div>\n            <div ui-card-content class=\"space-y-4 pt-0\">\n              <div class=\"space-y-2\">\n                <span class=\"text-foreground text-xs font-semibold tracking-wider uppercase\"\n                  >Compare Multimodal Options:</span\n                >\n                <div [class]=\"quoteOptionClass('ocean-fcl')\" (click)=\"selectedMode.set('ocean-fcl')\">\n                  <div class=\"flex flex-wrap items-center justify-between\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"Ship\" class=\"text-primary size-4 shrink-0\" />\n                      <div>\n                        <div class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n                          Ocean FCL (20ft Container)\n                          <span ui-badge variant=\"default\" class=\"px-1.5 py-0.5 text-xs\">Recommended</span>\n                        </div>\n                        <div class=\"text-muted-foreground text-xs\">Transit: 14-18 days · 1.2 tCO2</div>\n                      </div>\n                    </div>\n                    <div class=\"text-right\">\n                      <div class=\"text-foreground text-sm font-bold tabular-nums\">\n                        {{ formatMoney(oceanFclBaseUsd() + addonsTotalUsd()) }}\n                      </div>\n                      <div class=\"text-muted-foreground text-xs\">Port-to-Port</div>\n                    </div>\n                  </div>\n                </div>\n                <div [class]=\"quoteOptionClass('ocean-lcl')\" (click)=\"selectedMode.set('ocean-lcl')\">\n                  <div class=\"flex flex-wrap items-center justify-between\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"Boxes\" class=\"text-primary size-4 shrink-0\" />\n                      <div>\n                        <div class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n                          Ocean LCL (Shared)\n                          <span ui-badge variant=\"outline\" class=\"px-1.5 py-0.5 text-xs font-normal\">Economy</span>\n                        </div>\n                        <div class=\"text-muted-foreground text-xs\">Transit: 18-22 days · 0.8 tCO2</div>\n                      </div>\n                    </div>\n                    <div class=\"text-right\">\n                      <div class=\"text-foreground text-sm font-bold tabular-nums\">\n                        {{ formatMoney(oceanLclBaseUsd() + addonsTotalUsd()) }}\n                      </div>\n                      <div class=\"text-muted-foreground text-xs\">CFS-to-CFS</div>\n                    </div>\n                  </div>\n                </div>\n                <div [class]=\"quoteOptionClass('air')\" (click)=\"selectedMode.set('air')\">\n                  <div class=\"flex flex-wrap items-center justify-between\">\n                    <div class=\"flex items-center gap-2\">\n                      <lucide-icon [img]=\"Plane\" class=\"text-primary size-4 shrink-0\" />\n                      <div>\n                        <div class=\"text-foreground flex items-center gap-1.5 text-xs font-semibold\">\n                          Express Air Freight\n                          <span ui-badge variant=\"secondary\" class=\"px-1.5 py-0.5 text-xs\">Fastest</span>\n                        </div>\n                        <div class=\"text-muted-foreground text-xs\">Transit: 3-5 days · 4.8 tCO2</div>\n                      </div>\n                    </div>\n                    <div class=\"text-right\">\n                      <div class=\"text-foreground text-sm font-bold tabular-nums\">\n                        {{ formatMoney(airFreightBaseUsd() + addonsTotalUsd()) }}\n                      </div>\n                      <div class=\"text-muted-foreground text-xs\">Airport-to-Airport</div>\n                    </div>\n                  </div>\n                </div>\n              </div>\n              <ui-separator />\n              <div class=\"space-y-2 text-xs\">\n                <span class=\"text-muted-foreground font-medium tracking-wider uppercase\">Itemized Breakdown</span>\n                <div class=\"space-y-1.5\">\n                  <div class=\"flex flex-wrap items-center justify-between\">\n                    <span class=\"text-muted-foreground\">Base Freight Carrier Rate</span>\n                    <span class=\"text-foreground font-medium tabular-nums\">{{\n                      formatMoney(activeBasePriceUsd())\n                    }}</span>\n                  </div>\n                  @if (addCustoms()) {\n                    <div class=\"flex flex-wrap items-center justify-between\">\n                      <span class=\"text-muted-foreground\">Customs Clearance Service</span>\n                      <span class=\"text-foreground font-medium tabular-nums\">+{{ formatMoney(customsUsd) }}</span>\n                    </div>\n                  }\n                  @if (addInsurance()) {\n                    <div class=\"flex flex-wrap items-center justify-between\">\n                      <span class=\"text-muted-foreground\">All-Risk Cargo Insurance</span>\n                      <span class=\"text-foreground font-medium tabular-nums\">+{{ formatMoney(insuranceUsd) }}</span>\n                    </div>\n                  }\n                  @if (addLiftgate()) {\n                    <div class=\"flex flex-wrap items-center justify-between\">\n                      <span class=\"text-muted-foreground\">Destination Liftgate Equipment</span>\n                      <span class=\"text-foreground font-medium tabular-nums\">+{{ formatMoney(liftgateUsd) }}</span>\n                    </div>\n                  }\n                  <div class=\"text-muted-foreground flex flex-wrap items-center justify-between\">\n                    <span>Bunker / Fuel Surcharge (BAF)</span>\n                    <span class=\"text-success font-medium\">Included</span>\n                  </div>\n                </div>\n              </div>\n              <ui-separator />\n              <ul class=\"text-muted-foreground space-y-1.5 text-xs\">\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>Rate locked for 7 calendar days</span>\n                </li>\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>IATA &amp; FMC compliant licensed forwarders</span>\n                </li>\n                <li class=\"flex items-center gap-2\">\n                  <lucide-icon [img]=\"Check\" class=\"text-primary size-3.5 shrink-0\" />\n                  <span>Automated EDI customs documentation &amp; bill of lading</span>\n                </li>\n              </ul>\n            </div>\n            <div ui-card-footer class=\"flex flex-col gap-2.5 pt-2\">\n              <button ui-button class=\"w-full gap-2 font-semibold shadow-xs\" size=\"lg\" (click)=\"handleBookQuote()\">\n                @if (isBooked()) {\n                  <lucide-icon [img]=\"CheckCircle2\" class=\"text-success size-4\" />\n                  Freight Quote Booked!\n                } @else {\n                  Book Freight Quote\n                  <lucide-icon [img]=\"ArrowRight\" class=\"size-4\" />\n                }\n              </button>\n              <button\n                ui-button\n                aria-label=\"Download attachment\"\n                variant=\"outline\"\n                class=\"w-full gap-2 text-xs\"\n                size=\"default\"\n                (click)=\"handleDownloadPdf()\"\n              >\n                @if (!isDownloading()) {\n                  <lucide-icon [img]=\"Download\" class=\"size-3.5\" />\n                } @else {\n                  <lucide-icon [img]=\"FileText\" class=\"size-3.5 animate-pulse\" />\n                }\n                {{ isDownloading() ? 'Generating PDF Manifest...' : 'Download Detailed Quote PDF' }}\n              </button>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  `,\n})\nexport class UiFreightQuoteCalculatorComponent {\n  protected readonly ArrowLeftRight = ArrowLeftRight\n  protected readonly ArrowRight = ArrowRight\n  protected readonly Boxes = Boxes\n  protected readonly Check = Check\n  protected readonly CheckCircle2 = CheckCircle2\n  protected readonly Clock = Clock\n  protected readonly Download = Download\n  protected readonly FileText = FileText\n  protected readonly Info = Info\n  protected readonly Leaf = Leaf\n  protected readonly Package = Package\n  protected readonly Plane = Plane\n  protected readonly Scale = Scale\n  protected readonly Ship = Ship\n  protected readonly Truck = Truck\n  protected readonly Zap = Zap\n\n  protected readonly originPorts = ORIGIN_PORTS\n  protected readonly destinationPorts = DESTINATION_PORTS\n  protected readonly currencyList = CURRENCY_LIST\n  protected readonly customsUsd = CUSTOMS_USD\n  protected readonly insuranceUsd = INSURANCE_USD\n  protected readonly liftgateUsd = LIFTGATE_USD\n\n  @Input() set initialOrigin(v: string) {\n    if (v) this.origin.set(v)\n  }\n  @Input() set initialDestination(v: string) {\n    if (v) this.destination.set(v)\n  }\n  @Input() set initialPallets(v: number | string) {\n    const n = Number(v)\n    if (n !== undefined && n !== null && !Number.isNaN(n)) this.pallets.set(n)\n  }\n  @Input() set initialLengthCm(v: number | string) {\n    const n = Number(v)\n    if (n !== undefined && n !== null && !Number.isNaN(n)) this.lengthCm.set(n)\n  }\n  @Input() set initialWidthCm(v: number | string) {\n    const n = Number(v)\n    if (n !== undefined && n !== null && !Number.isNaN(n)) this.widthCm.set(n)\n  }\n  @Input() set initialHeightCm(v: number | string) {\n    const n = Number(v)\n    if (n !== undefined && n !== null && !Number.isNaN(n)) this.heightCm.set(n)\n  }\n  @Input() set initialGrossWeightKg(v: number | string) {\n    const n = Number(v)\n    if (n !== undefined && n !== null && !Number.isNaN(n)) this.grossWeightKg.set(n)\n  }\n  @Input() set initialCurrency(v: string) {\n    if (v) this.selectedCurrency.set(v)\n  }\n  @Input('class') className?: string\n\n  readonly origin = signal('CNSHA')\n  readonly destination = signal('USLAX')\n  readonly pallets = signal(6)\n  readonly lengthCm = signal(120)\n  readonly widthCm = signal(80)\n  readonly heightCm = signal(160)\n  readonly grossWeightKg = signal(400)\n  readonly selectedCurrency = signal('USD')\n\n  readonly addCustoms = signal(false)\n  readonly addInsurance = signal(false)\n  readonly addLiftgate = signal(false)\n\n  readonly selectedMode = signal<FreightMode>('ocean-fcl')\n  readonly isBooked = signal(false)\n  readonly isDownloading = signal(false)\n  private bookTimer: ReturnType<typeof setTimeout> | undefined\n  private downloadTimer: ReturnType<typeof setTimeout> | undefined\n\n  readonly singlePalletVolumeCbm = computed(() => {\n    const l = Number(this.lengthCm()) || 0\n    const w = Number(this.widthCm()) || 0\n    const h = Number(this.heightCm()) || 0\n    return (l * w * h) / 1_000_000\n  })\n  readonly totalVolumeCbm = computed(() => (Number(this.pallets()) || 0) * this.singlePalletVolumeCbm())\n  readonly totalGrossWeightKg = computed(() => (Number(this.pallets()) || 0) * (Number(this.grossWeightKg()) || 0))\n  readonly airVolumetricWeightKg = computed(() => Math.round(this.totalVolumeCbm() * (1000 / 6)))\n  readonly oceanRevenueTon = computed(() => Math.max(this.totalVolumeCbm(), this.totalGrossWeightKg() / 1000))\n  readonly fclContainersCount = computed(() => Math.max(1, Math.ceil((Number(this.pallets()) || 0) / 10)))\n  readonly oceanFclBaseUsd = computed(() => this.fclContainersCount() * 2850)\n  readonly oceanLclBaseUsd = computed(() => Math.round((this.totalVolumeCbm() || 1) * 140 + 130))\n  readonly airFreightBaseUsd = computed(() => Math.round((this.airVolumetricWeightKg() || 100) * 4.18))\n  readonly groundFreightBaseUsd = computed(() => (Number(this.pallets()) || 1) * 265)\n  readonly addonsTotalUsd = computed(\n    () =>\n      (this.addCustoms() ? CUSTOMS_USD : 0) +\n      (this.addInsurance() ? INSURANCE_USD : 0) +\n      (this.addLiftgate() ? LIFTGATE_USD : 0),\n  )\n  readonly activeBasePriceUsd = computed(() => {\n    switch (this.selectedMode()) {\n      case 'ocean-fcl':\n        return this.oceanFclBaseUsd()\n      case 'ocean-lcl':\n        return this.oceanLclBaseUsd()\n      case 'air':\n        return this.airFreightBaseUsd()\n      case 'ground':\n        return this.groundFreightBaseUsd()\n    }\n  })\n  readonly activeTotalPriceUsd = computed(() => this.activeBasePriceUsd() + this.addonsTotalUsd())\n  readonly activeTransitTime = computed(() => {\n    switch (this.selectedMode()) {\n      case 'ocean-fcl':\n        return '14-18 days'\n      case 'ocean-lcl':\n        return '18-22 days'\n      case 'air':\n        return '3-5 days'\n      case 'ground':\n        return '5-7 days'\n    }\n  })\n  readonly activeCarbonEmission = computed(() => {\n    switch (this.selectedMode()) {\n      case 'ocean-fcl':\n        return `${(1.2 * this.fclContainersCount()).toFixed(1)} tCO2`\n      case 'ocean-lcl':\n        return `${(this.totalVolumeCbm() * 0.087).toFixed(1)} tCO2`\n      case 'air':\n        return `${(this.airVolumetricWeightKg() * 0.003125).toFixed(1)} tCO2`\n      case 'ground':\n        return `${(Number(this.pallets()) * 0.26).toFixed(1)} tCO2`\n    }\n  })\n\n  get rootClass(): string {\n    return cn('mx-auto w-full max-w-6xl space-y-8 p-4 sm:p-6 lg:p-8', this.className)\n  }\n\n  modeCardClass(mode: FreightMode): string {\n    return cn(\n      'border-border focus-visible:ring-ring relative flex flex-col justify-between gap-3 rounded-lg border p-3.5 text-left transition-colors outline-none focus-visible:ring-2',\n      this.selectedMode() === mode\n        ? 'border-primary bg-primary/5 ring-primary ring-1'\n        : 'hover:border-border/80 hover:bg-muted/30 bg-card',\n    )\n  }\n\n  quoteOptionClass(mode: FreightMode): string {\n    return cn(\n      'border-border hover:border-primary/50 cursor-pointer rounded-lg border p-3 transition-colors',\n      this.selectedMode() === mode ? 'border-primary bg-primary/5 ring-primary ring-1' : 'bg-card',\n    )\n  }\n\n  formatMoney(amountInUsd: number): string {\n    const current = CURRENCIES[this.selectedCurrency()] ?? CURRENCIES['USD']!\n    const converted = amountInUsd * current.rate\n    return new Intl.NumberFormat('en-US', {\n      style: 'currency',\n      currency: current.code,\n      minimumFractionDigits: 2,\n      maximumFractionDigits: 2,\n    }).format(converted)\n  }\n\n  swapRoute(): void {\n    const prev = this.origin()\n    this.origin.set(this.destination())\n    this.destination.set(prev)\n  }\n\n  setPallets(raw: string | number): void {\n    this.pallets.set(Math.max(1, parseInt(String(raw), 10) || 1))\n  }\n  setLengthCm(raw: string | number): void {\n    this.lengthCm.set(Math.max(1, parseInt(String(raw), 10) || 1))\n  }\n  setWidthCm(raw: string | number): void {\n    this.widthCm.set(Math.max(1, parseInt(String(raw), 10) || 1))\n  }\n  setHeightCm(raw: string | number): void {\n    this.heightCm.set(Math.max(1, parseInt(String(raw), 10) || 1))\n  }\n  setGrossWeightKg(raw: string | number): void {\n    this.grossWeightKg.set(Math.max(1, parseInt(String(raw), 10) || 1))\n  }\n\n  handleBookQuote(): void {\n    this.isBooked.set(true)\n    clearTimeout(this.bookTimer)\n    this.bookTimer = setTimeout(() => this.isBooked.set(false), 3500)\n  }\n\n  handleDownloadPdf(): void {\n    this.isDownloading.set(true)\n    clearTimeout(this.downloadTimer)\n    this.downloadTimer = setTimeout(() => this.isDownloading.set(false), 2000)\n  }\n}\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/freight-quote-calculator/freight-quote-calculator.component.ts"
    },
    {
      "path": "packages/registry-angular/blocks/freight-quote-calculator/index.ts",
      "content": "export { UiFreightQuoteCalculatorComponent, type FreightMode } from './freight-quote-calculator.component'\n",
      "type": "registry:block",
      "target": "~/src/app/components/blocks/freight-quote-calculator/index.ts"
    }
  ],
  "dependencies": [
    "lucide-angular"
  ],
  "devDependencies": [],
  "registryDependencies": [
    "https://uipkge.dev/r/angular/badge.json",
    "https://uipkge.dev/r/angular/button.json",
    "https://uipkge.dev/r/angular/card.json",
    "https://uipkge.dev/r/angular/checkbox.json",
    "https://uipkge.dev/r/angular/input.json",
    "https://uipkge.dev/r/angular/select.json",
    "https://uipkge.dev/r/angular/separator.json"
  ],
  "description": "Multimodal Air, Ocean, and Ground freight rate estimator with CBM volumetric weight calculation, dynamic route pricing, value-added services, and live comparison quotes.",
  "categories": [
    "logistics",
    "calculator"
  ]
}