{% set g = module.style %}
{% set a = module.advanced %}

{# Prepare data source - manual cards or HubDB #}
{% set card_items = [] %}
{% set hubdb_error = none %}

{% if module.data_source == 'hubdb' %}
  {# HubDB data source #}
  {% if module.hubdb_config.table_id %}
    {% set table = hubdb_table(module.hubdb_config.table_id) %}
    {% if table %}
      {% set query_params = {} %}
      {% if module.hubdb_config.filter_column and module.hubdb_config.filter_value %}
        {% do query_params.update({module.hubdb_config.filter_column: module.hubdb_config.filter_value}) %}
      {% endif %}
      {# limit must be a query param — hubdb_table_rows() has no 3rd positional arg. #}
      {% do query_params.update({"limit": module.hubdb_config.limit|default(9)}) %}
      {% set card_items = hubdb_table_rows(table.id, query_params) %}
    {% else %}
      {% set hubdb_error = "HubDB table not found. Please check the table ID in module settings." %}
    {% endif %}
  {% else %}
    {% set hubdb_error = "HubDB table not selected. Please select a table in module settings." %}
  {% endif %}
{% else %}
  {# Manual cards data source #}
  {% set card_items = module.cards %}
{% endif %}

{# Mobile-first: Determine visible cards on mobile viewport #}
{% set mobile_visible_count = g.cols_mobile == '1' ? 2 : 4 %}
{% set tablet_visible_count = g.cols_tablet|int * 2 %}

{# No preload here: it fetched the FULL-SIZE original while the first <img> renders a
   resized srcset candidate (double download), and fired even for the 'no-image'
   variant. The first card image is already loading="eager" + fetchpriority="high". #}

{# Main grid section #}
<section class="c-cards-grid{{ g.enable_masonry ? ' c-cards-grid--masonry' : '' }}{{ a.container_class ? ' ' ~ a.container_class : '' }}"
  {% if a.container_id %}id="{{ a.container_id }}"{% endif %}
  style="
    --grid-gap: {{ g.gap }};
    --grid-ai: {{ g.align_items }};
    --grid-cols-mobile: {{ g.cols_mobile }};
    --grid-cols-tablet: {{ g.cols_tablet }};
    --grid-cols-desktop: {{ g.cols_desktop }};
    --label-bg: {% if g.label_color.color %}rgba({{ g.label_color.color|convert_rgb }}, {{ (g.label_color.opacity|default(100)) / 100 }}){% else %}{{ theme.core.colors.primary.color or '#007bff' }}{% endif %};
    --card-p: {{ g.card_padding }};
    --card-r: {{ g.card_radius == '0' ? '0' : 'var(--radius, ' ~ g.card_radius ~ ')' }};
    {% if g.card_bg.color %}--card-bg: rgba({{ g.card_bg.color|convert_rgb }}, {{ (g.card_bg.opacity|default(100)) / 100 }});{% endif %}
    {% if g.card_text_color.color %}--card-fg: rgba({{ g.card_text_color.color|convert_rgb }}, {{ (g.card_text_color.opacity|default(100)) / 100 }});{% endif %}
    --card-bw: {{ g.card_border_width|default('1px') }};
    {% if g.card_border_color.color %}--card-bc: rgba({{ g.card_border_color.color|convert_rgb }}, {{ (g.card_border_color.opacity|default(100)) / 100 }});{% endif %}
    --card-aspect: {{ g.image_aspect|default('auto') }};
    --card-img-fit: {{ g.image_fit|default('contain') }};
    --card-img-min-height: {{ g.image_min_height|default('200px') }};
    --card-img-max-height: {{ g.image_max_height|default('400px') }};
    --card-img-max-width: {% if g.image_max_width_type == 'percent' %}{{ g.image_max_width_percent|default(75) }}%{% elif g.image_max_width_type == 'pixels' %}{{ g.image_max_width_pixels|default(200) }}px{% else %}100%{% endif %};
  "
  {% if a.enable_animation %}data-animate="true"{% endif %}
>
  {# Optional section header #}
  {% if a.section_title or a.section_subtitle %}
    <header class="c-cards-grid__header">
      {% if a.section_title %}
        <h2 class="c-cards-grid__title">{{ a.section_title|escape }}</h2>
      {% endif %}
      {% if a.section_subtitle %}
        <p class="c-cards-grid__subtitle">{{ a.section_subtitle|escape }}</p>
      {% endif %}
    </header>
  {% endif %}

  {# HubDB error message #}
  {% if hubdb_error %}
    <div class="c-cards-grid__error" role="alert">
      <p><strong>HubDB Configuration Error:</strong> {{ hubdb_error }}</p>
      <p style="font-size: 0.875rem; margin-top: 0.5rem;">
        Please configure the HubDB settings in the module editor or switch to Manual Cards.
      </p>
    </div>
  {% endif %}

  {# Cards grid container #}
  <div class="c-cards-grid__container">
    {# Responsive column widths — constant per module instance, computed once #}
    {% set mobile_width = g.cols_mobile == '1' ? 350 : 263 %}
    {% set tablet_width = (768 / g.cols_tablet|int)|round %}
    {% set desktop_width = (1200 / g.cols_desktop|int)|round %}
    {% for item in card_items %}
      {% set card_index = loop.index %}
      {# Mobile-first priority: Only first visible cards on mobile get high priority #}
      {% set is_mobile_priority = (g.cols_mobile == '1' and card_index == 1) or (g.cols_mobile == '2' and card_index <= 2) %}
      {% set is_priority = is_mobile_priority %}

      <article class="c-card c-card--{{ g.card_variant }}"
        {% if a.enable_animation %}data-animate-item="{{ card_index }}"{% endif %}
      >
        {# Corner-flag label if present #}
        {% if item.badge %}
          <span class="card-flag" aria-label="Badge: {{ item.badge }}">{{ item.badge|escape }}</span>
        {% endif %}

        {# Image - not shown for no-image variant #}
        {# Handle both manual and HubDB image sources #}
        {% if module.data_source == 'hubdb' %}
          {# HubDB: image may be URL string or object with .url property #}
          {% set image_url = item.image.url if item.image.url else (item.image if item.image is string else '') %}
          {% set img_width = item.image.width if item.image.width else null %}
          {% set img_height = item.image.height if item.image.height else null %}
          {% set img_size_known = img_width and img_height %}
        {% else %}
          {# Manual: standard module image field - check for valid src string #}
          {% set image_url = item.image.src if item.image.src else (item.image.url if item.image.url else (item.image if item.image is string else '')) %}
          {% set img_width = item.image.width if item.image.width else null %}
          {% set img_height = item.image.height if item.image.height else null %}
          {% set img_size_known = img_width and img_height %}
        {% endif %}

        {# Only render image if we have a valid URL string #}
        {% if g.card_variant != 'no-image' and image_url and image_url is string and image_url != '' %}
          <div class="c-card__media" {% if not item.image_alt %}aria-hidden="true"{% endif %}>
            {% set loading = is_priority ? 'eager' : (a.lazy_load ? 'lazy' : (item.image.loading|default('lazy'))) %}
            {% set fetchpriority = is_priority ? 'high' : 'auto' %}

            {# Check if image is HubSpot-hosted (resize_image_url only works with HubSpot URLs) #}
            {% set is_hubspot_image = image_url is string_containing 'hubspot' or image_url is string_containing 'hubspotusercontent' or image_url is string_containing 'f.hubspotusercontent' %}

            {# Generate properly sized URLs — only when native dimensions are known to avoid upscaling #}
            {% if is_hubspot_image and img_size_known %}
              {% set img_mobile = resize_image_url(image_url, mobile_width) %}
              {% set img_tablet = resize_image_url(image_url, tablet_width) %}
              {% set img_desktop = resize_image_url(image_url, desktop_width) %}
              {% set img_desktop_2x = resize_image_url(image_url, desktop_width * 2) %}
            {% endif %}

            <img
              src="{{ image_url }}"
              alt="{{ item.image_alt|default('', true)|escape }}"
              loading="{{ loading }}"
              {% if fetchpriority == 'high' %}fetchpriority="high"{% endif %}
              decoding="async"
              {% if img_size_known %}
              width="{{ img_width }}"
              height="{{ img_height }}"
              style="--img-width: {{ img_width }}px; --img-height: {{ img_height }}px;"
              {% endif %}
              {% if is_hubspot_image and img_size_known %}
              sizes="(max-width: 480px) {{ mobile_width }}px,
                     (max-width: 768px) {{ tablet_width }}px,
                     (max-width: 1024px) {{ desktop_width }}px,
                     {{ desktop_width }}px"
              srcset="{{ img_mobile }} {{ mobile_width }}w,
                      {{ img_tablet }} {{ tablet_width }}w,
                      {{ img_desktop }} {{ desktop_width }}w,
                      {{ img_desktop_2x }} {{ desktop_width * 2 }}w"
              {% endif %}
            >
          </div>
        {% endif %}

        {# Card body content #}
        <div class="c-card__body">
          {% if item.eyebrow %}
            <p class="c-card__eyebrow">{{ item.eyebrow|escape }}</p>
          {% endif %}

          <h3 class="c-card__title">{{ item.title|escape }}</h3>

          {% if item.content and g.card_variant != 'compact' %}
            <div class="c-card__text">{{ item.content|sanitize_html }}</div>
          {% endif %}

          {# CTA button or link.
             Manual cards: cta_url is a url-field dict (use .href).
             HubDB rows: cta_url is a plain string. Support both. #}
          {% if (item.cta_url.href or item.cta_url is string) and item.cta_text %}
            {% set cta_href = item.cta_url.href|default(item.cta_url, true) %}
            {% set target_attrs = item.new_tab ? 'target="_blank" rel="noopener noreferrer"' : '' %}
            {% set aria_label = item.cta_aria_label ? 'aria-label="' ~ item.cta_aria_label|escape ~ '"' : '' %}

            {% if item.cta_as_button %}
              <a class="c-card__btn"
                 href="{{ cta_href }}"
                 {{ target_attrs }}
                 {{ aria_label }}>
                <span>{{ item.cta_text|escape }}</span>
              </a>
            {% else %}
              <a class="c-card__link"
                 href="{{ cta_href }}"
                 {{ target_attrs }}
                 {{ aria_label }}>
                {{ item.cta_text|escape }}
                <span aria-hidden="true">→</span>
              </a>
            {% endif %}
          {% endif %}
        </div>
      </article>
    {% endfor %}
  </div>
</section>

{# Mobile performance monitoring and adaptive loading #}
{% if a.mobile_optimization == 'adaptive' %}
<script type="module">
  // Adaptive loading based on connection speed
  if ('connection' in navigator && navigator.connection.effectiveType) {
    const conn = navigator.connection.effectiveType;
    const grid = document.querySelector('.c-cards-grid');
    if (grid) {
      if (conn === 'slow-2g' || conn === '2g') {
        // Disable animations and reduce quality for slow connections
        grid.setAttribute('data-connection', 'slow');
      } else if (conn === '3g') {
        grid.setAttribute('data-connection', 'medium');
      } else {
        grid.setAttribute('data-connection', 'fast');
      }
    }
  }

  // Intersection Observer for truly lazy loading beyond viewport
  // IMPORTANT: Only enable if explicitly configured, as replacing src can cause issues
  if ({{ a.enable_intersection_observer|lower }} && 'IntersectionObserver' in window) {
    const imageObserver = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const img = entry.target;
          // Restore original src from data attribute
          if (img.dataset.lazySrc) {
            img.src = img.dataset.lazySrc;
            img.removeAttribute('data-lazy-src');
            if (img.dataset.lazySrcset) {
              img.srcset = img.dataset.lazySrcset;
              img.removeAttribute('data-lazy-srcset');
            }
            observer.unobserve(img);
          }
        }
      });
    }, {
      rootMargin: '100px 0px',
      threshold: 0.01
    });

    // Observe images beyond the fold - only if src is valid
    document.querySelectorAll('.c-card:nth-child(n+{{ mobile_visible_count + 1 }}) img').forEach(img => {
      // Only apply lazy loading if image has a valid src (not undefined or empty)
      if (img.loading === 'lazy' && img.src && img.src !== 'undefined' && !img.src.startsWith('data:')) {
        img.dataset.lazySrc = img.src;
        if (img.srcset) {
          img.dataset.lazySrcset = img.srcset;
        }
        img.src = 'data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1 1"%3E%3C/svg%3E';
        img.srcset = '';
        imageObserver.observe(img);
      }
    });
  }
</script>
{% endif %}

{# Mobile-first critical CSS - only load what mobile needs #}
{% require_css %}
<style>
  /* Mobile-first base styles */
  :where(.c-cards-grid) {
    container-type: inline-size;
    padding: clamp(1rem, 4vw, 2rem) 0;
  }

  :where(.c-cards-grid__header) {
    text-align: center;
    margin-bottom: calc(var(--grid-gap) * 2);
  }

  :where(.c-cards-grid__title) {
    font-size: clamp(1.5rem, 3vw, 2.5rem);
    margin: 0 0 0.5rem;
  }

  :where(.c-cards-grid__subtitle) {
    font-size: clamp(1rem, 2vw, 1.25rem);
    opacity: 0.8;
    margin: 0;
  }

  /* Error message styling */
  :where(.c-cards-grid__error) {
    background: #fff3cd;
    border: 1px solid #ffc107;
    border-radius: var(--radius, 0.5rem);
    padding: 1rem 1.5rem;
    margin: 1rem 0;
    color: #856404;
  }

  :where(.c-cards-grid__error p) {
    margin: 0;
    line-height: 1.5;
  }

  :where(.c-cards-grid__error p + p) {
    margin-top: 0.5rem;
  }

  /* Mobile-first grid layout */
  :where(.c-cards-grid__container) {
    display: grid;
    gap: clamp(0.75rem, 3vw, var(--grid-gap));
    align-items: var(--grid-ai);
    grid-template-columns: repeat(var(--grid-cols-mobile), minmax(0, 1fr));
  }

  /* Masonry layout option */
  {% if g.enable_masonry %}
  :where(.c-cards-grid--masonry .c-cards-grid__container) {
    display: block;
    columns: var(--grid-cols-mobile);
    column-gap: var(--grid-gap);
  }

  @media (min-width: 641px) {
    :where(.c-cards-grid--masonry .c-cards-grid__container) {
      columns: var(--grid-cols-tablet);
    }
  }

  @media (min-width: 1025px) {
    :where(.c-cards-grid--masonry .c-cards-grid__container) {
      columns: var(--grid-cols-desktop);
    }
  }

  :where(.c-cards-grid--masonry .c-card) {
    break-inside: avoid;
    margin-bottom: var(--grid-gap);
  }
  {% endif %}

  /* Mobile-optimized card styles */
  :where(.c-card) {
    position: relative;
    display: grid;
    gap: clamp(0.5rem, 2vw, 0.75rem);
    background: var(--card-bg, var(--color-bg, #ffffff));
    color: var(--card-fg, var(--color-text, #111827));
    border: var(--card-bw) solid var(--card-bc, var(--color-border, #e5e7eb));
    border-radius: var(--card-r);
    padding: clamp(0.75rem, 3vw, var(--card-p));
    contain: layout style paint;
  }

  /* Media row stays at image height; body row fills the rest */
  :where(.c-card:has(.c-card__media)) {
    grid-template-rows: auto 1fr;
  }

  /* Shadow variations using theme variables */
  {% if g.card_shadow == 'none' %}
    :where(.c-card) { box-shadow: none; }
  {% elif g.card_shadow == 'sm' %}
    :where(.c-card) { box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.06)); }
  {% elif g.card_shadow == 'md' %}
    :where(.c-card) { box-shadow: var(--shadow-md, 0 4px 10px rgba(0,0,0,0.10)); }
  {% elif g.card_shadow == 'lg' %}
    :where(.c-card) { box-shadow: var(--shadow-lg, 0 10px 24px rgba(0,0,0,0.14)); }
  {% endif %}

  /* Tablet and desktop layouts - progressive enhancement */
  @media (min-width: 641px) {
    :where(.c-cards-grid__container) {
      grid-template-columns: repeat(var(--grid-cols-tablet), minmax(0, 1fr));
    }

    :where(.c-card) {
      padding: var(--card-p);
      gap: 0.75rem;
    }
  }

  @media (min-width: 1025px) {
    :where(.c-cards-grid__container) {
      grid-template-columns: repeat(var(--grid-cols-desktop), minmax(0, 1fr));
      gap: var(--grid-gap);
    }
  }

  /* Hover effects only for devices with hover capability */
  {% if g.hover_raise or g.hover_shadow %}
  @media (hover: hover) and (pointer: fine) {
    :where(.c-card) {
      transition: var(--transition, transform 0.15s ease, box-shadow 0.15s ease);
    }

    :where(.c-card:hover) {
      {% if g.hover_raise %}transform: translateY(-2px) scale(1.01);{% endif %}
      {% if g.hover_shadow %}box-shadow: var(--shadow-lg, 0 12px 28px rgba(0,0,0,0.18));{% endif %}
    }
  }

  @media (prefers-reduced-motion: reduce) {
    :where(.c-card) {
      transition: none !important;
    }
    :where(.c-card:hover) {
      transform: none !important;
    }
  }
  {% endif %}

  /* Card media container */
  :where(.c-card__media) {
    position: relative;
    overflow: hidden;
    border-radius: calc(var(--card-r) - 2px);
    {% if g.image_aspect != 'auto' %}
    aspect-ratio: var(--card-aspect);
    {% endif %}
    {% if g.image_fit == 'cover' or g.image_fit == 'fill' %}
    min-height: var(--card-img-min-height);
    max-height: var(--card-img-max-height);
    {% endif %}
    {% if g.image_fit == 'contain' or g.image_fit == 'scale-down' %}
    background: var(--color-bg-secondary, #f9fafb);
    {% endif %}
  }

  {% if g.image_fit == 'cover' or g.image_fit == 'fill' %}
  .c-card__media > img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: var(--card-img-fit);
    object-position: center;
  }
  {% else %}
  /* Contain/scale-down: natural size, horizontally centered, capped at native width */
  .c-card__media > img {
    display: block;
    margin: 0 auto;
    max-width: min(100%, var(--img-width, 100%));
    height: auto;
  }
  {% endif %}

  {% include "../../includes/card-flag.css.html" %}

  :where(.c-card__body) {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
  }

  :where(.c-card__eyebrow) {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    opacity: 0.8;
    margin: 0;
  }

  :where(.c-card__title) {
    font-size: clamp(1rem, 1vw + 0.9rem, 1.25rem);
    line-height: 1.25;
    margin: 0;
  }

  :where(.c-card__text) {
    font-size: 0.9375rem;
    line-height: inherit;
  }

  :where(.c-card__text > *:first-child) {
    margin-top: 0;
  }

  :where(.c-card__text > *:last-child) {
    margin-bottom: 0;
  }

  :where(.c-card__btn) {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    /* 48px minimum touch target for mobile */
    min-height: 48px;
    padding: 0.75rem 1rem;
    border-radius: 6px;
    background: var(--card-fg, var(--color-text, #111827));
    color: var(--card-bg, var(--color-bg, #ffffff));
    text-decoration: none;
    font-weight: 500;
    align-self: flex-start;
  }

  :where(.c-card__btn:hover) {
    opacity: 0.9;
  }

  :where(.c-card__btn:focus-visible) {
    outline: 2px solid var(--color-primary, var(--card-fg));
    outline-offset: 2px;
  }

  :where(.c-card__link) {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    /* Larger touch target for mobile */
    min-height: 44px;
    padding: 0.5rem 0;
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 500;
  }

  :where(.c-card__link:hover) {
    text-decoration-thickness: 2px;
  }

  :where(.c-card__link:focus-visible) {
    outline: 2px solid var(--color-primary, var(--card-fg));
    outline-offset: 2px;
    border-radius: 2px;
  }

  /* Variant: no-image */
  {% if g.card_variant == 'no-image' %}
  :where(.c-card--no-image .c-card__media) {
    display: none;
  }
  {% endif %}

  /* Variant: minimal */
  {% if g.card_variant == 'minimal' %}
  :where(.c-card--minimal) {
    border: none;
    box-shadow: none;
    background: transparent;
    padding: 0;
  }
  {% endif %}

  /* Variant: compact */
  {% if g.card_variant == 'compact' %}
  :where(.c-card--compact) {
    padding: calc(var(--card-p) * 0.75);
    gap: 0.5rem;
  }

  {% if g.image_aspect != 'auto' %}
  :where(.c-card--compact .c-card__media) {
    aspect-ratio: 21/9;
  }
  {% endif %}

  :where(.c-card--compact .c-card__title) {
    font-size: clamp(0.875rem, 1vw + 0.8rem, 1.125rem);
  }
  {% endif %}

  /* Mobile-specific optimizations based on connection */
  {% if a.mobile_optimization == 'aggressive' %}
  /* Aggressive mobile optimization - remove non-essential styles */
  @media (max-width: 640px) {
    :where(.c-card) {
      box-shadow: none !important;
      border-width: 1px !important;
    }

    {% if g.image_aspect != 'auto' %}
    :where(.c-card__media) {
      /* Lower aspect ratio for mobile to reduce image bytes */
      aspect-ratio: 3/2;
    }
    {% endif %}

    :where(.c-card__text) {
      /* Limit text on mobile to save space */
      display: -webkit-box;
      -webkit-line-clamp: 3;
      -webkit-box-orient: vertical;
      overflow: hidden;
    }
  }
  {% endif %}

  /* Connection-based styles */
  [data-connection="slow"] .c-card {
    box-shadow: none !important;
    transition: none !important;
  }

  [data-connection="slow"] .c-card__media img {
    filter: blur(0); /* Disable blur-up effect */
  }

  /* Animation support */
  {% if a.enable_animation %}
  @media (prefers-reduced-motion: no-preference) {
    :where([data-animate-item]) {
      opacity: 0;
      transform: translateY(20px);
      animation: cardEntrance 0.5s ease forwards;
      animation-delay: calc(var(--item-index, 0) * 0.05s);
    }

    @keyframes cardEntrance {
      to {
        opacity: 1;
        transform: translateY(0);
      }
    }

    {% for i in range(1, 13) %}
    :where([data-animate-item="{{ i }}"]) {
      --item-index: {{ i - 1 }};
    }
    {% endfor %}
  }
  {% endif %}
</style>
{% end_require_css %}