<script setup>
const props = defineProps({
  orders: { type: Array, default: () => [] },
  loading: { type: Boolean, default: false },
  fetchError: { type: String, default: '' },
  hasMore: { type: Boolean, default: false },
})

const emit = defineEmits(['loadMore', 'retry'])

const { t, isGerman } = useTranslation()
const tabs = useTabs()

// Org-bpartner isHideQty flag: hides the per-line stock badge.
const { showStockQty, load: loadHideQty } = useHideQty()
onMounted(() => { loadHideQty() })

const loadingMore = ref(false)

// Infinite scroll
const scrollContainer = ref(null)
const onScroll = () => {
  if (!scrollContainer.value || loadingMore.value || !props.hasMore) return
  const el = scrollContainer.value
  if (el.scrollTop + el.clientHeight >= el.scrollHeight - 200) {
    loadingMore.value = true
    emit('loadMore')
  }
}

watch(() => props.orders, () => {
  loadingMore.value = false
})

const navigateToOrder = (order) => {
  tabs.value.push({
    link: `/sales/orders/${order.id}/edit`,
    text: order.documentNo || 'Order',
    active: true,
  })
  navigateTo(`/sales/orders/${order.id}/edit`)
}

// iDempiere returns timestamps as Berlin wall-clock with a spurious "Z"; parse
// the wall-clock verbatim so the time isn't pushed +1h/+2h into the future.
// (see app/composables/useIdempiereDate.ts)
const formatDate = (dateStr) => {
  if (!dateStr) return ''
  try {
    const d = parseIdempiereTimestamp(dateStr)
    if (!d) return dateStr
    return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' })
  } catch {
    return dateStr
  }
}

const formatTime = (dateStr) => {
  if (!dateStr) return ''
  try {
    const d = parseIdempiereTimestamp(dateStr)
    if (!d) return dateStr
    return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
  } catch {
    return dateStr
  }
}

const getTimeAgo = (dateStr) => {
  if (!dateStr) return ''
  try {
    const now = new Date()
    const d = parseIdempiereTimestamp(dateStr)
    if (!d) return ''
    const diffMs = now - d
    const diffMins = Math.floor(diffMs / 60000)
    const diffHours = Math.floor(diffMs / 3600000)
    const diffDays = Math.floor(diffMs / 86400000)
    if (diffMins < 1) return isGerman ? 'gerade eben' : 'just now'
    if (diffMins < 60) return isGerman ? `vor ${diffMins} Min.` : `${diffMins}m ago`
    if (diffHours < 24) return isGerman ? `vor ${diffHours} Std.` : `${diffHours}h ago`
    if (diffDays < 30) return isGerman ? `vor ${diffDays} Tag${diffDays > 1 ? 'en' : ''}` : `${diffDays}d ago`
    const diffMonths = Math.floor(diffDays / 30)
    return isGerman ? `vor ${diffMonths} Monat${diffMonths > 1 ? 'en' : ''}` : `${diffMonths}mo ago`
  } catch {
    return ''
  }
}

const formatDateTime = (dateStr) => {
  if (!dateStr) return ''
  try {
    const d = parseIdempiereTimestamp(dateStr)
    if (!d) return dateStr
    return d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }) +
      ' ' + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })
  } catch {
    return dateStr
  }
}

const formatCurrency = (val, currency) => {
  if (val == null) return ''
  const num = parseFloat(val)
  if (isNaN(num)) return ''
  return num.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + (currency ? ' ' + currency : ' EUR')
}

const statusConfig = {
  incoming: { label: () => t.value.incoming || 'Incoming', bg: 'lightskyblue', color: 'royalblue', icon: 'ti-plane-arrival' },
  packing: { label: () => t.value.packing || 'Packing', bg: 'mediumpurple', color: 'rebeccapurple', icon: 'ti-plane-departure' },
  shipped: { label: () => t.value.shipped || 'Shipped', bg: '#d4f8d4', color: '#2e7d32', icon: 'ti-plane' },
  voided: { label: () => t.value.voided || 'Voided', bg: 'lightpink', color: 'palevioletred', icon: 'ti-ban' },
  drafted: { label: () => t.value.draft || 'Draft', bg: 'lightgrey', color: 'dimgrey', icon: 'ti-writing' },
  in_progress: { label: () => t.value.in_progress || 'In Progress', bg: 'lightgrey', color: 'dimgrey', icon: 'ti-edit' },
}

const getStatus = (status) => statusConfig[status] || { label: () => status || '–', bg: '#f0f0f0', color: '#666', icon: 'ti-help' }

const getOrderSourceIcon = (order) => {
  const key = order.marketplaceKey?.toLowerCase() || order.orderSource?.Name?.toLowerCase() || ''
  if (key.includes('amazon')) return { icon: 'ti-brand-amazon', color: '#ff9900' }
  if (key.includes('shopify')) return { icon: 'ti-brand-shopify', color: '#96bf48' }
  if (key.includes('ebay')) return { icon: 'ti-letter-e', color: '#e53238' }
  if (key.includes('shopware')) return { icon: 'ti-shopping-bag', color: '#189eff' }
  if (key.includes('plenty')) return { icon: 'ti-letter-p', color: '#00b2a5' }
  if (key.includes('jtl')) return { icon: 'ti-letter-j', color: '#1a1a2e' }
  if (key.includes('manual') || key.includes('web')) return { icon: 'ti-world', color: '#6b7280' }
  return null
}

const getFullAddress = (order) => {
  const parts = []
  if (order.partnerLocationName) parts.push(order.partnerLocationName)
  if (order.partnerLocationName2) parts.push(order.partnerLocationName2)
  return parts.join(', ')
}

const getAddressLine = (order) => {
  const parts = []
  if (order.locationStreet) parts.push(order.locationStreet)
  if (order.locationStreet2) parts.push(order.locationStreet2)
  return parts.join(', ')
}

const getCityLine = (order) => {
  const parts = []
  if (order.locationZipCode) parts.push(order.locationZipCode)
  if (order.locationCity) parts.push(order.locationCity)
  if (order.countryCode) parts.push(order.countryCode)
  return parts.join(' ')
}

// Product image cache
const config = useRuntimeConfig()
const imageCache = ref({})
const getProductImageUrl = async (strapiProductId) => {
  if (!strapiProductId || imageCache.value[strapiProductId] !== undefined) return
  imageCache.value[strapiProductId] = ''
  try {
    const res = await $fetch(`/api/materials/products/${strapiProductId}/galleries/images`, { headers: useRequestHeaders(['cookie']) })
    if (Array.isArray(res) && res.length > 0) {
      const fmt = res[0]?.formats?.thumbnail || res[0]?.formats?.small
      if (fmt?.hash && fmt?.ext) imageCache.value[strapiProductId] = config.public.strapi + '/files-api/' + fmt.hash + fmt.ext
    }
  } catch {}
}
watch(() => props.orders, (orders) => {
  if (!orders) return
  const ids = new Set()
  for (const o of orders) for (const l of (o.orderLines || [])) if (l.strapiProductId && !imageCache.value[l.strapiProductId]) ids.add(l.strapiProductId)
  ids.forEach(id => getProductImageUrl(id))
}, { immediate: true })

const getProductCodes = (line) => {
  const codes = []
  if (line.sku) codes.push({ label: 'SKU', value: line.sku })
  if (line.asin) codes.push({ label: 'ASIN', value: line.asin })
  if (line.upc) codes.push({ label: 'EAN', value: line.upc })
  if (line.value) codes.push({ label: 'Value', value: line.value })
  return codes
}

// Marketplace confirmation log icon — only shown to non-limited (non-customer) roles.
const { getMenuType } = useRoleMenu()
const isLimitedRole = computed(() => getMenuType() === 'c')
const marketplaceConfirmLog = useMarketplaceConfirmLog()
const marketplaceConfirmLogParam = useMarketplaceConfirmLogParam()
const openConfirmLog = (order) => {
  marketplaceConfirmLogParam.value = {
    documentNo: order?.documentNo || '',
    trackingNo: order?.minout?.[0]?.TrackingNo || order?.trackingNo || '',
    fetchOrderId: order?.id
  }
  marketplaceConfirmLog.value = true
}

// Expose refresh method for parent
defineExpose({})
</script>

<template>
  <div class="ocl-wrapper" ref="scrollContainer" @scroll="onScroll">
    <!-- Loading skeleton -->
    <div v-if="loading" class="ocl-loading">
      <div v-for="i in 6" :key="i" class="ocl-skeleton">
        <div class="ocl-skeleton-line ocl-skeleton-line--wide"></div>
        <div class="ocl-skeleton-line ocl-skeleton-line--medium"></div>
        <div class="ocl-skeleton-line ocl-skeleton-line--narrow"></div>
      </div>
    </div>

    <!-- Fetch error -->
    <div v-else-if="fetchError" class="ocl-empty-state">
      <i class="ti ti-alert-triangle" style="color: #dc2626;"></i>
      <p style="color: #dc2626;">{{ fetchError }}</p>
      <button class="ocl-action-btn" style="margin-top: 8px;" @click="emit('retry')">
        <i class="ti ti-refresh"></i> Retry
      </button>
    </div>

    <!-- Order cards -->
    <div v-else-if="orders.length > 0" class="ocl-list">
      <div
        v-for="order in orders"
        :key="order.id"
        class="ocl-card"
        :class="{ 'ocl-card--shipped': order.shipmentStatus === 'shipped', 'ocl-card--voided': order.shipmentStatus === 'voided' }"
      >
        <!-- Row 1: Header bar -->
        <div class="ocl-header">
          <div class="ocl-header-left">
            <span
              class="ocl-status-badge"
              :style="{ backgroundColor: getStatus(order.shipmentStatus).bg, color: getStatus(order.shipmentStatus).color }"
            >
              <i class="ti" :class="getStatus(order.shipmentStatus).icon"></i>
              {{ getStatus(order.shipmentStatus).label() }}
            </span>

            <span v-if="getOrderSourceIcon(order)" class="ocl-source-icon" :style="{ color: getOrderSourceIcon(order).color }">
              <i class="ti" :class="getOrderSourceIcon(order).icon"></i>
            </span>
          </div>

          <div class="ocl-header-right">
            <span class="ocl-date" :title="isGerman ? 'Importiert' : 'Imported'">
              <i class="ti ti-calendar-event"></i>
              {{ formatDateTime(order.created) }}
            </span>
            <span v-if="order.organization" class="ocl-org">
              {{ order.organization }}
            </span>
          </div>
        </div>

        <!-- Row 2: Main content - 5 column layout -->
        <div class="ocl-body">
          <!-- Column 1: Time since imported -->
          <div class="ocl-col ocl-col--time">
            <div class="ocl-time-ago">{{ getTimeAgo(order.created) }}</div>
            <div class="ocl-time-date">{{ formatDate(order.created) }}</div>
            <div class="ocl-time-clock">{{ formatTime(order.created) }}</div>
          </div>

          <!-- Column 2: Document & Address -->
          <div class="ocl-col ocl-col--document">
            <a class="ocl-body-doc-no" @click.prevent="navigateToOrder(order)">{{ order.documentNo }}</a>
            <div v-if="order.externalorderId" class="ocl-body-external">{{ order.externalorderId }}</div>
            <div class="ocl-customer-name">{{ getFullAddress(order) || '–' }}</div>
            <div class="ocl-address" v-if="getAddressLine(order)">{{ getAddressLine(order) }}</div>
            <div class="ocl-address" v-if="getCityLine(order)">
              <img v-if="order.countryCode" :src="`/flags/${order.countryCode?.toLowerCase()}.svg`" class="ocl-flag" :alt="order.countryCode" @error="($event) => $event.target.style.display = 'none'" />
              {{ getCityLine(order) }}
            </div>
          </div>

          <!-- Column 3: Articles / Order Lines -->
          <div class="ocl-col ocl-col--articles">
            <template v-if="order.orderLines && order.orderLines.length > 0 && order.orderLines[0].productName">
              <div v-for="(line, lineIdx) in order.orderLines" :key="line.id || lineIdx" class="ocl-line-item">
                <div class="ocl-line-thumb">
                  <img v-if="line.strapiProductId && imageCache[line.strapiProductId]" :src="imageCache[line.strapiProductId]" :alt="line.productName" class="ocl-product-img" @error="($event) => $event.target.style.display = 'none'" />
                  <div v-else class="ocl-product-img-placeholder"><i class="ti ti-photo-off"></i></div>
                </div>
                <div class="ocl-line-details">
                  <div class="ocl-line-name">{{ line.productName || '–' }}</div>
                  <div v-for="code in getProductCodes(line)" :key="code.label" class="ocl-line-code">
                    <span class="ocl-code-label">{{ code.label }}:</span> {{ code.value }}
                  </div>
                  <div class="ocl-line-qty-price">
                    <span class="ocl-line-qty">{{ line.qty }}x</span>
                    <span v-if="showStockQty" class="ocl-qty-on-hand" :class="line.qtyOnHand > 0 ? 'ocl-qty--green' : 'ocl-qty--red'">{{ line.qtyOnHand }}</span>
                    <span class="ocl-line-price">{{ formatCurrency(line.price, order.currency) }}</span>
                  </div>
                </div>
              </div>
            </template>
            <div v-else class="ocl-empty">–</div>
          </div>

          <!-- Column 4: Shipped Date & Status -->
          <div class="ocl-col ocl-col--status">
            <div v-if="order.shippingDate" class="ocl-shipped-date">
              <i class="ti ti-calendar-check"></i> {{ formatDate(order.shippingDate) }}
            </div>
            <div class="ocl-status-row">
              <span class="ocl-status-badge" :style="{ backgroundColor: getStatus(order.shipmentStatus).bg, color: getStatus(order.shipmentStatus).color }">
                <i class="ti" :class="getStatus(order.shipmentStatus).icon"></i>
                {{ getStatus(order.shipmentStatus).label() }}
              </span>
              <button
                v-if="!isLimitedRole && order.shipmentStatus === 'shipped'"
                type="button"
                class="ocl-confirm-log-btn"
                @click.stop="openConfirmLog(order)"
                :title="isGerman ? 'Marketplace-Bestätigungs-Log anzeigen' : 'Show marketplace confirmation log'"
              >
                <i class="ti ti-list-details"></i>
              </button>
            </div>
          </div>

          <!-- Column 5: Items & Totals -->
          <div class="ocl-col ocl-col--totals">
            <div class="ocl-items-qty">
              <i class="ti ti-package"></i>
              {{ order.totalQty || 0 }} {{ isGerman ? 'Artikel' : 'items' }}
            </div>
            <div class="ocl-grand-total">
              {{ formatCurrency(order.grandTotal, order.currency) }}
            </div>
            <div v-if="order.fulfillmentFeeTotal" class="ocl-fee">
              <span class="ocl-fee-label">Fee:</span> {{ formatCurrency(order.fulfillmentFeeTotal) }}
            </div>
          </div>
        </div>

        <!-- Row 3: Footer with quick actions -->
        <div class="ocl-footer">
          <div class="ocl-footer-left">
            <span v-if="order.partner" class="ocl-partner">
              <i class="ti ti-user"></i> {{ order.partner }}
            </span>
            <span v-if="order.daysSinceCreated != null && order.shipmentStatus !== 'shipped'" class="ocl-age" :class="{ 'ocl-age--warn': order.daysSinceCreated > 3, 'ocl-age--danger': order.daysSinceCreated > 7 }">
              {{ order.daysSinceCreated }}d {{ isGerman ? 'alt' : 'old' }}
            </span>
          </div>
          <div class="ocl-footer-right">
            <button class="ocl-action-btn" @click="navigateToOrder(order)" :title="isGerman ? 'Bearbeiten' : 'Edit'">
              <i class="ti ti-pencil"></i>
            </button>
          </div>
        </div>
      </div>

      <!-- Load more -->
      <div v-if="loadingMore" class="ocl-loading-more">
        <i class="ti ti-loader-2 ocl-spin"></i>
        {{ isGerman ? 'Lade mehr...' : 'Loading more...' }}
      </div>
      <div v-else-if="!hasMore && orders.length > 0" class="ocl-end">
        {{ isGerman ? 'Alle Aufträge geladen' : 'All orders loaded' }} ({{ orders.length }})
      </div>
    </div>

    <!-- Empty state -->
    <div v-else class="ocl-empty-state">
      <i class="ti ti-inbox-off"></i>
      <p>{{ isGerman ? 'Keine Aufträge gefunden' : 'No orders found' }}</p>
    </div>
  </div>
</template>

<style scoped>
.ocl-wrapper {
  height: 100%;
  overflow-y: auto;
  padding: 0 4px;
}

/* Skeleton loading */
.ocl-loading {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px;
}
.ocl-skeleton {
  background: #f8f9fb;
  border: 1px solid #e8eaed;
  border-radius: 8px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ocl-skeleton-line {
  height: 12px;
  border-radius: 4px;
  background: linear-gradient(90deg, #e8eaed 25%, #f3f4f6 50%, #e8eaed 75%);
  background-size: 200% 100%;
  animation: ocl-shimmer 1.5s ease-in-out infinite;
}
.ocl-skeleton-line--wide { width: 90%; }
.ocl-skeleton-line--medium { width: 60%; }
.ocl-skeleton-line--narrow { width: 35%; }

@keyframes ocl-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* Card list */
.ocl-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 6px 2px;
}

/* Card */
.ocl-card {
  background: #fff;
  border: 1px solid #e2e5ea;
  border-radius: 8px;
  overflow: hidden;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.ocl-card:hover {
  border-color: #c0c6d0;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.ocl-card--shipped {
  border-left: 3px solid #2e7d32;
}
.ocl-card--voided {
  border-left: 3px solid palevioletred;
  opacity: 0.7;
}

/* Header */
.ocl-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: #f8f9fb;
  border-bottom: 1px solid #eef0f3;
  gap: 8px;
  flex-wrap: wrap;
}
.ocl-header-left,
.ocl-header-right {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.ocl-status-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 11px;
  font-weight: 700;
  padding: 2px 8px;
  border-radius: 10px;
  white-space: nowrap;
}
.ocl-status-badge i {
  font-size: 13px;
}

.ocl-source-icon {
  font-size: 16px;
  display: inline-flex;
}

.ocl-doc-no {
  font-size: 13px;
  font-weight: 600;
  color: #3273dc;
  cursor: pointer;
  text-decoration: none;
}
.ocl-doc-no:hover {
  text-decoration: underline;
}

.ocl-external-id {
  font-size: 11px;
  color: #7c8494;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.ocl-external-id i {
  font-size: 12px;
}

.ocl-date {
  font-size: 11px;
  color: #7c8494;
  display: inline-flex;
  align-items: center;
  gap: 3px;
  white-space: nowrap;
}
.ocl-date i {
  font-size: 13px;
}

.ocl-org {
  font-size: 11px;
  color: #fff;
  background: #6b7280;
  padding: 1px 6px;
  border-radius: 4px;
  font-weight: 500;
  white-space: nowrap;
}

/* Body - 5 columns */
.ocl-body {
  display: grid;
  grid-template-columns: auto 1fr 1.8fr auto 0.6fr;
  gap: 0;
  padding: 10px 12px;
  min-height: 60px;
  align-items: start;
}

.ocl-col {
  padding: 0 8px;
}
.ocl-col:first-child {
  padding-left: 0;
}
.ocl-col:last-child {
  padding-right: 0;
}
.ocl-col + .ocl-col {
  border-left: 1px solid #eef0f3;
}

.ocl-col-label {
  font-size: 10px;
  font-weight: 600;
  color: #9ca3af;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  margin-bottom: 4px;
}

/* Customer column */
.ocl-customer-name {
  font-size: 13px;
  font-weight: 600;
  color: #1f2937;
  line-height: 1.3;
}
.ocl-address {
  font-size: 12px;
  color: #6b7280;
  line-height: 1.4;
  display: flex;
  align-items: center;
  gap: 4px;
}
.ocl-flag {
  width: 14px;
  height: 10px;
  border-radius: 1px;
  object-fit: cover;
  flex-shrink: 0;
}

/* Time column */
.ocl-col--time {
  min-width: 156px;
  text-align: center;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: center;
  padding-right: 16px;
}
.ocl-time-ago { font-size: 12px; font-weight: 700; color: #3273dc; line-height: 1.3; }
.ocl-time-date { font-size: 12px; color: #4b5563; line-height: 1.4; }
.ocl-time-clock { font-size: 11px; color: #9ca3af; line-height: 1.4; }

/* Document column */
.ocl-body-doc-no { font-size: 13px; font-weight: 600; color: #3273dc; cursor: pointer; text-decoration: none; display: block; margin-bottom: 1px; }
.ocl-body-doc-no:hover { text-decoration: underline; }
.ocl-body-external { font-size: 11px; color: #7c8494; margin-bottom: 4px; }

/* Articles column */
.ocl-line-item { display: flex; gap: 8px; align-items: flex-start; }
.ocl-line-item + .ocl-line-item { padding-top: 6px; margin-top: 6px; border-top: 1px dashed #eef0f3; }
.ocl-line-thumb { flex-shrink: 0; width: 40px; height: 40px; }
.ocl-product-img { width: 40px; height: 40px; object-fit: cover; border-radius: 4px; border: 1px solid #e2e5ea; }
.ocl-product-img-placeholder { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; background: #f3f4f6; border-radius: 4px; border: 1px solid #e8eaed; color: #d1d5db; font-size: 16px; }
.ocl-line-details { flex: 1; min-width: 0; }
.ocl-line-name { font-size: 12px; font-weight: 600; color: #1f2937; line-height: 1.3; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ocl-line-code { font-size: 10px; color: #7c8494; }
.ocl-code-label { font-weight: 600; color: #9ca3af; }
.ocl-line-qty-price { display: flex; align-items: center; gap: 6px; margin-top: 3px; }
.ocl-line-qty { font-size: 11px; font-weight: 700; color: #4b5563; }
.ocl-qty-on-hand { font-size: 10px; font-weight: 700; padding: 1px 5px; border-radius: 3px; min-width: 20px; text-align: center; display: inline-block; }
.ocl-qty--green { background: #16a34a; color: #fff; }
.ocl-qty--red { background: #dc2626; color: #fff; }
.ocl-line-price { font-size: 11px; color: #6b7280; }

/* Status column */
.ocl-col--status { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; min-width: 100px; }
.ocl-shipped-date { font-size: 11px; color: #4b5563; display: flex; align-items: center; gap: 3px; white-space: nowrap; }
.ocl-shipped-date i { font-size: 13px; color: #9ca3af; }
.ocl-status-row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.ocl-confirm-log-btn {
  background: transparent;
  border: 1px solid #c9d9f4;
  color: #3273dc;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 12px;
  padding: 0;
  line-height: 1;
  transition: background 0.15s, color 0.15s;
}
.ocl-confirm-log-btn:hover {
  background: #eef4ff;
  color: #1e3a8a;
}

/* Shipping column */
.ocl-shipper,
.ocl-tracking,
.ocl-ship-date,
.ocl-weight,
.ocl-parcel-type {
  font-size: 12px;
  color: #4b5563;
  display: flex;
  align-items: center;
  gap: 4px;
  line-height: 1.5;
}
.ocl-shipper i,
.ocl-tracking i,
.ocl-ship-date i,
.ocl-weight i,
.ocl-parcel-type i {
  font-size: 13px;
  color: #9ca3af;
  flex-shrink: 0;
}
.ocl-tracking-no {
  font-family: 'SF Mono', 'Fira Code', monospace;
  font-size: 11px;
  color: #3273dc;
  word-break: break-all;
}

/* Totals column */
.ocl-items-qty {
  font-size: 12px;
  color: #4b5563;
  display: flex;
  align-items: center;
  gap: 4px;
}
.ocl-items-qty i {
  font-size: 13px;
  color: #9ca3af;
}
.ocl-grand-total {
  font-size: 15px;
  font-weight: 700;
  color: #1f2937;
  margin-top: 2px;
}
.ocl-fee {
  font-size: 11px;
  color: #e67e22;
  margin-top: 1px;
}
.ocl-fee-label {
  font-weight: 600;
}

.ocl-empty {
  font-size: 12px;
  color: #d1d5db;
}

/* Footer */
.ocl-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 5px 12px;
  background: #fafbfc;
  border-top: 1px solid #eef0f3;
}
.ocl-footer-left,
.ocl-footer-right {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ocl-partner {
  font-size: 11px;
  color: #6b7280;
  display: inline-flex;
  align-items: center;
  gap: 3px;
}
.ocl-partner i {
  font-size: 12px;
}
.ocl-age {
  font-size: 10px;
  font-weight: 600;
  color: #9ca3af;
  padding: 1px 5px;
  border-radius: 4px;
  background: #f3f4f6;
}
.ocl-age--warn {
  color: #d97706;
  background: #fef3c7;
}
.ocl-age--danger {
  color: #dc2626;
  background: #fef2f2;
}

.ocl-action-btn {
  background: none;
  border: 1px solid #e2e5ea;
  border-radius: 5px;
  cursor: pointer;
  padding: 3px 8px;
  color: #6b7280;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  transition: all 0.15s;
}
.ocl-action-btn:hover {
  background: #f3f4f6;
  color: #3273dc;
  border-color: #3273dc;
}

/* Loading more / end */
.ocl-loading-more,
.ocl-end {
  text-align: center;
  padding: 12px;
  font-size: 12px;
  color: #9ca3af;
}
.ocl-spin {
  animation: ocl-rotate 1s linear infinite;
  display: inline-block;
}
@keyframes ocl-rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

/* Empty state */
.ocl-empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 300px;
  color: #9ca3af;
}
.ocl-empty-state i {
  font-size: 48px;
  margin-bottom: 12px;
}
.ocl-empty-state p {
  font-size: 14px;
}

/* Responsive */
@media (max-width: 768px) {
  .ocl-body {
    grid-template-columns: 1fr;
    gap: 8px;
  }
  .ocl-col--time {
    flex-direction: row;
    gap: 8px;
    text-align: left;
    align-items: flex-start;
  }
  .ocl-col + .ocl-col {
    border-left: none;
    border-top: 1px solid #eef0f3;
    padding-left: 0;
    padding-top: 6px;
  }
  .ocl-header {
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>
