/** Shopify CDN: Minification failed

Line 6021:65 Unexpected "*"

**/
/* SHOPIFY_STYLESHEETS_VERSION: 1.0 */


/* CSS from section stylesheet tags */
/* START_SECTION:404 (INDEX:0) */
.ne-404__inner {
    max-width: 720px;
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-6, 24px);
  }
  .ne-404--align-left   .ne-404__inner { text-align: left;   align-items: flex-start; }
  .ne-404--align-center .ne-404__inner { text-align: center; align-items: center; }
  .ne-404--align-right  .ne-404__inner { text-align: right;  align-items: flex-end; }

  .ne-404__card {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 12px);
    align-items: inherit;
    padding: var(--ne-space-10, 40px);
    border-radius: var(--ne-404-card-radius, 16px);
    width: 100%;
  }
  /* Card surface: glass (opt-in) */
  .ne-404--card-glass .ne-404__card {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    /* No border — white glass-border reads as a bottom contour on dark
       bg (user 2026-05-24). */
    border: 0;
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
  }
  .ne-404--card-tint .ne-404__card {
    background: color-mix(in srgb, var(--ne-scheme-accent) 6%, var(--ne-glass-bg, rgba(255,255,255,0.06)));
    border-color: color-mix(in srgb, var(--ne-scheme-accent) 18%, var(--ne-glass-border, rgba(255,255,255,0.12)));
  }
  /* Card glow / shadow (gated by section settings) */
  .ne-404--card-glow .ne-404__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 32px color-mix(in srgb, var(--ne-scheme-accent) 24%, transparent),
      0 8px 24px rgba(0,0,0,0.18);
  }
  .ne-404--card-shadow .ne-404__card {
    box-shadow: 0 12px 40px rgba(0,0,0,0.2);
  }
  .ne-404--card-glow.ne-404--card-shadow .ne-404__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 32px color-mix(in srgb, var(--ne-scheme-accent) 24%, transparent),
      0 12px 40px rgba(0,0,0,0.22);
  }

  /* Big 404 code with accent gradient */
  .ne-404__code {
    margin: 0;
    font-size: clamp(96px, 18vw, 180px);
    font-weight: 800;
    line-height: 0.9;
    letter-spacing: -0.04em;
    background: linear-gradient(135deg,
      var(--ne-scheme-accent),
      color-mix(in srgb, var(--ne-scheme-accent) 65%, white)
    );
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    text-shadow: 0 0 60px color-mix(in srgb, var(--ne-scheme-accent) 28%, transparent);
  }

  .ne-404__heading { margin: 0; }
  .ne-404__subtext { margin: 0; max-width: 480px; line-height: 1.5; }
  .ne-404--align-center .ne-404__subtext { margin-inline: auto; }

  /* Search form */
  .ne-404__search { width: 100%; max-width: 520px; }
  .ne-404--align-center .ne-404__search { margin-inline: auto; }
  .ne-404__search-wrap {
    position: relative;
    display: flex;
    align-items: center;
  }
  .ne-404__search-icon {
    position: absolute;
    left: 14px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.6;
    pointer-events: none;
  }
  .ne-404__search-input {
    width: 100%;
    padding: 12px 16px 12px 44px;
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.15));
    border-radius: var(--ne-radius-lg, 12px);
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-font-size-base, 15px);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  .ne-404__search-input::-webkit-search-cancel-button { -webkit-appearance: none; }
  .ne-404__search-input:focus {
    outline: none;
    border-color: var(--ne-scheme-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ne-scheme-accent) 18%, transparent);
  }

  .ne-404__cta { align-self: flex-start; }
  .ne-404--align-center .ne-404__cta { align-self: center; }
  .ne-404--align-right  .ne-404__cta { align-self: flex-end; }

  .ne-404__blocks {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    margin-top: var(--ne-space-8, 32px);
  }
  .ne-404__blocks > .shopify-block { display: contents; }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-404.ne-404--custom-mobile-pad {
      padding-top: var(--ne-404-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-404-mobile-pad-bottom, 24px) !important;
    }
    .ne-404__card {
      padding: var(--ne-space-6, 24px);
    }
  }
/* END_SECTION:404 */

/* START_SECTION:announcement-bar (INDEX:1) */
.ne-announcement {
    font-size: var(--ne-font-size-sm, 0.875rem);
    color: var(--ne-scheme-text);
    text-align: center;
    overflow: hidden;
  }
  .ne-announcement.ne-glass {
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  /* Background gate: skip non-tinted glass when --tinted is on so the
     global .ne-glass--tinted rule (critical.css) can paint accent-tint. */
  .ne-announcement.ne-glass:not(.ne-glass--tinted) {
    background: var(--ne-glass-bg, rgba(255,255,255,0.05)) !important;
  }
  .ne-announcement a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  .ne-announcement__message {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    padding: 0 var(--ne-space-8, 2rem);
  }
  .ne-announcement__message a {
    display: inline-flex;
    align-items: center;
    color: inherit;
  }
  /* Each icon-wrap controls its own margin to text via inline style;
     direction-agnostic so DOM order = visual order (no flex-reverse). */
  .ne-announcement__icon-wrap {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
  }
  /* Library SVG (ne-icon emits 1em sizing + currentColor stroke). */
  .ne-announcement__message .ne-icon { width: 1em; height: 1em; }
  /* Plain custom icon (<img>): scale to text height, original colors. */
  .ne-announcement__icon { height: 1em; width: auto; flex-shrink: 0; }
  /* Recolored custom icon (<span> with mask-image): painted with
     currentColor so the icon-wrap's `color` setting (or inherited
     scheme text color) flows through. */
  .ne-announcement__icon--mask {
    display: inline-block;
    width: 1em;
    height: 1em;
    background-color: currentColor;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
  }

  .ne-marquee { position: relative; overflow: hidden; }
  /* Fade lives on the inner marquee only — same approach as brand-slider.
     Width driven by --ne-ann-fade (set inline from fade_width setting).
     Section box (solid / glass / transparent) is never masked, so its
     edges stay visually crisp regardless of BG mode. */
  .ne-announcement--fade .ne-marquee {
    mask-image: linear-gradient(to right, transparent, black var(--ne-ann-fade, 6%), black calc(100% - var(--ne-ann-fade, 6%)), transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, black var(--ne-ann-fade, 6%), black calc(100% - var(--ne-ann-fade, 6%)), transparent);
  }
  .ne-marquee__track {
    display: flex;
    width: max-content;
    animation: ne-marquee var(--ne-marquee-speed, 30s) linear infinite;
  }
  .ne-marquee__track--reverse { animation-name: ne-marquee-reverse; }
  /* -16.6667% = -1/6 = exactly one set out of six. See Liquid above. */
  @keyframes ne-marquee         { 0% { transform: translateX(0); }            100% { transform: translateX(-16.6667%); } }
  @keyframes ne-marquee-reverse { 0% { transform: translateX(-16.6667%); }    100% { transform: translateX(0); } }

  .ne-announcement__static { display: flex; justify-content: center; }
  .ne-announcement__message--active { display: inline; }

  @media (prefers-reduced-motion: reduce) {
    .ne-marquee__track { animation: none; }
  }
/* END_SECTION:announcement-bar */

/* START_SECTION:article (INDEX:2) */
.ne-article__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-6);
    margin: 0 auto;
  }
  /* Heading alignment */
  .ne-article--align-left   .ne-article__inner h1,
  .ne-article--align-left   .ne-article__breadcrumb,
  .ne-article--align-left   .ne-article__meta { text-align: left; justify-content: flex-start; }
  .ne-article--align-center .ne-article__inner h1,
  .ne-article--align-center .ne-article__breadcrumb,
  .ne-article--align-center .ne-article__meta { text-align: center; justify-content: center; }
  .ne-article--align-right  .ne-article__inner h1,
  .ne-article--align-right  .ne-article__breadcrumb,
  .ne-article--align-right  .ne-article__meta { text-align: right; justify-content: flex-end; }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-article.ne-article--custom-mobile-pad {
      padding-top: var(--ne-article-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-article-mobile-pad-bottom, 24px) !important;
    }
  }

  /* Common label style — replaces inline font-weight inline-styles */
  .ne-article__label { font-weight: 500; }
  .ne-article__form-success { color: var(--ne-color-success, #10b981); }
  .ne-article__form-errors  { color: var(--ne-color-error, #ef4444); }

  /* Fill viewport: extend section to remaining viewport-height when content
     is short, so footer sits at the bottom of the screen instead of leaving
     body-bg show through under a stub article. */
  .ne-article--fill-viewport {
    min-height: calc(100vh - var(--ne-header-height, 70px) - var(--ne-section-spacing, 40px));
  }

  .ne-article__breadcrumb a { text-decoration: none; color: inherit; }
  .ne-article__breadcrumb a:hover { color: var(--ne-scheme-accent); }

  .ne-article__meta { display: flex; gap: var(--ne-space-3); }
  .ne-article__meta a { color: inherit; }

  .ne-article__featured-image {
    overflow: hidden;
    border-radius: var(--ne-article-img-radius, 8px);
    width: 100%;
  }
  /* aspect-ratio class-modifiers (Rule 239 — class > [style*=]) */
  .ne-article__featured-image--aspect-auto    { /* natural image height */ }
  .ne-article__featured-image--aspect-16-9    { aspect-ratio: 16 / 9; }
  .ne-article__featured-image--aspect-3-2     { aspect-ratio: 3 / 2; }
  .ne-article__featured-image--aspect-4-3     { aspect-ratio: 4 / 3; }
  .ne-article__featured-image--aspect-1-1     { aspect-ratio: 1 / 1; }
  .ne-article__featured-image--aspect-3-4     { aspect-ratio: 3 / 4; }
  .ne-article__featured-image--aspect-2-3     { aspect-ratio: 2 / 3; }
  .ne-article__featured-image--aspect-21-9    { aspect-ratio: 21 / 9; }
  /* aspect-custom: inline-style sets height directly */
  .ne-article__featured-image--aspect-custom  { aspect-ratio: auto; }

  .ne-article__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
  }
  /* When aspect=auto, image renders natural height */
  .ne-article__featured-image--aspect-auto .ne-article__image {
    height: auto;
    object-fit: contain;
  }

  .ne-article__content { line-height: 1.8; color: var(--ne-scheme-text); }
  .ne-article__content h2, .ne-article__content h3 { color: var(--ne-scheme-heading); margin-top: var(--ne-space-4); }
  .ne-article__content img { max-width: 100%; height: auto; border-radius: var(--ne-radius-md); }

  .ne-article__tags { display: flex; flex-wrap: wrap; gap: var(--ne-space-2); }
  .ne-article__tag {
    padding: var(--ne-space-1) var(--ne-space-3);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-full);
    font-size: var(--ne-font-size-xs);
    text-decoration: none;
    color: var(--ne-scheme-text-muted);
    transition: all var(--ne-transition-fast);
  }
  .ne-article__tag:hover { border-color: var(--ne-scheme-accent); color: var(--ne-scheme-accent); }

  .ne-article__share { display: flex; align-items: center; gap: var(--ne-space-3); padding: var(--ne-space-4) 0; border-top: 1px solid var(--ne-scheme-border); }
  .ne-article__share-links { display: flex; gap: var(--ne-space-3); }
  .ne-article__share-links a { color: var(--ne-scheme-text-muted); transition: color var(--ne-transition-fast); }
  .ne-article__share-links a:hover { color: var(--ne-scheme-accent); }

  .ne-article__comments { border-top: 1px solid var(--ne-scheme-border); padding-top: var(--ne-space-6); }
  .ne-article__comment { padding: var(--ne-space-4) 0; border-bottom: 1px solid var(--ne-scheme-border); }
  .ne-article__comment-header { display: flex; justify-content: space-between; margin-bottom: var(--ne-space-2); }
  .ne-article__comment-body { color: var(--ne-scheme-text-muted); }

  .ne-article__comment-form { margin-top: var(--ne-space-6); }
  .ne-article__form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--ne-space-4); }
  .ne-article__form-field { display: flex; flex-direction: column; gap: var(--ne-space-1); margin-bottom: var(--ne-space-3); }
  .ne-article__form-field label { font-size: var(--ne-font-size-sm); font-weight: 500; }

  .ne-input {
    padding: var(--ne-space-2) var(--ne-space-3);
    background: var(--ne-glass-bg);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md);
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-sm);
    transition: border-color var(--ne-transition-fast);
  }
  .ne-input:focus { outline: none; border-color: var(--ne-scheme-accent); }

  @media (max-width: 749px) { .ne-article__form-row { grid-template-columns: 1fr; } }
/* END_SECTION:article */

/* START_SECTION:before-after (INDEX:3) */
.ne-ba__inner {
    width: 100%;
    max-width: var(--ne-ba-max, 1000px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-ba-gap, 24px);
  }
  .ne-ba__inner > .shopify-block { display: contents; }

  .ne-ba--align-left .ne-ba__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
  }
  .ne-ba--align-center .ne-ba__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
  }
  .ne-ba--align-right .ne-ba__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-ba.ne-ba--custom-mobile-pad {
      padding-top: var(--ne-ba-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-ba-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:before-after */

/* START_SECTION:bento-grid (INDEX:4) */
/* Allow tile glow/shadow to escape section bounds */
  .ne-bento.ne-section { overflow: visible; }

  .ne-bento__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-bento-cols-d, 6), minmax(0, 1fr));
    grid-auto-rows: var(--ne-bento-row-h, 220px);
    gap: var(--ne-bento-gap, 12px);
    width: 100%;
    /* No perspective property. Tilt is now 2D-only (skewX + skewY +
       translateY) so no 3D compositing context is needed. Both tilt
       AND glass tiles coexist without backdrop-filter trapping or
       compositor eviction on adjacent sections (user report 2026-05-23). */
  }
  /* Aspect-locked mode: tiles use cs:rs ratio for aspect-ratio so tiles
     stay proportional regardless of viewport width. cs=rs=2 = square. */
  .ne-bento--aspect-locked .ne-bento__grid {
    grid-auto-rows: auto;
  }
  .ne-bento--aspect-locked .ne-bento__tile {
    aspect-ratio: calc(var(--ne-bento-cs-d, 2)) / calc(var(--ne-bento-rs-d, 1));
  }

  /* shopify-block wrappers transparent → tiles become direct grid items */
  .ne-bento__grid > .shopify-block { display: contents; }

  @media (max-width: 749px) {
    .ne-bento__grid {
      grid-template-columns: repeat(var(--ne-bento-cols-m, 2), minmax(0, 1fr));
      grid-auto-rows: calc(var(--ne-bento-row-h, 220px) * 0.85);
    }
  }

  /* Section-level effects */
  .ne-bento.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-bento.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-bento.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
/* END_SECTION:bento-grid */

/* START_SECTION:bento-hero (INDEX:5) */
.ne-bhero.ne-section {
    position: relative;
    overflow: visible; /* let cell glow/shadow escape (Rule 206) */
  }

  /* Background + overlay clipped to the section radius via their own wrap */
  .ne-bhero__bgwrap {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    overflow: clip;
    z-index: 0;
  }
  .ne-bhero__bg-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-bhero__overlay { position: absolute; inset: 0; pointer-events: none; }

  .ne-bhero__grid {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: repeat(var(--ne-bh-cols-d, 6), minmax(0, 1fr));
    grid-auto-rows: var(--ne-bh-row-h, 200px);
    gap: var(--ne-bh-gap, 12px);
    width: 100%;
  }
  /* Fill-viewport: grid is a DEFINITE height so the rows share it as true equal
     1fr fractions. A min-height (indefinite) grid resolves `1fr` to each track's
     max-content instead — so a width-proportional child (e.g. a Shape block with
     aspect-ratio:1/1) forces the row taller and the whole hero grows past the
     viewport as the window widens. Definite height + minmax(0,1fr) locks it to
     <vh>; oversized cell content is clipped by the cell, not by growing the grid. */
  .ne-bhero--fill .ne-bhero__grid {
    height: var(--ne-bh-vh, 80vh);
    grid-auto-rows: minmax(0, 1fr);
  }

  .ne-bhero__grid > .shopify-block { display: contents; }

  @media (max-width: 749px) {
    .ne-bhero__grid {
      grid-template-columns: repeat(var(--ne-bh-cols-m, 2), minmax(0, 1fr));
      grid-auto-rows: calc(var(--ne-bh-row-h, 200px) * 0.85);
    }
    /* Mobile: back to growable/scrollable (a stacked hero should not be
       viewport-locked) — restore min-height + content-driven rows. */
    .ne-bhero--fill .ne-bhero__grid {
      height: auto;
      min-height: var(--ne-bh-vh, 80vh);
      grid-auto-rows: minmax(calc(var(--ne-bh-row-h, 200px) * 0.85), 1fr);
    }
  }

  /* Section-level effects */
  .ne-bhero.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-bhero.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-bhero.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
/* END_SECTION:bento-hero */

/* START_SECTION:blog (INDEX:6) */
/* Top block-zone */
  .ne-blog__blocks-top {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    margin-bottom: var(--ne-space-6, 24px);
  }
  .ne-blog__blocks-top > .shopify-block { display: contents; }

  /* Header */
  .ne-blog__header {
    margin-bottom: var(--ne-space-8, 32px);
  }
  .ne-blog__title {
    margin: 0;
    color: var(--ne-blog-heading-color, inherit);
  }
  .ne-blog--align-left   .ne-blog__header { text-align: left; }
  .ne-blog--align-center .ne-blog__header { text-align: center; }
  .ne-blog--align-right  .ne-blog__header { text-align: right; }

  /* Tag filter */
  .ne-blog__tags {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ne-space-2, 8px);
    margin-top: var(--ne-space-4, 16px);
  }
  .ne-blog--align-center .ne-blog__tags { justify-content: center; }
  .ne-blog--align-right  .ne-blog__tags { justify-content: flex-end; }

  .ne-blog__tag {
    padding: 4px 12px;
    border: 1px solid var(--ne-scheme-border);
    border-radius: 999px;
    font-size: 12px;
    text-decoration: none;
    color: var(--ne-scheme-text-muted);
    transition: border-color 0.15s ease, color 0.15s ease, background-color 0.15s ease;
  }
  .ne-blog__tag:hover,
  .ne-blog__tag--active {
    border-color: var(--ne-scheme-accent);
    color: var(--ne-scheme-accent);
  }

  /* Article grid */
  .ne-blog__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-blog-columns, 3), minmax(0, 1fr));
    gap: var(--ne-space-6, 24px);
  }

  /* Article card — base */
  .ne-blog__card {
    position: relative;
    display: flex;
    flex-direction: column;
    border-radius: var(--ne-blog-card-radius, 8px);
    overflow: hidden;
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s ease, border-color 0.2s ease, background-color 0.2s ease;
    padding: var(--ne-blog-card-padding, 0);
  }
  /* Surface: solid (default) — opaque card with border */
  .ne-blog--card-solid .ne-blog__card {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }
  /* Surface: transparent — no card chrome, content sits on section bg */
  .ne-blog--card-transparent .ne-blog__card {
    background: transparent;
    border: 1px solid transparent;
  }
  /* Surface: glass — frosted backdrop blur */
  .ne-blog--card-glass .ne-blog__card {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    /* No border: on dark page-bg the 1px white glass-border reads as a
       bottom contour line (same root cause + fix as product-card glass,
       user 2026-05-24). Backdrop blur + bg-tint define the boundary. */
    border: 0;
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
  }
  /* Glass with accent tint */
  .ne-blog--card-tint .ne-blog__card {
    background: color-mix(in srgb, var(--ne-scheme-accent) 6%, var(--ne-glass-bg, rgba(255,255,255,0.06)));
    border-color: color-mix(in srgb, var(--ne-scheme-accent) 18%, var(--ne-glass-border, rgba(255,255,255,0.12)));
  }

  .ne-blog__card:hover {
    transform: translateY(-3px);
  }

  /* Card glow (gated by section setting) */
  .ne-blog--card-glow .ne-blog__card:hover {
    box-shadow:
      0 6px 18px rgba(0,0,0,0.18),
      0 0 24px color-mix(in srgb, var(--ne-scheme-accent) 28%, transparent);
  }
  /* Card shadow (gated by section setting) */
  .ne-blog--card-shadow .ne-blog__card {
    box-shadow: 0 2px 8px rgba(0,0,0,0.12);
  }
  .ne-blog--card-shadow .ne-blog__card:hover {
    box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  }

  /* Card media */
  .ne-blog__card-media {
    display: block;
    overflow: hidden;
    border-radius: var(--ne-blog-image-radius, 0);
  }
  .ne-blog__card[style*="--ne-blog-card-padding: 0"] .ne-blog__card-media,
  .ne-blog__card-media {
    /* When no card padding, image extends to card edges except radius */
  }
  .ne-blog__card-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.6s cubic-bezier(0.2, 0, 0.2, 1);
  }
  .ne-blog__card:hover .ne-blog__card-image { transform: scale(1.04); }

  /* Card body */
  .ne-blog__card-body {
    padding: var(--ne-space-4, 16px);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2, 8px);
    flex: 1;
  }

  .ne-blog__card-meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ne-space-2, 8px);
  }
  .ne-blog__card-meta time::after { content: '·'; padding-left: 8px; opacity: 0.5; }
  .ne-blog__card-meta time:last-child::after { content: ''; }

  .ne-blog__card-title {
    margin: 0;
    font-size: var(--ne-font-size-lg, 18px);
    line-height: 1.3;
  }
  .ne-blog__card-title a {
    text-decoration: none;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    transition: color 0.15s ease;
  }
  .ne-blog__card-title a:hover { color: var(--ne-scheme-accent); }

  .ne-blog__card-excerpt {
    margin: 0;
    line-height: 1.5;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .ne-blog__read-more {
    color: var(--ne-scheme-accent);
    text-decoration: none;
    margin-top: auto;
    align-self: flex-start;
    transition: gap 0.2s ease;
  }
  .ne-blog__read-more:hover { text-decoration: underline; text-underline-offset: 3px; }

  /* Empty state */
  .ne-blog__empty {
    text-align: center;
    padding: var(--ne-space-12, 48px) var(--ne-space-4, 16px);
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-blog.ne-blog--custom-mobile-pad {
      padding-top: var(--ne-blog-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-blog-mobile-pad-bottom, 24px) !important;
    }
    .ne-blog__grid {
      grid-template-columns: repeat(min(var(--ne-blog-columns, 3), 2), minmax(0, 1fr));
      gap: var(--ne-space-4, 16px);
    }
    .ne-blog__card-title {
      font-size: 16px;
    }
  }

  @media (max-width: 599px) {
    .ne-blog__grid {
      grid-template-columns: 1fr;
    }
  }
/* END_SECTION:blog */

/* START_SECTION:brand-slider (INDEX:7) */
.ne-brand-slider__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ne-bs-content-gap, 24px);
    width: 100%;
  }
  .ne-brand-slider__inner .shopify-block { display: contents; }
  /* Text-based atom blocks centered inside the section */
  .ne-brand-slider__inner > .ne-block-heading,
  .ne-brand-slider__inner > .ne-block-text { text-align: center; max-width: 65ch; margin-inline: auto; }
  .ne-brand-slider__inner > .ne-block-button-wrap { justify-content: center; }
  /* Sliders span full inner width */
  .ne-brand-slider__inner > .ne-slider { width: 100%; }

  @media (max-width: 749px) {
    .ne-brand-slider.ne-brand-slider--custom-mobile-pad {
      padding-top: var(--ne-brand-slider-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-brand-slider-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:brand-slider */

/* START_SECTION:cart (INDEX:8) */
.ne-cart {
    --ne-cart-summary-min: 320px;
  }

  .ne-cart__heading {
    margin: 0 0 var(--ne-space-6);
  }

  .ne-cart__form { display: contents; }

  /* Shopify wraps each theme-block in <div class="shopify-block-...">.
     The wrapper is needed for grid placement via :has() so we keep it
     as a real box, BUT we defensively reset properties that could
     otherwise form a backdrop-root and starve the glass blocks below
     of a sampling backdrop (per CSS Backdrop Filter spec, isolation /
     filter / transform / opacity<1 / mask / will-change all qualify).
     Customizer block-selection styling can inject some of these. */
  .ne-cart__layout > .shopify-block {
    background: transparent !important;
    isolation: auto;
    filter: none;
    transform: none;
    mix-blend-mode: normal;
    /* Wrapper still needs to be a positioned-context for the :has()
       grid rules — `position: relative` is fine, doesn't form
       backdrop-root unless paired with z-index. */
    position: relative;
  }

  .ne-cart__layout {
    display: grid;
    gap: var(--ne-space-6) var(--ne-space-8);
    align-items: start;
  }

  /* Layout split presets — desktop only. cart-items + cart-summary occupy
     the two columns; everything else (cart-promo, heading, image, etc.)
     spans the full width. Mobile collapses to single column. */
  .ne-cart--layout-split-50-50 .ne-cart__layout { grid-template-columns: 1fr 1fr; }
  .ne-cart--layout-split-60-40 .ne-cart__layout { grid-template-columns: 1.5fr 1fr; }
  .ne-cart--layout-split-70-30 .ne-cart__layout { grid-template-columns: 7fr 3fr; }
  .ne-cart--layout-stacked .ne-cart__layout { grid-template-columns: 1fr; }

  /* Position-driven grid placement.
     - cart-promo: full-width, row 1
     - cart-items: column 1, row 2 (track height = items height)
     - cart-summary: column 2, row 2 + self-aligned to start
       (sits at top of the row's track, sticky works because the row
       is as tall as the items column)
     - other blocks: full-width, in document order */
  .ne-cart__layout > * { grid-column: 1 / -1; }
  .ne-cart__layout > *:has(.ne-cart-items) { grid-column: 1; grid-row: 2; }
  .ne-cart__layout > *:has(.ne-cart-summary) {
    grid-column: 2;
    grid-row: 2;
    /* Wrapper stretches to match items-column height (grid-row default) so
       position:sticky inside has scroll-room. align-self:stretch is the
       Grid default, but we set explicit for clarity. */
    align-self: stretch;
  }
  .ne-cart__layout > *:has(.ne-cart-promo) { grid-column: 1 / -1; grid-row: 1; }
  .ne-cart--layout-stacked .ne-cart__layout > * { grid-column: 1 / -1 !important; grid-row: auto !important; height: auto !important; }

  /* Empty state */
  .ne-cart__empty {
    text-align: center;
    padding: var(--ne-space-16) var(--ne-space-4);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ne-space-4);
  }
  .ne-cart__empty-image {
    max-width: 200px;
    height: auto;
  }
  .ne-cart__empty-message {
    color: var(--ne-scheme-text-muted);
    font-size: var(--ne-font-size-base, 1rem);
  }

  @media (max-width: 989px) {
    .ne-cart__layout {
      display: flex !important;
      flex-direction: column !important;
      grid-template-columns: 1fr !important;
    }
    /* Reset position-driven placement on small screens so blocks flow
       in document order (promo → items → summary) in a single column.
       Switching the layout to flex (instead of grid with !important
       overrides) makes the children naturally size to content height,
       avoiding the position:sticky + grid-row interaction that was
       collapsing the items wrapper on mobile. */
    .ne-cart__layout > * {
      grid-column: auto !important;
      grid-row: auto !important;
      align-self: auto !important;
      height: auto !important;
      width: 100%;
    }
  }

  @media (max-width: 749px) {
    .ne-cart {
      padding-top: var(--ne-section-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-section-mobile-pad-bottom, 24px) !important;
      padding-left: var(--ne-section-mobile-pad-left, 16px) !important;
      padding-right: var(--ne-section-mobile-pad-right, 16px) !important;
    }
    .ne-cart__heading {
      font-size: var(--ne-font-size-xl, 1.5rem);
      margin-bottom: var(--ne-space-4);
    }
    .ne-cart__layout { gap: var(--ne-space-4); }

    /* When the summary is body-mounted as a sticky bar, the last item
       would otherwise sit hidden behind it. JS toggles
       .ne-cart--has-sticky-bar on the section while the body-mounted
       state is active; once the IntersectionObserver restores the
       summary to flow at the section bottom, the class drops and the
       extra padding goes away (so the footer sits flush). */
    .ne-cart.ne-cart--has-sticky-bar {
      padding-bottom: calc(var(--ne-section-mobile-pad-bottom, 24px) + 220px) !important;
    }
  }
/* END_SECTION:cart */

/* START_SECTION:collapsible-content (INDEX:9) */
.ne-collapsible__inner {
    width: 100%;
    max-width: var(--ne-collapsible-max, 800px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-collapsible-gap, 8px);
  }
  /* Theme-block wrapper collapse (Rule 70). */
  .ne-collapsible__inner > .shopify-block { display: contents; }

  @media (max-width: 749px) {
    .ne-collapsible.ne-collapsible--custom-mobile-pad {
      padding-top: var(--ne-collapsible-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-collapsible-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:collapsible-content */

/* START_SECTION:collection-list (INDEX:10) */
.ne-cl__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-cl-content-gap, 24px);
    width: 100%;
  }
  /* Section-level alignment — drives flex cross-axis alignment of
     atom-block wrappers AND inherited vars that atom blocks read
     (Rule 126). Child's own modifier still wins at element scope. */
  .ne-cl--align-left   .ne-cl__inner { align-items: flex-start; --ne-tp--align: left;   --ne-bw-justify: flex-start; }
  .ne-cl--align-center .ne-cl__inner { align-items: center;     --ne-tp--align: center; --ne-bw-justify: center; }
  .ne-cl--align-right  .ne-cl__inner { align-items: flex-end;   --ne-tp--align: right;  --ne-bw-justify: flex-end; }

  /* Theme-block wrapper collapses (Rule 70). */
  .ne-cl__inner > .shopify-block { display: contents; }

  /* Collection grid spans full inner width regardless of align-items. */
  .ne-cl__inner > .ne-cg { width: 100%; align-self: stretch; }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-cl.ne-cl--custom-mobile-pad {
      padding-top: var(--ne-cl-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-cl-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:collection-list */

/* START_SECTION:collection-switcher (INDEX:11) */
.ne-cs__inner { width: 100%; display: flex; flex-direction: column; gap: 28px; }

  /* ── Switcher header ─────────────────────────────────────────────── */
  .ne-cs__eyebrow {
    text-align: center;
    text-transform: uppercase;
    letter-spacing: 0.22em;
    font-size: 0.72rem;
    opacity: 0.7;
    margin: 0;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  .ne-cs__nav {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: 8px;
  }
  /* When arrows are hidden the grid collapses to a single centred column. */
  .ne-cs__nav:not(:has(.ne-cs__arrow)) { grid-template-columns: minmax(0, 1fr); }

  .ne-cs__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    background: transparent;
    border: 0;
    color: var(--ne-scheme-text, currentColor);
    cursor: pointer;
    border-radius: 999px;
    transition: color .15s ease, transform .15s ease, opacity .15s ease;
  }
  .ne-cs__arrow:hover { color: var(--ne-scheme-accent, currentColor); transform: scale(1.08); }
  /* No transform reset here — disabled buttons don't receive :hover, so the
     hover-scale never applies; resetting transform would clobber the
     translateY(-50%) centering of the in-box arrows (disabled prev arrow
     dropped below the active arrow — user QA 2026-06-12). */
  .ne-cs__arrow:disabled { opacity: 0.25; cursor: default; }

  /* ── Coverflow style ─────────────────────────────────────────────── */
  .ne-cs__labels-viewport {
    overflow: hidden;
    /* Soft-fade the faded neighbours at the edges (HBO look) instead of
       a hard clip. Labels are plain text — no glass descendants — so the
       mask creates no backdrop-root issue (Rule 49 N/A here). */
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 13%, #000 87%, transparent 100%);
  }
  .ne-cs__labels {
    display: flex;
    align-items: center;
    gap: clamp(28px, 5vw, 64px);
    position: relative;
    width: max-content;
    /* No margin:auto — the track must start at the viewport's left edge so
       the JS translateX centering (which measures offsetLeft relative to
       the track = relative to the viewport) lands the active label dead
       centre. margin:auto added a second offset that knocked it off on
       desktop when the track was narrower than the viewport. */
    padding: 6px 0;
    transition: transform .55s cubic-bezier(.22, 1, .36, 1);
  }
  .ne-cs__label {
    all: unset;
    box-sizing: border-box;
    white-space: nowrap;
    cursor: pointer;
    font-family: var(--font-heading--family, inherit);
    font-weight: 600;
    font-size: clamp(1.4rem, 4.2vw, 2.6rem);
    line-height: 1.1;
    color: var(--ne-scheme-text, currentColor);
    opacity: 0.32;
    transform: scale(0.66);
    transform-origin: center;
    transition: opacity .4s ease, transform .45s cubic-bezier(.22,1,.36,1), color .4s ease;
  }
  .ne-cs__label:focus-visible { outline: 2px solid var(--ne-scheme-accent); outline-offset: 4px; border-radius: 6px; }
  .ne-cs__label.is-adjacent { opacity: 0.5; transform: scale(0.78); }
  .ne-cs__label.is-active {
    opacity: 1;
    transform: scale(1);
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  /* ── Glass-box style ─────────────────────────────────────────────── */
  .ne-cs__box {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 84px;
    /* Inline padding clears the in-box arrows so the title never overlaps. */
    padding: 18px 56px;
    border-radius: var(--ne-cs-box-radius, 18px);
    margin: 0 auto;
    width: min(100%, 560px);
  }
  .ne-cs__box-stack { display: grid; width: 100%; }
  .ne-cs__box-item {
    all: unset;
    grid-area: 1 / 1;
    box-sizing: border-box;
    text-align: center;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity .4s ease;
  }
  .ne-cs__box-item.is-active { opacity: 1; visibility: visible; }
  .ne-cs__box-title {
    display: block;
    font-family: var(--font-heading--family, inherit);
    font-weight: 600;
    font-size: clamp(1.3rem, 3.6vw, 2.2rem);
    line-height: 1.15;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }
  .ne-cs__box-meta {
    display: block;
    margin-top: 4px;
    font-size: 0.8rem;
    letter-spacing: 0.04em;
    opacity: 0.7;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  /* Glass-box nav holds a single child (the box — arrows live inside it),
     so collapse the 3-column arrow grid to one centred column. */
  .ne-cs--style-glass-box .ne-cs__nav { grid-template-columns: minmax(0, 1fr); }
  /* Overlap the grid: box sits over the top edge of the panels. */
  .ne-cs--style-glass-box .ne-cs__switcher { position: relative; z-index: 2; margin-bottom: -26px; }
  .ne-cs--style-glass-box .ne-cs__panels { padding-top: 42px; }

  /* In-box arrows (glass-box) — overlaid at the box's left/right edges. */
  .ne-cs__arrow--in-box {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
  }
  .ne-cs__arrow--in-box.ne-cs__arrow--prev { left: 10px; }
  .ne-cs__arrow--in-box.ne-cs__arrow--next { right: 10px; }
  .ne-cs__arrow--in-box:hover { transform: translateY(-50%) scale(1.08); }

  /* Glass-box glow/shadow — same accent-glow + shadow tokens as the theme's
     section/card effects (scales with the global intensity sliders), so the
     box reads as a first-class glass surface, not a flat panel. .ne-glass
     sets no box-shadow itself, so these compose cleanly. */
  .ne-cs__box--glow {
    box-shadow: 0 0 calc(32px * var(--ne-section-glow-intensity, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3));
  }
  .ne-cs__box--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-cs__box--glow.ne-cs__box--shadow {
    box-shadow:
      0 0 calc(28px * var(--ne-section-glow-intensity, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)),
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  /* The section clips by default (rounded-corner protection); let the box's
     own glow/shadow bleed past the section edge (Rule 206/208). */
  .ne-cs:has(.ne-cs__box--glow),
  .ne-cs:has(.ne-cs__box--shadow) { overflow: visible; }

  /* ── Panels + grid (mirrors product-grid subgrid CSS) ───────────────── */
  .ne-cs__panels { position: relative; }
  .ne-cs__view-all { display: flex; justify-content: center; margin-top: var(--ne-space-5, 24px); }
  .ne-cs__panel[hidden] { display: none; }
  .ne-cs__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-cs-cols, 4), minmax(0, 1fr));
    grid-template-rows: var(--ne-cs-rows-tpl, auto 1fr auto auto auto);
    grid-auto-rows: var(--ne-cs-rows-tpl, auto 1fr auto auto auto);
    align-items: stretch;
    row-gap: var(--ne-cs-gap, 16px);
    column-gap: var(--ne-cs-gap, 16px);
    /* Scheme class only supplies vars — never paints the grid bg
       (Rule 173). Cards float on the section background. */
    background: transparent !important;
  }
  /* Card spans its own subgrid rows — literal integer required
     (Rule 129). Card declares grid-template-rows: subgrid itself. */
  .ne-cs__grid--rows-3 > .ne-product-card { grid-row: span 3; }
  .ne-cs__grid--rows-4 > .ne-product-card { grid-row: span 4; }
  .ne-cs__grid--rows-5 > .ne-product-card { grid-row: span 5; }

  @media (max-width: 989px) {
    .ne-cs__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  }
  @media (max-width: 749px) {
    .ne-cs__grid {
      grid-template-columns: repeat(var(--ne-cs-cols-mobile, 2), minmax(0, 1fr));
      column-gap: calc(var(--ne-cs-gap, 16px) * 0.75);
      row-gap: var(--ne-cs-gap, 16px);
    }
    .ne-cs__label { font-size: clamp(1.2rem, 7vw, 2rem); }
    .ne-cs__arrow { width: 38px; height: 38px; }
    .ne-cs.ne-cs--custom-mobile-pad {
      padding-top: var(--ne-cs-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-cs-mobile-pad-bottom, 24px) !important;
    }
  }

  .ne-cs__empty {
    text-align: center;
    padding: 48px 16px;
    opacity: 0.65;
    border: 1px dashed var(--ne-scheme-border, rgba(127,127,127,0.4));
    border-radius: 12px;
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-cs__labels, .ne-cs__label, .ne-cs__box-item, .ne-cs__arrow { transition: none; }
  }
/* END_SECTION:collection-switcher */

/* START_SECTION:collection (INDEX:12) */
/* Top block-zone — composed of theme + app blocks */
  .ne-collection__blocks-top {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    margin-bottom: var(--ne-space-6, 24px);
  }
  .ne-collection__blocks-top > .shopify-block { display: contents; }

  /* Toolbar */
  .ne-collection__toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--ne-space-4, 16px);
    padding-bottom: var(--ne-space-4, 16px);
    border-bottom: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
    margin-bottom: var(--ne-space-6, 24px);
    flex-wrap: wrap;
  }
  .ne-collection__toolbar-left,
  .ne-collection__toolbar-right {
    display: flex;
    align-items: center;
    gap: var(--ne-space-3, 12px);
  }

  .ne-collection__count { margin: 0; }

  .ne-collection__filter-toggle {
    display: none;
    align-items: center;
    gap: var(--ne-space-2, 8px);
  }
  .ne-collection__filter-toggle-count {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-radius: 999px;
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
  }

  /* Sort dropdown — native select with custom chevron */
  .ne-collection__sort {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-3, 12px);
  }
  .ne-collection__sort-label {
    font-size: var(--ne-font-size-sm, 14px);
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.85;
    white-space: nowrap;
  }
  .ne-collection__sort-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 8px 32px 8px 14px;
    background: transparent;
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.15));
    border-radius: var(--ne-btn-radius, 8px);
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-font-size-sm, 14px);
    cursor: pointer;
    transition: border-color 0.15s ease;
    /* Tells the browser the form control supports both light + dark UI
       so OS-rendered chrome auto-matches the resolved background colour. */
    color-scheme: light dark;
    /* Highlight colour for the currently-selected <option> inside the
       OS-rendered panel (Chrome/Edge, partial Firefox; ignored Safari). */
    accent-color: var(--ne-scheme-accent, currentColor);
  }
  .ne-collection__sort-select:hover { border-color: var(--ne-scheme-accent); }
  .ne-collection__sort-select:focus { outline: none; border-color: var(--ne-scheme-accent); }
  /* OS-rendered <option> dropdown panel uses scheme bg + text (without this
     the panel falls back to OS default white + black, unreadable on dark
     schemes). All modern browsers respect option-level bg styling. */
  .ne-collection__sort-select option {
    background-color: var(--ne-scheme-bg, #fff);
    color: var(--ne-scheme-text, #000);
  }
  .ne-collection__sort-chevron {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    opacity: 0.6;
  }

  /* Layout grid: filters + products */
  .ne-collection__layout {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: var(--ne-space-8, 32px);
  }
  .ne-collection:not(.ne-collection--has-filters) .ne-collection__layout {
    grid-template-columns: 1fr;
  }

  /* Filters sidebar — desktop sticky */
  .ne-collection__filters {
    align-self: start;
    position: sticky;
    top: calc(var(--ne-header-height, 80px) + 24px);
    max-height: calc(100vh - var(--ne-header-height, 80px) - 48px);
    overflow-y: auto;
    padding-right: var(--ne-space-2, 8px);
  }

  .ne-collection__filters-header {
    display: none;
    justify-content: space-between;
    align-items: center;
    padding-bottom: var(--ne-space-3, 12px);
    margin-bottom: var(--ne-space-3, 12px);
    border-bottom: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }
  .ne-collection__filters-title {
    font-size: var(--ne-font-size-lg, 16px);
    font-weight: 600;
  }
  .ne-collection__filters-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    background: transparent;
    border: none;
    color: inherit;
    cursor: pointer;
    border-radius: 50%;
    transition: background-color 0.15s ease;
  }
  .ne-collection__filters-close:hover { background: rgba(255,255,255,0.06); }

  .ne-collection__filter-backdrop {
    display: none;
  }

  /* Product grid — subgrid pattern (Rule 163) for cross-card row
     alignment (media / title / price / swatches / cart all align
     to the same vertical positions across the row regardless of
     title length). Cards opt into subgrid via the [data-product-grid]
     selector in product-card.liquid. */
  .ne-collection__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-col-columns, 3), minmax(0, 1fr));
    grid-template-rows: auto 1fr auto auto auto;
    grid-auto-rows: auto 1fr auto auto auto;
    column-gap: var(--ne-space-4, 16px);
    row-gap: 0;
    align-items: stretch;
  }
  /* Custom card scheme: the grid carries color-{scheme} so the cards inherit
     that scheme's vars — but suppress the grid's own bg paint (Rule 173) so
     the section background + grid gaps keep the SECTION scheme; only the cards
     switch. */
  .ne-collection__grid[class*="color-"] {
    background: transparent !important;
  }
  .ne-collection__product-item {
    display: contents;
  }
  /* Card spans 5 rows of parent grid for subgrid sync */
  .ne-collection__grid .ne-product-card {
    grid-row: span 5;
    margin-bottom: var(--ne-space-4, 16px);
  }

  /* Empty state */
  .ne-collection__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ne-space-3, 12px);
    text-align: center;
    padding: var(--ne-space-12, 48px) var(--ne-space-4, 16px);
  }

  /* ─────────────────────────────────────────────────────────────────
     Tablet (≤989px): filter sidebar collapses to drawer
     ───────────────────────────────────────────────────────────────── */
  @media (max-width: 989px) {
    .ne-collection__layout {
      grid-template-columns: 1fr;
    }
    .ne-collection__filter-toggle { display: inline-flex; }

    .ne-collection__filters {
      position: fixed;
      inset: 0 auto 0 0;
      width: min(360px, 92vw);
      z-index: 1001;
      background: var(--ne-scheme-bg, #111);
      box-shadow: 0 0 40px rgba(0,0,0,0.4);
      padding: var(--ne-space-4, 16px) var(--ne-space-5, 20px);
      max-height: 100vh;
      overflow-y: auto;
      transform: translateX(-100%);
      transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      visibility: hidden;
    }
    .ne-collection__filters[data-open="1"] {
      transform: translateX(0);
      visibility: visible;
    }
    .ne-collection__filters-header { display: flex; }

    .ne-collection__filter-backdrop {
      display: block;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.55);
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
      z-index: 1000;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.25s ease, visibility 0s linear 0.25s;
    }
    .ne-collection__filter-backdrop[data-open="1"] {
      opacity: 1;
      visibility: visible;
      transition: opacity 0.25s ease, visibility 0s linear 0s;
    }

    .ne-collection__grid {
      grid-template-columns: repeat(min(var(--ne-col-columns, 3), 3), 1fr);
    }
  }

  /* ─────────────────────────────────────────────────────────────────
     Mobile (≤749px): 2-col grid
     ───────────────────────────────────────────────────────────────── */
  @media (max-width: 749px) {
    .ne-collection__grid {
      grid-template-columns: repeat(2, 1fr);
      gap: var(--ne-space-3, 12px);
    }
    .ne-collection__toolbar {
      gap: var(--ne-space-2, 8px);
    }
    .ne-collection__sort-select {
      font-size: 13px;
      padding: 6px 28px 6px 12px;
    }
  }
/* END_SECTION:collection */

/* START_SECTION:collections (INDEX:13) */
/* Top block-zone */
  .ne-cols__blocks-top {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    margin-bottom: var(--ne-space-6, 24px);
  }
  .ne-cols__blocks-top > .shopify-block { display: contents; }

  /* Header */
  .ne-cols__header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--ne-space-3, 12px);
    margin-bottom: var(--ne-space-6, 24px);
    flex-wrap: wrap;
  }
  .ne-cols__title { margin: 0; }
  .ne-cols__count { margin: 0; }
  .ne-cols--align-left   .ne-cols__header { justify-content: flex-start; text-align: left; }
  .ne-cols--align-center .ne-cols__header { justify-content: center; text-align: center; flex-direction: column; align-items: center; }
  .ne-cols--align-right  .ne-cols__header { justify-content: flex-end; text-align: right; }

  /* Grid */
  .ne-cols__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-cols-columns, 3), minmax(0, 1fr));
    gap: var(--ne-space-5, 20px);
  }

  /* Card — base */
  .ne-cols__card {
    position: relative;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    border-radius: var(--ne-cols-card-radius, 12px);
    overflow: hidden;
    transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.25s ease, border-color 0.2s ease;
    padding: var(--ne-cols-card-padding, 0);
  }

  /* Surfaces */
  .ne-cols--card-solid .ne-cols__card {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }
  .ne-cols--card-transparent .ne-cols__card {
    background: transparent;
    border: 1px solid transparent;
  }
  .ne-cols--card-glass .ne-cols__card {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    /* No border: on dark page-bg the 1px white glass-border reads as a
       bottom contour line (same root cause + fix as product-card glass,
       user 2026-05-24). Backdrop blur + bg-tint define the boundary. */
    border: 0;
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
  }
  .ne-cols--card-tint .ne-cols__card {
    background: color-mix(in srgb, var(--ne-scheme-accent) 6%, var(--ne-glass-bg, rgba(255,255,255,0.06)));
    border-color: color-mix(in srgb, var(--ne-scheme-accent) 18%, var(--ne-glass-border, rgba(255,255,255,0.12)));
  }

  .ne-cols__card:hover { transform: translateY(-3px); }

  /* Hide-card-border (User-opt-in): drop border on solid+transparent cards */
  .ne-cols--no-border.ne-cols--card-solid .ne-cols__card,
  .ne-cols--no-border.ne-cols--card-transparent .ne-cols__card,
  .ne-cols--no-border.ne-cols--card-glass .ne-cols__card {
    border-color: transparent;
  }

  /* Card glow / shadow (gated by section setting) */
  .ne-cols--card-glow .ne-cols__card:hover {
    box-shadow:
      0 6px 18px rgba(0,0,0,0.18),
      0 0 24px color-mix(in srgb, var(--ne-scheme-accent) 28%, transparent);
  }
  .ne-cols--card-shadow .ne-cols__card {
    box-shadow: 0 2px 8px rgba(0,0,0,0.12);
  }
  .ne-cols--card-shadow .ne-cols__card:hover {
    box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  }

  /* Card media */
  .ne-cols__card-media {
    position: relative;
    overflow: hidden;
    border-radius: var(--ne-cols-image-radius, 0);
  }
  /* Title-overlay mode: card collapses to media-only, body absolute over image */
  .ne-cols--title-overlay .ne-cols__card-media {
    border-radius: var(--ne-cols-card-radius, 12px);
  }
  .ne-cols__card-gradient {
    position: absolute;
    inset: 0;
    background: linear-gradient(
      to top,
      rgba(0, 0, 0, var(--ne-cols-overlay-alpha, 0.55)) 0%,
      rgba(0, 0, 0, calc(var(--ne-cols-overlay-alpha, 0.55) * 0.5)) 40%,
      transparent 75%
    );
    pointer-events: none;
    z-index: 1;
    border-radius: inherit;
  }
  .ne-cols__card-body--overlay {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--ne-space-4, 16px);
    z-index: 2;
    color: #fff;
    text-shadow: 0 1px 2px rgba(0,0,0,0.4);
  }
  .ne-cols__card-body--overlay .ne-cols__card-title { color: #fff; }
  .ne-cols__card-body--overlay .ne-cols__card-description,
  .ne-cols__card-body--overlay .ne-cols__card-count {
    color: rgba(255,255,255,0.85);
  }
  .ne-cols__card-media--square    { aspect-ratio: 1 / 1; }
  .ne-cols__card-media--portrait  { aspect-ratio: 3 / 4; }
  .ne-cols__card-media--landscape { aspect-ratio: 4 / 3; }
  .ne-cols__card-media--16-9      { aspect-ratio: 16 / 9; }
  .ne-cols__card-media--natural   { aspect-ratio: auto; }

  .ne-cols__card-image,
  .ne-cols__card-placeholder {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    display: block;
    transition: transform 0.6s cubic-bezier(0.2, 0, 0.2, 1);
  }
  /* Natural ratio has no aspect-ratio on the wrapper, so the image must stay
     in-flow to define the box height (absolute would collapse it to 0). */
  .ne-cols__card-media--natural .ne-cols__card-image,
  .ne-cols__card-media--natural .ne-cols__card-placeholder {
    position: relative;
    height: auto;
  }
  .ne-cols__card-placeholder svg {
    width: 100%; height: 100%;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.4;
  }
  .ne-cols__card:hover .ne-cols__card-image { transform: scale(1.04); }

  /* Card body */
  .ne-cols__card-body {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2, 8px);
    padding: var(--ne-space-4, 16px);
    flex: 1;
  }
  .ne-cols__card-title {
    margin: 0;
    font-size: var(--ne-font-size-lg, 18px);
    font-weight: 600;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    transition: color 0.15s ease;
  }
  .ne-cols__card:hover .ne-cols__card-title { color: var(--ne-scheme-accent); }
  .ne-cols__card-description {
    margin: 0;
    line-height: 1.5;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .ne-cols__card-count { margin: 0; }

  /* Empty state */
  .ne-cols__empty {
    text-align: center;
    padding: var(--ne-space-12, 48px) var(--ne-space-4, 16px);
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 989px) {
    .ne-cols__grid {
      grid-template-columns: repeat(min(var(--ne-cols-columns, 3), 2), minmax(0, 1fr));
      gap: var(--ne-space-4, 16px);
    }
  }
  @media (max-width: 599px) {
    .ne-cols__grid { grid-template-columns: 1fr; }
  }
  @media (max-width: 749px) {
    .ne-cols.ne-cols--custom-mobile-pad {
      padding-top: var(--ne-cols-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-cols-mobile-pad-bottom, 24px) !important;
    }
    .ne-cols__card-title { font-size: 16px; }
  }
/* END_SECTION:collections */

/* START_SECTION:complementary-products (INDEX:14) */
.ne-rp__inner {
    width: 100%;
    max-width: var(--ne-rp-max, 1400px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-rp-gap, 32px);
  }
  .ne-rp__inner > .shopify-block { display: contents; }

  .ne-rp--align-left .ne-rp__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-rp--align-center .ne-rp__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-rp--align-right .ne-rp__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  @media (max-width: 749px) {
    .ne-rp.ne-rp--custom-mobile-pad {
      padding-top: var(--ne-rp-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-rp-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:complementary-products */

/* START_SECTION:contact-form (INDEX:15) */
.ne-contact__inner {
    width: 100%;
    max-width: var(--ne-contact-max, 720px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-contact-gap, 24px);
  }
  .ne-contact__inner > .shopify-block { display: contents; }

  .ne-contact--align-left .ne-contact__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-contact--align-center .ne-contact__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-contact--align-right .ne-contact__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  @media (max-width: 749px) {
    .ne-contact.ne-contact--custom-mobile-pad {
      padding-top: var(--ne-contact-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-contact-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:contact-form */

/* START_SECTION:countdown (INDEX:16) */
.ne-countdown__inner {
    width: 100%;
    max-width: var(--ne-countdown-max, 800px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-countdown-gap, 24px);
  }
  .ne-countdown__inner > .shopify-block { display: contents; }

  /* Alignment — sets flex align-items for atom-blocks AND
     --ne-bw-justify (button-wrap reads this CSS-var to align the
     button inside its full-width wrapper) AND --ne-tp--align (text-
     preset reads for text-align). */
  .ne-countdown--align-left .ne-countdown__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
  }
  .ne-countdown--align-center .ne-countdown__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
  }
  .ne-countdown--align-right .ne-countdown__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
  }

  @media (max-width: 749px) {
    .ne-countdown.ne-countdown--custom-mobile-pad {
      padding-top: var(--ne-countdown-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-countdown-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:countdown */

/* START_SECTION:cursor-fx (INDEX:17) */
/* Reveal strength (0 = no cone, 1 = full). Registered so it can be
     transitioned — mask-image itself can't animate, but the var driving its
     alpha can. Lets the spotlight fade in/out instead of snapping (e.g. when
     the cursor leaves the window). */
  @property --fx-on {
    syntax: "<number>";
    inherits: true;
    initial-value: 0;
  }

  .ne-fx.ne-section {
    position: relative;
    overflow: clip;
    display: grid;
    place-items: center;
    isolation: isolate;
  }

  .ne-fx__bg { position: absolute; inset: 0; z-index: 0; }
  .ne-fx__bg-img { width: 100%; height: 100%; object-fit: cover; display: block; }

  /* Overlay image — top "cover" layer (e.g. logo / panel) that the
     spotlight cuts a hole into, revealing the background image beneath. */
  /* Solid placeholder so the overlay layer covers the background from the first
     frame — even before its image decodes — otherwise the (revealed) background
     flashes through while the overlay is still loading (user QA 2026-06-11). */
  .ne-fx__overlay-img { position: absolute; inset: 0; z-index: 1; pointer-events: none; background-color: var(--ne-scheme-bg, #0a0a0f); }
  .ne-fx__overlay-img-el { width: 100%; height: 100%; object-fit: var(--ne-fx-ov-fit, cover); display: block; }

  .ne-fx__overlay {
    position: absolute; inset: 0; z-index: 2; pointer-events: none;
    background: var(--ne-fx-ov-color, #000);
    opacity: var(--ne-fx-ov-alpha, 0);
  }

  /* Cursor-follow effect layer — between bg and content */
  .ne-fx__spot {
    position: absolute; inset: 0; z-index: 3; pointer-events: none;
    opacity: 0;
    transition: opacity 0.35s ease;
  }
  .ne-fx.is-cursor-active .ne-fx__spot { opacity: 1; }

  /* spotlight = the overlay (constant darkness = overlay opacity) is LIFTED
     inside a cursor-following light cone. The overlay sets the darkness;
     intensity only sets how far it's lifted in the cone. No darkening on
     hover — only the cone brightens. The spot layer isn't used here. */
  .ne-fx--spotlight .ne-fx__spot { display: none; }
  /* Mask always applied in spotlight mode; --fx-on drives the reveal depth
     (0 = uniform overlay = no cone, 1 = full cone). Transitioning --fx-on
     fades the cone in/out smoothly instead of snapping. */
  .ne-fx--spotlight .ne-fx__overlay,
  .ne-fx--spotlight .ne-fx__overlay-img {
    transition: --fx-on 0.4s ease;
    -webkit-mask-image: radial-gradient(
      circle var(--ne-fx-spot-size, 260px) at var(--fx-mx, 50%) var(--fx-my, 50%),
      rgba(0, 0, 0, calc(1 - var(--ne-fx-spot-int, 0.8) * var(--fx-on, 0))) 0,
      rgba(0, 0, 0, calc(1 - var(--ne-fx-spot-int, 0.8) * var(--fx-on, 0))) calc(100% - 70px),
      #000 100%
    );
    mask-image: radial-gradient(
      circle var(--ne-fx-spot-size, 260px) at var(--fx-mx, 50%) var(--fx-my, 50%),
      rgba(0, 0, 0, calc(1 - var(--ne-fx-spot-int, 0.8) * var(--fx-on, 0))) 0,
      rgba(0, 0, 0, calc(1 - var(--ne-fx-spot-int, 0.8) * var(--fx-on, 0))) calc(100% - 70px),
      #000 100%
    );
  }
  /* glow = accent halo trailing the cursor (additive) */
  .ne-fx--glow .ne-fx__spot {
    background: radial-gradient(
      circle var(--ne-fx-spot-size, 260px) at var(--fx-mx, 50%) var(--fx-my, 50%),
      var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4)) 0%,
      transparent 70%
    );
    mix-blend-mode: screen;
  }
  .ne-fx--none .ne-fx__spot { display: none; }

  .ne-fx__content {
    position: relative;
    z-index: 4;
    width: 100%;
    max-width: var(--ne-content-width, 1200px);
    display: flex;
  }
  .ne-fx__content-inner { max-width: 42rem; }
  .ne-fx--align-left .ne-fx__content { justify-content: flex-start; text-align: left; }
  .ne-fx--align-center .ne-fx__content { justify-content: center; text-align: center; }
  .ne-fx--align-center .ne-fx__content-inner { margin-inline: auto; }
  .ne-fx--align-right .ne-fx__content { justify-content: flex-end; text-align: right; }

  /* Vertical position (combines with horizontal align → 9 positions) */
  .ne-fx--vpos-top .ne-fx__content { align-self: start; }
  .ne-fx--vpos-center .ne-fx__content { align-self: center; }
  .ne-fx--vpos-bottom .ne-fx__content { align-self: end; }

  /* Full-bleed on mobile zeroes the section's inner padding (edge-to-edge bg),
     so restore the content's mobile inner padding on the wrapper itself —
     otherwise the text block ignores the theme's custom mobile padding and runs
     to the screen edge (R358 / user QA 2026-06-13). */
  @media (max-width: 749px) {
    .ne-fx.ne-section--full-width-mobile .ne-fx__content {
      padding-inline: var(--ne-mobile-section-inner-pad-x, 16px);
    }
  }

  /* Optional frame around the content */
  .ne-fx--framed .ne-fx__content-inner {
    border: 1px solid var(--ne-fx-frame-color, var(--ne-scheme-border, rgba(255,255,255,0.2)));
    border-radius: var(--ne-fx-frame-radius, 14px);
    padding: clamp(20px, 4vw, 44px);
  }

  /* Text follows the section's (or custom) colour scheme — no separate
     light/dark toggle (use the scheme / dark-light pair instead). */
  .ne-fx__content { color: var(--ne-scheme-text, #fff); }
  .ne-fx__sub { color: var(--ne-scheme-text-muted, rgba(255, 255, 255, 0.85)); }

  .ne-fx__eyebrow {
    margin: 0 0 12px;
    font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600; opacity: 0.9;
  }
  .ne-fx__title {
    margin: 0 0 16px;
    font-size: var(--ne-h2--size-responsive, clamp(30px, 5vw, 56px));
    line-height: 1.1; text-wrap: balance;
  }
  .ne-fx__sub { font-size: clamp(15px, 1.5vw, 19px); line-height: 1.6; }
  .ne-fx__btns { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 28px; }
  .ne-fx--align-center .ne-fx__btns { justify-content: center; }
  .ne-fx--align-right .ne-fx__btns { justify-content: flex-end; }
  .ne-fx__btn { will-change: transform; transition: transform 0.18s ease-out; }

  /* Section-level effects */
  .ne-fx.ne-section--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-fx.ne-section--glow { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-fx.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* Touch + reduced motion → effects off, plain legible content */
  @media (hover: none), (prefers-reduced-motion: reduce) {
    .ne-fx__spot { display: none !important; }
    .ne-fx__btn { transform: none !important; transition: none; }
  }
/* END_SECTION:cursor-fx */

/* START_SECTION:custom-liquid (INDEX:18) */
.ne-cl__inner {
    width: 100%;
    max-width: var(--ne-cl-max, 1200px);
    margin-inline: auto;
  }
/* END_SECTION:custom-liquid */

/* START_SECTION:custom-section (INDEX:19) */
.ne-cs {
    position: relative;
    isolation: isolate;
  }
  .ne-cs__bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: clip;
    border-radius: inherit;
  }
  .ne-cs__bg-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-cs__bg-overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, var(--ne-cs-overlay, 0));
    pointer-events: none;
  }
  .ne-cs__inner {
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: var(--ne-cs-max, 1200px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-cs-gap, 24px);
  }
  .ne-cs__inner > .shopify-block { display: contents; }

  .ne-cs--align-left .ne-cs__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-cs--align-center .ne-cs__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-cs--align-right .ne-cs__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-cs.ne-cs--custom-mobile-pad {
      padding-top: var(--ne-cs-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-cs-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:custom-section */

/* START_SECTION:customer-login (INDEX:23) */
.ne-customer__recover { margin-top: var(--ne-space-4, 16px); }
  .ne-customer__recover[hidden] { display: none; }
/* END_SECTION:customer-login */

/* START_SECTION:customer-order (INDEX:24) */
.ne-customer-order__cancelled {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    border: 1px solid var(--ne-color-error, #ef4444);
    border-radius: var(--ne-radius-md, 12px);
    padding: var(--ne-space-4, 16px);
    margin-top: var(--ne-space-4, 16px);
  }
  .ne-customer-order__app-blocks {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 12px);
    margin-top: var(--ne-space-4, 16px);
  }
  .ne-customer-order__app-blocks > .shopify-block { display: contents; }
/* END_SECTION:customer-order */

/* START_SECTION:featured-article (INDEX:27) */
.ne-fart__inner {
    max-width: var(--ne-fart-content-width, 800px);
    margin-inline: auto;
    padding-inline: calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-5, 20px);
  }
  .ne-fart__blocks-top {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
  }
  .ne-fart__blocks-top > .shopify-block { display: contents; }

  .ne-fart__title { margin: 0; }
  .ne-fart--align-left   .ne-fart__title,
  .ne-fart--align-left   .ne-fart__meta,
  .ne-fart--align-left   .ne-fart__subheading { text-align: left; justify-content: flex-start; }
  .ne-fart--align-center .ne-fart__title,
  .ne-fart--align-center .ne-fart__meta,
  .ne-fart--align-center .ne-fart__subheading { text-align: center; justify-content: center; }
  .ne-fart--align-right  .ne-fart__title,
  .ne-fart--align-right  .ne-fart__meta,
  .ne-fart--align-right  .ne-fart__subheading { text-align: right; justify-content: flex-end; }

  .ne-fart__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ne-space-3, 12px);
  }

  .ne-fart__subheading {
    font-size: var(--ne-font-size-lg, 18px);
    line-height: 1.5;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.9;
  }
  .ne-fart__subheading > *:first-child { margin-top: 0; }
  .ne-fart__subheading > *:last-child  { margin-bottom: 0; }

  .ne-fart__featured-image {
    display: block;
    overflow: hidden;
    border-radius: var(--ne-fart-image-radius, 8px);
    aspect-ratio: 16/9;
  }
  .ne-fart__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.6s cubic-bezier(0.2, 0, 0.2, 1);
  }
  .ne-fart__featured-image:hover .ne-fart__image { transform: scale(1.04); }

  .ne-fart__content {
    line-height: 1.7;
    color: var(--ne-scheme-text);
  }
  .ne-fart__content > *:first-child { margin-top: 0; }
  .ne-fart__content > *:last-child  { margin-bottom: 0; }
  .ne-fart__content h2,
  .ne-fart__content h3,
  .ne-fart__content h4 {
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    margin-top: var(--ne-space-6, 24px);
  }
  .ne-fart__content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-fart__content a {
    color: var(--ne-scheme-link, var(--ne-scheme-accent));
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .ne-fart__read-more {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--ne-space-2, 8px);
  }

  .ne-fart__editor-empty {
    max-width: 600px;
    margin-inline: auto;
    padding: var(--ne-space-8, 32px) var(--ne-space-4, 16px);
    text-align: center;
    color: var(--ne-scheme-text-muted, currentColor);
    border: 1px dashed var(--ne-scheme-border, rgba(255,255,255,0.2));
    border-radius: var(--ne-radius-md, 8px);
    opacity: 0.8;
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-fart.ne-fart--custom-mobile-pad {
      padding-top: var(--ne-fart-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-fart-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:featured-article */

/* START_SECTION:featured-collection (INDEX:28) */
.ne-fc__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-fc-content-gap, 24px);
    width: 100%;
  }
  /* Section-level alignment — drives both the flex cross-axis
     alignment of block wrappers and the inherited vars that atom
     blocks read (Rule 126). Child's own modifier class still wins
     at element scope, so a per-block override still works. */
  .ne-fc--align-left   .ne-fc__inner { align-items: flex-start; --ne-tp--align: left;   --ne-bw-justify: flex-start; }
  .ne-fc--align-center .ne-fc__inner { align-items: center;     --ne-tp--align: center; --ne-bw-justify: center; }
  .ne-fc--align-right  .ne-fc__inner { align-items: flex-end;   --ne-tp--align: right;  --ne-bw-justify: flex-end; }

  /* Theme-block wrapper collapse — direct block elements become
     flex children (Rule 70). */
  .ne-fc__inner > .shopify-block { display: contents; }

  /* Product grid spans full inner width regardless of align-items. */
  .ne-fc__inner > .ne-pg { width: 100%; align-self: stretch; }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-fc.ne-fc--custom-mobile-pad {
      padding-top: var(--ne-fc-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-fc-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:featured-collection */

/* START_SECTION:featured-page (INDEX:29) */
/* Background overlay (over background-image, under content) */
  .ne-fpg__overlay {
    position: absolute;
    inset: 0;
    background-color: var(--ne-fpg-overlay-color, #000);
    opacity: var(--ne-fpg-overlay-alpha, 0);
    pointer-events: none;
    z-index: 0;
    border-radius: inherit;
  }
  .ne-fpg--has-overlay .ne-fpg__inner { position: relative; z-index: 1; }

  .ne-fpg__inner {
    max-width: var(--ne-fpg-content-width, 800px);
    margin-inline: auto;
    padding-inline: calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-6, 24px);
  }
  /* Custom mobile padding — class-modifier driven (Rule 211/239) */
  @media (max-width: 749px) {
    .ne-fpg.ne-fpg--custom-mobile-pad {
      padding-top: var(--ne-fpg-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-fpg-mobile-pad-bottom, 24px) !important;
    }
  }
  .ne-fpg__inner--blocks-after  .ne-fpg__title       { order: 1; }
  .ne-fpg__inner--blocks-after  .ne-fpg__subheading  { order: 2; }
  .ne-fpg__inner--blocks-after  .ne-fpg__content     { order: 3; }
  .ne-fpg__inner--blocks-after  .ne-fpg__blocks      { order: 4; }
  .ne-fpg__inner--blocks-before .ne-fpg__title       { order: 1; }
  .ne-fpg__inner--blocks-before .ne-fpg__subheading  { order: 2; }
  .ne-fpg__inner--blocks-before .ne-fpg__blocks      { order: 3; }
  .ne-fpg__inner--blocks-before .ne-fpg__content     { order: 4; }

  .ne-fpg__title { margin: 0; }
  .ne-fpg--align-left   .ne-fpg__title,
  .ne-fpg--align-left   .ne-fpg__subheading { text-align: left; }
  .ne-fpg--align-center .ne-fpg__title,
  .ne-fpg--align-center .ne-fpg__subheading { text-align: center; }
  .ne-fpg--align-right  .ne-fpg__title,
  .ne-fpg--align-right  .ne-fpg__subheading { text-align: right; }

  .ne-fpg__subheading {
    font-size: var(--ne-font-size-lg, 18px);
    line-height: 1.5;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.9;
  }
  .ne-fpg__subheading > *:first-child { margin-top: 0; }
  .ne-fpg__subheading > *:last-child  { margin-bottom: 0; }

  .ne-fpg__content {
    line-height: 1.7;
    color: var(--ne-scheme-text);
  }
  .ne-fpg__content > *:first-child { margin-top: 0; }
  .ne-fpg__content > *:last-child  { margin-bottom: 0; }
  .ne-fpg__content h2,
  .ne-fpg__content h3,
  .ne-fpg__content h4 {
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    margin-top: var(--ne-space-6, 24px);
  }
  .ne-fpg__content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-fpg__content a {
    color: var(--ne-scheme-link, var(--ne-scheme-accent));
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .ne-fpg__content blockquote {
    border-left: 3px solid var(--ne-scheme-accent);
    padding-left: var(--ne-space-4, 16px);
    margin: var(--ne-space-4, 16px) 0;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  .ne-fpg__read-more {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--ne-space-2, 8px);
  }

  .ne-fpg__blocks {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
  }
  .ne-fpg__blocks > .shopify-block { display: contents; }

  .ne-fpg__editor-empty {
    max-width: 600px;
    margin-inline: auto;
    padding: var(--ne-space-8, 32px) var(--ne-space-4, 16px);
    text-align: center;
    color: var(--ne-scheme-text-muted, currentColor);
    border: 1px dashed var(--ne-scheme-border, rgba(255,255,255,0.2));
    border-radius: var(--ne-radius-md, 8px);
    opacity: 0.8;
  }
/* END_SECTION:featured-page */

/* START_SECTION:footer-bar (INDEX:30) */
.ne-footer-bar-section {
    width: 100%;
  }
  .ne-footer-bar-section--top-border {
    border-top: 1px solid color-mix(in srgb, var(--ne-scheme-text) 12%, transparent);
  }
  /* Connected-top: flatten top corners + collapse top margin ONLY
     when a Footer with the matching --connected-bottom class exists
     in the DOM (both sides must opt-in). If either toggle is off,
     Footer-Bar keeps its full radius and section spacing applies. */
  body:has(.ne-footer.ne-footer--connected-bottom) .ne-footer-bar-section--connected-top {
    border-top-left-radius: 0 !important;
    border-top-right-radius: 0 !important;
    margin-top: 0;
    /* Connected = one continuous surface with the Footer above. The
       Footer carries the group's glow/shadow; the bar must NOT add its
       own box-shadow (would read as a second halo/seam on the bar).
       CSS-level kill is robust regardless of which side's toggle set the
       connection (user QA 2026-05-25 #30). */
    box-shadow: none !important;
  }
  /* Broader kill: footer-bar carries NO own glow/shadow whenever the Footer
     above declares itself connected — even if the footer-bar's OWN connect
     toggle is off (user sets "optisch verbunden" on the Footer side only).
     The previous rule only matched when the bar also had --connected-top, so
     a footer-side-only connection left the bar's glow visible (user QA
     2026-05-25 #32). The Footer carries the group's effect. */
  body:has(.ne-footer.ne-footer--connected-bottom) .ne-footer-bar-section {
    box-shadow: none !important;
  }
  /* When NOT connected: re-apply the global section spacing so
     Footer + Footer-bar render with the standard gap between them. */
  .ne-footer-bar-section:not(.ne-footer-bar-section--connected-top) {
    margin-top: var(--ne-section-spacing, 40px);
  }
  /* Optional spacing BELOW the footer group (theme setting:
     "Apply section spacing below footer"). When the user enables
     this, the var resolves to the section-spacing value; otherwise 0. */
  .ne-footer-bar-section {
    margin-bottom: var(--ne-footer-bottom-spacing, 0);
  }
  /* Radius: connected → use Footer's :root var directly; not connected
     → use own. Two distinct selectors avoid nested-var resolution
     issues that some browsers/editors hit with chained var() inside
     inline-style attributes. */
  section.ne-footer-bar-section.ne-section.ne-footer-bar-section--connected-top {
    border-radius: var(--ne-footer-shared-radius, var(--ne-radius-global, 12px));
  }
  section.ne-footer-bar-section.ne-section:not(.ne-footer-bar-section--connected-top) {
    border-radius: var(--ne-fb-own-radius, var(--ne-radius-global, 12px));
  }

  .ne-footer-bar-section__inner {
    max-width: var(--ne-fb-own-width, 1400px);
    margin-inline: auto;
    padding-inline: calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    display: flex;
    align-items: var(--ne-fb-valign, center);
    gap: var(--ne-fb-gap, 24px);
    width: 100%;
  }

  /* 1 child → center */
  .ne-footer-bar-section__inner:has(> .shopify-block:only-child) {
    justify-content: center;
  }
  /* 2 children → left | right (1fr 1fr grid) */
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(2):last-child) {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(2):last-child) > .shopify-block:nth-child(1) { justify-self: start; }
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(2):last-child) > .shopify-block:nth-child(2) { justify-self: end; }
  /* 3 children → left | center | right (1fr auto 1fr) */
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(3)) {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
  }
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(3)) > .shopify-block:nth-child(1) { justify-self: start; }
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(3)) > .shopify-block:nth-child(2) { justify-self: center; }
  .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(3)) > .shopify-block:nth-child(3) { justify-self: end; }

  /* Mobile: stack vertically */
  @media (max-width: 749px) {
    .ne-footer-bar-section__inner,
    .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(2):last-child),
    .ne-footer-bar-section__inner:has(> .shopify-block:nth-child(3)) {
      display: flex;
      flex-direction: column;
      grid-template-columns: none;
    }
    .ne-footer-bar-section__inner > .shopify-block {
      justify-self: stretch;
      display: flex;
      justify-content: center;
    }
  }
/* END_SECTION:footer-bar */

/* START_SECTION:footer (INDEX:31) */
.ne-footer { width: 100%; }
  .ne-footer.ne-footer--top-border {
    border-top: 1px solid color-mix(in srgb, var(--ne-scheme-text) 12%, transparent);
  }
  .ne-footer__inner {
    max-width: var(--ne-footer-max-width, 1400px);
    margin-inline: auto;
    padding-inline: calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--ne-footer-gap, 48px);
  }
  /* Mobile: ensure a minimum padding so footer text never butts up
     against the viewport edge, even when --ne-mobile-page-margin is
     set very low (or 0 for edge-to-edge sections). */
  @media (max-width: 749px) {
    .ne-footer__inner {
      padding-inline: max(16px, calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px)));
    }
  }
  /* Direct child blocks render as full-width by default; users can wrap
     them in a `group` block to build column layouts. */
  .ne-footer__inner > .shopify-block { display: contents; }

  /* Connected-bottom: flatten bottom corners + collapse outer margin
     ONLY when a Footer-bar with the matching --connected-top class
     exists in the DOM (both sides must opt-in). If either toggle is
     off, Footer keeps its full radius and the natural section gap. */
  body:has(.ne-footer-bar-section--connected-top) .ne-footer.ne-footer--connected-bottom {
    border-bottom-left-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
    margin-bottom: 0;
  }
  /* Optional spacing BELOW the footer group (theme setting:
     "Apply section spacing below footer"). Applies when Footer is
     the last footer-group section (no Footer-bar present). */
  body:not(:has(.ne-footer-bar-section)) .ne-footer {
    margin-bottom: var(--ne-footer-bottom-spacing, 0);
  }

  /* Equal-height columns + equal flex-basis for any horizontal group
     used inside the footer: ensures the column boxes line up at the
     same height, regardless of how much content each block holds.
     Content stays top-aligned within each column. */
  .ne-footer .ne-group--horizontal {
    align-items: stretch;
  }
  .ne-footer .ne-group--horizontal > * {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
  }
  /* Footer-block direct children stretch to fill their column box so
     the visual rectangles align — content remains top-aligned. */
  .ne-footer .ne-group--horizontal > .shopify-block > * {
    height: 100%;
  }
/* END_SECTION:footer */

/* START_SECTION:gallery (INDEX:32) */
.ne-gallery__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-gallery-content-gap, 32px);
    width: 100%;
  }
  /* Section-level alignment (Rule 126) — drives cross-axis + inherited
     vars for atom blocks. Child modifier class still wins at element
     scope, so per-block override keeps working. */
  .ne-gallery--align-left   .ne-gallery__inner { align-items: flex-start; --ne-tp--align: left;   --ne-bw-justify: flex-start; }
  .ne-gallery--align-center .ne-gallery__inner { align-items: center;     --ne-tp--align: center; --ne-bw-justify: center; }
  .ne-gallery--align-right  .ne-gallery__inner { align-items: flex-end;   --ne-tp--align: right;  --ne-bw-justify: flex-end; }

  /* Atom wrappers collapse (Rule 70). */
  .ne-gallery__inner > .shopify-block { display: contents; }

  /* gallery-grid spans full inner width regardless of cross-axis
     alignment — the items inside have their own alignment. */
  .ne-gallery__inner > .ne-gg { width: 100%; align-self: stretch; }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-gallery.ne-gallery--custom-mobile-pad {
      padding-top: var(--ne-gallery-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-gallery-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:gallery */

/* START_SECTION:gift-card-main (INDEX:33) */
/* Span full .shopify-section grid (= viewport-edge) so background-image
     and overlay extend to true viewport edges, not just content column.
     Default .shopify-section > * gets grid-column: 2 which leaves the
     page-margin gutters showing through. */
  .ne-gc {
    position: relative;
    grid-column: 1 / -1 !important;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ne-space-6, 24px);
    color: var(--ne-scheme-text, #fff);
    background-color: var(--ne-scheme-bg, #111);
    border-radius: 0 !important;
  }
  .ne-gc[style*="--ne-gc-bg-image"] {
    background-image: var(--ne-gc-bg-image);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  .ne-gc__bg-overlay {
    position: fixed;
    inset: 0;
    background-color: var(--ne-gc-overlay-color, #000);
    opacity: var(--ne-gc-overlay-alpha, 0);
    pointer-events: none;
    z-index: 0;
  }

  .ne-gc__inner {
    position: relative;
    z-index: 1;
    max-width: 520px;
    width: 100%;
    text-align: center;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
  }

  /* Card surfaces */
  .ne-gc__card {
    position: relative;
    border-radius: max(var(--ne-radius-global, 12px), var(--ne-gc-card-radius, 24px));
    padding: var(--ne-gc-card-padding, 40px) var(--ne-space-6, 24px);
    overflow: hidden;
  }
  .ne-gc--surface-glass .ne-gc__card {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    /* No border — white glass-border = bottom contour line on dark bg
       (+ a dark top edge under glow). User 2026-05-24. */
    border: 0;
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
  }
  .ne-gc--tint .ne-gc__card {
    background: color-mix(in srgb, var(--ne-scheme-accent) 6%, var(--ne-glass-bg, rgba(255,255,255,0.06)));
    border-color: color-mix(in srgb, var(--ne-scheme-accent) 18%, var(--ne-glass-border, rgba(255,255,255,0.12)));
  }
  .ne-gc--surface-solid .ne-gc__card {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.1));
  }
  .ne-gc--surface-transparent .ne-gc__card {
    background: transparent;
    border: 1px solid transparent;
  }
  .ne-gc--glow .ne-gc__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 48px color-mix(in srgb, var(--ne-scheme-accent) 22%, transparent);
  }
  .ne-gc--shadow .ne-gc__card {
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
  }
  .ne-gc--glow.ne-gc--shadow .ne-gc__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 48px color-mix(in srgb, var(--ne-scheme-accent) 22%, transparent),
      0 12px 40px rgba(0, 0, 0, 0.3);
  }

  /* Animated gradient sheen (optional) */
  .ne-gc--sheen .ne-gc__card::before {
    content: '';
    position: absolute;
    top: -50%; left: -50%;
    width: 200%; height: 200%;
    background: conic-gradient(
      from 0deg,
      transparent 0deg,
      color-mix(in srgb, var(--ne-scheme-accent) 24%, transparent) 60deg,
      transparent 120deg,
      transparent 360deg
    );
    opacity: 0.4;
    animation: ne-gc-rotate 12s linear infinite;
    pointer-events: none;
    z-index: 0;
  }
  @keyframes ne-gc-rotate { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) {
    .ne-gc--sheen .ne-gc__card::before { animation: none; opacity: 0.2; }
  }

  .ne-gc__card-inner { position: relative; z-index: 1; }

  /* Content */
  .ne-gc__logo {
    max-width: 180px;
    height: auto;
    margin: 0 auto var(--ne-space-5, 20px);
    display: block;
  }
  .ne-gc__shop-name {
    margin: 0 0 var(--ne-space-5, 20px);
    font-size: clamp(20px, 4vw, 28px);
    font-weight: 600;
    letter-spacing: -0.01em;
  }

  /* Shown only when printing (see @media print in templates/gift_card.liquid). */
  .ne-gc__print-id { display: none; }

  .ne-gc__heading-label {
    margin: 0 0 var(--ne-space-2, 8px);
    font-size: var(--ne-font-size-sm, 14px);
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: color-mix(in srgb, var(--ne-scheme-text) 70%, transparent);
  }

  .ne-gc__balance {
    margin: 0 0 var(--ne-space-4, 16px);
    font-size: clamp(48px, 12vw, 88px);
    font-weight: 800;
    line-height: 0.95;
    letter-spacing: -0.04em;
    background: linear-gradient(135deg,
      var(--ne-scheme-accent),
      color-mix(in srgb, var(--ne-scheme-accent) 65%, white)
    );
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    text-shadow: 0 0 60px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent);
  }

  .ne-gc__expired {
    display: inline-block;
    padding: 4px 12px;
    background: color-mix(in srgb, #ef4444 18%, transparent);
    color: #fca5a5;
    border: 1px solid color-mix(in srgb, #ef4444 40%, transparent);
    border-radius: 999px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.04em;
  }
  .ne-gc__expires {
    margin: var(--ne-space-2, 8px) 0 0;
    font-size: 13px;
    color: color-mix(in srgb, var(--ne-scheme-text) 60%, transparent);
  }

  .ne-gc__code {
    font-family: 'Courier New', monospace;
    font-size: clamp(16px, 3.5vw, 22px);
    font-weight: 600;
    letter-spacing: 0.18em;
    padding: var(--ne-space-3, 12px) var(--ne-space-4, 16px);
    background: rgba(255, 255, 255, 0.05);
    border: 2px dashed var(--ne-scheme-border, rgba(255,255,255,0.25));
    border-radius: max(var(--ne-radius-global, 8px), 12px);
    display: inline-block;
    margin: var(--ne-space-5, 20px) 0 var(--ne-space-2, 8px);
    cursor: pointer;
    transition: border-color 0.18s ease, background-color 0.18s ease, transform 0.18s ease;
    user-select: all;
    color: var(--ne-scheme-text);
  }
  .ne-gc__code:hover {
    border-color: var(--ne-scheme-accent);
    background: color-mix(in srgb, var(--ne-scheme-accent) 8%, rgba(255,255,255,0.05));
    transform: translateY(-1px);
  }
  .ne-gc__code--copied {
    border-color: #10b981 !important;
    background: color-mix(in srgb, #10b981 12%, rgba(255,255,255,0.05)) !important;
  }

  .ne-gc__hint {
    margin: 0;
    font-size: 12px;
    color: color-mix(in srgb, var(--ne-scheme-text) 55%, transparent);
    letter-spacing: 0.02em;
  }

  .ne-gc__qr {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    align-self: center;
    width: 144px;
    height: 144px;
    padding: 8px;
    background: #fff;
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-gc__qr img,
  .ne-gc__qr canvas { display: block; width: 128px; height: 128px; }

  .ne-gc__apple-wallet {
    display: inline-block;
    margin-top: var(--ne-space-5, 20px);
    transition: transform 0.18s ease;
  }
  .ne-gc__apple-wallet:hover { transform: translateY(-2px); }
  .ne-gc__apple-wallet img {
    height: 44px;
    width: auto;
    display: block;
  }

  /* Action buttons */
  .ne-gc__actions {
    display: flex;
    gap: var(--ne-space-3, 12px);
    justify-content: center;
    flex-wrap: wrap;
  }
  .ne-gc__actions .ne-btn { font-size: 14px; }
  .ne-gc__actions .ne-btn svg { vertical-align: middle; }

  .ne-gc__footer {
    margin: 0;
    font-size: 12px;
    color: color-mix(in srgb, var(--ne-scheme-text) 50%, transparent);
  }
  .ne-gc__footer a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
  }

  @media (max-width: 599px) {
    .ne-gc__card { padding: var(--ne-space-8, 32px) var(--ne-space-4, 16px); }
    .ne-gc__actions { flex-direction: column; }
    .ne-gc__actions .ne-btn { width: 100%; justify-content: center; }
  }

  @media print {
    .ne-gc {
      background: #fff !important;
      color: #000 !important;
    }
    .ne-gc__bg-overlay { display: none !important; }
    .ne-gc__card {
      background: #fff !important;
      border: 2px solid #000 !important;
      box-shadow: none !important;
      backdrop-filter: none !important;
      -webkit-backdrop-filter: none !important;
      color: #000;
    }
    .ne-gc__card::before { display: none !important; }
    .ne-gc__balance {
      color: #000 !important;
      background: none !important;
      -webkit-text-fill-color: #000 !important;
      text-shadow: none !important;
    }
    .ne-gc__code {
      background: #fff !important;
      border-color: #000 !important;
      color: #000 !important;
    }
    .ne-gc__actions { display: none !important; }
    .ne-gc__footer { display: none !important; }
  }
/* END_SECTION:gift-card-main */

/* START_SECTION:hero-video (INDEX:35) */
.ne-hv {
    position: relative;
    isolation: isolate;
    /* Override .ne-section's 40px top/bottom padding. Hero-video is a
       full-bleed visual; padding leaves visible scheme-bg stripes
       above and below the sticky-wrapper in scroll-scrub mode (sticky
       respects parent's content-box, unlike absolute which fills the
       padding-box too). Other modes use position:absolute on the
       wrapper so they're unaffected, but we zero padding for
       consistency across modes. */
    padding: 0;
    /* NO overflow: hidden on the section itself — would create a new
       scroll container and break the scroll-scrub sticky behaviour
       (sticky child would have nowhere to scroll within). Each
       sticky-wrapper handles its own clipping. */
  }

  /* === Mode: scroll-scrub ===========================================
     Section is `scrub_distance`vh tall (e.g. 300vh). The inner sticky
     wrapper pins to the viewport, the user keeps scrolling, and JS
     maps the scroll progress to video.currentTime. The page is
     "locked" on the video for (scrub_distance - 100)vh worth of
     scroll, then resumes flowing. */
  .ne-hv--mode-scroll-scrub {
    height: var(--ne-hv-scrub-distance, 300vh);
  }
  .ne-hv--mode-scroll-scrub .ne-hv__sticky {
    position: sticky;
    top: 0;
    height: 100vh;
    width: 100%;
    display: flex;
    overflow: hidden;
  }

  /* === Mode: autoplay-loop / click-to-play ==========================
     Section has min-height; sticky wrapper fills it via position:absolute
     so the video covers the full section bounds (not just the content
     box, which previously left scheme-bg stripes top/bottom). */
  .ne-hv--mode-autoplay,
  .ne-hv--mode-click-to-play {
    min-height: var(--ne-hv-h-desktop, 80vh);
  }
  @media (max-width: 749px) {
    .ne-hv--mode-autoplay,
    .ne-hv--mode-click-to-play {
      min-height: var(--ne-hv-h-mobile, 60vh);
    }
  }
  .ne-hv--full {
    min-height: 100vh;
  }
  .ne-hv--mode-autoplay .ne-hv__sticky,
  .ne-hv--mode-click-to-play .ne-hv__sticky {
    position: absolute;
    inset: 0;
    display: flex;
    overflow: hidden;
    border-radius: inherit;
  }

  /* Scrub spacer — 0 height by default (no impact). Stretched in
     mobile stack-scrub combo to provide explicit scroll distance. */
  .ne-hv__scrub-spacer {
    height: 0;
    pointer-events: none;
  }

  /* === Mobile stack modes (above / below the video) ===============
     Active only on mobile and only when mobile_stack_mode is not
     'overlay'. The section becomes a flex column; sticky-wrapper drops
     to relative positioning with the mobile height. JS moves the
     content-wrap out of the sticky into the section root so flex
     ordering works (column-reverse for above, column for below).

     Stack modes are visible_if-gated to autoplay-loop and click-to-play
     in the schema — they conflict with scroll-scrub's tall section
     height + sticky mechanics. Scroll-scrub uses identical mechanics
     on mobile as on desktop (touch-scroll inertia on iOS Safari can
     introduce stutter — known trade-off). */
  @media (max-width: 749px) {
    .ne-hv--mstack-above,
    .ne-hv--mstack-below {
      display: flex;
      flex-direction: column;
      height: auto;
      min-height: 0;
      background: transparent;
    }
    .ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mstack-below .ne-hv__sticky {
      position: relative;
      inset: auto;
      width: 100%;
      height: var(--ne-hv-h-mobile, 60vh);
      flex: 0 0 auto;
      overflow: hidden;
      /* Inherits border-radius from the section (via the autoplay /
         click-to-play sticky rule above). User picked "separated cards"
         design: video stays as its own rounded card with all 4 corners,
         content sits below with its own group-block rounding. */
    }
    .ne-hv--mstack-above .ne-hv__content-wrap,
    .ne-hv--mstack-below .ne-hv__content-wrap {
      position: relative;
      inset: auto;
      height: auto;
      background-color: var(--ne-scheme-bg, transparent);
      padding: var(--ne-space-4, 1.5rem) calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    }
    /* No flex-direction: column-reverse — JS-DOM-move places content
       before/after the sticky in DOM order to match the desired visual
       order. Normal block/flex flow is correct. */

    /* === Stack mode + scroll-scrub combo on mobile ===================
       Stack base CSS above makes sticky position:relative + 60vh height,
       which kills scrub mechanics. Override for the scroll-scrub combo:
       sticky stays pinned, content sits in flow before/after the sticky
       wrapper (JS-DOM-move places it appropriately). Margin on the
       sticky reserves the scroll distance for scrub-progress so the
       page is "locked" while video scrubs, then sticky releases and
       the content scrolls into view. */
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-above,
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-below {
      display: block;
      flex-direction: unset;
      height: auto;
      min-height: 0;
    }
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-below .ne-hv__sticky {
      position: sticky;
      top: 0;
      width: 100%;
      height: 100vh;
      flex: unset;
    }
    /* Spacer expands to provide scroll distance for sticky pinning.
       (scrub_distance − 100vh) of flow space after the sticky video. */
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-above .ne-hv__scrub-spacer,
    .ne-hv--mode-scroll-scrub.ne-hv--mstack-below .ne-hv__scrub-spacer {
      height: calc(var(--ne-hv-scrub-distance, 300vh) - 100vh);
      display: block;
    }
  }

  /* === Media layer ================================================== */
  .ne-hv__media {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    background-color: #000;
    container-type: size;
  }
  /* Glass-shine top light-bar over the video — opt-in via the section toggle
     (user QA 2026-05-25). overflow:hidden on the media clips it cleanly.
     pointer-events:none so video controls stay usable. */
  .ne-hv--media-shine .ne-hv__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.20) 1px, rgba(255,255,255,0.06) 2px, transparent 3px));
    pointer-events: none;
    z-index: 5;
    border-radius: inherit;
  }
  .ne-hv__video,
  .ne-hv__poster-fallback {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  /* Firefox keeps a paused, scrubbed video on its own compositor layer
     instead of blanking it to black when the sticky region unpins
     (user-reported 2026-06-17, Firefox-only; Chromium/DDG unaffected). */
  .ne-hv--mode-scroll-scrub .ne-hv__video {
    will-change: transform;
    transform: translateZ(0);
    backface-visibility: hidden;
  }
  /* Embedded iframes (YT/Vimeo) don't respect object-fit. Cover the
     container by sizing to whichever axis is larger (16:9 lock). */
  .ne-hv__video--embed {
    inset: auto;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: max(100cqw, calc(100cqh * 16 / 9));
    height: max(100cqh, calc(100cqw * 9 / 16));
    pointer-events: none;
  }

  /* === Aspect ratios (video sized inside the media box) ============= */
  .ne-hv--aspect-cover .ne-hv__media { /* Default — fills sticky wrapper */ }
  .ne-hv--aspect-16-9 .ne-hv__media,
  .ne-hv--aspect-21-9 .ne-hv__media,
  .ne-hv--aspect-4-3 .ne-hv__media,
  .ne-hv--aspect-9-16 .ne-hv__media,
  .ne-hv--aspect-1-1 .ne-hv__media {
    inset: auto;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: auto;
    max-width: 100%;
    max-height: 100%;
  }
  .ne-hv--aspect-16-9 .ne-hv__media { aspect-ratio: 16 / 9; height: 100%; }
  .ne-hv--aspect-21-9 .ne-hv__media { aspect-ratio: 21 / 9; height: 100%; }
  .ne-hv--aspect-4-3  .ne-hv__media { aspect-ratio: 4 / 3;  height: 100%; }
  .ne-hv--aspect-9-16 .ne-hv__media { aspect-ratio: 9 / 16; height: 100%; }
  .ne-hv--aspect-1-1  .ne-hv__media { aspect-ratio: 1 / 1;  height: 100%; }

  /* Mobile aspect ratio override. Strategy: the aspect-ratio lives on
     the CONTAINER (section in overlay mode, sticky in stack mode), not
     on the media. The container resizes to match the aspect, and media
     fills the container via inset:0. This avoids letterboxing inside a
     fixed-height container (which would leave a large empty gap below
     the video when the aspect is much wider than height_mobile, e.g.
     21:9 inside a 55vh sticky). */
  @media (max-width: 749px) {
    /* Media fills its container in ALL mobile-aspect modes (cover or
       fixed). The desktop aspect rule may have set transform / max-X
       / aspect-ratio on the media for the desktop variant — undo all
       of that here so media is a clean inset:0 cover. */
    .ne-hv--mobile-aspect-cover .ne-hv__media,
    .ne-hv--mobile-aspect-16-9 .ne-hv__media,
    .ne-hv--mobile-aspect-21-9 .ne-hv__media,
    .ne-hv--mobile-aspect-4-3 .ne-hv__media,
    .ne-hv--mobile-aspect-9-16 .ne-hv__media,
    .ne-hv--mobile-aspect-1-1 .ne-hv__media {
      inset: 0;
      position: absolute;
      top: auto;
      left: auto;
      transform: none;
      width: 100%;
      height: 100%;
      aspect-ratio: auto;
      max-width: none;
      max-height: none;
    }

    /* OVERLAY mode (no stack class): apply aspect to the section. Section
       has min-height set by height_mobile; override to 0 so aspect can
       size the section freely. Sticky inside (position:absolute; inset:0)
       fills the resized section automatically. */
    .ne-hv--mobile-aspect-16-9:not(.ne-hv--mstack-above):not(.ne-hv--mstack-below) { min-height: 0; aspect-ratio: 16 / 9; }
    .ne-hv--mobile-aspect-21-9:not(.ne-hv--mstack-above):not(.ne-hv--mstack-below) { min-height: 0; aspect-ratio: 21 / 9; }
    .ne-hv--mobile-aspect-4-3:not(.ne-hv--mstack-above):not(.ne-hv--mstack-below)  { min-height: 0; aspect-ratio: 4 / 3; }
    .ne-hv--mobile-aspect-9-16:not(.ne-hv--mstack-above):not(.ne-hv--mstack-below) { min-height: 0; aspect-ratio: 9 / 16; }
    .ne-hv--mobile-aspect-1-1:not(.ne-hv--mstack-above):not(.ne-hv--mstack-below)  { min-height: 0; aspect-ratio: 1 / 1; }

    /* STACK mode: apply aspect to the sticky. Sticky's height_mobile is
       overridden by the aspect; content-wrap sits right below (or above)
       the aspect-ratio'd video with no extra empty gap. */
    .ne-hv--mobile-aspect-16-9.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mobile-aspect-16-9.ne-hv--mstack-below .ne-hv__sticky { height: auto; aspect-ratio: 16 / 9; }
    .ne-hv--mobile-aspect-21-9.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mobile-aspect-21-9.ne-hv--mstack-below .ne-hv__sticky { height: auto; aspect-ratio: 21 / 9; }
    .ne-hv--mobile-aspect-4-3.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mobile-aspect-4-3.ne-hv--mstack-below .ne-hv__sticky  { height: auto; aspect-ratio: 4 / 3; }
    .ne-hv--mobile-aspect-9-16.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mobile-aspect-9-16.ne-hv--mstack-below .ne-hv__sticky { height: auto; aspect-ratio: 9 / 16; }
    .ne-hv--mobile-aspect-1-1.ne-hv--mstack-above .ne-hv__sticky,
    .ne-hv--mobile-aspect-1-1.ne-hv--mstack-below .ne-hv__sticky  { height: auto; aspect-ratio: 1 / 1; }
  }

  .ne-hv__overlay {
    position: absolute;
    inset: 0;
    background: var(--ne-hv-overlay-color, #000);
    opacity: var(--ne-hv-overlay-alpha, 0);
    pointer-events: none;
    z-index: 1;
  }

  /* === External embed (YT / Vimeo) ==================================
     Placeholder lives inside .ne-hv__media for click-to-play; JS swaps
     it for an iframe when the user clicks. The cover lives OUTSIDE
     media (sibling of media in .ne-hv__sticky) so its z-index sits in
     the section stacking context above media's z:0, ensuring it covers
     the iframe's brief play-button UI during autoplay handshake. */
  .ne-hv__embed-placeholder {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    background-color: #000;
  }
  .ne-hv__embed-placeholder img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-hv__embed-cover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    pointer-events: none;
    z-index: 2;
    transition: opacity 0.6s ease;
  }
  .ne-hv__embed-cover--solid {
    background-color: #000;
  }

  /* === Click-to-play button ========================================= */
  .ne-hv__play-btn {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    z-index: 3;
    width: 88px;
    height: 88px;
    border-radius: 50%;
    border: 2px solid rgba(255, 255, 255, 0.9);
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    transition: transform 0.2s ease, background 0.2s ease;
  }
  .ne-hv__play-btn:hover,
  .ne-hv__play-btn:focus-visible {
    transform: translate(-50%, -50%) scale(1.08);
    background: rgba(0, 0, 0, 0.7);
  }
  .ne-hv__play-btn svg {
    margin-left: 4px; /* visual-center the play triangle */
  }
  .ne-hv--playing .ne-hv__play-btn {
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
  }

  /* === Sound toggle ================================================ */
  .ne-hv__sound-toggle {
    position: absolute;
    bottom: 20px;
    right: 20px;
    z-index: 4;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, 0.4);
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    transition: background 0.2s ease, transform 0.2s ease;
  }
  .ne-hv__sound-toggle:hover,
  .ne-hv__sound-toggle:focus-visible {
    transform: scale(1.05);
    background: rgba(0, 0, 0, 0.75);
  }
  .ne-hv__sound-icon--on { display: none; }
  .ne-hv__sound-toggle[data-muted="0"] .ne-hv__sound-icon--muted { display: none; }
  .ne-hv__sound-toggle[data-muted="0"] .ne-hv__sound-icon--on { display: block; }

  /* === Content layer =============================================== */
  .ne-hv__content-wrap {
    position: relative;
    z-index: 2;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: var(--ne-hv-ai, center);
    justify-content: var(--ne-hv-jc, center);
    padding: var(--ne-space-5, 2rem) calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    text-align: var(--ne-hv-ta, center);
    /* No CSS transition on opacity — JS sets it per rAF during scroll-scrub
       and a CSS transition would lag/queue against rapid updates. */
  }
  /* Mobile content position override (when custom_mobile_settings on, the
     -mobile vars differ from desktop; otherwise they're identical and this
     rule is a no-op). */
  @media (max-width: 749px) {
    .ne-hv__content-wrap {
      align-items: var(--ne-hv-ai-mobile, var(--ne-hv-ai, center));
      justify-content: var(--ne-hv-jc-mobile, var(--ne-hv-jc, center));
      text-align: var(--ne-hv-ta-mobile, var(--ne-hv-ta, center));
    }
  }
  .ne-hv__content {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 1rem);
    width: auto;
    max-width: 100%;
  }
  .ne-hv__content-wrap[style*="text-align: left"]   .ne-hv__content { align-items: flex-start; }
  .ne-hv__content-wrap[style*="text-align: center"] .ne-hv__content { align-items: center; }
  .ne-hv__content-wrap[style*="text-align: right"]  .ne-hv__content { align-items: flex-end; }
  .ne-hv__content > .shopify-block { display: contents; }

  /* === Effects ===================================================== */
  .ne-hv--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-hv--glow   { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-hv--shadow.ne-hv--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
/* END_SECTION:hero-video */

/* START_SECTION:hero (INDEX:36) */
.ne-hero {
    position: relative;
    display: flex;
    /* overflow: visible to let drop-shadow/glow escape the section box */
    overflow: hidden;
    /* Cap the height at the frame's visible inner area so a tall hero (slider near
       100vh) doesn't overflow the frame bottom and clip its own bottom padding
       (which reads as "more space at the top"). --ne-hero-frame-adjust is 0 when the
       frame is off, so lower values pass through unchanged. */
    min-height: min(var(--ne-hero-h-desktop, 80vh), calc(100vh - var(--ne-hero-frame-adjust, 0px)));
    isolation: isolate;
  }
  @media (max-width: 640px) {
    .ne-hero { min-height: min(var(--ne-hero-h-mobile, 60vh), calc(100vh - var(--ne-hero-frame-adjust, 0px))); }
  }
  .ne-hero--full { min-height: calc(100vh - var(--ne-hero-frame-adjust, 0px)); }

  .ne-hero__media {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    /* Clip the media to the hero's rounded corners on its own, so corner
       rounding survives when .ne-hero flips to overflow:visible (e.g. a child
       group/block with shadow/glow needs its effect to escape the section). */
    border-radius: inherit;
    container-type: size;
  }
  .ne-hero__img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* Image-only blur — scoped to the media so it does NOT soften the
       heading/text/buttons the way section-level sharpness does. */
    filter: blur(var(--ne-hero-img-blur, 0));
  }
  /* Slight overscan when blurred so the soft blurred edge is pushed past the
     media clip (a blur on an edge-to-edge image would otherwise fade to
     transparent at the section edges). */
  .ne-hero--img-blur .ne-hero__img { transform: scale(1.06); }
  /* Mobile / desktop image swap — CSS-driven so both <img> ship but only one shows */
  .ne-hero__img--mobile { display: none; }
  @media (max-width: 640px) {
    .ne-hero__img--desktop { display: none; }
    .ne-hero__img--mobile  { display: block; }
  }
  .ne-hero__overlay {
    position: absolute;
    inset: 0;
    background: var(--ne-hero-overlay-color, #000);
    opacity: var(--ne-hero-overlay-alpha, 0);
    pointer-events: none;
    z-index: 1;
  }

  .ne-hero__content-wrap {
    position: relative;
    z-index: 2;
    width: 100%;
    display: flex;
    align-items: var(--ne-hero-ai, center);
    justify-content: var(--ne-hero-jc, center);
    padding: var(--ne-space-5, 2rem) calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    text-align: var(--ne-hero-ta, center);
  }
  .ne-hero__content {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 1rem);
    /* width:auto so the parent's justify-content (horizontal in flex-row) can
       position this column left/center/right. max-width caps it on long copy. */
    width: auto;
    max-width: 100%;
  }
  .ne-hero__content-wrap[style*="text-align: left"]   .ne-hero__content { align-items: flex-start; }
  .ne-hero__content-wrap[style*="text-align: center"] .ne-hero__content { align-items: center; }
  .ne-hero__content-wrap[style*="text-align: right"]  .ne-hero__content { align-items: flex-end; }
  .ne-hero__content > .shopify-block { display: contents; }

  /* Mobile: drop the global .ne-section padding-top/bottom on hero —
     hero designs its own internal layout (bg-image fills box or stack-
     mode uses flex). Without this override, .ne-section's 24px mobile
     padding-top creates a visible gap ABOVE the hero box on mobile,
     particularly in mstack-above/below modes where media switches from
     position:absolute (fills padding-box) to position:relative (respects
     content-box → padding becomes visible empty space above the image).
     Apply at 749px breakpoint to catch all mobile viewports (mstack
     itself activates at 640px, but the .ne-section padding rule fires
     at 749px). */
  @media (max-width: 749px) {
    .ne-hero {
      padding-top: 0 !important;
      padding-bottom: 0 !important;
    }
  }

  /* Mobile stack modes — above/below switch to a vertical flex layout on mobile.
     Section-bg goes transparent so the scheme color doesn't paint a strip
     above/below the image. Content-wrap takes over the scheme-bg job — only
     the content half gets the scheme color. */
  @media (max-width: 640px) {
    .ne-hero--mstack-above,
    .ne-hero--mstack-below {
      min-height: 0 !important;
      display: flex;
      flex-direction: column;
      background: transparent !important;
    }
    .ne-hero--mstack-above .ne-hero__media,
    .ne-hero--mstack-below .ne-hero__media {
      position: relative;
      inset: auto;
      width: 100%;
      height: var(--ne-hero-h-mobile, 60vh);
      flex: 0 0 auto;
    }
    .ne-hero--mstack-above .ne-hero__content-wrap,
    .ne-hero--mstack-below .ne-hero__content-wrap {
      position: relative;
      padding: var(--ne-space-4, 1.5rem) calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
      background-color: var(--ne-scheme-bg, transparent);
    }
    /* Respect transparent_bg and enable_glass settings even in
       stacked-above/below modes — earlier this branch painted
       --ne-scheme-bg unconditionally, ignoring the toggles (user
       report 2026-05-23: glass + transparent had no effect on text
       part in stack-below mode). */
    .ne-hero.ne-section--transparent.ne-hero--mstack-above .ne-hero__content-wrap,
    .ne-hero.ne-section--transparent.ne-hero--mstack-below .ne-hero__content-wrap {
      background-color: transparent !important;
    }
    .ne-hero.ne-section--glass.ne-hero--mstack-above .ne-hero__content-wrap,
    .ne-hero.ne-section--glass.ne-hero--mstack-below .ne-hero__content-wrap {
      background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
      backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
      -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    }
    .ne-hero--mstack-above { flex-direction: column-reverse; }
  }

  /* Drop shadow / glow — applied to the section itself */
  .ne-hero--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-hero--glow   { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-hero--shadow.ne-hero--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
/* END_SECTION:hero */

/* START_SECTION:horizontal-lookbook (INDEX:37) */
.ne-hs.ne-section { overflow: visible; padding: 0; }

  /* ── Base / fallback: plain native horizontal scroller ────── */
  .ne-hs__pin { position: relative; }
  .ne-hs__track {
    display: flex;
    gap: var(--ne-hs-gap, 24px);
    overflow-x: auto;
    overflow-y: hidden;
    padding: clamp(40px, 6vh, 80px) var(--ne-section-inner-pad-x, 24px);
    scrollbar-width: none;
    scroll-snap-type: x proximity;
  }
  .ne-hs__track::-webkit-scrollbar { display: none; }

  /* All panels share one width + aspect-ratio → identical height across
     media panels and editorial text frames (no more mismatched heights). */
  .ne-hs__panel {
    flex: 0 0 var(--ne-hs-panel-w, 62vw);
    width: var(--ne-hs-panel-w, 62vw);
    max-width: 680px;
    aspect-ratio: 3 / 4;
    scroll-snap-align: center;
  }
  .ne-hs--ratio-square .ne-hs__panel { aspect-ratio: 1 / 1; }
  .ne-hs--ratio-landscape .ne-hs__panel { aspect-ratio: 16 / 10; }
  .ne-hs__panel-link { display: block; text-decoration: none; color: inherit; height: 100%; }

  .ne-hs__media {
    position: relative;
    height: 100%;
    border-radius: var(--ne-hs-panel-radius, var(--ne-card-radius, var(--ne-radius-lg, 16px)));
    overflow: clip;
    background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.05));
  }
  .ne-hs__img { width: 100%; height: 100%; object-fit: cover; display: block; -webkit-user-drag: none; }

  .ne-hs__overlay-cap {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 24px 20px 18px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
    color: #fff;
  }
  .ne-hs__cap-title { margin: 0; font-size: 18px; }
  .ne-hs__cap-meta { display: block; margin-top: 4px; font-size: 14px; opacity: 0.85; }
  /* Captions sit on a dark scrim over the image → white by default. The
     explicit selector beats any global heading-colour rule that would
     otherwise pull the h3 title to the (often dark) scheme heading. */
  .ne-hs__overlay-cap .ne-hs__cap-title,
  .ne-hs__overlay-cap .ne-hs__cap-meta { color: #fff; }
  /* Per-panel custom scheme drives the caption colours instead (user QA
     2026-06-13) — e.g. a dark scheme gives a white title on a light image. */
  .ne-hs__panel--custom-scheme .ne-hs__overlay-cap .ne-hs__cap-title { color: var(--ne-scheme-heading, #fff); }
  .ne-hs__panel--custom-scheme .ne-hs__overlay-cap .ne-hs__cap-meta { color: var(--ne-scheme-text, #fff); }
  /* The .color-X must not paint the article bg behind a transparent/glass
     surface (Rule 173) — the per-panel surface still defines the background. */
  .ne-hs__panel--custom-scheme { background: transparent !important; }

  .ne-hs__frame {
    height: 100%;
    min-height: 0;
    overflow: clip;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: clamp(28px, 4vw, 56px);
    border-radius: var(--ne-hs-panel-radius, var(--ne-card-radius, var(--ne-radius-lg, 16px)));
    background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.04));
    border: 1px solid var(--ne-scheme-border, rgba(128, 128, 128, 0.18));
  }
  .ne-hs__frame-title { margin: 0 0 14px; font-size: var(--ne-h3--size-responsive, clamp(24px, 3vw, 38px)); line-height: 1.15; }
  .ne-hs__frame-text { color: var(--ne-scheme-text-muted, currentColor); font-size: clamp(15px, 1.4vw, 18px); line-height: 1.6; }
  .ne-hs__frame-btn { margin-top: 24px; align-self: flex-start; }

  /* ── Per-panel surface (mirrors the v7 card surfaces) ─────────
     Background shows on text frames + transparent-PNG / contained
     product panels; cover images fill the card so glass is hidden. */
  .ne-hs__panel--surface-transparent .ne-hs__media,
  .ne-hs__panel--surface-transparent .ne-hs__frame { background: transparent; }
  .ne-hs__panel--surface-transparent .ne-hs__frame { border-color: transparent; }
  .ne-hs__panel--surface-glass .ne-hs__media,
  .ne-hs__panel--surface-glass .ne-hs__frame {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
  }
  .ne-hs__panel--surface-glass .ne-hs__frame { border-color: var(--ne-glass-border, rgba(255, 255, 255, 0.12)); }
  .ne-hs__panel--glass-tint .ne-hs__media,
  .ne-hs__panel--glass-tint .ne-hs__frame { background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.06))); }

  /* Per-panel glow / shadow */
  .ne-hs__panel--glow .ne-hs__media,
  .ne-hs__panel--glow .ne-hs__frame { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-hs__panel--shadow .ne-hs__media,
  .ne-hs__panel--shadow .ne-hs__frame { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-hs__panel--glow.ne-hs__panel--shadow .ne-hs__media,
  .ne-hs__panel--glow.ne-hs__panel--shadow .ne-hs__frame {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  .ne-hs__label {
    position: absolute; top: 20px;
    left: var(--ne-hs-label-pad-x, var(--ne-section-inner-pad-x, 24px));
    right: auto;
    z-index: 5;
    font-size: 13px; letter-spacing: 0.14em; text-transform: uppercase; font-weight: 600;
    opacity: 0.8;
  }
  /* Heading alignment + side padding (user QA 2026-06-13). */
  .ne-hs__label--center {
    left: var(--ne-hs-label-pad-x, 24px);
    right: var(--ne-hs-label-pad-x, 24px);
    text-align: center;
  }
  .ne-hs__label--right {
    left: auto;
    right: var(--ne-hs-label-pad-x, var(--ne-section-inner-pad-x, 24px));
    text-align: right;
  }
  .ne-hs__progress {
    position: absolute; left: var(--ne-section-inner-pad-x, 24px); right: var(--ne-section-inner-pad-x, 24px); bottom: 18px;
    z-index: 5;
    height: 3px; border-radius: 999px;
    background: var(--ne-scheme-border, rgba(128, 128, 128, 0.25));
    overflow: hidden;
    display: none;
  }
  .ne-hs__progress-bar {
    display: block; height: 100%; width: 100%;
    transform-origin: left center; transform: scaleX(0.12);
    background: var(--ne-scheme-accent, #6366f1);
  }

  .ne-hs__empty {
    padding: 48px 24px; text-align: center;
    color: var(--ne-scheme-text-muted, currentColor);
    border: 1px dashed var(--ne-scheme-border, rgba(128, 128, 128, 0.4));
    border-radius: var(--ne-radius-lg, 16px);
    margin-inline: var(--ne-section-inner-pad-x, 24px);
  }

  /* Section-level effects */
  .ne-hs.ne-section--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-hs.ne-section--glow { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-hs.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* ── The wedge: pinned vertical→horizontal scroll ─────────────
     JS-driven (rAF) so it runs everywhere — editor, live, Firefox and
     mobile — and the travel end-point is computed from the REAL track +
     pin width, so the last panel lands exactly at the section edge
     (never the raw viewport edge) on any browser width. No-JS / reduced
     motion → the base native horizontal scroller above is the fallback. */
  .ne-hs--js {
    height: var(--ne-hs-scroll-len, 300vh);
  }
  .ne-hs--js .ne-hs__pin {
    position: sticky;
    top: 0;
    height: 100svh;
    overflow: clip;
    display: flex;
    align-items: center;
  }
  /* Track viewport — wraps ONLY the (transforming) track so the edge-fade mask
     can fade at FIXED edges without touching the pinned label/progress (user QA
     2026-06-13: the section heading must stay above the fade). display:contents
     in the base scroller adds no box; a real flex box in JS mode fills the pin
     and clips the translated track. */
  .ne-hs__viewport { display: contents; }
  .ne-hs--js .ne-hs__viewport {
    display: flex;
    align-items: center;
    position: absolute;
    inset: 0;
    overflow: clip;
  }
  /* Height-driven panels fill the pinned stage (width follows the aspect
     ratio) → no empty band above/below. */
  .ne-hs--js .ne-hs__panel {
    height: var(--ne-hs-panel-h, 82svh);
    width: auto;
    /* Height-driven: the width follows height × aspect-ratio, so the panel
       ratio (portrait / square / landscape) is what visibly sets the width.
       max-width:none overrides the base 680px cap — that cap (or a
       panel_width cap) would freeze the width once height×ratio exceeds it,
       making the ratio appear to do nothing (user QA 2026-06-13). panel_width
       applies to the mobile / fallback scroller only. */
    max-width: none;
    flex: 0 0 auto;
  }
  .ne-hs--js .ne-hs__track {
    overflow: visible;
    width: max-content;
    scroll-snap-type: none;
    padding-block: 0;
    will-change: transform;
  }
  /* Edge fade — gradient OVERLAYS, not a mask. A mask on the pin forms a
     backdrop-root that kills the panels' glass backdrop-filter (Rule 49 /
     user QA 2026-06-11: glass blurred only with edge-fade off). position:absolute
     keeps the pseudos out of the pin's flex flow so the track isn't squeezed. */
  .ne-hs--js.ne-hs--edge-fade .ne-hs__pin::before,
  .ne-hs--js.ne-hs--edge-fade .ne-hs__pin::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 48px;
    z-index: 2;
    pointer-events: none;
  }
  .ne-hs--js.ne-hs--edge-fade .ne-hs__pin::before {
    left: 0;
    background: linear-gradient(to right, var(--ne-scheme-bg, #0a0a0f), transparent);
  }
  .ne-hs--js.ne-hs--edge-fade .ne-hs__pin::after {
    right: 0;
    background: linear-gradient(to left, var(--ne-scheme-bg, #0a0a0f), transparent);
  }
  /* Transparent section → UNCOLOURED edge fade. The colour-gradient overlays
     above would paint a scheme-coloured band over the transparent background
     (user QA 2026-06-13: green stripe). Instead drop the overlays and alpha-mask
     the pin so the edges fade to true transparency, revealing the page. The
     mask must live on the pin (fixed edges) — the track transforms during the
     pinned scroll, so a mask there would drift with the content. Trade-off
     (Rule 49/356): a pin mask forms a backdrop-root that disables child glass
     blur — acceptable on a transparent section (glass has nothing to sample). */
  .ne-hs--js.ne-hs--edge-fade.ne-section--transparent .ne-hs__pin::before,
  .ne-hs--js.ne-hs--edge-fade.ne-section--transparent .ne-hs__pin::after { background: none; }
  .ne-hs--js.ne-hs--edge-fade.ne-section--transparent .ne-hs__viewport {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 48px, #000 calc(100% - 48px), transparent 100%);
  }
  .ne-hs--js.ne-hs--has-progress .ne-hs__progress { display: block; }
  .ne-hs--js .ne-hs__progress-bar { transform-origin: left center; transform: scaleX(0); }

  /* Mobile pinned panels: a touch shorter so they sit comfortably */
  @media (max-width: 749px) {
    .ne-hs--js .ne-hs__panel { height: var(--ne-hs-panel-h-mobile, 70svh); }
  }
/* END_SECTION:horizontal-lookbook */

/* START_SECTION:hotspot-image (INDEX:38) */
/* Section may render dots/cards beyond image bounds → overflow visible */
  .ne-hotspot-image.ne-section { overflow: visible; }

  .ne-hotspot-image__stage {
    position: relative;
    width: 100%;
  }

  /* Section-level heading + text above the image */
  .ne-hotspot-image__intro {
    text-align: center;
    margin-bottom: 24px;
    color: var(--ne-scheme-text);
  }
  .ne-hotspot-image__intro-heading {
    margin: 0 0 8px;
    font-size: clamp(1.5rem, 3vw, 2.25rem);
    font-weight: 700;
    line-height: 1.15;
  }
  .ne-hotspot-image__intro-text {
    font-size: clamp(0.95rem, 1.4vw, 1.05rem);
    line-height: 1.5;
    max-width: 720px;
    margin: 0 auto;
    opacity: 0.9;
  }
  .ne-hotspot-image__intro-text > * { margin: 0; }
  .ne-hotspot-image__intro-text > * + * { margin-top: 0.5em; }

  /* Media wrapper — overflow:visible so hotspot cards can extend beyond
     image edges. img + overlay clip themselves via clip-path so the
     border-radius visual is preserved without overflow:hidden.
     Layers (hotspots + heroes) live INSIDE media so they position
     relative to image bounds — critical when stage grows on mobile
     flow-mode (hero stacked above/below). */
  .ne-hotspot-image__media {
    position: relative;
    width: 100%;
    overflow: visible;
    border-radius: var(--ne-hi-img-radius, 0px);
    isolation: isolate;
  }
  .ne-hotspot-image--clip-to-section .ne-hotspot-image__media {
    border-radius: max(var(--ne-hi-img-radius, 0px), var(--section-border-radius, var(--ne-radius-global, 12px)));
  }
  /* Glass-shine top light-bar over the main image — opt-in via the section's
     own toggle (user QA 2026-05-25 #6). pointer-events:none so hotspot dots
     stay clickable; z-index above the image + overlay, below the dots. */
  /* Full-size overlay (inset:0) + border-radius:inherit: the shine gradient
     is visible only in the top ~3px and gets clipped to the media's rounded
     top corners (background-clip honours border-radius). A 3px-tall bar would
     clamp a 22px radius to ~1.5px and poke past the corners (user QA #6). */
  .ne-hotspot-image--img-shine .ne-hotspot-image__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.20) 1px, rgba(255,255,255,0.06) 2px, transparent 3px));
    pointer-events: none;
    z-index: 3;
    border-radius: inherit;
  }
  /* clip-path on img + overlay enforces rounded corners without
     overflow:hidden (which would clip cards). */
  .ne-hotspot-image__bg,
  .ne-hotspot-image__overlay {
    border-radius: inherit;
    clip-path: inset(0 round var(--ne-hi-img-radius, 0px));
  }
  .ne-hotspot-image--clip-to-section .ne-hotspot-image__bg,
  .ne-hotspot-image--clip-to-section .ne-hotspot-image__overlay {
    clip-path: inset(0 round max(var(--ne-hi-img-radius, 0px), var(--section-border-radius, var(--ne-radius-global, 12px))));
  }
  /* Bump section z-index when any hotspot is active so cards
     overlay following sections (Rule 146 alternative without body-mount).
     DESKTOP ONLY: on mobile cards are bottom-sheets at z=9998 which
     would be trapped within section's stacking context if section had
     z-index — body's backdrop (z=9997) would then paint OVER the card.
     Mobile keeps z-auto so fixed-positioned card escapes to viewport. */
  @media (min-width: 750px) {
    .ne-hotspot-image:has([data-hotspot-active]) {
      position: relative;
      z-index: 50;
    }
  }

  .ne-hotspot-image__bg {
    display: block;
    width: 100%;
    height: auto;
    object-fit: cover;
    object-position: 50% 50%;
  }
  .ne-hotspot-image__placeholder {
    background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.05));
    aspect-ratio: 16 / 9;
  }
  .ne-hotspot-image__placeholder-svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.4;
  }

  .ne-hotspot-image__overlay {
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, var(--ne-hi-overlay, 0));
    pointer-events: none;
    z-index: 1;
  }

  /* Layers: container for hotspot + hotspot-hero blocks.
     INSIDE __media — anchors to image bounds via inset:0 over media. */
  .ne-hotspot-image__layers {
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;
  }
  .ne-hotspot-image__layers > .shopify-block { display: contents; }

  /* Section-level shadow + glow */
  .ne-hotspot-image.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-hotspot-image.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-hotspot-image.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  body.ne-hotspot-sheet-open { overflow: hidden; }

  /* Mobile: scale padding values down (50%) so px-values that look
     right on desktop don't dominate small viewports. */
  @media (max-width: 749px) {
    .ne-hotspot-image {
      padding-block: calc(var(--ne-hi-pad-top, 48px) * 0.5) calc(var(--ne-hi-pad-bottom, 48px) * 0.5) !important;
      padding-inline: calc(var(--ne-hi-pad-x, 48px) * 0.5) !important;
    }
  }
/* END_SECTION:hotspot-image */

/* START_SECTION:image-with-text (INDEX:39) */
/* fr units share the row's free space (after gap) — % would sum to
     100% and push the second column past the section's right edge by
     exactly --ne-iwt-gap. */
  .ne-iwt__grid {
    display: grid;
    grid-template-columns: var(--ne-iwt-l, 1fr) var(--ne-iwt-r, 1fr);
    grid-template-areas: "media content";
    gap: var(--ne-iwt-gap, 32px);
    align-items: center;
    width: 100%;
  }
  .ne-iwt--reversed .ne-iwt__grid {
    grid-template-columns: var(--ne-iwt-r, 1fr) var(--ne-iwt-l, 1fr);
    grid-template-areas: "content media";
  }

  /* Theme-block wrapper collapse — direct block elements become grid items.
     Without this the .shopify-block wrapper becomes the grid item and the
     real block sits as a non-positioned child (Rule 70/88). */
  .ne-iwt__grid > .shopify-block { display: contents; }

  /* Slot routing — image / video block → media area, everything else
     (group, heading, text, button, atoms) → content area. Multiple
     content blocks stack inside the area (intended for one group). */
  .ne-iwt__grid > .ne-block-image-wrap,
  .ne-iwt__grid > .ne-block-video-wrap {
    grid-area: media;
    width: 100%;
    min-width: 0;
    align-self: stretch;
  }
  /* In image-with-text the image IS the column — default the media
     wrap's align-items to stretch so the image fills the slot
     edge-to-edge when the block itself has no explicit alignment.
     Var-based so a user-set block alignment still wins (element-scope
     var beats inherited ancestor var). */
  .ne-iwt__grid { --ne-iw-align: stretch; }
  .ne-iwt__grid .ne-block-image-wrap .ne-block-image,
  .ne-iwt__grid .ne-block-video-wrap .ne-block-video { width: 100%; }
  .ne-iwt__grid > .ne-group,
  .ne-iwt__grid > .ne-block-heading,
  .ne-iwt__grid > .ne-block-text,
  .ne-iwt__grid > .ne-block-button-wrap,
  .ne-iwt__grid > .ne-block-icon,
  .ne-iwt__grid > .ne-block-divider,
  .ne-iwt__grid > .ne-block-spacer {
    grid-area: content;
    min-width: 0;
  }

  /* Mobile: collapse to single column, media first regardless of
     image_position. Most common e-commerce pattern (image leads,
     copy follows). */
  @media (max-width: 990px) {
    .ne-iwt__grid,
    .ne-iwt--reversed .ne-iwt__grid {
      grid-template-columns: 1fr;
      grid-template-areas: "media" "content";
    }
  }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-iwt.ne-iwt--custom-mobile-pad {
      padding-top: var(--ne-iwt-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-iwt-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:image-with-text */

/* START_SECTION:map (INDEX:41) */
/* Override .ne-section { overflow: clip } from critical.css so block-level
     glow/shadow on .ne-store-map__embed isn't truncated at the section border-
     box on narrow viewports. The map iframe inside the block keeps its own
     overflow:clip + border-radius for corner-rounding, so we don't lose any
     radius-clipping behaviour at the block level. */
  .ne-map-section {
    overflow: visible;
  }

  .ne-map-section__inner {
    width: 100%;
    max-width: var(--ne-map-max, 1200px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-map-gap, 24px);
  }
  .ne-map-section__inner > .shopify-block { display: contents; }

  .ne-map-section--align-left .ne-map-section__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-map-section--align-center .ne-map-section__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-map-section--align-right .ne-map-section__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding (opt-in) */
  @media (max-width: 749px) {
    .ne-map-section.ne-map-section--custom-mobile-pad {
      padding-top: var(--ne-map-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-map-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:map */

/* START_SECTION:multicolumn (INDEX:42) */
/* Allow child group glow/shadow to escape the section's border-radius clip */
  .ne-multicolumn.ne-section { overflow: visible; }
  .ne-multicolumn__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-mc-columns, 3), minmax(0, 1fr));
    gap: var(--ne-mc-gap, 24px);
    align-items: start;
    width: 100%;
    padding: 8px; /* headroom for inner glow/shadow */
  }
  @media (max-width: 990px) {
    .ne-multicolumn__grid { grid-template-columns: repeat(var(--ne-mc-columns-tablet, 2), minmax(0, 1fr)); }
  }
  @media (max-width: 640px) {
    .ne-multicolumn__grid { grid-template-columns: repeat(var(--ne-mc-columns-mobile, 1), minmax(0, 1fr)); }
  }

  /* shopify-block wrappers collapse so inner blocks become direct grid items */
  .ne-multicolumn__grid > .shopify-block { display: contents; }

  /* Section-level heading + text span all columns */
  .ne-multicolumn__grid > .ne-block-heading,
  .ne-multicolumn__grid > .ne-block-text {
    grid-column: 1 / -1;
    justify-self: var(--ne-mc-align, center);
    max-width: 720px;
    width: 100%;
  }
  .ne-multicolumn__grid[style*="--ne-mc-align: center"] > .ne-block-heading,
  .ne-multicolumn__grid > .ne-block-heading { text-align: var(--ne-mc-align, center); }

  /* Top-align column tops: when columns stretch to equal height (set by
     the grid track based on the tallest column's content), Group's own
     justify-content: center default would vertically-center shorter
     columns' content — making icons sit at different y-positions
     between columns with different text lengths. Force flex-start so
     every column's icon/heading/text starts at the top edge.

     Descendant combinator (not `>`) — Shopify wraps each theme-block
     in `<div class="shopify-block-...">` which has `display: contents`
     (line above) but the DOM still has the wrapper, so direct-child
     `>` from grid → .ne-group would miss. Descendant matches through
     the wrapper. */
  .ne-multicolumn__grid .ne-group {
    height: 100%;
    min-width: 0;
    justify-content: flex-start !important;
  }

  /* Force inner blocks (icon, heading, text) to NOT stretch vertically —
     keep their intrinsic heights so the next block starts immediately
     below the previous one. Without this, if the Group has any
     flex-grow on children, content distributes the column's extra
     height. align-self: flex-start guarantees top-anchored stacking. */
  .ne-multicolumn__grid .ne-group > * {
    align-self: stretch;
    flex: 0 0 auto;
  }

  /* Scoped shadow/glow — strong enough to read on dark schemes */
  .ne-multicolumn.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-multicolumn.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-multicolumn.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-multicolumn.ne-multicolumn--custom-mobile-pad {
      padding-top: var(--ne-multicolumn-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-multicolumn-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:multicolumn */

/* START_SECTION:newsletter (INDEX:43) */
.ne-newsletter__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-nl-content-gap, 16px);
    width: 100%;
    max-width: var(--ne-nl-content-w, 600px);
    margin-inline: auto;
  }
  /* Section-level alignment (Rule 126) — sets cross-axis + inheritable
     vars for atom blocks. Child modifier class still wins at element
     scope, so per-block overrides continue to work. */
  .ne-newsletter--align-left   .ne-newsletter__inner { align-items: flex-start; --ne-tp--align: left;   --ne-bw-justify: flex-start; }
  .ne-newsletter--align-center .ne-newsletter__inner { align-items: center;     --ne-tp--align: center; --ne-bw-justify: center; }
  .ne-newsletter--align-right  .ne-newsletter__inner { align-items: flex-end;   --ne-tp--align: right;  --ne-bw-justify: flex-end; }

  /* Atom-block wrappers collapse to their inner element (Rule 70). */
  .ne-newsletter__inner > .shopify-block { display: contents; }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-newsletter.ne-newsletter--custom-mobile-pad {
      padding-top: var(--ne-newsletter-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-newsletter-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:newsletter */

/* START_SECTION:page (INDEX:45) */
/* Background overlay (over background-image, under content) */
  .ne-page__overlay {
    position: absolute;
    inset: 0;
    background-color: var(--ne-page-overlay-color, #000);
    opacity: var(--ne-page-overlay-alpha, 0);
    pointer-events: none;
    z-index: 0;
    border-radius: inherit;
  }
  .ne-page--has-overlay .ne-page__inner { position: relative; z-index: 1; }

  .ne-page__inner {
    max-width: var(--ne-page-content-width, 800px);
    margin-inline: auto;
    padding-inline: calc(var(--page-margin, 20px) + var(--ne-section-inner-pad-x, 0px));
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-6, 24px);
  }
  /* Custom mobile padding — class-modifier driven (no JS dependency,
     survives section reload in customizer). Specificity (0,2,0) >
     base .ne-section (0,1,0) on mobile media query. */
  @media (max-width: 749px) {
    .ne-page.ne-page--custom-mobile-pad {
      padding-top: var(--ne-page-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-page-mobile-pad-bottom, 24px) !important;
    }
  }
  /* DOM source order = "before" mode (title → blocks → content).
     For "after" mode, only blocks needs to flip to the end via flex
     order. Title and content keep natural source order. */
  .ne-page__inner--blocks-after .ne-page__blocks { order: 1; }
  .ne-page__title {
    margin: 0;
  }
  .ne-page--align-left  .ne-page__title { text-align: left; }
  .ne-page--align-center .ne-page__title { text-align: center; }
  .ne-page--align-right  .ne-page__title { text-align: right; }
  .ne-page__content {
    line-height: 1.7;
    color: var(--ne-scheme-text);
  }
  .ne-page__content > *:first-child { margin-top: 0; }
  .ne-page__content > *:last-child  { margin-bottom: 0; }
  .ne-page__content h2,
  .ne-page__content h3,
  .ne-page__content h4 {
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    margin-top: var(--ne-space-6, 24px);
  }
  .ne-page__content img {
    max-width: 100%;
    height: auto;
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-page__content a {
    color: var(--ne-scheme-link, var(--ne-scheme-accent));
    text-decoration: underline;
    text-underline-offset: 2px;
  }
  .ne-page__content blockquote {
    border-left: 3px solid var(--ne-scheme-accent);
    padding-left: var(--ne-space-4, 16px);
    margin: var(--ne-space-4, 16px) 0;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  .ne-page__blocks {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
  }
  .ne-page__blocks > .shopify-block { display: contents; }
/* END_SECTION:page */

/* START_SECTION:password (INDEX:46) */
.ne-password {
    position: relative;
    /* Span full .shopify-section grid (= viewport-edge) so background-image
       and overlay extend to true viewport edges, not just the content
       column. Default .shopify-section > * gets grid-column: 2 which
       leaves the page-margin gutters showing through. */
    grid-column: 1 / -1 !important;
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ne-space-8, 32px);
    overflow: clip;
    border-radius: 0 !important;
  }

  /* Background overlay */
  .ne-password__overlay {
    position: absolute;
    inset: 0;
    background-color: var(--ne-pw-overlay-color, #000);
    opacity: var(--ne-pw-overlay-alpha, 0);
    pointer-events: none;
    z-index: 0;
  }
  .ne-password--has-overlay .ne-password__inner { position: relative; z-index: 1; }

  .ne-password__inner {
    max-width: 540px;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--ne-space-4, 16px);
    position: relative;
    z-index: 1;
  }
  .ne-password--align-left   .ne-password__inner { align-items: flex-start; text-align: left; }
  .ne-password--align-center .ne-password__inner { align-items: center; text-align: center; }
  .ne-password--align-right  .ne-password__inner { align-items: flex-end; text-align: right; }

  .ne-password__card {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    align-items: inherit;
    padding: var(--ne-space-10, 40px);
    border-radius: var(--ne-pw-card-radius, 24px);
  }
  /* Card surface: glass (opt-in, default true) */
  .ne-password--card-glass .ne-password__card {
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    /* No border — white glass-border reads as a bottom contour on dark
       bg (user 2026-05-24). */
    border: 0;
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 24px)) saturate(var(--ne-glass-saturate, 140%));
  }
  .ne-password--card-tint .ne-password__card {
    background: color-mix(in srgb, var(--ne-scheme-accent) 6%, var(--ne-glass-bg, rgba(255,255,255,0.06)));
    border-color: color-mix(in srgb, var(--ne-scheme-accent) 18%, var(--ne-glass-border, rgba(255,255,255,0.12)));
  }
  /* Card glow / shadow */
  .ne-password--card-glow .ne-password__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 36px color-mix(in srgb, var(--ne-scheme-accent) 28%, transparent),
      0 8px 24px rgba(0,0,0,0.18);
  }
  .ne-password--card-shadow .ne-password__card {
    box-shadow: 0 16px 48px rgba(0,0,0,0.32);
  }
  .ne-password--card-glow.ne-password--card-shadow .ne-password__card {
    box-shadow:
      0 0 0 1px color-mix(in srgb, var(--ne-scheme-accent) 30%, transparent),
      0 0 36px color-mix(in srgb, var(--ne-scheme-accent) 28%, transparent),
      0 16px 48px rgba(0,0,0,0.32);
  }

  /* X-Ray cursor effect — gaussian-falloff radial spotlight reveals
     BG-image through overlay. Pattern mirrors theme-global X-Ray
     (assets/critical.css line 297+). Active state via JS class — initial
     state has no mask (overlay fully opaque). On mouseleave, class is
     removed and overlay returns to fully opaque. */
  .ne-password--xray .ne-password__overlay {
    --ne-xray-mouse-x: 50%;
    --ne-xray-mouse-y: 50%;
    transition: -webkit-mask-image 0.08s linear, mask-image 0.08s linear;
  }
  .ne-password--xray.ne-password--xray-active .ne-password__overlay {
    -webkit-mask-image: radial-gradient(
      ellipse calc(var(--ne-xray-size, 300px) * 2) calc(var(--ne-xray-size, 300px) * 2)
      at var(--ne-xray-mouse-x) var(--ne-xray-mouse-y),
      rgba(0,0,0,0)    0%,
      rgba(0,0,0,0.12) 20%,
      rgba(0,0,0,0.4)  40%,
      rgba(0,0,0,0.72) 62%,
      rgba(0,0,0,0.93) 80%,
      black            100%
    );
    mask-image: radial-gradient(
      ellipse calc(var(--ne-xray-size, 300px) * 2) calc(var(--ne-xray-size, 300px) * 2)
      at var(--ne-xray-mouse-x) var(--ne-xray-mouse-y),
      rgba(0,0,0,0)    0%,
      rgba(0,0,0,0.12) 20%,
      rgba(0,0,0,0.4)  40%,
      rgba(0,0,0,0.72) 62%,
      rgba(0,0,0,0.93) 80%,
      black            100%
    );
  }
  @media (prefers-reduced-motion: reduce) {
    .ne-password--xray .ne-password__overlay { transition: none; }
  }
  /* Without glass: solid card with scheme bg */
  .ne-password:not(.ne-password--card-glass) .ne-password__card {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }

  .ne-password__logo {
    margin-bottom: var(--ne-space-2, 8px);
  }
  .ne-password__logo-img {
    max-width: 200px;
    height: auto;
    display: block;
  }
  .ne-password--align-center .ne-password__logo-img { margin-inline: auto; }
  .ne-password--align-right  .ne-password__logo-img { margin-left: auto; }

  .ne-password__shop-name { margin: 0; }

  .ne-password__heading {
    margin: 0;
    font-size: var(--ne-font-size-xl, 22px);
    font-weight: 500;
    color: var(--ne-scheme-text);
    letter-spacing: -0.01em;
  }

  .ne-password__message {
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    line-height: 1.6;
  }
  .ne-password__message > *:first-child { margin-top: 0; }
  .ne-password__message > *:last-child  { margin-bottom: 0; }

  /* Newsletter form */
  .ne-password__newsletter {
    width: 100%;
    margin-top: var(--ne-space-2, 8px);
  }
  .ne-password__newsletter-text {
    margin: 0 0 var(--ne-space-3, 12px);
    font-size: var(--ne-font-size-sm, 14px);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  .ne-password__form-row {
    display: flex;
    gap: var(--ne-space-2, 8px);
    width: 100%;
  }
  .ne-password__input {
    flex: 1;
    min-width: 0;
    padding: 12px 16px;
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.15));
    border-radius: var(--ne-radius-md, 8px);
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-font-size-sm, 14px);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }
  .ne-password__input:focus {
    outline: none;
    border-color: var(--ne-scheme-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ne-scheme-accent) 18%, transparent);
  }
  .ne-password__submit { flex-shrink: 0; }
  .ne-password__form-success { color: var(--ne-color-success, #10b981); margin: 0 0 var(--ne-space-3, 12px); }
  .ne-password__form-errors  { color: var(--ne-color-error, #ef4444); margin: 0 0 var(--ne-space-3, 12px); font-size: var(--ne-font-size-sm, 14px); }

  /* Password entry (admin) */
  .ne-password__entry {
    width: 100%;
    margin-top: var(--ne-space-4, 16px);
    padding-top: var(--ne-space-4, 16px);
    border-top: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.1));
  }
  .ne-password__toggle {
    background: none;
    border: none;
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
    font-size: var(--ne-font-size-sm, 14px);
    color: var(--ne-scheme-text-muted, currentColor);
    padding: 0;
    transition: color 0.15s ease;
  }
  .ne-password__toggle:hover { color: var(--ne-scheme-accent); }
  .ne-password__form-wrap[hidden] { display: none; }
  .ne-password__form-wrap { margin-top: var(--ne-space-3, 12px); }

  /* Social + copyright */
  .ne-password__social {
    display: flex;
    gap: var(--ne-space-3, 12px);
    align-items: center;
    margin-top: var(--ne-space-4, 16px);
  }
  .ne-password__copyright {
    font-size: var(--ne-font-size-xs, 12px);
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.7;
    margin: 0;
  }

  /* Block-zone inside the card (countdown, custom heading/text/etc.) */
  .ne-password__blocks {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 12px);
    width: 100%;
  }
  .ne-password__blocks > .shopify-block { display: contents; }

  /* Countdown-Timer scaling — fit all 4 digit boxes in one row inside
     the narrow password card. Override block defaults at section scope. */
  .ne-password__blocks .ne-cd-timer {
    flex-wrap: nowrap !important;
    gap: 6px !important;
  }
  .ne-password__blocks .ne-cd-timer__unit {
    flex: 1 1 0 !important;
    min-width: 0 !important;
    padding: clamp(8px, 1.5vw, 14px) clamp(4px, 1vw, 10px) !important;
  }
  .ne-password__blocks .ne-cd-timer__num {
    font-size: clamp(20px, 5vw, 36px) !important;
    line-height: 1 !important;
  }
  .ne-password__blocks .ne-cd-timer__label {
    font-size: clamp(8px, 1.5vw, 11px) !important;
    margin-top: 4px !important;
  }
  .ne-password__blocks .ne-cd-timer__sep {
    display: none !important;
  }
  /* Mobile: keep all 4 in one row, even tighter values */
  @media (max-width: 599px) {
    .ne-password__blocks .ne-cd-timer {
      gap: 4px !important;
    }
    .ne-password__blocks .ne-cd-timer__unit {
      padding: 8px 2px !important;
    }
    .ne-password__blocks .ne-cd-timer__num {
      font-size: clamp(16px, 6vw, 26px) !important;
    }
    .ne-password__blocks .ne-cd-timer__label {
      font-size: 9px !important;
      letter-spacing: 0.02em !important;
    }
  }

  /* Mobile */
  @media (max-width: 599px) {
    .ne-password { padding: var(--ne-space-4, 16px); }
    .ne-password__card { padding: var(--ne-space-6, 24px); }
    .ne-password__form-row { flex-direction: column; }
    .ne-password__submit { width: 100%; }
  }
/* END_SECTION:password */

/* START_SECTION:physics-gallery (INDEX:47) */
.ne-pgal.ne-section { overflow: visible; }

  .ne-pgal__intro {
    max-width: 720px;
    margin: 0 0 clamp(24px, 4vw, 48px);
    padding-inline: var(--ne-section-inner-pad-x, 24px);
  }
  .ne-pgal__title {
    margin: 0 0 10px;
    font-size: var(--ne-h2--size-responsive, clamp(28px, 4vw, 44px));
  }
  .ne-pgal__sub {
    color: var(--ne-scheme-text-muted, currentColor);
    font-size: clamp(15px, 1.4vw, 18px);
    line-height: 1.6;
  }

  .ne-pgal__track {
    display: flex;
    gap: var(--ne-pgal-gap, 24px);
    overflow-x: auto;
    overflow-y: hidden;
    padding-inline: var(--ne-section-inner-pad-x, 24px);
    padding-block: 8px;
    scrollbar-width: none;
    -ms-overflow-style: none;
    cursor: grab;
    scroll-behavior: smooth;
    /* native horizontal scroll = keyboard / touch / trackpad accessible */
    touch-action: pan-x;
  }
  .ne-pgal__track::-webkit-scrollbar { display: none; }
  .ne-pgal__track.is-dragging {
    cursor: grabbing;
    scroll-behavior: auto;
    scroll-snap-type: none;
    user-select: none;
  }
  .ne-pgal__track:focus-visible {
    outline: 2px solid var(--ne-scheme-accent, #6366f1);
    outline-offset: 4px;
  }
  .ne-pgal--snap .ne-pgal__track { scroll-snap-type: x proximity; }
  .ne-pgal--snap .ne-pgal__item { scroll-snap-align: start; }

  .ne-pgal__item {
    position: relative;
    flex: 0 0 var(--ne-pgal-item-w, 340px);
    width: var(--ne-pgal-item-w, 340px);
  }
  /* Cursor-parallax moves the WHOLE card. The eased transition means entering
     anywhere glides (never snaps); offset is ~0 at the gallery centre and grows
     toward the edges. */
  .ne-pgal--parallax .ne-pgal__item {
    transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
  }
  /* Vertical breathing room so card parallax never clips against the track's
     overflow (overflow-x:auto forces overflow-y to clip). */
  .ne-pgal--parallax .ne-pgal__track { padding-block: 52px; }
  /* Guarantee the last item can scroll fully into view (trailing gutter
     beyond the track's inline padding — fixes "last item cut off"). */
  .ne-pgal__item:last-child { margin-inline-end: var(--ne-section-inner-pad-x, 24px); }
  .ne-pgal__item-link { text-decoration: none; color: inherit; display: block; -webkit-user-drag: none; user-select: none; }

  .ne-pgal__media {
    position: relative;
    border-radius: var(--ne-card-radius, var(--ne-radius-lg, 16px));
    overflow: clip;
    background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.04));
    aspect-ratio: 3 / 4;
  }
  .ne-pgal--ratio-square .ne-pgal__media { aspect-ratio: 1 / 1; }
  .ne-pgal--ratio-landscape .ne-pgal__media { aspect-ratio: 4 / 3; }
  .ne-pgal--ratio-natural .ne-pgal__media { aspect-ratio: auto; }

  .ne-pgal__img,
  .ne-pgal__ph {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    -webkit-user-drag: none;
    user-select: none;
  }
  .ne-pgal--ratio-natural .ne-pgal__img { height: auto; }
  .ne-pgal__ph { display: grid; place-items: center; }
  .ne-pgal__ph-svg { width: 55%; height: 55%; opacity: 0.4; }

  .ne-pgal__caption { padding: 14px 4px 4px; }
  /* Overlay caption (over the image bottom, like collection cards) */
  .ne-pgal--caption-overlay .ne-pgal__caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 16px;
    color: #fff;
    border-radius: 0 0 var(--ne-card-radius, var(--ne-radius-lg, 16px)) var(--ne-card-radius, var(--ne-radius-lg, 16px));
  }
  .ne-pgal--caption-gradient .ne-pgal__caption {
    background: linear-gradient(to top, rgba(0, 0, 0, 0.72), transparent);
  }
  .ne-pgal--caption-overlay .ne-pgal__caption-price,
  .ne-pgal--caption-overlay .ne-pgal__caption-text { color: rgba(255, 255, 255, 0.85); }
  .ne-pgal__caption-title { margin: 0; font-size: 16px; line-height: 1.3; }
  .ne-pgal__caption-price,
  .ne-pgal__caption-text {
    display: block;
    margin-top: 4px;
    font-size: 14px;
    color: var(--ne-scheme-text-muted, currentColor);
  }
  .ne-pgal__empty {
    padding: 48px 24px;
    text-align: center;
    color: var(--ne-scheme-text-muted, currentColor);
    border: 1px dashed var(--ne-scheme-border, rgba(128, 128, 128, 0.4));
    border-radius: var(--ne-radius-lg, 16px);
    margin-inline: var(--ne-section-inner-pad-x, 24px);
  }

  /* Section-level effects */
  .ne-pgal.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-pgal.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-pgal.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* Mobile: center items (snap-center + symmetric gutters so the first
     and last item sit centered at rest, not left-aligned). */
  @media (max-width: 749px) {
    .ne-pgal__track {
      scroll-snap-type: x mandatory;
      padding-inline: calc((100vw - var(--ne-pgal-item-w-m, 82vw)) / 2);
      gap: 16px;
    }
    .ne-pgal__item {
      flex-basis: var(--ne-pgal-item-w-m, 82vw);
      width: var(--ne-pgal-item-w-m, 82vw);
      scroll-snap-align: center;
    }
    .ne-pgal__item:last-child { margin-inline-end: 0; }
    .ne-pgal__caption { text-align: center; }
  }

  /* Optional edge fade — cards dissolve at the track's left/right edges. */
  .ne-pgal--edge-fade .ne-pgal__track {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-section-inner-pad-x, 24px), #000 calc(100% - var(--ne-section-inner-pad-x, 24px)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-section-inner-pad-x, 24px), #000 calc(100% - var(--ne-section-inner-pad-x, 24px)), transparent 100%);
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-pgal__item { transform: none !important; transition: none; }
    .ne-pgal--parallax .ne-pgal__img { transform: none !important; transition: none; }
  }
/* END_SECTION:physics-gallery */

/* START_SECTION:popup (INDEX:49) */
.ne-popup {
    position: fixed;
    inset: 0;
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    margin: 0;
    padding: 0;
    border: none;
    background: transparent;
    color: var(--ne-scheme-text);
  }
  .ne-popup::backdrop {
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(var(--ne-popup-backdrop-blur, 6px));
    -webkit-backdrop-filter: blur(var(--ne-popup-backdrop-blur, 6px));
  }

  .ne-popup__panel {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: min(var(--ne-popup-max, 500px), calc(100% - 32px));
    max-height: calc(100vh - 64px);
    overflow-y: auto;
    border-radius: var(--ne-popup-radius, 12px);
    padding: var(--ne-popup-pad, 32px);
    /* Solid surface — reads --ne-scheme-bg from the .color-{scheme}
       class on the dialog (cascade). Glass / transparent modifiers
       override below. */
    background: var(--ne-scheme-bg);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-popup--transparent .ne-popup__panel {
    background: transparent !important;
    border: none;
  }
  .ne-popup--glass .ne-popup__panel {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(140%);
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-popup--glass.ne-glass--tinted .ne-popup__panel {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg)) !important;
    border-color: var(--ne-glass-border-tinted, var(--ne-glass-border));
  }

  .ne-popup--shadow .ne-popup__panel {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-popup--glow .ne-popup__panel {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-popup--shadow.ne-popup--glow .ne-popup__panel {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  .ne-popup__close {
    position: absolute;
    top: 12px;
    right: 12px;
    z-index: 2;
    width: 32px;
    height: 32px;
    display: grid;
    place-items: center;
    background: var(--ne-scheme-bg-muted, rgba(0, 0, 0, 0.15));
    border: none;
    border-radius: 50% !important;
    color: var(--ne-scheme-text);
    cursor: pointer;
    padding: 0;
    transition: background 0.15s ease, transform 0.15s ease;
  }
  .ne-popup__close:hover {
    background: var(--ne-scheme-border, rgba(127, 127, 127, 0.25));
    transform: scale(1.08);
  }

  .ne-popup__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-popup-gap, 16px);
  }
  .ne-popup__inner > .shopify-block { display: contents; }

  .ne-popup--align-left .ne-popup__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-popup--align-center .ne-popup__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-popup--align-right .ne-popup__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  @media (max-width: 549px) {
    .ne-popup__panel {
      width: calc(100% - 24px);
      padding: calc(var(--ne-popup-pad, 32px) * 0.7);
    }
  }
/* END_SECTION:popup */

/* START_SECTION:pretext-banner (INDEX:50) */
/* Container ────────────────────────────────────────────────────── */
  /* overflow:clip + overflow-clip-margin lets the section clip the
     animation chars at its bounds (matrix-rain canvas / DOM layers
     stay inside) WHILE giving group blocks' drop-shadow / accent-glow
     a 30px halo to bleed past the section edge. overflow:hidden alone
     was clipping at the exact edge so shadows on content positioned
     near the corners got cut. Modern only — Chrome 90+/Safari 14.1+/
     Firefox 102+ — graceful fallback to plain clip for older browsers. */
  .ne-pb {
    position: relative;
    /* No isolation:isolate — that forms a backdrop-root per CSS Filter
       Effects spec, which traps the theme's global .ne-section--glass
       backdrop-filter inside the section. With isolation removed, glass
       on the section (and on inner group blocks) samples the page-bg
       beyond the section as expected. z-index containment is fine —
       overflow:clip plus absolute-positioned children already keep
       descendants inside the section box. */
    padding: 0;
    overflow: clip;
    overflow-clip-margin: 30px;
    min-height: var(--ne-pb-h-desktop, 120px);
  }
  @media (max-width: 749px) {
    .ne-pb {
      min-height: var(--ne-pb-h-mobile, 80px);
      /* Override --ne-pb-font-size with the mobile value via !important.
         CSS custom properties follow standard cascade rules: the inline
         style declaration on the section (specificity 1,0,0,0) would
         normally beat this stylesheet rule (specificity 0,1,0), so
         without !important the mobile override is silently ignored.
         The JS canvas-mode readNum reads getComputedStyle which honours
         this cascaded value, so both DOM-layer CSS and canvas JS pick
         up the mobile font-size automatically. */
      --ne-pb-font-size: var(--ne-pb-font-size-mobile, 14px) !important;
    }
  }
  .ne-pb.ne-pb--transparent-bg { background-color: transparent !important; }
  /* Glass mode — handled entirely by the theme-global .ne-section--glass
     rule (critical.css). That rule sets background + backdrop-filter +
     border using --ne-glass-bg / --ne-glass-blur / --ne-glass-saturate
     (read live from Theme Settings → Effects → Glassmorphism), and uses
     !important so it overrides the section's inline scheme bg. NO local
     override here — local overrides would shadow the theme vars and
     silently break the global Glassmorphism settings. */
  .ne-pb__inner {
    position: relative;
    width: 100%;
    height: 100%;
    min-height: inherit;
  }
  /* Top + Bottom fade applied to the ANIMATION layers only (canvas + DOM).
     The overlay image is deliberately excluded: it's a separate, often
     shorter element, so masking it with the same gradient fades it at its
     own (small) box edges — jarring against the full-height animation
     (user 2026-06-02). In carved-out / char-compose modes the image is
     hidden anyway and the fade is baked into the SVG carve mask instead.
     Content blocks (headings, group blocks) also stay fully visible.
     Conditional on has-fade so the degenerate gradient when both fades=0
     doesn't paint everything transparent. 4-stop gradient: transparent 0%
     → black at top_fade% → black at (100% - bottom_fade)% → transparent
     100%. top_fade=0 collapses the first two stops to a sharp top edge. */
  .ne-pb--has-fade .ne-pb__canvas,
  .ne-pb--has-fade .ne-pb__dom {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0%, #000 var(--ne-pb-top-fade, 0%), #000 calc(100% - var(--ne-pb-bottom-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0%, #000 var(--ne-pb-top-fade, 0%), #000 calc(100% - var(--ne-pb-bottom-fade, 0%)), transparent 100%);
  }

  /* Canvas layer ─────────────────────────────────────────────────── */
  .ne-pb__canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    z-index: 1;
  }
  /* JS hides canvas when not in use (most non-canvas modes) by adding
     hidden attribute; CSS default is visible. */
  .ne-pb__canvas[hidden] { display: none; }

  /* DOM animation layer ──────────────────────────────────────────── */
  .ne-pb__dom {
    position: absolute;
    inset: 0;
    z-index: 1;
    overflow: hidden;
    font-family: ui-monospace, 'Cascadia Mono', 'JetBrains Mono', 'Source Code Pro', Menlo, Consolas, monospace;
    font-size: var(--ne-pb-font-size, 14px);
    line-height: 1.4;
    color: var(--ne-scheme-accent, currentColor);
    user-select: none;
    pointer-events: none;
  }
  .ne-pb__dom[hidden] { display: none; }
  .ne-pb__track {
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  /* Static marquee — multiple rows fill the section, each row scrolls
     in alternating directions at slightly varied speeds (midjourney-
     style dense text wall). JS injects N rows depending on section
     height; per-row --row-speed multiplier randomises pace. */
  .ne-pb--anim-static-marquee .ne-pb__track {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    padding: 0;
    gap: 0;
  }
  .ne-pb__row {
    flex: 1 1 0;
    min-height: 0;
    display: flex;
    align-items: center;
    white-space: nowrap;
    will-change: transform;
  }
  .ne-pb--anim-static-marquee .ne-pb__row {
    animation: ne-pb-marquee linear infinite;
    animation-duration: calc(var(--row-speed, 1) * (3000s / max(var(--ne-pb-anim-speed, 50), 1)));
  }
  .ne-pb--anim-static-marquee .ne-pb__row--reverse {
    animation-direction: reverse;
  }
  .ne-pb__row > span {
    display: inline-block;
    padding-right: 1.2em;
  }
  @keyframes ne-pb-marquee {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
  }

  /* Jumping letters — grid of cells filling the section. Per-cell
     animation-delay derived from --i (col) and --j (row) gives a
     wave-like phase pattern. */
  .ne-pb--anim-jumping-letters .ne-pb__track {
    display: grid;
    grid-template-columns: repeat(var(--cols, 40), 1fr);
    /* Rows stretch to fill the full section height (minmax floor keeps the
       natural 1.2em line when the row count fills exactly; 1fr absorbs any
       leftover so the grid never leaves an empty band at the bottom even if
       the row count was computed from a smaller height). JS sets --rows. */
    grid-template-rows: repeat(var(--rows, 30), minmax(1.2em, 1fr));
    align-content: stretch;
    width: 100%;
    height: 100%;
    padding: 0;
    gap: 0;
  }
  .ne-pb--anim-jumping-letters .ne-pb__track > span {
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    animation: ne-pb-jump calc(2800ms - var(--ne-pb-anim-speed, 50) * 16ms) ease-in-out infinite;
    animation-delay: calc((var(--i, 0) * 7 + var(--j, 0) * 13) * 28ms);
  }
  @keyframes ne-pb-jump {
    0%, 100% { transform: translateY(0); opacity: 0.55; }
    50%      { transform: translateY(-35%); opacity: 1; }
  }

  /* Scramble reveal — grid of cells, JS cycles random chars and
     locks them to a target sequence (custom_text repeated across the
     grid). Locked cells get the highlight class. */
  .ne-pb--anim-scramble-reveal .ne-pb__track {
    display: grid;
    grid-template-columns: repeat(var(--cols, 40), 1fr);
    /* Rows stretch to fill the full section height (minmax floor keeps the
       natural 1.2em line when the row count fills exactly; 1fr absorbs any
       leftover so the grid never leaves an empty band at the bottom even if
       the row count was computed from a smaller height). JS sets --rows. */
    grid-template-rows: repeat(var(--rows, 30), minmax(1.2em, 1fr));
    align-content: stretch;
    width: 100%;
    height: 100%;
    padding: 0;
    gap: 0;
  }
  .ne-pb--anim-scramble-reveal .ne-pb__track > span {
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0.4;
    line-height: 1;
    transition: opacity 200ms ease, color 200ms ease;
  }
  .ne-pb--anim-scramble-reveal .ne-pb__track > span.is-locked {
    opacity: 1;
    text-shadow: 0 0 8px var(--ne-scheme-accent-glow, transparent);
  }

  /* Scramble pin — like scramble-reveal but the target text appears at a
     pinned position (one-shot reveal). Non-target cells continue cycling
     random chars forever. Locked target cells get a stronger highlight. */
  .ne-pb--anim-scramble-pin .ne-pb__track {
    display: grid;
    grid-template-columns: repeat(var(--cols, 40), 1fr);
    /* Rows stretch to fill the full section height (minmax floor keeps the
       natural 1.2em line when the row count fills exactly; 1fr absorbs any
       leftover so the grid never leaves an empty band at the bottom even if
       the row count was computed from a smaller height). JS sets --rows. */
    grid-template-rows: repeat(var(--rows, 30), minmax(1.2em, 1fr));
    align-content: stretch;
    width: 100%;
    height: 100%;
    padding: 0;
    gap: 0;
  }
  .ne-pb--anim-scramble-pin .ne-pb__track > span {
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0.35;
    line-height: 1;
    transition: opacity 220ms ease, color 220ms ease;
  }
  .ne-pb--anim-scramble-pin .ne-pb__track > span.is-locked {
    opacity: 1;
    text-shadow: 0 0 10px var(--ne-scheme-accent-glow, transparent);
    font-weight: 600;
  }

  /* Content layer ────────────────────────────────────────────────── */
  /* Absolute-positioned anchor — flex alignment was getting overridden
     by the group block's own internal layout (align-self / margin auto
     / etc). Absolute positioning per anchor is deterministic and
     doesn't depend on child styles. */
  .ne-pb__content {
    position: absolute;
    z-index: 3;
    pointer-events: none;
    max-width: calc(100% - 32px);
    /* Explicit no-mask — content sits inside .ne-pb__inner which has
       the bottom-fade mask. CSS mask isn't inherited by default but
       this defends against any cascade leak so block-level shadow /
       group glow / heading stays fully visible regardless of fade. */
    -webkit-mask: none !important;
    mask: none !important;
  }
  .ne-pb__content > * { pointer-events: auto; }
  .ne-pb__content > .shopify-block { display: block; }

  .ne-pb__content--pos-top-left      { top: 16px; left: 16px; }
  .ne-pb__content--pos-top-center    { top: 16px; left: 50%; transform: translateX(-50%); }
  .ne-pb__content--pos-top-right     { top: 16px; right: 16px; }
  .ne-pb__content--pos-middle-left   { top: 50%; left: 16px; transform: translateY(-50%); }
  .ne-pb__content--pos-center        { top: 50%; left: 50%; transform: translate(-50%, -50%); }
  .ne-pb__content--pos-middle-right  { top: 50%; right: 16px; transform: translateY(-50%); }
  .ne-pb__content--pos-bottom-left   { bottom: 16px; left: 16px; }
  .ne-pb__content--pos-bottom-center { bottom: 16px; left: 50%; transform: translateX(-50%); }
  .ne-pb__content--pos-bottom-right  { bottom: 16px; right: 16px; }

  /* Image overlay ────────────────────────────────────────────────── */
  /* Desktop: width = image_size% of section width, height auto (natural
     aspect, may overflow on landscape sections + portrait artwork —
     section's overflow:clip handles visual clipping). char-compose
     samples the same iRect so all three modes match visually. */
  .ne-pb__image {
    position: absolute;
    z-index: 2;
    pointer-events: none;
    width: var(--ne-pb-image-size, 50%);
    height: auto;
  }
  /* Mobile: switch to height-anchor sizing. Mobile sections are
     typically portrait (narrow + tall); a width-anchored image at the
     same % gives a much smaller visual than the landscape desktop
     equivalent. Anchoring to height means image_size% reads as % of
     section HEIGHT — image fills the mobile section similarly to how
     the desktop equivalent fills its landscape section. iRect updates
     accordingly, so char-compose + carved-out re-sample at the new
     dimensions automatically. */
  @media (max-width: 749px) {
    .ne-pb__image {
      width: auto;
      height: var(--ne-pb-image-size, 50%);
      max-width: 100%;
    }
  }
  /* In char-compose mode the canvas consumes the image — hide the
     overlay visually but keep it in layout (visibility:hidden, not
     display:none) so getBoundingClientRect() can read its position
     and size. The char-compose JS uses that rect to sample the image
     at the correct location, honoring image_position + image_size. */
  .ne-pb--image-char-compose .ne-pb__image,
  .ne-pb--image-carved-out .ne-pb__image {
    visibility: hidden;
    opacity: 0;
    pointer-events: none;
  }
  .ne-pb--image-char-compose .ne-pb__dom   { display: none; }

  /* Image positions (9 anchor points). Default centers; named
     positions override via inset + transform. */
  .ne-pb--image-pos-top-left .ne-pb__image    { top: 8px;  left: 16px;  bottom: auto; right: auto; }
  .ne-pb--image-pos-top-center .ne-pb__image  { top: 8px;  left: 50%;   transform: translateX(-50%); }
  .ne-pb--image-pos-top-right .ne-pb__image   { top: 8px;  right: 16px; bottom: auto; left: auto; }
  .ne-pb--image-pos-middle-left .ne-pb__image { top: 50%;  left: 16px;  transform: translateY(-50%); }
  .ne-pb--image-pos-center .ne-pb__image      { top: 50%;  left: 50%;   transform: translate(-50%, -50%); }
  .ne-pb--image-pos-middle-right .ne-pb__image{ top: 50%;  right: 16px; transform: translateY(-50%); }
  .ne-pb--image-pos-bottom-left .ne-pb__image { bottom: 8px; left: 16px; top: auto; right: auto; }
  .ne-pb--image-pos-bottom-center .ne-pb__image{ bottom: 8px; left: 50%; top: auto; transform: translateX(-50%); }
  .ne-pb--image-pos-bottom-right .ne-pb__image{ bottom: 8px; right: 16px; top: auto; left: auto; }

  /* Carved-out — SVG mask approach (shape-accurate, customizer-safe).
     Inline SVG <mask> with mask-type:luminance: a white rect is the
     base coverage, then the image is drawn on top with all RGB forced
     to 0 (black via colour-matrix filter). Final mask luminance:
     white where image is transparent (visible), black where image is
     opaque (hidden). JS sizes/positions the SVG <image> to match the
     rendered <img>. Mask is applied to canvas + DOM layers — chars
     get clipped to the exact image silhouette. */
  .ne-pb__svg-mask {
    position: absolute;
    width: 0;
    height: 0;
    pointer-events: none;
    overflow: visible;
  }
  .ne-pb--image-carved-out .ne-pb__canvas,
  .ne-pb--image-carved-out .ne-pb__dom {
    /* Mask URL set per-section via inline style on the section element
       through the data-mask-url attribute; CSS picks it up from the
       --ne-pb-mask-url custom property which Liquid emits. */
    -webkit-mask: var(--ne-pb-mask-url);
    mask: var(--ne-pb-mask-url);
  }

  /* Effects ──────────────────────────────────────────────────────── */
  .ne-pb--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.3)); }
  .ne-pb--glow   { box-shadow: var(--ne-section-glow-shadow, 0 0 30px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-pb--shadow.ne-pb--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.3)),
      var(--ne-section-glow-shadow, 0 0 30px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
/* END_SECTION:pretext-banner */

/* START_SECTION:product (INDEX:51) */
/* =============================================================================
     PRODUCT SECTION v7.0 — Layout Engine
     ============================================================================= */

  .ne-product {
    --ne-pd-card-radius: 12px;
    color: var(--ne-scheme-text);
    /* Force overflow visible on desktop so card glow/shadow can escape
       the section bounds — !important to defeat any inherited clip. */
    overflow: visible !important;
  }
  @media (max-width: 749px) {
    /* No overflow clipping — let card glow/shadow escape on mobile too.
       Long titles are handled by word-break: break-word on the title block. */
    .ne-product { overflow: visible !important; }
  }

  /* Optional horizontal section padding — additive to the global section
     padding-inline. When 0 (default), nothing is added. */
  .ne-product { padding-inline: var(--ne-pd-padding-x, 0px); }

  /* Suppress wrapper bg paint from .color-scheme-X (Rule 173) — surface
     comes from the section's transparent_bg / glass / cards directly. */
  .ne-product[class*="color-"] { background: transparent !important; }

  /* Section-level border radius: consume --section-border-radius var
     emitted by Liquid when use_global_radius is OFF. Falls through to
     the theme's global radius var. Full-width modes have no radius
     because the section paints edge-to-edge. */
  .ne-product {
    border-radius: var(--section-border-radius, var(--ne-radius-global, 0px));
  }
  .ne-product.ne-section--full-width,
  .ne-product.ne-section--full-width .ne-product__card,
  .ne-product.ne-section--full-width .ne-product__media-main {
    border-radius: 0 !important;
  }
  @media (max-width: 749px) {
    .ne-product.ne-section--full-width-mobile,
    .ne-product.ne-section--full-width-mobile .ne-product__card,
    .ne-product.ne-section--full-width-mobile .ne-product__media-main {
      border-radius: 0 !important;
    }
  }

  /* Cards — applied to columns (split/stacked) or unified wrapper */
  .ne-product__card {
    border-radius: var(--ne-pd-card-radius, 12px);
    overflow: hidden;
  }
  /* In unified mode the SAME card wraps the media + info columns. Keep
     overflow visible so sticky on the media-col can engage — overflow:
     hidden on any ancestor breaks position: sticky. */
  .ne-product--layout-unified .ne-product__card { overflow: visible; }
  /* Surface-specific card styling. !important on padding to defeat
     anything that might zero it out (some surface-modifier collisions
     in customizer). */
  .ne-product--card-solid .ne-product__card,
  .ne-product--card-glass .ne-product__card,
  .ne-product--card-transparent .ne-product__card {
    padding: var(--ne-space-5) !important;
  }
  .ne-product--card-solid .ne-product__card {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border);
  }
  /* Custom color scheme → use the EXACT scheme background (no +8% surface
     lighten) so the card matches the chosen scheme. Global scheme keeps the
     auto-lightened surface above so cards lift off the page. (0,3,0) > (0,2,0). */
  .ne-product--card-exact-bg.ne-product--card-solid .ne-product__card {
    background: var(--ne-scheme-bg);
  }
  .ne-product--card-glass .ne-product__card {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border — the 1px white glass-border reads as a bottom contour
       line on dark bg (product-card fix, user 2026-05-24). */
    border: 0;
  }
  .ne-product--card-glass-tint .ne-product__card {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.06)));
  }
  .ne-product--card-transparent .ne-product__card {
    background: transparent;
    border: 1px solid var(--ne-scheme-border);
  }
  .ne-product--card-none .ne-product__card {
    background: transparent;
    border: none;
    padding: 0;
    overflow: visible;
  }

  /* Per-card glow / shadow.
     - Split / stacked: each col-wrap gets its own effect.
     - Unified: only ONE shared card → media_effects controls the
       wrapper; info_effects is hidden in schema (visible_if).
     The :not(unified) prefix prevents double-application (effect
     was previously hitting BOTH the wrap AND the inner col).  */
  .ne-product:not(.ne-product--layout-unified).ne-product--media-fx-glow .ne-product__col-wrap--media {
    box-shadow: 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-product:not(.ne-product--layout-unified).ne-product--media-fx-shadow .ne-product__col-wrap--media {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  }
  .ne-product:not(.ne-product--layout-unified).ne-product--media-fx-both .ne-product__col-wrap--media {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-product:not(.ne-product--layout-unified).ne-product--info-fx-glow .ne-product__col-wrap--info {
    box-shadow: 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-product:not(.ne-product--layout-unified).ne-product--info-fx-shadow .ne-product__col-wrap--info {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  }
  .ne-product:not(.ne-product--layout-unified).ne-product--info-fx-both .ne-product__col-wrap--info {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  /* Unified: single shared card, media_effects drives it. */
  .ne-product--layout-unified.ne-product--media-fx-glow .ne-product__unified {
    box-shadow: 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-product--layout-unified.ne-product--media-fx-shadow .ne-product__unified {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
  }
  .ne-product--layout-unified.ne-product--media-fx-both .ne-product__unified {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }

  /* Layout: split (2-col grid) */
  .ne-product--layout-split .ne-product__layout,
  .ne-product--layout-unified .ne-product__layout {
    display: grid;
    grid-template-columns: var(--ne-pd-media-pct, 50%) 1fr;
    gap: var(--ne-pd-gap, 40px);
    align-items: start;
  }
  /* Sticky needs stretched cells so the media-col has room to live
     while info scrolls. Without stretch, the cell is exactly its
     content-height and sticky has no scroll-distance to engage. */
  .ne-product--scroll-sticky.ne-product--layout-split .ne-product__layout,
  .ne-product--scroll-sticky.ne-product--layout-unified .ne-product__layout {
    align-items: stretch;
  }

  /* Swap columns toggle — info on left, media on right. The grid-template
     is flipped so media keeps its user-set % even when on the right side.
     For stacked layouts, swap means info above image (column-reverse).
     !important defeats the mobile media-query collapse rule which would
     otherwise force grid-template-columns: 1fr at <989px. */
  .ne-product--swap-cols.ne-product--layout-split .ne-product__layout,
  .ne-product--swap-cols.ne-product--layout-unified .ne-product__layout {
    grid-template-columns: 1fr var(--ne-pd-media-pct, 50%) !important;
  }
  .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--info,
  .ne-product--swap-cols.ne-product--layout-unified .ne-product__info-col {
    grid-column: 1 !important;
    grid-row: 1 !important;
  }
  .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--media,
  .ne-product--swap-cols.ne-product--layout-unified .ne-product__media-col {
    grid-column: 2 !important;
    grid-row: 1 !important;
  }
  .ne-product--swap-cols.ne-product--layout-stacked .ne-product__layout {
    flex-direction: column-reverse !important;
  }
  /* Mobile collapse: even with swap, single-column means stacked.
     The selectors here MUST have at least the same specificity as the
     desktop swap rules above (which use .ne-product--swap-cols.ne-product--layout-X)
     — otherwise the desktop !important on grid-row: 1 keeps both cells
     in row 1, leaving the second cell empty/clipped on mobile. */
  @media (max-width: 989px) {
    .ne-product--swap-cols.ne-product--layout-split .ne-product__layout,
    .ne-product--swap-cols.ne-product--layout-unified .ne-product__layout {
      grid-template-columns: 1fr !important;
    }
    .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--info,
    .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--media,
    .ne-product--swap-cols.ne-product--layout-unified .ne-product__info-col,
    .ne-product--swap-cols.ne-product--layout-unified .ne-product__media-col {
      grid-column: 1 !important;
      grid-row: auto !important;
    }
    /* Image first (DOM order), description after — same as no-swap mobile. */
    .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--media,
    .ne-product--swap-cols.ne-product--layout-unified .ne-product__media-col {
      order: 1;
    }
    .ne-product--swap-cols.ne-product--layout-split .ne-product__col-wrap--info,
    .ne-product--swap-cols.ne-product--layout-unified .ne-product__info-col {
      order: 2;
    }
  }

  /* Sticky in stacked mode: image acts as a background layer that the
     description scrolls OVER. Image is first in document order and
     sticky at viewport-top (z-index 0). Info-col has higher z-index
     so as the user scrolls, info rises and covers the image. With a
     card surface on the info-col its bg occludes the image; without
     surface it remains visible behind the text. */
  .ne-product--scroll-sticky.ne-product--layout-stacked .ne-product__col-wrap--media,
  .ne-product--scroll-sticky.ne-product--layout-stacked .ne-product__media-col {
    position: sticky;
    top: var(--ne-content-top-offset, calc(var(--ne-header-height, 80px) + var(--ne-space-4, 16px)));
    z-index: 0;
    align-self: start;
  }
  .ne-product--scroll-sticky.ne-product--layout-stacked .ne-product__col-wrap--info {
    position: relative;
    z-index: 1;
  }

  /* Layout: stacked (1-col vertical). Force flex column + nuke any grid
     template that may linger from a previous mode in customizer cache.
     gap uses the theme's global Section Abstand directly (no fallback
     to local --ne-pd-gap) so user expectation matches reality. */
  .ne-product--layout-stacked .ne-product__layout {
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: 1fr !important;
    gap: var(--ne-section-spacing, 16px) !important;
    align-items: stretch;
  }
  .ne-product--layout-stacked .ne-product__col-wrap {
    width: 100%;
    max-width: none;
  }
  .ne-product--layout-stacked .ne-product__media-col,
  .ne-product--layout-stacked .ne-product__media-main {
    width: 100%;
    max-width: none;
  }

  /* Layout: unified — one shared card wraps the 2-col grid inside */
  .ne-product--layout-unified .ne-product__unified {
    /* card class supplies bg + radius; layout grid lives inside */
  }

  /* Sticky scroll behavior — media column sticks while info scrolls.
     Works in BOTH split (col-wrap--media) and unified (media-col direct
     child of layout grid) modes. align-self: start anchors the sticky
     element at the top of the row track. */
  .ne-product--scroll-sticky .ne-product__col-wrap--media,
  .ne-product--layout-unified.ne-product--scroll-sticky .ne-product__media-col {
    align-self: start;
    position: sticky;
    top: var(--ne-content-top-offset, calc(var(--ne-header-height, 80px) + var(--ne-space-4, 16px)));
    max-height: calc(100vh - var(--ne-header-height, 80px) - var(--ne-space-8, 32px));
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: thin;
  }
  /* Hide scrollbar visually, keep functionality */
  .ne-product--scroll-sticky .ne-product__col-wrap--media::-webkit-scrollbar,
  .ne-product--layout-unified.ne-product--scroll-sticky .ne-product__media-col::-webkit-scrollbar { width: 4px; }
  .ne-product--scroll-sticky .ne-product__col-wrap--media::-webkit-scrollbar-track,
  .ne-product--layout-unified.ne-product--scroll-sticky .ne-product__media-col::-webkit-scrollbar-track { background: transparent; }
  .ne-product--scroll-sticky .ne-product__col-wrap--media::-webkit-scrollbar-thumb,
  .ne-product--layout-unified.ne-product--scroll-sticky .ne-product__media-col::-webkit-scrollbar-thumb {
    background: var(--ne-scheme-border);
    border-radius: 2px;
  }

  /* =============================================================================
     MEDIA GALLERY
     ============================================================================= */

  .ne-product__media-col {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }
  .ne-product__media-main {
    position: relative;
    overflow: hidden;
    /* Image radius is now separate from the card box (--ne-pd-image-radius,
       driven by the repurposed image-radius setting). Falls back to the card
       radius if the var isn't set. */
    border-radius: var(--ne-pd-image-radius, var(--ne-pd-card-radius, 12px));
    /* Transparent media-main lets the section's own bg show through
       transparent product images (PNGs with alpha). Earlier revisions
       used --ne-glass-bg here which painted a faint white tint behind
       the image — visible as a soft backdrop on PNGs with cut-out
       backgrounds (user report 2026-05-23). The card-surface modifier
       handles any surface paint at the column-wrap level; this inner
       wrapper just needs to clip + radius. */
    background: transparent;
    width: 100%;
  }
  .ne-product__media-viewer {
    position: relative;
    width: 100%;
  }
  .ne-product__media-viewer:focus-visible {
    outline: 2px solid var(--ne-scheme-accent);
    outline-offset: 2px;
  }
  .ne-product__media-slide {
    display: none;
  }
  .ne-product__media-slide--active {
    display: block;
  }
  .ne-product__media-zoom {
    display: block;
    width: 100%;
    background: none;
    border: none;
    padding: 0;
    cursor: zoom-in;
  }
  .ne-product__media-img {
    width: 100%;
    height: auto;
    display: block;
    object-fit: cover;
  }
  /* Aspect-ratio modes — class-based so we don't depend on inline-style
     attribute parsing. natural keeps the image's intrinsic ratio. */
  .ne-product--aspect-1-1   .ne-product__media-main { aspect-ratio: 1 / 1; }
  .ne-product--aspect-4-5   .ne-product__media-main { aspect-ratio: 4 / 5; }
  .ne-product--aspect-3-4   .ne-product__media-main { aspect-ratio: 3 / 4; }
  .ne-product--aspect-16-9  .ne-product__media-main { aspect-ratio: 16 / 9; }

  /* When a non-natural aspect-ratio is forced, the inner layers must
     fill the cell — otherwise the image keeps its natural ratio and
     the cell looks empty below. */
  .ne-product--aspect-1-1 .ne-product__media-viewer,
  .ne-product--aspect-4-5 .ne-product__media-viewer,
  .ne-product--aspect-3-4 .ne-product__media-viewer,
  .ne-product--aspect-16-9 .ne-product__media-viewer,
  .ne-product--aspect-1-1 .ne-product__media-slide--active,
  .ne-product--aspect-4-5 .ne-product__media-slide--active,
  .ne-product--aspect-3-4 .ne-product__media-slide--active,
  .ne-product--aspect-16-9 .ne-product__media-slide--active {
    position: absolute;
    inset: 0;
  }
  .ne-product--aspect-1-1 .ne-product__media-zoom,
  .ne-product--aspect-4-5 .ne-product__media-zoom,
  .ne-product--aspect-3-4 .ne-product__media-zoom,
  .ne-product--aspect-16-9 .ne-product__media-zoom,
  .ne-product--aspect-1-1 .ne-product__media-img,
  .ne-product--aspect-4-5 .ne-product__media-img,
  .ne-product--aspect-3-4 .ne-product__media-img,
  .ne-product--aspect-16-9 .ne-product__media-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .ne-product__media-placeholder {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ne-space-12);
    color: var(--ne-scheme-text-muted);
  }

  .ne-product__media-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    border: none;
    border-radius: var(--ne-radius-full);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    opacity: 0;
    transition: opacity var(--ne-transition-fast), background var(--ne-transition-fast);
    z-index: 2;
  }
  .ne-product__media-main:hover .ne-product__media-nav,
  .ne-product__media-viewer:focus-visible ~ .ne-product__media-nav,
  .ne-product__media-nav:focus-visible {
    opacity: 1;
  }
  .ne-product__media-nav:hover { background: rgba(0, 0, 0, 0.75); }
  .ne-product__media-nav--prev { left: var(--ne-space-3); }
  .ne-product__media-nav--next { right: var(--ne-space-3); }
  /* Always-visible on touch devices */
  @media (hover: none) {
    .ne-product__media-nav { opacity: 1; background: rgba(0, 0, 0, 0.45); }
  }

  .ne-product__badges {
    position: absolute;
    top: var(--ne-space-3);
    left: var(--ne-space-3);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
    z-index: 1;
  }

  /* Certification / emblem strip (metafield-driven, see media-col markup).
     Overlay variants float on the media-main; pointer-events pass through
     to the zoom button underneath. */
  .ne-product__certs {
    display: flex;
    align-items: center;
    /* NEVER wrap — when the row runs out of space (mobile), the icons
       shrink proportionally instead (user 2026-07-23). Shrink mechanics
       live on the img rule below (transferred-size: max-height caps the
       size, min-width:0 + flex-shrink lets the row compress each icon
       keeping its aspect ratio). */
    flex-wrap: nowrap;
    gap: var(--ne-space-3);
    pointer-events: none;
  }
  .ne-product__certs--bottom-left,
  .ne-product__certs--bottom-center,
  .ne-product__certs--bottom-right,
  .ne-product__certs--top-left,
  .ne-product__certs--top-center,
  .ne-product__certs--top-right {
    position: absolute;
    z-index: 1;
    max-width: calc(100% - 2 * var(--ne-space-3));
  }
  .ne-product__certs--bottom-left,
  .ne-product__certs--bottom-center,
  .ne-product__certs--bottom-right { bottom: var(--ne-space-3); }
  .ne-product__certs--top-left,
  .ne-product__certs--top-center,
  .ne-product__certs--top-right { top: var(--ne-space-3); }
  .ne-product__certs--bottom-left,
  .ne-product__certs--top-left { left: var(--ne-space-3); }
  .ne-product__certs--bottom-right,
  .ne-product__certs--top-right { right: var(--ne-space-3); justify-content: flex-end; }
  .ne-product__certs--bottom-center,
  .ne-product__certs--top-center {
    left: 50%;
    transform: translateX(-50%);
    justify-content: center;
    width: max-content;
  }
  .ne-product__certs--below,
  .ne-product__certs--below-thumbs {
    margin-top: var(--ne-space-3);
    justify-content: center;
    pointer-events: auto;
  }
  .ne-product__cert-img {
    /* Fixed box height → flex-basis is the aspect-correct width at
       cert-size (NOT the SVG's huge natural width — with height:auto +
       max-height the basis was the natural width and shrink crushed the
       icons to ~5px). When the nowrap row overflows, min-width:0 lets the
       boxes compress and object-fit keeps each mark proportional inside
       its narrower box. */
    height: var(--ne-cert-size, 36px);
    width: auto;
    min-width: 0;
    flex: 0 1 auto;
    object-fit: contain;
    display: block;
  }
  /* Soft drop-shadow keeps white SVG marks legible on light photos */
  .ne-product__certs--shadow .ne-product__cert-img {
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 6px rgba(0, 0, 0, 0.35));
  }

  /* Thumbnails */
  .ne-product__thumbs {
    display: flex;
    gap: var(--ne-space-2);
    overflow-x: auto;
    scrollbar-width: thin;
    scroll-snap-type: x proximity;
    padding-bottom: var(--ne-space-1);
    /* CRITICAL: min-width: 0 allows the flex container to shrink below
       its children's intrinsic width so overflow-x: auto can actually
       engage. Without this, the thumb-strip expands to fit all thumbs
       (e.g. 10 × 64px + gaps = 712px) and pushes its parent grid-item
       past viewport on mobile — entire product section then scrolls
       horizontally as a side-effect. Same trap exists at every grid-
       /flex-ancestor up the tree (rule below). */
    min-width: 0;
    max-width: 100%;
  }
  .ne-product__thumb {
    flex: 0 0 auto;
    width: var(--ne-pd-thumb-size, 64px);
    height: var(--ne-pd-thumb-size, 64px);
    padding: 0;
    background: none;
    border: 2px solid transparent;
    border-radius: var(--ne-card-radius, var(--ne-radius-md, 6px));
    cursor: pointer;
    overflow: hidden;
    transition: border-color var(--ne-transition-fast), opacity var(--ne-transition-fast);
    opacity: 0.65;
    scroll-snap-align: start;
  }
  .ne-product__thumb:hover { opacity: 1; }
  .ne-product__thumb--active {
    border-color: var(--ne-scheme-accent);
    opacity: 1;
  }
  .ne-product__thumb-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  /* Thumbnail GRID mode — auto-fills column count from --ne-pd-thumb-size so
     larger thumbnails = fewer columns. Mobile reflows naturally as the
     container narrows (no separate breakpoint needed). Each cell stays
     square via aspect-ratio + absolute-positioned image, so vertical
     rhythm holds even when grid auto-row sizing doesn't pick up
     aspect-ratio reliably. */
  .ne-product__thumbs--grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--ne-pd-thumb-size, 64px), 1fr));
    gap: var(--ne-space-2);
    overflow: visible;
    scroll-snap-type: none;
    padding-bottom: 0;
  }
  .ne-product__thumbs--grid .ne-product__thumb {
    /* Reset slider's fixed width/height — let the grid cell control width
       and aspect-ratio derive height. */
    width: auto;
    height: auto;
    aspect-ratio: 1 / 1;
    flex: none;
    scroll-snap-align: none;
    /* position: relative gives the absolute image inside a deterministic
       containing block, so it fills the thumb's exact bounds regardless
       of how grid resolves the row height. This prevents overlap between
       rows when aspect-ratio + grid-auto-rows interaction is fuzzy. */
    position: relative;
  }
  .ne-product__thumbs--grid .ne-product__thumb-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  /* AUTO mode — grid on desktop, slider on mobile. The desktop rules
     mirror --grid, but only fire at min-width 750px. Below that, the
     base flex-slider .ne-product__thumbs rules apply by default. */
  @media (min-width: 750px) {
    .ne-product__thumbs--auto {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(var(--ne-pd-thumb-size, 64px), 1fr));
      gap: var(--ne-space-2);
      overflow: visible;
      scroll-snap-type: none;
      padding-bottom: 0;
    }
    .ne-product__thumbs--auto .ne-product__thumb {
      width: auto;
      height: auto;
      aspect-ratio: 1 / 1;
      flex: none;
      scroll-snap-align: none;
      position: relative;
    }
    .ne-product__thumbs--auto .ne-product__thumb-img {
      position: absolute;
      inset: 0;
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }
  }

  /* =============================================================================
     INFO COLUMN
     ============================================================================= */

  .ne-product__info-col {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4);
    /* See .ne-product__thumbs note — same min-width: 0 protection so
       a long unbreakable string (technical SKU, long URL in description,
       monospace H1 title) can't push the info column past viewport. */
    min-width: 0;
  }

  /* =============================================================================
     OVERFLOW SAFETY — grid/flex chain
     =============================================================================
     The product layout uses a CSS grid (split/unified mode) and on mobile
     it collapses to 1fr. Grid items default to `min-width: auto` which
     means they cannot shrink below their content's intrinsic width — so
     any descendant with `overflow-x: auto` (thumb strip) or long
     unbreakable text (monospace title) makes the grid track wider than
     the viewport, and the entire .ne-product section overflows.

     Adding `min-width: 0` at every grid/flex ancestor of overflowing
     content unlocks proper shrink + descendant scroll behavior.

     Also force word-wrap on title/description so long product handles
     and monospace headings break at viewport edge instead of expanding
     the column. */
  .ne-product__col-wrap,
  .ne-product__col-wrap--media,
  .ne-product__col-wrap--info,
  .ne-product__media-col {
    min-width: 0;
  }
  .ne-product .ne-product-title,
  .ne-product .ne-product-description {
    overflow-wrap: anywhere;
    word-break: break-word;
  }

  /* =============================================================================
     LIGHTBOX — flex-column, full viewport, with thumbs strip
     ============================================================================= */

  .ne-product__lightbox {
    position: fixed;
    inset: 0;
    margin: auto;
    width: 100vw;
    height: 100vh;
    max-width: none;
    max-height: none;
    padding: 0;
    border: none;
    /* Lightbox bg uses the active color scheme's background colour, so
       a dark→light scheme toggle also flips the lightbox tint. alpha
       comes from the lightbox_bg_opacity setting. color-mix with
       transparent applies the alpha to whatever scheme-bg resolves to
       (Chrome 111+, Safari 16.2+, Firefox 113+). User report 2026-05-23. */
    background: color-mix(in srgb, var(--ne-scheme-bg, #000) calc(var(--ne-pd-lb-bg-alpha, 0.4) * 100%), transparent);
    color: var(--ne-scheme-text, #fff);
    flex-direction: column;
    overflow: hidden;
  }
  /* Native <dialog> is display: none by default and display: block when
     [open]. Only switch to flex when actually open — otherwise the
     lightbox would be visible on initial load and impossible to close. */
  .ne-product__lightbox[open] { display: flex; }
  .ne-product__lightbox::backdrop {
    background: color-mix(in srgb, var(--ne-scheme-bg, #000) calc(var(--ne-pd-lb-bg-alpha, 0.4) * 100%), transparent);
    backdrop-filter: blur(8px);
  }

  .ne-product__lightbox-close {
    position: absolute;
    top: var(--ne-space-4);
    right: var(--ne-space-4);
    width: 44px;
    height: 44px;
    /* scheme-text based so the chip + icon contrast the lightbox backdrop
       (= scheme-bg) in BOTH modes. Hardcoded white was invisible on a
       light scheme bg (user 2026-05-24). */
    background: color-mix(in srgb, var(--ne-scheme-text, #fff) 14%, transparent);
    color: var(--ne-scheme-text, #fff);
    border: none;
    border-radius: var(--ne-radius-full);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 3;
  }
  .ne-product__lightbox-close:hover { background: color-mix(in srgb, var(--ne-scheme-text, #fff) 26%, transparent); }

  .ne-product__lightbox-main {
    flex: 1;
    min-height: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ne-space-3);
    padding: var(--ne-space-6) var(--ne-space-4);
  }
  .ne-product__lightbox-img-wrap {
    flex: 1;
    min-width: 0;
    min-height: 0;
    /* align-self: stretch overrides the parent's `align-items: center`
       (lightbox-main) just for this wrap, so it fills the full main
       cross-axis height. Without it, the wrap collapses to its content
       height (0 with an absolutely-positioned img child), making the
       image invisible. The nav buttons keep their `align-items: center`
       centering because align-self only affects the wrap. */
    align-self: stretch;
    /* position: relative establishes a containing block with a definite
       height so the absolutely-positioned image inside can use 100%
       width/height reliably. Combined with align-self: stretch this gives
       a deterministic max-bounds for object-fit: contain regardless of
       how the flex algorithm resolves percentages. */
    position: relative;
  }
  .ne-product__lightbox-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    /* Allow vertical gestures (close/scroll) while leaving horizontal drags
       free for the JS swipe-to-paginate handler (Rule 205). */
    touch-action: pan-y;
  }

  .ne-product__lightbox-nav {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    /* scheme-text based — contrasts the scheme-bg backdrop in both modes
       (hardcoded white vanished on a light scheme bg, user 2026-05-24). */
    background: color-mix(in srgb, var(--ne-scheme-text, #fff) 14%, transparent);
    color: var(--ne-scheme-text, #fff);
    border: none;
    border-radius: var(--ne-radius-full);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
  .ne-product__lightbox-nav:hover { background: color-mix(in srgb, var(--ne-scheme-text, #fff) 26%, transparent); }

  .ne-product__lightbox-thumbs {
    flex: 0 0 auto;
    display: flex;
    gap: var(--ne-space-2);
    padding: var(--ne-space-3) var(--ne-space-4);
    justify-content: center;
    overflow-x: auto;
    scrollbar-width: thin;
    background: transparent;
  }
  .ne-product__lightbox-thumb {
    flex: 0 0 64px;
    width: 64px;
    height: 64px;
    padding: 0;
    background: none;
    border: 2px solid transparent;
    border-radius: var(--ne-card-radius, var(--ne-radius-md, 6px));
    cursor: pointer;
    overflow: hidden;
    opacity: 0.55;
    transition: opacity var(--ne-transition-fast), border-color var(--ne-transition-fast);
  }
  .ne-product__lightbox-thumb:hover { opacity: 1; }
  .ne-product__lightbox-thumb--active {
    border-color: var(--ne-scheme-accent, #6366f1);
    opacity: 1;
  }
  .ne-product__lightbox-thumb-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  .ne-product__lightbox-counter {
    flex: 0 0 auto;
    text-align: center;
    color: rgba(255, 255, 255, 0.7);
    font-size: var(--ne-font-size-sm);
    padding: var(--ne-space-2) 0 var(--ne-space-3);
  }

  /* =============================================================================
     EMPTY STATE
     ============================================================================= */

  .ne-product__empty {
    text-align: center;
    padding: var(--ne-space-16) var(--ne-space-8);
    color: var(--ne-scheme-fg-muted);
  }

  /* =============================================================================
     RESPONSIVE
     ============================================================================= */

  @media (max-width: 989px) {
    .ne-product--layout-split .ne-product__layout,
    .ne-product--layout-unified .ne-product__layout {
      grid-template-columns: 1fr;
      /* When the desktop split/unified grid collapses to single column
         on mobile, fall through to global Section Abstand so vertical
         rhythm matches stacked-mode UX. */
      gap: var(--ne-section-spacing, 16px) !important;
    }
    /* Sticky disabled on mobile — info typically goes below media so
       sticky has no value */
    .ne-product--scroll-sticky .ne-product__col-wrap--media,
    .ne-product--layout-unified.ne-product--scroll-sticky .ne-product__media-col {
      position: static;
      max-height: none;
      overflow: visible;
    }
  }

  /* Custom mobile padding — applied directly with !important so it
     bypasses any cascade from the global section padding system. */
  @media (max-width: 749px) {
    .ne-product[style*="--ne-pd-mobile-pad-y"] {
      padding-top: var(--ne-pd-mobile-pad-y) !important;
      padding-bottom: var(--ne-pd-mobile-pad-y) !important;
    }
    .ne-product[style*="--ne-pd-mobile-pad-x"] {
      padding-inline: var(--ne-pd-mobile-pad-x) !important;
    }
  }

  @media (max-width: 749px) {
    .ne-product__lightbox-main { padding: var(--ne-space-3) var(--ne-space-2); gap: var(--ne-space-1); }
    .ne-product__lightbox-nav { width: 44px; height: 44px; }
    .ne-product__lightbox-thumb { flex: 0 0 48px; width: 48px; height: 48px; }

    /* Mobile cards: tighter padding so content has room. Reduced from
       desktop's --ne-space-5 (24px) to --ne-space-2 (8px) — the box
       around the image stays visible but doesn't eat the viewport. */
    .ne-product--card-solid .ne-product__card,
    .ne-product--card-glass .ne-product__card,
    .ne-product--card-transparent .ne-product__card {
      padding: var(--ne-space-2) !important;
    }
    /* Mobile media gallery: arrows pushed further OUT of the image
       edges (user feedback #74), smaller + lighter visual style. */
    .ne-product__media-nav {
      width: 32px;
      height: 32px;
      opacity: 1;
      background: transparent;
      color: #fff;
      filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 4px rgba(0, 0, 0, 0.35));
      backdrop-filter: none;
      -webkit-backdrop-filter: none;
    }
    .ne-product__media-nav--prev { left: 0; }
    .ne-product__media-nav--next { right: 0; }
  }

  /* Touch-hint integration — when the global theme setting
     `enable_mobile_touch_hint` is active, body has .ne-touch-hint-active
     and the gallery prev/next arrows render as pulsing chevrons just
     like the slider/carousel blocks (Rule 209). */
  body.ne-touch-hint-active .ne-product__media-nav {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    color: #fff !important;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5));
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    width: 32px !important;
    height: 32px !important;
    opacity: 0.85 !important;
  }
  body.ne-touch-hint-active .ne-product__media-nav > svg { display: none !important; }
  body.ne-touch-hint-active .ne-product__media-nav::before {
    display: inline-block;
    font-size: 32px;
    line-height: 1;
    color: inherit;
    animation: ne-touch-hint-pulse-opacity 1.6s ease-in-out infinite;
  }
  body.ne-touch-hint-active .ne-product__media-nav--prev::before { content: "‹"; }
  body.ne-touch-hint-active .ne-product__media-nav--next::before { content: "›"; }

  /* =============================================================================
     SECTION-ANIMATION GLASS-TRAP FIX (Rule 237)
     The product section may contain glass cards (card_surface=glass). The
     global section-in animation (translateY 56px → 0) forms a backdrop-
     root for descendants — glass would bloom in late. Skip the animation
     when card_surface=glass. Other surfaces are unaffected.
     ============================================================================= */
  [data-ne-animations] .ne-product--card-glass {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
/* END_SECTION:product */

/* START_SECTION:recently-viewed (INDEX:53) */
.ne-rv__inner {
    width: 100%;
    max-width: var(--ne-rv-max, 1400px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-rv-gap, 32px);
  }
  .ne-rv__inner > .shopify-block { display: contents; }

  .ne-rv--align-left .ne-rv__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-rv--align-center .ne-rv__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-rv--align-right .ne-rv__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding (opt-in) */
  @media (max-width: 749px) {
    .ne-rv.ne-rv--custom-mobile-pad {
      padding-top: var(--ne-rv-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-rv-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:recently-viewed */

/* START_SECTION:related-products (INDEX:54) */
.ne-rp__inner {
    width: 100%;
    max-width: var(--ne-rp-max, 1400px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-rp-gap, 32px);
  }
  .ne-rp__inner > .shopify-block { display: contents; }

  .ne-rp--align-left .ne-rp__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-rp--align-center .ne-rp__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-rp--align-right .ne-rp__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding (opt-in) */
  @media (max-width: 749px) {
    .ne-rp.ne-rp--custom-mobile-pad {
      padding-top: var(--ne-rp-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-rp-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:related-products */

/* START_SECTION:rich-text (INDEX:55) */
.ne-rich-text__inner {
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }
  .ne-rich-text__inner[style*="text-align: left"]   { align-items: flex-start; --ne-bw-justify: flex-start; }
  .ne-rich-text__inner[style*="text-align: center"] { align-items: center;     --ne-bw-justify: center; }
  .ne-rich-text__inner[style*="text-align: right"]  { align-items: flex-end;   --ne-bw-justify: flex-end; }
  .ne-rich-text__inner > .shopify-block { display: contents; }

  .ne-section--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-rich-text.ne-rich-text--custom-mobile-pad {
      padding-top: var(--ne-rich-text-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-rich-text-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:rich-text */

/* START_SECTION:scroll-parallax (INDEX:56) */
.ne-px.ne-section {
    overflow: visible;
    padding: 0;
  }

  .ne-px__scene {
    position: relative;
    width: 100%;
    min-height: var(--ne-px-scene-h, 100svh);
    overflow: clip;
    border-radius: inherit;
    display: grid;
    place-items: center;
    isolation: isolate;
  }

  /* Background — oversized so parallax travel never exposes edges */
  .ne-px__bg {
    position: absolute;
    inset: -28% 0;
    z-index: 0;
    will-change: transform;
  }
  .ne-px__bg-img,
  .ne-px__bg-ph {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-px__bg-ph { display: grid; place-items: center; background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.05)); }
  .ne-px__bg-ph-svg { width: 50%; height: 50%; opacity: 0.4; }

  /* LQIP blur-up: the container carries a tiny (40px) version of the image as its
     background; the full <img> fades in over it on load → no empty box, the image
     reads as "coming into focus" while the big file streams in (user QA 2026-06-11).
     Robust without JS: the image stays visible (JS only sets opacity:0 on images
     that haven't loaded yet, then fades them in). */
  .ne-px__bg,
  .ne-px__layer { background-size: cover; background-repeat: no-repeat; background-position: center; }
  .ne-px__bg-img,
  .ne-px__layer-img { transition: opacity 0.6s ease; }

  .ne-px__overlay {
    position: absolute;
    inset: 0;
    z-index: 1;
    background: var(--ne-px-ov-color, #000);
    opacity: var(--ne-px-ov-alpha, 0.3);
    pointer-events: none;
  }

  /* Floating depth layers. Each is a fixed cell — width% of the section width
     × width% of the section height — and the image covers it (object-fit:cover),
     so "pos 25/75 + width 50" tiles the section edge-to-edge on ANY viewport
     (no outer margin) and the image is cropped on the shorter axis instead of
     leaving gaps. Width/height are set inline per layer. */
  .ne-px__layer {
    position: absolute;
    /* z-index follows the block order (layer/content blocks interleave) */
    z-index: var(--px-z, 2);
    transform: translate(-50%, -50%);
    will-change: transform;
    pointer-events: none;
  }
  /* Content box as a reorderable layer — z-index follows its block position,
     and it carries its OWN colour scheme (.color-X) so the surface + every
     text role (heading / muted subheading / accent eyebrow) are coordinated
     and legible regardless of the box surface (user QA 2026-06-13). */
  .ne-px__content--layered {
    position: absolute;
    inset: 0;
    z-index: var(--px-z, 50);
    /* !important: the base .ne-px__content rule (max-width: content-width)
       sits LATER in source at equal specificity and would otherwise cap the
       full-bleed overlay at 1200px + anchor it left → the centred inner then
       only centres within the left 1200px (user QA 2026-06-13). */
    max-width: none !important;
    /* The .color-X wrapper would paint the scheme bg across the whole overlay —
       suppress it; the inner cbox paints the actual surface. (Rule 173) */
    background: transparent !important;
    /* !important on the block's own text/alignment so its scheme wins over the
       section-level .ne-px--text-X / --align-X rules (which only style the
       fallback content). Scoped to --layered → the fallback is untouched. */
    color: var(--ne-scheme-text, #fff) !important;
  }
  .ne-px__content--layered .ne-px__title { color: var(--ne-scheme-heading, var(--ne-scheme-text, #fff)) !important; }
  .ne-px__content--layered .ne-px__sub { color: var(--ne-scheme-text-muted, var(--ne-scheme-text, rgba(255, 255, 255, 0.85))) !important; }
  .ne-px__content--layered .ne-px__eyebrow { color: var(--ne-scheme-accent, currentColor) !important; }
  /* Per-block alignment (the block carries its own, not the section's). */
  .ne-px__content--ah-left { justify-content: flex-start !important; text-align: left; }
  .ne-px__content--ah-center { justify-content: center !important; text-align: center; }
  .ne-px__content--ah-right { justify-content: flex-end !important; text-align: right; }
  .ne-px__content--ah-center .ne-px__content-inner { margin-inline: auto; }
  .ne-px__content--av-top { align-items: flex-start; }
  .ne-px__content--av-center { align-items: center; }
  .ne-px__content--av-bottom { align-items: flex-end; }
  /* Per-block corner radius + scheme-based transparent scrim. */
  .ne-px__content--layered .ne-px__cbox { border-radius: var(--cbox-radius, var(--ne-card-radius, 12px)); }
  .ne-px__content--layered .ne-px__cbox--transparent { background-color: var(--ne-glass-bg, rgba(0, 0, 0, 0.3)) !important; }
  .ne-px__layer-link { pointer-events: auto; display: block; width: 100%; height: 100%; }
  .ne-px__layer-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--px-obj-pos, 50% 50%);
    display: block;
    /* No filter by default — full-bleed layers shouldn't cast a synthetic
       shadow (any soft shadow in a full-bleed photo is baked into the image
       itself, not CSS). Shadow + glow are opt-in via explicit class combos
       below, so OFF is guaranteed no-filter (user QA 2026-06-13). */
    filter: none;
  }

  /* Foreground content (section-level fallback) — sits above all layers (90)
     unless "behind layers" is on; content BLOCKS instead interleave by order. */
  .ne-px__content {
    width: 100%;
    max-width: var(--ne-content-width, 1200px);
    padding: clamp(32px, 6vw, 96px) var(--ne-section-inner-pad-x, 24px);
    display: flex;
    will-change: transform;
  }
  /* The section-level fallback sits above all layers (90). A content BLOCK,
     by contrast, interleaves by block order via its own --px-z — so the
     fallback's position/z-index must NOT clobber the layered block (they
     share the .ne-px__content class). Scope them to the non-layered fallback
     so the box honours its drag-to-reorder depth (user QA 2026-06-13). */
  .ne-px__content:not(.ne-px__content--layered) {
    position: relative;
    z-index: 90;
  }
  .ne-px__content-inner { max-width: 40rem; }
  /* Section-level alignment styles ONLY the fallback content. A content BLOCK
     shares the .ne-px__content / .ne-px__content-inner classes, so an unscoped
     section rule (esp. margin-inline:auto) leaked onto the block's box and
     pinned it centred — defeating the block's own --ah-*/--av-* control
     (user QA 2026-06-13). Scope to :not(--layered) so the block owns its align. */
  .ne-px--align-left .ne-px__content:not(.ne-px__content--layered) { justify-content: flex-start; text-align: left; }
  .ne-px--align-center .ne-px__content:not(.ne-px__content--layered) { justify-content: center; text-align: center; }
  .ne-px--align-center .ne-px__content:not(.ne-px__content--layered) .ne-px__content-inner { margin-inline: auto; }
  .ne-px--align-right .ne-px__content:not(.ne-px__content--layered) { justify-content: flex-end; text-align: right; }

  .ne-px--text-light .ne-px__content { color: #fff; }
  .ne-px--text-light .ne-px__sub { color: rgba(255, 255, 255, 0.88); }
  .ne-px--text-dark .ne-px__content { color: #14141c; }
  .ne-px--text-dark .ne-px__sub { color: rgba(20, 20, 28, 0.78); }
  /* Text follows the active colour scheme */
  .ne-px--text-scheme .ne-px__content { color: var(--ne-scheme-text, #fff); }
  .ne-px--text-scheme .ne-px__title { color: var(--ne-scheme-heading, var(--ne-scheme-text, #fff)); }
  .ne-px--text-scheme .ne-px__sub { color: var(--ne-scheme-text-muted, var(--ne-scheme-text, rgba(255,255,255,0.85))); }
  .ne-px--text-scheme .ne-px__eyebrow { color: var(--ne-scheme-accent, currentColor); }

  /* Optional panel behind the text group (solid / glass / transparent).
     The "glass" substring on the box element lets the theme-global accent
     tint (.ne-glass--tinted[class*="glass"]) and glass-shine
     (body[data-ne-glass-shine] [class*="glass"]) rules apply automatically. */
  .ne-px__cbox {
    padding: clamp(20px, 3vw, 40px);
    border-radius: var(--ne-card-radius, var(--ne-radius-md, 12px));
  }
  .ne-px__cbox--solid {
    background-color: var(--ne-scheme-bg, #14141c);
  }
  .ne-px__cbox--glass {
    /* background-color longhand → glass-shine's background-image survives (Rule LZ5) */
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 18px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 18px)) saturate(var(--ne-glass-saturate, 140%));
    /* border: 0 — the shine (top light-bar) + blur define the edge. A 1px
       glass border renders as a white hairline along the bottom on dark
       backgrounds (Rule LG3, user QA). */
    border: 0;
  }
  /* Transparent = subtle scrim, no blur. Tint adapts to the text colour. */
  .ne-px--text-light .ne-px__cbox--transparent { background-color: rgba(0, 0, 0, 0.34); }
  .ne-px--text-dark .ne-px__cbox--transparent { background-color: rgba(255, 255, 255, 0.42); }
  .ne-px--text-scheme .ne-px__cbox--transparent { background-color: var(--ne-glass-bg, rgba(0, 0, 0, 0.30)); }

  /* Box glow / shadow — mirror the section effect tokens, composable together */
  .ne-px__cbox--glow {
    box-shadow: 0 0 calc(40px * var(--ne-section-glow-intensity, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3));
  }
  .ne-px__cbox--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-px__cbox--glow.ne-px__cbox--shadow {
    box-shadow:
      0 0 calc(40px * var(--ne-section-glow-intensity, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)),
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }

  /* Content box sits behind the floating layers → revealed as they drift on scroll */
  .ne-px--content-behind .ne-px__content:not(.ne-px__content--layered) { z-index: 5; }

  /* No-crop layer: the cell drops its forced square height and takes the
     image's natural aspect, so an elongated image shows in full instead of
     being cover-cropped to the square cell (user QA 2026-06-13). */
  .ne-px__layer--natural { height: auto !important; }
  .ne-px__layer--natural .ne-px__layer-img { height: auto; object-fit: contain; }

  /* Floating-layer image polish: radius, accent glow, light sheen */
  .ne-px__layer-img { border-radius: var(--ne-px-img-radius, 0); }
  /* Layer shadow + image glow — opt-in, explicit class combos so OFF is
     always exactly `filter: none` (no empty-var edge cases). Both can be on. */
  .ne-px--layer-shadow:not(.ne-px--img-glow) .ne-px__layer-img { filter: drop-shadow(0 18px 40px rgba(0, 0, 0, 0.35)); }
  .ne-px--img-glow:not(.ne-px--layer-shadow) .ne-px__layer-img { filter: drop-shadow(0 0 26px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5))); }
  .ne-px--layer-shadow.ne-px--img-glow .ne-px__layer-img { filter: drop-shadow(0 18px 40px rgba(0, 0, 0, 0.35)) drop-shadow(0 0 26px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5))); }
  .ne-px--img-shine .ne-px__layer::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: var(--ne-px-img-radius, 0);
    pointer-events: none;
    /* Same top light-line shine as the theme's cards/sections */
    background:
      linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.20) 1px, rgba(255, 255, 255, 0.06) 2px, transparent 3px),
      linear-gradient(180deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0) 100%);
    z-index: 3;
  }

  /* Scene fade-out — any edge, single-axis or all sides */
  .ne-px--fade-bottom .ne-px__scene {
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-top .ne-px__scene {
    -webkit-mask-image: linear-gradient(to top, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to top, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-left .ne-px__scene {
    -webkit-mask-image: linear-gradient(to left, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to left, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-right .ne-px__scene {
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to right, #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-vertical .ne-px__scene {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-horizontal .ne-px__scene {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
  }
  .ne-px--fade-all .ne-px__scene {
    -webkit-mask-image:
      linear-gradient(to bottom, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%),
      linear-gradient(to right, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
      linear-gradient(to bottom, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%),
      linear-gradient(to right, transparent 0, #000 var(--ne-px-fade, 0%), #000 calc(100% - var(--ne-px-fade, 0%)), transparent 100%);
    mask-composite: intersect;
  }

  /* Pinned scroll-distance: scene sticks while the tall section scrolls past,
     so the parallax/effects stay on screen longer (JS drives by progress). */
  .ne-px--pinned.ne-px.ne-section {
    display: block;
    min-height: var(--ne-px-pin-h, 250svh);
  }
  .ne-px--pinned .ne-px__scene {
    position: sticky;
    top: 0;
    height: 100svh;
    min-height: 100svh;
  }

  .ne-px__eyebrow {
    margin: 0 0 12px;
    font-size: 13px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    font-weight: 600;
    opacity: 0.9;
  }
  .ne-px__title {
    margin: 0 0 16px;
    font-size: var(--ne-h2--size-responsive, clamp(30px, 5vw, 56px));
    line-height: 1.1;
    text-wrap: balance;
  }
  .ne-px__sub {
    font-size: clamp(15px, 1.5vw, 19px);
    line-height: 1.6;
  }
  .ne-px__btn { margin-top: 28px; }

  /* Section-level effects */
  .ne-px.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-px.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-px.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  @media (max-width: 749px) {
    .ne-px__layer--hide-mobile { display: none; }
  }

  /* Parallax is JS-driven (rAF scroll) so it runs everywhere — editor, live,
     and browsers without scroll-driven CSS (Firefox). will-change hints the
     compositor; reduced motion → the JS bails and nothing transforms. */
  .ne-px__bg,
  .ne-px__layer,
  [data-px-content] { will-change: transform; }
/* END_SECTION:scroll-parallax */

/* START_SECTION:scrolling-text (INDEX:57) */
.ne-scrolling-text {
    color: var(--ne-scheme-text);
    text-align: center;
    overflow: hidden;
  }
  .ne-scrolling-text.ne-glass {
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  /* Non-tinted glass keeps its own bg; tinted variant gets bg from the
     global .ne-glass--tinted rule in critical.css. */
  .ne-scrolling-text.ne-glass:not(.ne-glass--tinted) {
    background: var(--ne-glass-bg, rgba(255,255,255,0.05)) !important;
  }

  .ne-scrolling-text__inner { width: 100%; }
  .ne-scrolling-text .ne-marquee { position: relative; overflow: hidden; }
  .ne-scrolling-text .ne-marquee__track {
    display: flex;
    align-items: center;
    width: max-content;
    animation: ne-st-left var(--ne-st-speed, 30s) linear infinite;
    will-change: transform;
  }
  .ne-scrolling-text--dir-right .ne-marquee__track { animation-name: ne-st-right; }

  /* Pause-on-hover, hardcoded (no separate toggle to keep settings count
     within the 22-soft-limit). */
  .ne-scrolling-text:hover .ne-marquee__track { animation-play-state: paused; }

  /* -16.6667% = -1/6 = exactly one set period of six. */
  @keyframes ne-st-left  { from { transform: translateX(0); }            to { transform: translateX(-16.6667%); } }
  @keyframes ne-st-right { from { transform: translateX(-16.6667%); }    to { transform: translateX(0); } }

  /* Fade lives on the inner marquee only — section box stays edge-to-edge
     (matches announcement-bar / brand-slider pattern). */
  .ne-scrolling-text--fade .ne-marquee {
    mask-image: linear-gradient(to right, transparent, black var(--ne-st-fade, 6%), black calc(100% - var(--ne-st-fade, 6%)), transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, black var(--ne-st-fade, 6%), black calc(100% - var(--ne-st-fade, 6%)), transparent);
  }

  .ne-scrolling-text__item {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    padding: 0 var(--ne-space-4, 16px);
    letter-spacing: 0.02em;
  }
  .ne-scrolling-text__item a {
    display: inline-flex;
    align-items: center;
    color: inherit;
    text-decoration: none;
    transition: opacity var(--ne-transition-fast, 0.15s);
  }
  .ne-scrolling-text__item a:hover {
    opacity: 0.7;
    text-decoration: underline;
    text-underline-offset: 3px;
  }

  .ne-scrolling-text__separator {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
    padding: 0 var(--ne-space-2, 8px);
    opacity: 0.4;
    pointer-events: none;
  }

  .ne-scrolling-text__icon-wrap {
    display: inline-flex;
    align-items: center;
    flex-shrink: 0;
  }
  .ne-scrolling-text__item .ne-icon { width: 1em; height: 1em; }
  .ne-scrolling-text__icon { height: 1em; width: auto; flex-shrink: 0; }
  .ne-scrolling-text__icon--mask {
    display: inline-block;
    width: 1em;
    height: 1em;
    background-color: currentColor;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-scrolling-text .ne-marquee__track {
      animation: none;
      flex-wrap: wrap;
      justify-content: center;
      width: auto;
      padding: 0 var(--page-margin, 20px);
    }
  }
/* END_SECTION:scrolling-text */

/* START_SECTION:scrollytelling (INDEX:58) */
.ne-scrolly.ne-section { overflow: visible; }

  .ne-scrolly__intro {
    max-width: 720px;
    margin: 0 auto clamp(32px, 6vw, 72px);
    padding-inline: var(--ne-section-inner-pad-x, 24px);
    text-align: center;
  }
  .ne-scrolly__intro-title {
    margin: 0 0 12px;
    font-size: var(--ne-h2--size-responsive, clamp(28px, 4vw, 44px));
  }
  .ne-scrolly__intro-sub {
    color: var(--ne-scheme-text-muted, currentColor);
    font-size: clamp(15px, 1.4vw, 18px);
    line-height: 1.6;
  }

  .ne-scrolly__inner {
    display: grid;
    grid-template-columns: var(--ne-scrolly-media-w, 50%) 1fr;
    gap: clamp(32px, 5vw, 80px);
    align-items: start;
    padding-inline: var(--ne-section-inner-pad-x, 24px);
  }
  .ne-scrolly--media-right .ne-scrolly__inner {
    grid-template-columns: 1fr var(--ne-scrolly-media-w, 50%);
  }
  .ne-scrolly--media-right .ne-scrolly__stage { order: 2; }
  .ne-scrolly--media-right .ne-scrolly__steps { order: 1; }

  /* ── Per-step alternating sides (opt-in) ──────────────────────
     The sticky stage + the steps share ONE grid cell (1/1). Each step's
     text is constrained to the half OPPOSITE its media side; the shared
     sticky stage slides between halves (transform) as the active step
     changes — no grid-order swap, so the steps column never jumps sides.
     (user request 2026-06-13) */
  .ne-scrolly.ne-scrolly--alt-sides .ne-scrolly__inner,
  .ne-scrolly.ne-scrolly--alt-sides.ne-scrolly--media-right .ne-scrolly__inner {
    grid-template-columns: 1fr;
    position: relative;
  }
  .ne-scrolly--alt-sides .ne-scrolly__stage,
  .ne-scrolly--alt-sides .ne-scrolly__steps { grid-area: 1 / 1; order: 0; }
  /* Registered so the slide interpolates smoothly when JS swaps the var
     (R216) — an unregistered custom property changes discretely → the
     transform would jump instead of glide. */
  @property --ne-scrolly-stage-x {
    syntax: "<length-percentage>";
    inherits: true;
    initial-value: 0%;
  }
  .ne-scrolly--alt-sides .ne-scrolly__stage {
    width: var(--ne-scrolly-media-w, 50%);
    transform: translateX(var(--ne-scrolly-stage-x, 0%));
    z-index: 1;
  }
  /* Side-switch transition modes: Slide (glide across) / Fade (out→swap→in) /
     None (instant jump). */
  .ne-scrolly--alt-sides.ne-scrolly--side-slide .ne-scrolly__stage {
    transition: --ne-scrolly-stage-x 0.55s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .ne-scrolly--alt-sides.ne-scrolly--side-fade .ne-scrolly__stage {
    transition: opacity 0.26s ease;
  }
  .ne-scrolly--alt-sides .ne-scrolly__steps {
    width: 100%;
    z-index: 2;             /* text above the sliding media */
    pointer-events: none;   /* empty areas pass clicks to the media chip below */
  }
  .ne-scrolly--alt-sides .ne-scrolly__step-inner { pointer-events: auto; }
  .ne-scrolly--alt-sides .ne-scrolly__step {
    --ne-scrolly-text-w: calc(100% - var(--ne-scrolly-media-w, 50%) - clamp(32px, 5vw, 80px));
  }
  .ne-scrolly--alt-sides .ne-scrolly__step--media-left .ne-scrolly__step-inner {
    max-width: var(--ne-scrolly-text-w); margin-left: auto; margin-right: 0;   /* media left → text right */
  }
  .ne-scrolly--alt-sides .ne-scrolly__step--media-right .ne-scrolly__step-inner {
    max-width: var(--ne-scrolly-text-w); margin-right: auto; margin-left: 0;   /* media right → text left */
  }

  /* ── Sticky media stage ───────────────────────────────────── */
  .ne-scrolly__stage {
    position: sticky;
    top: var(--ne-scrolly-offset, 24px);
    height: calc(100svh - var(--ne-scrolly-offset, 24px) - 24px);
    max-height: 820px;
  }
  /* Media aspect ratio (overrides the full-height stage when set) */
  .ne-scrolly:not(.ne-scrolly--mr-auto) .ne-scrolly__stage { height: auto; max-height: none; }
  .ne-scrolly:not(.ne-scrolly--mr-auto) .ne-scrolly__media { height: auto; }
  .ne-scrolly--mr-portrait .ne-scrolly__media { aspect-ratio: 3 / 4; }
  .ne-scrolly--mr-square .ne-scrolly__media { aspect-ratio: 1 / 1; }
  .ne-scrolly--mr-landscape .ne-scrolly__media { aspect-ratio: 4 / 3; }
  .ne-scrolly--mr-wide .ne-scrolly__media { aspect-ratio: 16 / 9; }
  .ne-scrolly:not(.ne-scrolly--mr-auto) .ne-scrolly__media-item { position: absolute; inset: 0; }

  /* Step lead icon */
  .ne-scrolly__step-icon {
    width: clamp(40px, 5vw, 56px);
    height: auto;
    display: block;
    margin: 0 0 16px;
    object-fit: contain;
  }
  /* Recolour via mask (Rule 30): the icon becomes a single-colour shape. */
  .ne-scrolly__step-icon--mask {
    height: clamp(40px, 5vw, 56px);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
  }
  .ne-scrolly__step-icon--accent { background-color: var(--ne-scheme-accent, #6366f1); }
  .ne-scrolly__step-icon--heading { background-color: var(--ne-scheme-text, currentColor); }
  .ne-scrolly--align-center .ne-scrolly__step-icon { margin-inline: auto; }
  .ne-scrolly--align-right .ne-scrolly__step-icon { margin-inline: auto 0; }

  /* Icon inline, in the same row as the heading */
  .ne-scrolly__titlerow {
    display: flex;
    align-items: center;
    gap: 14px;
    margin: 0 0 16px;
  }
  .ne-scrolly__titlerow .ne-scrolly__step-icon {
    width: clamp(28px, 3.5vw, 40px);
    height: clamp(28px, 3.5vw, 40px);
    margin: 0;
    flex: 0 0 auto;
  }
  .ne-scrolly__titlerow .ne-scrolly__step-title { margin: 0; }
  .ne-scrolly--align-center .ne-scrolly__titlerow { justify-content: center; }
  .ne-scrolly--align-right .ne-scrolly__titlerow { justify-content: flex-end; }
  .ne-scrolly__media {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: var(--ne-scrolly-media-radius, var(--ne-card-radius, var(--ne-radius-lg, 16px)));
    overflow: clip;
    background: transparent;
  }
  /* Media frame surface (mirrors the per-step surface) — visible where the
     image has transparency / lets the page show through (glass / transparent). */
  .ne-scrolly__media--solid { background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.04)); }
  .ne-scrolly__media--transparent { background: transparent; }
  .ne-scrolly__media--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    border: 0;
  }
  .ne-scrolly__media--glass-tint { background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.06))); }
  .ne-scrolly__media-item {
    position: absolute;
    inset: 0;
    opacity: 0;
    will-change: opacity, transform;
    transition: opacity 0.7s ease, transform 0.9s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .ne-scrolly__media-item.is-active { opacity: 1; }

  .ne-scrolly--tx-slide .ne-scrolly__media-item { transform: translateY(6%); }
  .ne-scrolly--tx-slide .ne-scrolly__media-item.is-active { transform: none; }
  .ne-scrolly--tx-scale .ne-scrolly__media-item { transform: scale(1.07); }
  .ne-scrolly--tx-scale .ne-scrolly__media-item.is-active { transform: scale(1); }

  .ne-scrolly__media-img,
  .ne-scrolly__media-ph {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-scrolly__media-ph { display: grid; place-items: center; }
  .ne-scrolly__media-ph-svg { width: 60%; height: 60%; opacity: 0.5; }

  /* Product chip overlaid on media */
  .ne-scrolly__chip {
    position: absolute;
    left: 16px;
    bottom: 16px;
    display: inline-flex;
    flex-direction: column;
    gap: 2px;
    padding: 12px 16px;
    border-radius: var(--ne-radius-md, 10px);
    background: var(--ne-glass-bg, rgba(20, 20, 28, 0.55));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    color: #fff;
    text-decoration: none;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
    transition: transform 0.25s ease;
  }
  .ne-scrolly__chip:hover { transform: translateY(-2px); }
  /* Chip background variants (readable over any image) */
  .ne-scrolly__chip--dark {
    background: rgba(10, 10, 14, 0.82);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .ne-scrolly__chip--accent {
    background: var(--ne-scheme-accent, #6366f1);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    color: #fff;
  }
  .ne-scrolly__chip--glow {
    box-shadow: 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.45)), 0 8px 24px rgba(0, 0, 0, 0.28);
  }
  /* Accent-tinted glass chip */
  .ne-scrolly__chip--tint { background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(20, 20, 28, 0.55))); }
  /* Chip text colour (independent of background) — set on title AND price
     explicitly so it never relies on inheritance. */
  .ne-scrolly__chip--text-white .ne-scrolly__chip-title,
  .ne-scrolly__chip--text-white .ne-scrolly__chip-price { color: #fff; }
  .ne-scrolly__chip--text-dark .ne-scrolly__chip-title { color: #14141c; }
  .ne-scrolly__chip--text-dark .ne-scrolly__chip-price { color: rgba(20, 20, 28, 0.72); }
  .ne-scrolly__chip--text-accent .ne-scrolly__chip-title,
  .ne-scrolly__chip--text-accent .ne-scrolly__chip-price { color: var(--ne-scheme-accent, #6366f1); }
  .ne-scrolly__chip-title { font-weight: 600; font-size: 14px; }
  .ne-scrolly__chip-price { font-size: 13px; opacity: 0.85; }

  /* Progress dots */
  .ne-scrolly__progress {
    position: absolute;
    z-index: 2;
    right: 16px;
    top: 50%;
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .ne-scrolly--progress-left .ne-scrolly__progress {
    right: auto;
    left: 16px;
  }
  .ne-scrolly--progress-bottom .ne-scrolly__progress {
    right: 50%;
    left: auto;
    top: auto;
    bottom: 16px;
    transform: translateX(50%);
    flex-direction: row;
  }
  .ne-scrolly__dot {
    width: 10px;
    height: 10px;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.45);
    box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15);
    cursor: pointer;
    transition: background 0.3s ease, transform 0.3s ease;
  }
  .ne-scrolly__dot.is-active {
    background: var(--ne-scheme-accent, #6366f1);
    transform: scale(1.35);
  }
  .ne-scrolly__dot:focus-visible {
    outline: 2px solid var(--ne-scheme-accent, #6366f1);
    outline-offset: 3px;
  }

  /* ── Steps column ─────────────────────────────────────────── */
  /* Split-mode lead-in/out so the first step enters from the bottom once the
     media is pinned (and the last gets centre time before the media unpins),
     mirroring the overlay behaviour. */
  .ne-scrolly--layout-split .ne-scrolly__steps {
    padding-block: 38vh;
  }
  .ne-scrolly__step {
    min-height: var(--ne-scrolly-step-h, 90vh);
    display: flex;
    flex-direction: column;
    justify-content: safe center;
    opacity: 0.32;
    transition: opacity 0.5s ease;
  }
  .ne-scrolly__step.is-active { opacity: 1; }
  .ne-scrolly__step-inner { max-width: 36rem; }

  /* Step content alignment */
  .ne-scrolly--align-center .ne-scrolly__step { align-items: center; text-align: center; }
  .ne-scrolly--align-center .ne-scrolly__step-inner { margin-inline: auto; }
  .ne-scrolly--align-center .ne-scrolly__step-btn { align-self: center; }
  .ne-scrolly--align-right .ne-scrolly__step { align-items: flex-end; text-align: right; }
  .ne-scrolly--align-right .ne-scrolly__step-btn { align-self: flex-end; }

  /* Per-step surface */
  .ne-scrolly__step-inner--solid,
  .ne-scrolly__step-inner--glass {
    padding: clamp(20px, 3vw, 36px);
    border-radius: var(--ne-scrolly-step-radius, var(--ne-card-radius, var(--ne-radius-lg, 16px)));
  }
  .ne-scrolly__step-inner--solid { background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.04)); }
  .ne-scrolly__step-inner--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
    border: 0;
  }
  .ne-scrolly__step-inner--glass-tint { background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.06))); }
  /* Per-step custom color scheme: the .color-X class supplies the scheme
     vars (text/accent + surface bg). For a "none" surface, suppress the
     scheme's painted background so only text colour changes. */
  .ne-scrolly__step-inner--none.ne-scrolly__step-inner--custom-scheme { background: transparent !important; }
  /* Forced 0 media radius only at full width (rounded full-bleed looks broken at the screen edges). */
  .ne-scrolly.ne-section--full-width .ne-scrolly__media { border-radius: 0; }
  .ne-scrolly__step-inner--glow { box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3))); }
  .ne-scrolly__step-inner--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }
  .ne-scrolly__step-inner--glow.ne-scrolly__step-inner--shadow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  .ne-scrolly__eyebrow {
    margin: 0 0 10px;
    font-size: 13px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--ne-scheme-accent, #6366f1);
    font-weight: 600;
  }
  .ne-scrolly__step-title {
    margin: 0 0 16px;
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-h3--size-responsive, clamp(24px, 3vw, 36px));
    line-height: 1.15;
  }
  .ne-scrolly__step-text {
    color: var(--ne-scheme-text, currentColor);
    font-size: clamp(15px, 1.3vw, 17px);
    line-height: 1.7;
  }
  .ne-scrolly__step-btn { margin-top: 24px; align-self: flex-start; }

  .ne-scrolly__empty {
    padding: 64px 24px;
    text-align: center;
    color: var(--ne-scheme-text-muted, currentColor);
    border: 1px dashed var(--ne-scheme-border, rgba(128, 128, 128, 0.4));
    border-radius: var(--ne-radius-lg, 16px);
    margin-inline: var(--ne-section-inner-pad-x, 24px);
  }

  /* ── Overlay / hero mode: full-bleed media, text scrolls over ── */
  /* Clip the pinned full-screen media to the section box so it can never paint
     over the previous section or the footer (overflow:clip keeps sticky working
     — it doesn't create a scroll container). */
  .ne-scrolly--layout-overlay.ne-section { padding-block: 0; overflow: clip; }
  .ne-scrolly--layout-overlay .ne-scrolly__inner {
    display: block;
    gap: 0;
    padding-inline: 0;
  }
  /* Specificity (0,3,0) so a media-ratio doesn't collapse the full-bleed stage.
     top:0 → the hero is always edge-to-edge (Pin offset is a split-mode control;
     a full-bleed hero sits flush and a transparent header floats over it). */
  .ne-scrolly.ne-scrolly--layout-overlay .ne-scrolly__stage {
    position: sticky;
    top: 0;
    height: 100svh;
    max-height: none;
    width: 100%;
    margin: 0;
  }
  .ne-scrolly--layout-overlay .ne-scrolly__media { height: 100%; }
  .ne-scrolly--layout-overlay .ne-scrolly__media-item { position: absolute; inset: 0; }
  /* Steps flow AFTER the pinned full-screen media (no negative pull), so the
     media pins first and the first step scrolls up from the bottom. */
  .ne-scrolly--layout-overlay .ne-scrolly__steps {
    position: relative;
    z-index: 1;
    /* Trailing room so the last step scrolls all the way to centre (and dwells)
       while the media is still pinned, before the section releases. */
    padding-bottom: 45vh;
    padding-inline: var(--ne-section-inner-pad-x, 24px);
  }
  /* Baseline legibility for bare text over media (pick a glass/solid step
     surface for stronger contrast). */
  .ne-scrolly--layout-overlay .ne-scrolly__step-inner--none {
    text-shadow: 0 2px 14px rgba(0, 0, 0, 0.55);
  }

  /* ── Centre the pinned media vertically in the full viewport ─── */
  /* Specificity (0,3,0) to beat the .ne-scrolly:not(--mr-auto) stage rule
     which would otherwise collapse the stage to the media height. */
  .ne-scrolly.ne-scrolly--media-center .ne-scrolly__stage {
    /* Stage = media height; JS sets --ne-scrolly-mc-top so the pinned media
       sits vertically centred in the viewport. Because the stage is only
       media-height (not full-viewport), the media flows in directly under the
       heading on entry (no empty gap), rises as you scroll, pins centred, then
       releases with the last step on exit. (user request 2026-06-17) */
    top: var(--ne-scrolly-mc-top, var(--ne-scrolly-offset, 24px));
    height: auto;
    max-height: none;
  }

  /* ── Scroll-linked text animation (JS sets opacity/transform on the
     INNER card, so glass backdrop-filter survives the fade — Rule 273:
     same-element opacity keeps the blur, ancestor opacity would kill it). */
  .ne-scrolly--anim .ne-scrolly__step {
    opacity: 1;
    transition: none;
  }
  .ne-scrolly--anim .ne-scrolly__step-inner {
    transition: none;
    will-change: opacity, transform, filter;
  }

  /* Section-level effects */
  .ne-scrolly.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-scrolly.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-scrolly.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* ── Mobile: media sticky on top, steps scroll beneath ────── */
  @media (max-width: 749px) {
    .ne-scrolly__inner,
    .ne-scrolly--media-right .ne-scrolly__inner {
      grid-template-columns: 1fr;
      gap: 0;
    }
    /* Alternating sides → stack on mobile (no overlap, no slide). */
    .ne-scrolly--alt-sides .ne-scrolly__stage,
    .ne-scrolly--alt-sides .ne-scrolly__steps {
      grid-area: auto; z-index: auto; pointer-events: auto;
    }
    .ne-scrolly--alt-sides .ne-scrolly__stage { width: auto; transform: none; }
    /* On mobile the per-step L/R text columns collapse to a full-width, centred
       block (like a non-alternating section). Must match the per-step selectors'
       (0,3,0) specificity to override them — a plain step-inner rule loses. */
    .ne-scrolly--alt-sides .ne-scrolly__step--media-left .ne-scrolly__step-inner,
    .ne-scrolly--alt-sides .ne-scrolly__step--media-right .ne-scrolly__step-inner {
      max-width: none; margin-inline: auto;
    }
    .ne-scrolly__stage,
    .ne-scrolly--media-right .ne-scrolly__stage {
      order: 0;
      height: 52svh;
      max-height: none;
      margin-bottom: 24px;
    }
    .ne-scrolly__steps,
    .ne-scrolly--media-right .ne-scrolly__steps {
      order: 1;
    }
    .ne-scrolly__step { min-height: 72vh; }
    .ne-scrolly__step-inner { max-width: none; }
    .ne-scrolly__progress {
      top: auto;
      bottom: 12px;
      right: 50%;
      transform: translateX(50%);
      flex-direction: row;
    }
  }

  /* ── Reduced motion: no animation, everything legible ─────── */
  @media (prefers-reduced-motion: reduce) {
    .ne-scrolly__media-item {
      transition: none;
      transform: none;
    }
    .ne-scrolly__step {
      opacity: 1;
      transition: none;
    }
    .ne-scrolly__chip,
    .ne-scrolly__dot { transition: none; }
    .ne-scrolly--alt-sides .ne-scrolly__stage { transition: none !important; }
  }
/* END_SECTION:scrollytelling */

/* START_SECTION:search (INDEX:59) */
/* Top block-zone */
  .ne-search__blocks-top {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4, 16px);
    margin-bottom: var(--ne-space-6, 24px);
  }
  .ne-search__blocks-top > .shopify-block { display: contents; }

  .ne-search__header {
    text-align: center;
    margin-bottom: var(--ne-space-4, 16px);
  }
  .ne-search__title { margin: 0; }

  /* Search form — centered, max-width */
  .ne-search__form-wrap {
    position: relative;
    max-width: 720px;
    margin: 0 auto var(--ne-space-6, 24px);
  }

  .ne-search__input-wrap {
    position: relative;
    display: flex;
    align-items: center;
  }

  .ne-search__icon {
    position: absolute;
    left: var(--ne-space-3, 12px);
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.6;
    pointer-events: none;
  }

  .ne-search__input {
    width: 100%;
    padding: 14px 44px 14px 48px;
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.15));
    border-radius: var(--ne-radius-lg, 12px);
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-font-size-base, 15px);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }

  .ne-search__input::-webkit-search-cancel-button { -webkit-appearance: none; }

  .ne-search__input:focus {
    outline: none;
    border-color: var(--ne-scheme-accent);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--ne-scheme-accent) 18%, transparent);
  }

  .ne-search__clear {
    position: absolute;
    right: var(--ne-space-3, 12px);
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.6;
    transition: opacity 0.15s ease;
  }
  .ne-search__clear:hover { opacity: 1; }

  /* Predictive dropdown */
  .ne-search__predictive {
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    right: 0;
    z-index: 100;
    border-radius: var(--ne-radius-lg, 12px);
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.12));
    max-height: 480px;
    overflow-y: auto;
    padding: var(--ne-space-3, 12px);
    box-shadow: 0 12px 40px rgba(0,0,0,0.25);
  }

  .ne-search__predictive[hidden] { display: none; }

  /* Predictive dropdown bleeds past the .ne-section's overflow:clip — open
     the section's overflow + lift its z-index ONLY while the dropdown is
     visible. Same :has() pattern used for cart block shadows in critical.css.
     position:relative is already inherited from .ne-section, z-index lifts
     the whole section above the next one in the stacking context so the
     dropdown paints OVER the section below. */
  .ne-search:has(.ne-search__predictive:not([hidden])) {
    overflow: visible;
    z-index: 10;
  }

  .ne-search__predictive-section { margin-bottom: var(--ne-space-3, 12px); }
  .ne-search__predictive-section:last-child { margin-bottom: 0; }

  .ne-search__predictive-heading {
    display: block;
    font-size: 11px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.7;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 6px;
    padding-top: var(--ne-space-2, 8px);
    border-top: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }
  .ne-search__predictive-section:first-child .ne-search__predictive-heading {
    border-top: none;
    padding-top: 0;
  }

  .ne-search__predictive-item {
    display: flex;
    gap: 12px;
    align-items: center;
    padding: 8px 6px;
    text-decoration: none;
    color: var(--ne-scheme-text, currentColor);
    border-radius: 6px;
    transition: background-color 0.15s ease;
  }
  .ne-search__predictive-item:hover { background: rgba(255,255,255,0.04); }

  .ne-search__predictive-img {
    width: 44px;
    height: 44px;
    object-fit: cover;
    border-radius: 6px;
    flex-shrink: 0;
  }

  .ne-search__predictive-text {
    flex: 1;
    min-width: 0;
  }
  .ne-search__predictive-title {
    font-size: 13px;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ne-search__predictive-meta {
    font-size: 11px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.7;
  }

  .ne-search__predictive-empty {
    font-size: 13px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.7;
    padding: var(--ne-space-2, 8px) 0;
    text-align: center;
  }

  /* Results section */
  .ne-search__results-meta {
    text-align: center;
    margin-bottom: var(--ne-space-5, 20px);
  }

  .ne-search__toolbar {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: var(--ne-space-4, 16px);
    padding-bottom: var(--ne-space-4, 16px);
    border-bottom: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
    margin-bottom: var(--ne-space-6, 24px);
    flex-wrap: wrap;
  }
  .ne-search__toolbar-left,
  .ne-search__toolbar-right {
    display: flex;
    align-items: center;
    gap: var(--ne-space-3, 12px);
  }

  .ne-search__filter-toggle {
    display: none;
    align-items: center;
    gap: var(--ne-space-2, 8px);
  }

  .ne-search__sort {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-3, 12px);
  }
  .ne-search__sort-label {
    font-size: var(--ne-font-size-sm, 14px);
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.85;
    white-space: nowrap;
  }
  .ne-search__sort-select {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding: 8px 32px 8px 14px;
    background: transparent;
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.15));
    border-radius: var(--ne-btn-radius, 8px);
    color: var(--ne-scheme-text, currentColor);
    font-size: var(--ne-font-size-sm, 14px);
    cursor: pointer;
    transition: border-color 0.15s ease;
    /* Tells the browser the form control supports both light + dark UI
       so OS-rendered chrome (scrollbar, panel border) auto-matches the
       resolved background colour. */
    color-scheme: light dark;
    /* Highlight colour for the currently-selected <option> inside the
       OS-rendered panel (Chrome/Edge, partial Firefox; ignored Safari). */
    accent-color: var(--ne-scheme-accent, currentColor);
  }
  .ne-search__sort-select:hover,
  .ne-search__sort-select:focus { outline: none; border-color: var(--ne-scheme-accent); }
  /* OS-rendered <option> dropdown panel uses scheme bg + text. Without this
     it falls back to the OS default (white panel + black text), unreadable
     on dark schemes. Chrome/Firefox/Safari all respect option-level bg. */
  .ne-search__sort-select option {
    background-color: var(--ne-scheme-bg, #fff);
    color: var(--ne-scheme-text, #000);
  }
  .ne-search__sort-chevron {
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    pointer-events: none;
    opacity: 0.6;
  }

  /* Layout: filters + results */
  .ne-search__layout {
    display: grid;
    grid-template-columns: 260px 1fr;
    gap: var(--ne-space-8, 32px);
  }
  .ne-search:not(.ne-search--has-filters) .ne-search__layout {
    grid-template-columns: 1fr;
  }

  .ne-search__filters {
    align-self: start;
    position: sticky;
    top: calc(var(--ne-header-height, 80px) + 24px);
    max-height: calc(100vh - var(--ne-header-height, 80px) - 48px);
    overflow-y: auto;
    padding-right: var(--ne-space-2, 8px);
  }

  .ne-search__filters-header {
    display: none;
    justify-content: space-between;
    align-items: center;
    padding-bottom: var(--ne-space-3, 12px);
    margin-bottom: var(--ne-space-3, 12px);
    border-bottom: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.08));
  }
  .ne-search__filters-title { font-size: 16px; font-weight: 600; }
  .ne-search__filters-close {
    width: 36px; height: 36px;
    display: inline-flex; align-items: center; justify-content: center;
    background: transparent; border: none; color: inherit; cursor: pointer; border-radius: 50%;
  }
  .ne-search__filters-close:hover { background: rgba(255,255,255,0.06); }
  .ne-search__filter-backdrop { display: none; }

  /* Results grid — subgrid pattern (Rule 163) for cross-card row
     alignment. Cards opt into subgrid via the [data-search-grid]
     selector in product-card.liquid. Article/page result cards
     are non-product so they don't participate in subgrid alignment. */
  .ne-search__results {
    display: grid;
    grid-template-columns: repeat(var(--ne-search-columns, 3), minmax(0, 1fr));
    /* All-auto rows (no `1fr` in row 2). Search results are MIXED content
       (product + article + page cards), unlike collection/featured grids
       which only contain product cards. With `auto 1fr auto auto auto`,
       Chrome resolves the 1fr image-row to a much larger value when a
       doc-card (non-subgridding flex content) is present in the same row
       block — stretching all cells in that row block (including product
       cards) to a huge height. All-auto sizes each row to its content
       (product image still has explicit aspect-ratio so it gets a
       predictable height). Firefox/Zen happen to handle the 1fr more
       gracefully in mixed-content rows, hence the cross-browser delta. */
    grid-template-rows: auto auto auto auto auto;
    grid-auto-rows: auto auto auto auto auto;
    column-gap: var(--ne-space-4, 16px);
    row-gap: 0;
    align-items: stretch;
  }

  .ne-search__result-item { display: contents; }
  .ne-search__results .ne-product-card {
    grid-row: span 5;
    margin-bottom: var(--ne-space-4, 16px);
  }
  /* Article/page cards span 5 rows so they occupy a full card cell.
     Inherit parent's align-items: stretch so doc-cards fill the row
     track height to match adjacent product cards (uniform cell height
     across a row). The Chrome row-stretching bug that previously
     forced align-self: start was rooted in the `1fr` row template;
     with all-auto row tracks (above) Chrome no longer pumps up the
     image-row, so doc-cards can safely stretch again. */
  .ne-search__results .ne-search__doc-card {
    grid-row: span 5;
    margin-bottom: var(--ne-space-4, 16px);
  }

  /* Article / Page result cards — surface modes (solid / glass / glass-tint
     / transparent) + glow + shadow modifiers driven by the section's
     card_surface theme settings. Mirrors the product-card visual treatment
     so all three search result card types share the same look. */
  .ne-search__doc-card {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2, 8px);
    border-radius: var(--ne-search-card-radius, 8px);
    overflow: hidden;
    text-decoration: none;
    color: var(--ne-scheme-text, currentColor);
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.15s ease;
  }
  /* Solid surface (default) — bg + visible border */
  .ne-search__doc-card--solid {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.1));
  }
  /* Glass surface — backdrop-filter on the card itself. Class contains
     "glass" → globale glass-shine rule in critical.css matches automatically.
     border: 0 follows Rule LG3 — a 1px glass-border reads as a hairline
     bottom strip on dark schemes (the shine + blur define the card's
     visual edge, no border needed). */
  .ne-search__doc-card--glass {
    /* background-COLOR (longhand) — NOT `background:` shorthand. Shorthand
       would reset background-image:none and the global glass-shine rule
       in critical.css (which sets background-image: var(--ne-glass-shine-bg))
       would be silently dropped — leaving the card without the soft
       top-fade gradient. */
    background-color: var(--ne-glass-bg, rgba(255,255,255,0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0;
    /* Inset top-bevel (1px white highlight inside top edge) — matches
       product-card's `--ne-card-shadow-inset: inset 0 1px 0 ...` pattern
       (snippets/product-card.liquid:553). Without it, the shine reads
       noticeably thinner than the product-card. Stored in a var so the
       glow/shadow modifier rules below can compose it into their
       box-shadow chains via fallback-default 0 0 0 transparent. */
    --ne-doc-card-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.1);
  }
  .ne-search__doc-card--glass:hover {
    --ne-doc-card-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.15);
  }
  /* Base box-shadow chain — single composed var pattern matching
     snippets/product-card.liquid:453-455. Modifiers below feed the
     inset and outer slots; resting state vs hover are slotted via
     dedicated rules so glow shows only on hover (= product-card UX). */
  .ne-search__doc-card {
    box-shadow:
      var(--ne-doc-card-shadow-inset, 0 0 0 transparent),
      var(--ne-doc-card-shadow-outer, 0 0 0 transparent);
  }
  /* Media-top-bar shine on glass cards with a full-bleed image at top:
     the card-level shine `background-image` is hidden behind the image,
     so we render the 3px shine stripe via ::after on the image wrapper
     instead — matches the .ne-blog/.ne-product-card pattern in
     critical.css (line ~1100). */
  .ne-search__doc-card--glass .ne-search__doc-image {
    position: relative;
  }
  body[data-ne-glass-shine="1"] .ne-search__doc-card--glass .ne-search__doc-image::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background-image: var(--ne-glass-shine-bg);
    pointer-events: none;
    z-index: 3;
  }
  /* Transparent surface — bare card on section bg */
  .ne-search__doc-card--transparent {
    background: transparent;
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.1));
  }
  /* Glow / Shadow modifiers — feed --ne-doc-card-shadow-outer slot,
     never set box-shadow directly. Glow is hover-only (matches
     product-card UX — glow only "lights up" on interaction). Shadow
     is visible at rest. */
  .ne-search__doc-card--shadow {
    --ne-doc-card-shadow-outer: var(--ne-block-shadow-effect, 0 8px 24px rgba(0,0,0,0.18));
  }
  .ne-search__doc-card--glow:hover {
    --ne-doc-card-shadow-outer: var(--ne-block-glow-effect, 0 0 32px var(--ne-scheme-accent-glow));
  }
  /* When both modifiers are present: at rest shows shadow only;
     on hover glow chip + shadow combine. */
  .ne-search__doc-card--shadow.ne-search__doc-card--glow:hover {
    --ne-doc-card-shadow-outer:
      var(--ne-block-glow-effect, 0 0 32px var(--ne-scheme-accent-glow)),
      var(--ne-block-shadow-effect, 0 8px 24px rgba(0,0,0,0.18));
  }
  /* Hover: lift for all; border-color accent shift only on solid/transparent
     (glass mode keeps its glass border to avoid washing out the accent). */
  .ne-search__doc-card:hover {
    transform: translateY(-2px);
  }
  .ne-search__doc-card--solid:hover,
  .ne-search__doc-card--transparent:hover {
    border-color: var(--ne-scheme-accent);
  }
  .ne-search__doc-image {
    aspect-ratio: 16/9;
    overflow: hidden;
  }
  .ne-search__doc-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .ne-search__doc-info {
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding: var(--ne-space-4, 16px);
  }
  .ne-search__doc-type {
    font-size: 11px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.7;
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  .ne-search__doc-title {
    font-size: 14px;
    font-weight: 600;
    margin: 0;
    line-height: 1.4;
  }
  .ne-search__doc-excerpt {
    font-size: 12px;
    color: var(--ne-scheme-text-muted, currentColor);
    opacity: 0.8;
    line-height: 1.5;
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* Tablet */
  @media (max-width: 989px) {
    .ne-search__layout { grid-template-columns: 1fr; }
    .ne-search__filter-toggle { display: inline-flex; }

    .ne-search__filters {
      position: fixed;
      inset: 0 auto 0 0;
      width: min(360px, 92vw);
      z-index: 1001;
      background: var(--ne-scheme-bg, #111);
      box-shadow: 0 0 40px rgba(0,0,0,0.4);
      padding: var(--ne-space-4, 16px) var(--ne-space-5, 20px);
      max-height: 100vh;
      overflow-y: auto;
      transform: translateX(-100%);
      transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
      visibility: hidden;
    }
    .ne-search__filters[data-open="1"] {
      transform: translateX(0);
      visibility: visible;
    }
    .ne-search__filters-header { display: flex; }

    .ne-search__filter-backdrop {
      display: block;
      position: fixed;
      inset: 0;
      background: rgba(0,0,0,0.55);
      backdrop-filter: blur(4px);
      -webkit-backdrop-filter: blur(4px);
      z-index: 1000;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.25s ease, visibility 0s linear 0.25s;
    }
    .ne-search__filter-backdrop[data-open="1"] {
      opacity: 1;
      visibility: visible;
      transition: opacity 0.25s ease, visibility 0s linear 0s;
    }

    .ne-search__results {
      grid-template-columns: repeat(min(var(--ne-search-columns, 3), 3), 1fr);
    }
  }

  /* Mobile */
  @media (max-width: 749px) {
    .ne-search__results {
      grid-template-columns: repeat(2, 1fr);
      gap: var(--ne-space-3, 12px);
    }
    .ne-search__input { padding: 12px 40px 12px 44px; font-size: 14px; }
    .ne-search__predictive { max-height: 70vh; }
  }
/* END_SECTION:search */

/* START_SECTION:section-grid (INDEX:60) */
.ne-grid__inner {
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(var(--ne-grid-count, 3), minmax(0, 1fr));
    width: 100%;
    justify-content: center;
  }
  @media (max-width: 990px) {
    .ne-grid__inner { grid-template-columns: repeat(var(--ne-grid-count-tablet, 2), minmax(0, 1fr)); }
  }
  @media (max-width: 640px) {
    .ne-grid__inner { grid-template-columns: 1fr; }
  }

  /* Theme-block wrapper collapse so direct block elements become grid items */
  .ne-grid__inner .shopify-block { display: contents; }

  /* Group blocks = grid cells — each group spans exactly 1 grid track */
  /* Top-align column tops: descendant combinator (not `>`) since
     Shopify wraps each block in `.shopify-block` (display:contents).
     See multicolumn.liquid for the same rationale. */
  .ne-grid__inner .ne-group {
    width: 100%;
    min-width: 0;
    height: 100%;
    justify-content: flex-start !important;
  }

  /* Pin inner blocks to intrinsic heights — no vertical stretching. */
  .ne-grid__inner .ne-group > * {
    align-self: stretch;
    flex: 0 0 auto;
  }

  /* Non-group blocks span ALL columns for section-level intro/CTA rows */
  .ne-grid__inner > :not(.ne-group) {
    grid-column: 1 / -1;
    justify-self: center;
    width: 100%;
  }

  /* Full-row default centering for common atoms */
  .ne-grid__inner > .ne-block-text { max-width: 65ch; margin-inline: auto; text-align: center; }
  .ne-grid__inner > .ne-block-heading { text-align: center; }
  .ne-grid__inner > .ne-block-button-wrap { justify-content: center; }
  .ne-grid__inner > .ne-block-image-wrap,
  .ne-grid__inner > .ne-block-video-wrap { align-items: center; }

  .ne-grid.ne-section--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }

  @media (max-width: 640px) {
    .ne-grid__inner > :not(.ne-group) { grid-column: 1; }
  }

  /* Custom mobile padding (opt-in) */
  @media (max-width: 749px) {
    .ne-grid.ne-grid--custom-mobile-pad {
      padding-top: var(--ne-grid-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-grid-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:section-grid */

/* START_SECTION:section-stack (INDEX:61) */
.ne-stack__inner {
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    width: 100%;
    align-items: stretch;
  }
  .ne-stack__inner .shopify-block { display: contents; }

  /* Descendant-selectors + !important: Shopify injects block stylesheets
     after section stylesheets, so source-order would otherwise let the
     block's own wrap--<align> rule win. */

  /* Button wrap: flex-row → justify-content */
  .ne-stack__inner--left   .ne-block-button-wrap { justify-content: flex-start !important; }
  .ne-stack__inner--center .ne-block-button-wrap { justify-content: center      !important; }
  .ne-stack__inner--right  .ne-block-button-wrap { justify-content: flex-end    !important; }
  .ne-stack__inner--stretch .ne-block-button-wrap { justify-content: stretch    !important; }
  .ne-stack__inner--stretch .ne-block-button-wrap .ne-block-button { width: 100%; justify-content: center; }

  /* Image/Video wrap: flex-column → align-items */
  .ne-stack__inner--left   .ne-block-image-wrap,
  .ne-stack__inner--left   .ne-block-video-wrap { align-items: flex-start !important; }
  .ne-stack__inner--center .ne-block-image-wrap,
  .ne-stack__inner--center .ne-block-video-wrap { align-items: center     !important; }
  .ne-stack__inner--right  .ne-block-image-wrap,
  .ne-stack__inner--right  .ne-block-video-wrap { align-items: flex-end   !important; }
  .ne-stack__inner--stretch .ne-block-image-wrap,
  .ne-stack__inner--stretch .ne-block-video-wrap { align-items: stretch   !important; }

  /* Heading/Text: .ne-tp reads text-align from --ne-tp--align CSS var.
     Override the var at ancestor level so every descendant .ne-tp inherits. */
  .ne-stack__inner--left   { --ne-tp--align: left; }
  .ne-stack__inner--center { --ne-tp--align: center; }
  .ne-stack__inner--right  { --ne-tp--align: right; }
  /* Plus direct !important text-align fallback for non-.ne-tp elements. */
  .ne-stack__inner--left   .ne-block-heading,
  .ne-stack__inner--left   .ne-block-text { text-align: left !important; }
  .ne-stack__inner--center .ne-block-heading,
  .ne-stack__inner--center .ne-block-text { text-align: center !important; }
  .ne-stack__inner--right  .ne-block-heading,
  .ne-stack__inner--right  .ne-block-text { text-align: right !important; }

  /* critical.css caps .ne-block-text at max-width: 65ch for readability and
     only applies margin:auto when the parent has inline text-align. Stack
     uses a class-based approach, so we need to position the 65ch box
     explicitly per alignment mode. */
  .ne-stack__inner--center .ne-block-text { margin-inline: auto; }
  .ne-stack__inner--right  .ne-block-text { margin-left: auto; margin-right: 0; }
  .ne-stack__inner--stretch .ne-block-text { max-width: none; margin-inline: 0; }

  .ne-stack.ne-section--shadow { box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)); }

  /* Custom mobile padding (opt-in) */
  @media (max-width: 749px) {
    .ne-stack.ne-stack--custom-mobile-pad {
      padding-top: var(--ne-stack-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-stack-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:section-stack */

/* START_SECTION:shader-background (INDEX:62) */
.ne-shader {
  position: relative;
  display: block;
  min-height: var(--ne-sh-minh, 480px);
}
.ne-shader.ne-shader--fill {
  min-height: var(--ne-sh-vh, 80svh);
}
.ne-shader__fallback,
.ne-shader__canvas,
.ne-shader__overlay {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  pointer-events: none;
}
.ne-shader__fallback {
  z-index: 0;
  background: linear-gradient(135deg,
    var(--ne-sh-a, #0a0a0f) 0%,
    var(--ne-sh-b, #6366f1) 55%,
    var(--ne-sh-c, var(--ne-sh-b, #6366f1)) 100%);
}
/* Transparent background: drop the opaque fallback gradient AND the section's own
   .color-X scheme fill (via !important, beating the 0,1,0 scheme rule), so the
   GLOBAL page background (.ne-layer-background — the theme's background image)
   shows through the faded effect. The live canvas still covers its unfaded area
   (that's the effect); the soft-edge fade is what lets the background come through. */
.ne-shader.ne-shader--bg-transparent { background: transparent !important; }
.ne-shader--bg-transparent .ne-shader__fallback { background: transparent; }
.ne-shader__canvas {
  z-index: 0;
  display: block;
}
.ne-shader__overlay {
  z-index: 1;
}
.ne-shader__content {
  position: relative;
  z-index: 2;
  display: grid;
  justify-items: var(--ne-sh-jx, center);
  align-content: var(--ne-sh-ay, center);
  text-align: var(--ne-sh-ta, center);
  gap: 16px;
  width: 100%;
  min-height: inherit;
  padding: clamp(24px, 6vw, 80px);
  color: var(--ne-scheme-text, #e8e8ef);
}
.ne-shader__content > .shopify-block { display: contents; }

/* Scope: whole-page / behind-next — section becomes a zero-height mount point.
   The engine relocates the canvas to a fixed (page) or absolute (next) host. */
.ne-shader--scope-page,
.ne-shader--scope-next {
  min-height: 0;
  padding: 0 !important;
}
.ne-shader--scope-next { overflow: visible; }
/* Whole-page scope: the engine relocates the canvas into a FIXED page host, so
   this section's slot in the content column is a phantom. Take the wrapper out
   of the flex flow entirely — an absolutely-positioned flex item does not
   participate in layout, so .ne-layer-content's row-gap (--ne-section-spacing)
   stops reserving a DOUBLED gap (once above, once below) and no seam/line shows
   at the section's list position. Absolute (not display:none) keeps it
   editor-selectable and lets the canvas mount before it relocates.
   'next' scope is excluded: its host is absolute INSIDE the section and uses the
   section box as its positioning reference, so that wrapper must stay in flow. */
.ne-layer-content > .shopify-section:has(> .ne-shader--scope-page) {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
/* The text content is hidden in background scopes; the overlay + fallback are
   relocated into the host by the engine (so the tint travels with the canvas). */
.ne-shader--scope-page .ne-shader__content,
.ne-shader--scope-next .ne-shader__content { display: none; }
.ne-shader__host .ne-shader__overlay,
.ne-shader__host .ne-shader__fallback { display: block; }
.ne-shader__host {
  position: absolute;
  top: 0;
  /* Full-bleed: span the whole viewport width even when the (collapsed) section
     is inset by page margins on mobile, so the backdrop covers the full-width
     next section instead of being inset. */
  left: 0;
  width: 100%;
  max-width: none;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

@media (max-width: 749px) {
  .ne-shader__content { padding: clamp(20px, 7vw, 48px) var(--ne-mobile-section-inner-pad-x, 16px); }
}

/* Custom mobile padding (Rule 253) */
@media (max-width: 749px) {
  .ne-shader.ne-shader--custom-mobile-pad {
    padding-top: var(--ne-shader-mobile-pad-top, 24px) !important;
    padding-bottom: var(--ne-shader-mobile-pad-bottom, 24px) !important;
  }
}
/* END_SECTION:shader-background */

/* START_SECTION:slideshow (INDEX:63) */
.ne-ss-section {
    position: relative;
    width: 100%;
    height: var(--ne-ss-height, 70vh);
    overflow: hidden;
    isolation: isolate;
  }
  @media (max-width: 749px) {
    .ne-ss-section { height: var(--ne-ss-height-mobile, 60vh); }
  }

  /* Track — horizontal scroll-snap rotator. Each direct child is a
     full-width flex item that snaps to the start. Native keyboard +
     touch + screen-reader navigation, no JS-positioning required. */
  .ne-ss__track {
    display: flex;
    flex-direction: row;
    width: 100%;
    height: 100%;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .ne-ss__track::-webkit-scrollbar { display: none; }
  .ne-ss__track > * {
    flex: 0 0 100%;
    width: 100%;
    height: 100%;
    scroll-snap-align: start;
    scroll-snap-stop: always;
    position: relative;
  }
  /* Generic non-slide cells (group / heading / text / button / image
     used as alternative slide layers) center their content. */
  .ne-ss__track > .shopify-block:not(:has(> .ne-slide)) {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(24px, 5vw, 64px);
  }

  /* ─────────────── Arrows ─────────────── */
  .ne-ss__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 5;
    width: 44px;
    height: 44px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    color: var(--ne-scheme-text, #fff);
    cursor: pointer;
    transition: transform .15s ease, background-color .15s ease, box-shadow .15s ease, opacity .2s ease;
  }
  .ne-ss__arrow:hover { transform: translateY(-50%) scale(1.05); }
  .ne-ss__arrow--prev { left: 16px; }
  .ne-ss__arrow--next { right: 16px; }
  .ne-ss__arrow[disabled] { opacity: 0.35; pointer-events: none; }

  .ne-ss--arrow-button .ne-ss__arrow {
    background: rgba(0, 0, 0, 0.45);
    border: 1px solid rgba(255, 255, 255, 0.25);
    color: #fff;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
  }
  .ne-ss--arrow-button .ne-ss__arrow:hover { background: rgba(0, 0, 0, 0.65); }
  .ne-ss--arrow-plain .ne-ss__arrow {
    background: transparent;
    border: 0;
    box-shadow: none;
    color: #fff;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.5);
  }

  /* ─────────────── Dots ─────────────── */
  .ne-ss__dots {
    position: absolute;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 5;
    display: flex;
    gap: 8px;
    padding: 6px 10px;
    background: rgba(0, 0, 0, 0.55);
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 999px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
  }
  .ne-ss__dot {
    position: relative;
    width: 8px;
    height: 8px;
    padding: 0;
    border: 1px solid rgba(255, 255, 255, 0.35);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.55);
    cursor: pointer;
    transition: width .25s ease, background-color .25s ease, border-color .25s ease;
  }
  /* Invisible 24px touch target around the 8px visual dot (WCAG 2.5.8) —
     absolute so it doesn't affect the pill layout or dot spacing. */
  .ne-ss__dot::after {
    content: '';
    position: absolute;
    inset: -8px;
  }
  .ne-ss__dot:hover { background: rgba(255, 255, 255, 0.9); }
  .ne-ss__dot--active {
    background: #fff;
    border-color: #fff;
    width: 22px;
  }

  @media (max-width: 749px) {
    .ne-ss__arrow { width: 36px; height: 36px; }
    .ne-ss__arrow--prev { left: 8px; }
    .ne-ss__arrow--next { right: 8px; }
    .ne-ss__dots { bottom: 16px; }
  }

  /* Stronger glow / shadow — base critical.css values are subtle and
     last-wins overrides each other. Combine + amplify for slideshow. */
  .ne-ss-section.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-ss-section.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-ss-section.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-ss-section.ne-ss-section--custom-mobile-pad {
      padding-top: var(--ne-ss-section-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-ss-section-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:slideshow */

/* START_SECTION:tabs (INDEX:64) */
.ne-tabs__inner {
    width: 100%;
    max-width: var(--ne-tabs-max, 900px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-tabs-gap, 24px);
  }
  /* Order via flex: header non-tab blocks (1), nav-bar (2), tab panels (3).
     `:has()` selector identifies tab-block wrappers (which contain
     .ne-tab__panel) vs header-block wrappers (which don't). */
  .ne-tabs__nav { order: 2; }
  .ne-tabs__inner > .shopify-block { order: 1; }
  .ne-tabs__inner > .shopify-block:has(.ne-tab__panel) { order: 3; }
  /* Collapse wrappers whose panel is hidden — otherwise empty
     wrappers still participate in flex-gap and shift the active
     panel down by N × gap. */
  .ne-tabs__inner > .shopify-block:has(.ne-tab__panel[hidden]) {
    display: none;
  }

  /* ─────────────── Tab nav ─────────────── */
  .ne-tabs__nav {
    display: flex;
    gap: var(--ne-tabs-btn-gap, 4px);
    overflow-x: auto;
    scrollbar-width: none;
    -ms-overflow-style: none;
  }
  .ne-tabs__nav::-webkit-scrollbar { display: none; }
  .ne-tabs--align-center .ne-tabs__nav { justify-content: center; }
  .ne-tabs--align-right  .ne-tabs__nav { justify-content: flex-end; }

  .ne-tabs__btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 12px 18px;
    background: transparent;
    border: 1px solid transparent;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    font-size: var(--ne-font-size-sm, 0.9375rem);
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    transition: color .15s ease, background-color .15s ease, border-color .15s ease;
  }
  .ne-tabs__btn:hover { color: var(--ne-scheme-text); }
  /* Icon inherits the button's currentColor — follows hover + active
     state changes automatically (text and icon shift together). */
  .ne-tabs__btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: currentColor;
    transition: transform .2s ease, filter .2s ease;
  }
  .ne-tabs__btn:hover .ne-tabs__btn-icon {
    transform: scale(1.18) rotate(-4deg);
    filter: drop-shadow(0 0 6px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5)));
  }
  .ne-tabs__btn-icon--custom {
    width: 16px;
    height: 16px;
    background-color: currentColor;
    mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;
    -webkit-mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: center;
  }

  /* Style modifiers — radius set with !important to defeat the
     critical.css button:not(.ne-btn) reset (Rule 144). */

  /* ── Style: underline (default) ── */
  .ne-tabs--style-underline .ne-tabs__nav {
    border-bottom: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.2));
  }
  .ne-tabs--style-underline .ne-tabs__btn {
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    border-radius: 0 !important;
  }
  .ne-tabs--style-underline .ne-tabs__btn--active {
    color: var(--ne-scheme-accent);
    border-bottom-color: var(--ne-scheme-accent);
  }

  /* ── Style: button (filled when active, lightly rounded) ── */
  .ne-tabs--style-button .ne-tabs__btn {
    border-radius: 8px !important;
    border-color: var(--ne-scheme-border, transparent);
  }
  .ne-tabs--style-button .ne-tabs__btn--active {
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-color: var(--ne-scheme-accent);
  }

  /* ── Style: pill (fully rounded ends) ── */
  .ne-tabs--style-pill .ne-tabs__btn {
    border-radius: 999px !important;
    border-color: var(--ne-scheme-border, transparent);
    padding-inline: 22px;
  }
  .ne-tabs--style-pill .ne-tabs__btn--active {
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-color: var(--ne-scheme-accent);
  }

  /* ── Style: minimal (text only, accent-on-active) ── */
  .ne-tabs--style-minimal .ne-tabs__btn--active {
    color: var(--ne-scheme-accent);
  }

  /* ─────────────── Panels ─────────────── */
  .ne-tab__panel { animation: ne-tab-fade .25s ease; }

  @keyframes ne-tab-fade {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  @media (prefers-reduced-motion: reduce) {
    .ne-tab__panel { animation: none; }
  }

  @media (max-width: 749px) {
    .ne-tabs.ne-tabs--custom-mobile-pad {
      padding-top: var(--ne-tabs-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-tabs-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:tabs */

/* START_SECTION:testimonials (INDEX:65) */
.ne-testimonials__inner {
    display: flex;
    flex-direction: column;
    gap: var(--ne-ts-content-gap, 32px);
    width: 100%;
  }
  /* Section-level alignment (Rule 126) — drives cross-axis + inherited
     vars for atom blocks. Child modifier class still wins at element
     scope, so per-block override keeps working. */
  .ne-testimonials--align-left   .ne-testimonials__inner { align-items: flex-start; --ne-tp--align: left;   --ne-bw-justify: flex-start; }
  .ne-testimonials--align-center .ne-testimonials__inner { align-items: center;     --ne-tp--align: center; --ne-bw-justify: center; }
  .ne-testimonials--align-right  .ne-testimonials__inner { align-items: flex-end;   --ne-tp--align: right;  --ne-bw-justify: flex-end; }

  /* Atom wrappers collapse (Rule 70). */
  .ne-testimonials__inner > .shopify-block { display: contents; }

  /* testimonial-list spans full inner width regardless of cross-axis
     alignment — the cards inside have their own alignment. */
  .ne-testimonials__inner > .ne-tl { width: 100%; align-self: stretch; }

  /* Custom mobile padding */
  @media (max-width: 749px) {
    .ne-testimonials.ne-testimonials--custom-mobile-pad {
      padding-top: var(--ne-testimonials-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-testimonials-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:testimonials */

/* START_SECTION:video (INDEX:66) */
.ne-video-section__inner {
    width: 100%;
    max-width: var(--ne-video-max, 1100px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-video-gap, 24px);
  }
  .ne-video-section__inner > .shopify-block { display: contents; }

  .ne-video-section--align-left .ne-video-section__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-video-section--align-center .ne-video-section__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-video-section--align-right .ne-video-section__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-video-section.ne-video-section--custom-mobile-pad {
      padding-top: var(--ne-video-section-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-video-section-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:video */

/* START_SECTION:wishlist (INDEX:67) */
.ne-wl__inner {
    width: 100%;
    max-width: var(--ne-wl-max, 1400px);
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    gap: var(--ne-wl-gap, 32px);
  }
  .ne-wl__inner > .shopify-block { display: contents; }

  .ne-wl--align-left .ne-wl__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
    --ne-tp--align: left;
    --ne-iw-align: flex-start;
  }
  .ne-wl--align-center .ne-wl__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
    --ne-tp--align: center;
    --ne-iw-align: center;
  }
  .ne-wl--align-right .ne-wl__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
    --ne-tp--align: right;
    --ne-iw-align: flex-end;
  }

  /* Custom mobile padding (Rule 253) */
  @media (max-width: 749px) {
    .ne-wl.ne-wl--custom-mobile-pad {
      padding-top: var(--ne-wl-mobile-pad-top, 24px) !important;
      padding-bottom: var(--ne-wl-mobile-pad-bottom, 24px) !important;
    }
  }
/* END_SECTION:wishlist */

/* CSS from block stylesheet tags */
/* START_BLOCK:accordion-item (INDEX:68) */
/* Outer container is a transparent shell — header and content
     paint themselves so they can carry separate schemes / surfaces /
     radii / shadows. */
  .ne-acc-item {
    width: 100%;
    background: transparent;
    border: 0;
  }

  /* ─────────────── Header (summary) ─────────────── */
  .ne-acc-item__trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 16px 22px;
    color: var(--ne-scheme-text);
    border: 1px solid transparent;
    border-radius: var(--ne-acc-item-radius, 8px);
    cursor: pointer;
    list-style: none;
    transition: background-color .15s ease, border-color .2s ease;
  }
  .ne-acc-item__trigger::-webkit-details-marker { display: none; }
  .ne-acc-item__trigger:hover {
    background: color-mix(in srgb, var(--ne-scheme-accent, #7777ff) 10%, var(--ne-scheme-bg, transparent));
  }

  /* Pill = full radius default */
  .ne-acc-item--pill .ne-acc-item__trigger {
    border-radius: 999px;
  }
  /* Connected mode — flatten header bottom so it fuses with content.
     EXCEPT when pill is on: pill keeps its full curve and the content
     auto-insets (28px) to sit inside the pill's straight bottom-middle
     section — preserves the pill silhouette while merging. */
  .ne-acc-item--connected:not(.ne-acc-item--pill) .ne-acc-item__trigger {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
  }

  .ne-acc-item__title {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    flex: 1 1 auto;
    min-width: 0;
  }
  .ne-acc-item__heading {
    flex: 1 1 auto;
    text-align: left;
    overflow-wrap: anywhere;
    font-weight: 500;
  }

  /* ─────────────── Icon ─────────────── */
  .ne-acc-item__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--ne-scheme-accent);
  }
  .ne-acc-item__icon--custom {
    width: 18px;
    height: 18px;
    background-color: var(--ne-scheme-accent);
    mask-size: contain;
    mask-repeat: no-repeat;
    mask-position: center;
    -webkit-mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    -webkit-mask-position: center;
  }

  /* ─────────────── Chevron ─────────────── */
  .ne-acc-item__chevron {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    transition: transform .25s ease;
  }
  .ne-acc-item[open] .ne-acc-item__chevron { transform: rotate(180deg); }

  /* ─────────────── Content panel ─────────────── */
  .ne-acc-item__content-wrap {
    margin-inline: var(--ne-acc-content-inset, 0);
  }
  .ne-acc-item--separated .ne-acc-item__content-wrap {
    margin-top: 4px;
  }
  .ne-acc-item__content {
    padding: 18px 22px;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    border: 1px solid transparent;
    border-radius: var(--ne-acc-content-radius, 8px);
    line-height: 1.65;
  }
  /* Connected mode — content top corners flatten so it fuses with header.
     With pill on, content is auto-inset (28px) so its straight top edge
     sits at the pill's straight bottom-middle section. */
  .ne-acc-item--connected .ne-acc-item__content {
    border-top-left-radius: 0;
    border-top-right-radius: 0;
  }
  .ne-acc-item__content > :first-child { margin-top: 0; }
  .ne-acc-item__content > :last-child { margin-bottom: 0; }

  /* ─────────────── Surface modes (per-part) ─────────────── */
  /* Default surface = scheme bg */
  .ne-acc-item__trigger--scheme {
    background: var(--ne-scheme-bg);
    border-color: var(--ne-scheme-border, transparent);
  }
  .ne-acc-item__content--scheme {
    background: var(--ne-scheme-bg);
    border-color: var(--ne-scheme-border, transparent);
  }
  /* Glass — glassmorphism with backdrop blur */
  .ne-acc-item__trigger--glass,
  .ne-acc-item__content--glass {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border: 1px glass-border reads as a bottom contour line on dark
       bg (same fix as blog/product-card glass, user QA 2026-05-25). */
    border: 0;
  }
  /* Accent-tinted glass — opt-in via glass_tint. */
  .ne-acc-item--glass-tint .ne-acc-item__trigger--glass,
  .ne-acc-item--glass-tint .ne-acc-item__content--glass {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(127, 127, 127, 0.08)));
  }
  /* Transparent — no paint */
  .ne-acc-item__trigger--transparent,
  .ne-acc-item__content--transparent {
    background: transparent;
    border-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Per-item effects — coupled to global block-intensity multiplier
     (Theme-Settings → Effects → Section glow/shadow intensity ×0.6).
     Uses --ne-block-glow-effect / --ne-block-shadow-effect composites. */
  .ne-acc-item--shadow .ne-acc-item__trigger,
  .ne-acc-item--shadow .ne-acc-item__content {
    box-shadow: var(--ne-block-shadow-effect, 0 8px 20px rgba(0, 0, 0, 0.25));
  }
  .ne-acc-item--glow .ne-acc-item__trigger,
  .ne-acc-item--glow .ne-acc-item__content {
    box-shadow: var(--ne-block-glow-effect, 0 0 24px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4)));
  }
  .ne-acc-item--shadow.ne-acc-item--glow .ne-acc-item__trigger,
  .ne-acc-item--shadow.ne-acc-item--glow .ne-acc-item__content {
    box-shadow:
      var(--ne-block-shadow-effect, 0 8px 20px rgba(0, 0, 0, 0.25)),
      var(--ne-block-glow-effect, 0 0 24px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4)));
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-acc-item__chevron { transition: none; }
  }
/* END_BLOCK:accordion-item */

/* START_BLOCK:arc-gallery (INDEX:69) */
.ne-arc {
  position: relative;
  width: 100%;
  height: var(--ne-arc-h, 460px);
  --ne-arc-card-w: 220px;
}
/* Dock to the section's top/bottom edge — pins the arc flush against the
   edge regardless of where it sits in the block order (a margin trick only
   works when the block is the LAST child; here a button often follows it).
   Absolute against the nearest positioned ancestor: the hero content-wrap is
   position:relative and stretches to fill the section, so top/bottom:0 lands
   on the section edge, full-width edge-to-edge. Rule 450. */
.ne-arc--dock-top,
.ne-arc--dock-bottom {
  position: absolute;
  left: 0;
  right: 0;
}
/* The nearest positioned ancestor is the hero content-wrap, which sits INSIDE
   the section's own padding (--section-padding-*). Pull past that padding with
   the same token so the arc lands on the section's true edge. The hero zeroes
   its section padding on mobile, so there the offset is 0. */
.ne-arc--dock-bottom { bottom: calc(-1 * var(--section-padding-bottom, 40px)); }
.ne-arc--dock-top { top: calc(-1 * var(--section-padding-top, 40px)); }
@media (max-width: 640px) {
  .ne-arc--dock-bottom { bottom: 0; }
  .ne-arc--dock-top { top: 0; }
}
.ne-arc__viewport {
  position: absolute;
  inset: 0;
  overflow: visible;
}
/* Wide-overflow clipper: extends 2000px past the sides + past the OPPOSITE
   edge so those never clip, but is flush at the ALIGNED edge. overflow:hidden
   gives a hard cut (glass-safe, Rule 49); mask gives a soft fade. The stage is
   re-centered inside the huge clip so the block area maps exactly (Rule 397). */
.ne-arc__clip {
  position: absolute;
  left: -2000px;
  right: -2000px;
}
.ne-arc--align-bottom .ne-arc__clip { top: -2000px; bottom: 0; }
.ne-arc--align-top .ne-arc__clip { top: 0; bottom: -2000px; }
.ne-arc--clip .ne-arc__clip { overflow: hidden; }
.ne-arc--fade.ne-arc--align-bottom .ne-arc__clip {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--ne-arc-fade-px, 60px)), transparent 100%);
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--ne-arc-fade-px, 60px)), transparent 100%);
}
.ne-arc--fade.ne-arc--align-top .ne-arc__clip {
  -webkit-mask-image: linear-gradient(to top, #000 calc(100% - var(--ne-arc-fade-px, 60px)), transparent 100%);
  mask-image: linear-gradient(to top, #000 calc(100% - var(--ne-arc-fade-px, 60px)), transparent 100%);
}
.ne-arc__stage {
  position: absolute;
  left: 2000px;
  right: 2000px;
  height: var(--ne-arc-h, 460px);
}
.ne-arc--align-bottom .ne-arc__stage { top: 2000px; }
.ne-arc--align-top .ne-arc__stage { top: 0; }
.ne-arc__stage > .shopify-block { display: contents; }

/* Each card is absolutely positioned; the JS engine drives --ax/--ay/--arot/--asc */
.ne-arc__card {
  position: absolute;
  left: 50%;
  top: 50%;
  width: var(--ne-arc-card-w, 220px);
  margin: 0;
  transform:
    translate(-50%, -50%)
    translate(var(--ax, 0px), var(--ay, 0px))
    rotate(var(--arot, 0deg))
    scale(calc(var(--asc, 1) * var(--ne-arc-hover, 1)));
  transform-origin: center center;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.4s ease;
  will-change: transform;
  z-index: var(--az, 1);
  --ne-arc-r: var(--ne-arc-card-radius-self, var(--ne-arc-card-radius, 14px));
  border-radius: calc(var(--ne-arc-r) + var(--ne-arc-card-border-w, 0px));
  border: var(--ne-arc-card-border-w, 0px) solid var(--ne-scheme-border, rgba(127, 127, 127, 0.2));
  overflow: hidden;
  backface-visibility: hidden;
}
.ne-arc__card[data-arc-hidden] {
  opacity: 0;
  pointer-events: none;
}
/* Hover zoom — multiplies the arc scale, lifts above siblings */
.ne-arc--hover-zoom .ne-arc__card:hover {
  --ne-arc-hover: var(--ne-arc-hover-amt, 1.1);
  z-index: 200;
}
.ne-arc__card-link { display: block; color: inherit; text-decoration: none; }

/* The configured radius IS the image/inner radius; the border grows OUTWARD, so the
   card's outer radius = inner + border width (above). The media then keeps the exact
   configured radius and the border's inner edge sits flush against it — no gap. */
.ne-arc__media {
  position: relative;
  width: 100%;
  aspect-ratio: var(--ne-arc-ratio, 3 / 4);
  overflow: hidden;
  border-radius: var(--ne-arc-r, 14px);
}
.ne-arc__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}
.ne-arc__img--ph { background: var(--ne-scheme-bg-surface, #1a1a22); }
/* Original image format — media keeps the image's natural ratio, image stays
   in-flow so the media box gets its height (Rule 309). */
.ne-arc--natural .ne-arc__media { aspect-ratio: auto; }
.ne-arc--natural .ne-arc__img {
  position: relative;
  inset: auto;
  height: auto;
}

.ne-arc__cap {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 30px 14px 12px;
  background: linear-gradient(to top, color-mix(in srgb, var(--ne-arc-cap-scrim-color, #000000) var(--ne-arc-cap-scrim-pct, 60%), transparent) 0%, color-mix(in srgb, var(--ne-arc-cap-scrim-color, #000000) var(--ne-arc-cap-scrim-pct2, 27%), transparent) 55%, transparent 100%);
  color: var(--ne-arc-cap-color, #fff);
  pointer-events: none;
}
.ne-arc__cap-title { font-size: 15px; font-weight: 600; line-height: 1.2; }
.ne-arc__cap-meta { font-size: 12px; opacity: 0.85; }

/* Surfaces (mirror product-card / lookbook) */
.ne-arc__card--surface-solid { background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg, #16161d)); }
.ne-arc__card--surface-transparent { background: transparent; }
.ne-arc__card--surface-glass {
  background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
  backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
  -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 140%));
  border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.12));
}
.ne-arc__card--tint { background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.06))); }
.ne-arc__card--glow { box-shadow: 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4)); }
.ne-arc__card--shadow { box-shadow: 0 12px 34px rgba(0, 0, 0, 0.34); }
.ne-arc__card--glow.ne-arc__card--shadow { box-shadow: 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4)), 0 12px 34px rgba(0, 0, 0, 0.34); }

/* Nav chevrons — siblings of the viewport (outside clip/mask) */
.ne-arc__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 300;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  color: var(--ne-scheme-text, #fff);
  background: var(--ne-glass-bg, rgba(0, 0, 0, 0.28));
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: opacity 0.2s ease, background 0.2s ease;
}
.ne-arc__nav--prev { left: 10px; }
.ne-arc__nav--next { right: 10px; }
.ne-arc__nav:hover { background: var(--ne-scheme-accent, rgba(99, 102, 241, 0.9)); color: #fff; }
.ne-arc__nav[disabled] { opacity: 0.3; cursor: default; pointer-events: none; }
.ne-arc--nav-hover-edge .ne-arc__nav,
.ne-arc--nav-drag .ne-arc__nav,
.ne-arc--nav-scroll .ne-arc__nav { display: none; }

@media (max-width: 749px) {
  .ne-arc__nav { display: none; }
}
/* END_BLOCK:arc-gallery */

/* START_BLOCK:before-after-comparison (INDEX:71) */
.ne-ba-compare {
    position: relative;
    width: 100%;
    background: transparent !important;
  }

  .ne-ba-compare__viewport {
    position: relative;
    width: 100%;
    aspect-ratio: var(--ne-ba-aspect, 16/9);
    border-radius: var(--ne-ba-radius, 12px);
    overflow: clip;
    isolation: isolate;
    cursor: ew-resize;
    user-select: none;
    -webkit-user-select: none;
    touch-action: pan-y;
    background: var(--ne-scheme-bg, transparent);
  }

  /* Block surface modes — applied to viewport so the area BEHIND the
     two image layers is transparent / glass. Useful for PNGs with
     alpha or aspect-ratios that don't fully cover. */
  .ne-ba-compare--transparent .ne-ba-compare__viewport {
    background: transparent !important;
  }
  .ne-ba-compare--glass .ne-ba-compare__viewport {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  /* Tinted glass — opt-in via `glass_tint`. LOCAL class (not global
     ne-glass--tinted): the modifier rides the .ne-ba-compare WRAPPER, and
     the global .ne-glass--tinted[class*="glass"] rule would paint the
     wrapper (no radius/clip) → tint bleeds past the viewport's rounded
     corners (user QA 2026-05-25 #26). Tint lives on the clipping viewport. */
  .ne-ba-compare--glass.ne-ba-compare--glass-tint .ne-ba-compare__viewport {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(127, 127, 127, 0.08))) !important;
  }

  /* Effects coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-ba-compare--shadow .ne-ba-compare__viewport {
    box-shadow: 0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25);
  }
  .ne-ba-compare--glow .ne-ba-compare__viewport {
    box-shadow: 0 0 calc(32px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-ba-compare--shadow.ne-ba-compare--glow .ne-ba-compare__viewport {
    box-shadow:
      0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25),
      0 0 calc(32px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }

  .ne-ba-compare__layer {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
  }

  .ne-ba-compare__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
    display: block;
    pointer-events: none;
    -webkit-user-drag: none;
  }
  .ne-ba-compare__img--placeholder {
    background: var(--ne-scheme-bg-muted, rgba(127, 127, 127, 0.1));
  }

  .ne-ba-compare__label {
    position: absolute;
    bottom: 16px;
    padding: 6px 14px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    border-radius: 999px;
    pointer-events: none;
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
  }
  .ne-ba-compare__label--before { left: 16px; }
  .ne-ba-compare__label--after  { right: 16px; }

  /* Divider line + handle */
  .ne-ba-compare__divider {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 0;
    z-index: 3;
    pointer-events: none;
  }

  /* Line is split into top + bottom segments — the gap matches the
     handle's height so the line never crosses behind a transparent
     handle. Gap = handle-size + 8px breathing room. */
  .ne-ba-compare__line {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    width: var(--ne-ba-divider-w, 2px);
    background: var(--ne-scheme-accent, #6366f1);
    box-shadow: 0 0 12px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-ba-compare__line--top {
    top: 0;
    bottom: calc(50% + var(--ne-ba-handle-size, 44px) / 2 + 4px);
  }
  .ne-ba-compare__line--bottom {
    top: calc(50% + var(--ne-ba-handle-size, 44px) / 2 + 4px);
    bottom: 0;
  }

  .ne-ba-compare__handle {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    width: var(--ne-ba-handle-w, 44px);
    height: var(--ne-ba-handle-size, 44px);
    padding: 0;
    margin: 0;
    border: 2px solid #fff;
    background: var(--ne-scheme-accent, #6366f1);
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0;
    cursor: ew-resize;
    pointer-events: auto;
    box-shadow:
      0 4px 14px rgba(0, 0, 0, 0.35),
      0 0 16px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5));
    transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
    -webkit-appearance: none;
    appearance: none;
  }

  /* Handle surface modes — defeat critical.css button reset
     by using !important. Solid is the default (uses divider color). */
  .ne-ba-compare--surface-glass .ne-ba-compare__handle {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.18)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.45));
    color: #fff;
  }
  .ne-ba-compare--surface-transparent .ne-ba-compare__handle {
    background: transparent !important;
    border: 2px solid var(--ne-scheme-accent, #fff);
    color: var(--ne-scheme-accent, #fff);
    box-shadow: 0 0 14px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5));
  }

  .ne-ba-compare__handle:hover,
  .ne-ba-compare__handle:focus-visible {
    transform: translate(-50%, -50%) scale(1.1);
    outline: none;
    box-shadow:
      0 6px 18px rgba(0, 0, 0, 0.4),
      0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.65));
  }

  .ne-ba-compare__handle svg {
    flex-shrink: 0;
  }
  .ne-ba-compare__handle svg + svg {
    margin-left: -2px;
  }

  /* Subtle dragging affordance */
  .ne-ba-compare[data-ba-dragging="1"] .ne-ba-compare__handle {
    transform: translate(-50%, -50%) scale(1.08);
  }
  .ne-ba-compare[data-ba-dragging="1"] .ne-ba-compare__viewport {
    cursor: ew-resize;
  }

  @media (max-width: 599px) {
    .ne-ba-compare__label {
      bottom: 10px;
      padding: 4px 10px;
      font-size: 0.65rem;
    }
    .ne-ba-compare__label--before { left: 10px; }
    .ne-ba-compare__label--after  { right: 10px; }
  }
/* END_BLOCK:before-after-comparison */

/* START_BLOCK:bento-cell (INDEX:72) */
.ne-bcell {
    position: relative;
    grid-column: span var(--ne-bc-cs-d, 3);
    grid-row: span var(--ne-bc-rs-d, 2);
    border-top-left-radius: var(--ne-bc-radius, 16px);
    border-top-right-radius: var(--ne-bc-radius, 16px);
    border-bottom-right-radius: var(--ne-bc-radius, 16px);
    border-bottom-left-radius: var(--ne-bc-radius, 16px);
    isolation: isolate;
    transform: translateZ(0);
    backface-visibility: hidden;
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.3s ease;
    min-width: 0;
    min-height: 0;
  }
  @media (max-width: 749px) {
    .ne-bcell {
      grid-column: span var(--ne-bc-cs-m, 2);
      grid-row: span var(--ne-bc-rs-m, 1);
    }
  }

  /* Solid backplate — paints scheme bg, clipped to radius (own box) */
  .ne-bcell--surface-solid::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg, #fff);
    border-radius: inherit;
    z-index: 0;
    pointer-events: none;
  }

  /* Glass on the cell HOST itself (mirrors the proven bento-grid tile,
     blocks/bento-tile.liquid). backdrop-filter on element X samples X's
     PARENT backdrop (the section/page behind the cell) — X's own transform/
     isolation makes X a stacking context but does NOT trap X's own filter.
     A backdrop-filter on a CHILD layer, by contrast, WAS trapped by the
     host's transform:translateZ(0)+isolation → Firefox blurred nothing
     (clear/flickering glass). Same-element filter is Firefox-stable. */
  .ne-bcell--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  .ne-bcell--surface-glass.ne-bcell--glass-tint {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.08)));
  }

  /* Background image + overlay always clip to the radius, independent of
     whether the content layer is clipped (clip_content toggle). */
  .ne-bcell__bg {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    overflow: hidden;
    z-index: 1;
    pointer-events: none;
  }
  .ne-bcell__bg-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .ne-bcell__overlay {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    z-index: 2;
    pointer-events: none;
  }

  /* Content layer — holds the nested @theme blocks */
  .ne-bcell__content {
    position: relative;
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: var(--ne-bc-gap, 12px);
    padding: var(--ne-bc-pad-y, 24px) var(--ne-bc-pad, 24px);
    width: 100%;
    height: 100%;
    box-sizing: border-box;
  }
  .ne-bcell--clip .ne-bcell__content {
    overflow: hidden;
    border-radius: inherit;
  }
  /* Fill image (aspect=parent, Rule 447) — clip it to the tile with the
     tile's rounded corners. With parallax the fill wrap overscans (Rule 448)
     so this clip is what turns the drift into a proper reveal instead of a
     hard cut at the block boundary / bleed into neighbours. */
  .ne-bcell__content:has(.ne-block-image-wrap--fill) {
    overflow: hidden;
    border-radius: inherit;
  }
  .ne-bcell__content > .shopify-block { display: contents; }

  /* Horizontal alignment (cross axis of the column flex) */
  .ne-bcell--ah-start .ne-bcell__content { align-items: flex-start; text-align: left; }
  .ne-bcell--ah-center .ne-bcell__content { align-items: center; text-align: center; }
  .ne-bcell--ah-end .ne-bcell__content { align-items: flex-end; text-align: right; }
  /* Vertical alignment (main axis) */
  .ne-bcell--av-start .ne-bcell__content { justify-content: flex-start; }
  .ne-bcell--av-center .ne-bcell__content { justify-content: center; }
  .ne-bcell--av-end .ne-bcell__content { justify-content: flex-end; }

  /* Effects (Rule 207 — lighter than section, share intensity vars) */
  .ne-bcell--shadow {
    box-shadow: 0 calc(8px * var(--ne-section-shadow-intensity, 1) * 0.6) calc(28px * var(--ne-section-shadow-intensity, 1) * 0.6) rgba(0, 0, 0, calc(0.32 * var(--ne-section-shadow-intensity, 1) * 0.6));
  }
  .ne-bcell--glow {
    box-shadow: 0 0 calc(28px * var(--ne-section-glow-intensity, 1) * 0.6) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-bcell--shadow.ne-bcell--glow {
    box-shadow:
      0 calc(8px * var(--ne-section-shadow-intensity, 1) * 0.6) calc(28px * var(--ne-section-shadow-intensity, 1) * 0.6) rgba(0, 0, 0, calc(0.32 * var(--ne-section-shadow-intensity, 1) * 0.6)),
      0 0 calc(28px * var(--ne-section-glow-intensity, 1) * 0.6) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }

  /* Hover effects */
  .ne-bcell--hover-lift:hover {
    transform: translateY(-4px) translateZ(0);
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
  }
  .ne-bcell--hover-zoom:hover .ne-bcell__bg-img {
    transform: scale(1.06);
  }
  .ne-bcell--hover-tilt {
    transition: transform 0.1s linear, box-shadow 0.3s ease;
  }
  .ne-bcell--hover-tilt:hover {
    transform: skewX(var(--ne-bc-tilt-y, 0deg)) skewY(var(--ne-bc-tilt-x, 0deg)) translateY(-2px);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
  }
  @keyframes ne-bcell-glow-pulse {
    0%, 100% { box-shadow: 0 0 24px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)); }
    50% { box-shadow: 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.6)); }
  }
  .ne-bcell--hover-glow-pulse:hover {
    animation: ne-bcell-glow-pulse 1.6s ease-in-out infinite;
  }
/* END_BLOCK:bento-cell */

/* START_BLOCK:bento-tile (INDEX:73) */
/* ── Tile base ─────────────────────────────────────────────── */
  .ne-bento__tile {
    position: relative;
    grid-column: span var(--ne-bento-cs-d, 2);
    grid-row: span var(--ne-bento-rs-d, 1);
    /* Explicit per-corner radius prevents GPU subpixel-rendering issues
       where shorthand border-radius can drop one corner under transform. */
    border-top-left-radius: var(--ne-bento-tile-radius, 16px);
    border-top-right-radius: var(--ne-bento-tile-radius, 16px);
    border-bottom-right-radius: var(--ne-bento-tile-radius, 16px);
    border-bottom-left-radius: var(--ne-bento-tile-radius, 16px);
    overflow: hidden;
    background: transparent;
    isolation: isolate;
    transform: translateZ(0);
    backface-visibility: hidden;
    transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), box-shadow 0.3s ease;
  }
  @media (max-width: 749px) {
    .ne-bento__tile {
      grid-column: span var(--ne-bento-cs-m, 1);
      grid-row: span var(--ne-bento-rs-m, 1);
    }
  }

  /* Backplate ::before — paints scheme bg below content */
  .ne-bento__tile::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg, #fff);
    border-radius: inherit;
    z-index: 0;
    pointer-events: none;
    transition: background 0.3s ease;
  }

  /* Direct children sit above backplate */
  .ne-bento__tile > * { position: relative; z-index: 1; }

  /* ── Surface modes ─────────────────────────────────────────── */
  .ne-bento__tile--surface-transparent::before { background: transparent; }
  .ne-bento__tile--surface-image-fill::before { background: transparent; }

  /* Glass: bg + backdrop-filter on the host directly (Rule 237 pattern,
     mirroring cart-promo/items/summary). Earlier revisions added
     translateZ(0) + will-change here to stabilize the compositor layer.
     Removed (user 2026-05-23) because BOTH translateZ(0) AND
     will-change: transform promote the tile into a 3D rendering context
     when the parent `.ne-bento__grid` has `perspective` set — the 3D
     context broke backdrop-filter sampling on EVERY tile, so glass
     always rendered transparent. Now: tile stays in 2D at rest; the
     hover rotateX/Y only briefly enters 3D space (acceptable trade-off).

     Border also dropped — a full-perimeter `border: 1px solid` produced
     a visible white bottom contour line. The backdrop-filter blur + bg
     tint already define the tile boundary visually. */
  .ne-bento__tile--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0;
  }
  /* Accent-tinted glass — opt-in via tile_glass_tint. */
  .ne-bento__tile--surface-glass.ne-bento__tile--glass-tint {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.08))) !important;
  }
  .ne-bento__tile--surface-glass::before { content: none; }

  /* Image-fill: tile bg becomes the image, content overlays.
     Works for image-tiles (only image fills) AND product/collection
     (image fills + body overlays at bottom with scrim). */
  .ne-bento__tile--surface-image-fill .ne-bento__tile-media {
    position: absolute !important;
    inset: 0 !important;
    z-index: 0 !important;
    width: 100% !important;
    height: 100% !important;
  }
  .ne-bento__tile--surface-image-fill .ne-bento__tile-product,
  .ne-bento__tile--surface-image-fill .ne-bento__tile-collection {
    position: relative;
    height: 100%;
  }
  .ne-bento__tile--surface-image-fill .ne-bento__tile-product-body,
  .ne-bento__tile--surface-image-fill .ne-bento__tile-collection-overlay {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    padding: 16px 18px;
    background: linear-gradient(to top, rgba(0,0,0,0.75) 0%, rgba(0,0,0,0) 100%);
    color: #fff;
    z-index: 2;
    text-shadow: 0 1px 4px rgba(0,0,0,0.4);
  }
  .ne-bento__tile--surface-image-fill .ne-bento__tile-price-compare,
  .ne-bento__tile--surface-image-fill .ne-bento__tile-product-title { color: #fff; }

  /* image_fill_text == 'scheme' (opt-in): overlay text follows the colour
     scheme instead of fixed white+scrim. For transparent PNGs / clean imagery
     where the area behind the text is the scheme surface (set both dark/light
     pickers to the same scheme to keep one colour across modes). Paints the
     scheme bg behind the (transparent) image + drops the scrim + scheme text. */
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme::before {
    background: var(--ne-scheme-bg, #fff);
  }
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme .ne-bento__tile-product-body,
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme .ne-bento__tile-collection-overlay {
    background: none;
    color: var(--ne-scheme-text);
    text-shadow: none;
  }
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme .ne-bento__tile-product-title,
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme .ne-bento__tile-collection-title {
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }
  .ne-bento__tile--surface-image-fill.ne-bento__tile--text-scheme .ne-bento__tile-price-compare {
    color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5));
  }
  .ne-bento__tile--surface-image-fill .ne-bento__tile-quick-add {
    z-index: 3;
  }

  /* Radius now via inline-style CSS-var on tile (range slider) */

  /* ── Block-effect coupling (Rule 207 — lighter than section) ── */
  .ne-bento__tile--shadow {
    box-shadow:
      0 calc(8px * var(--ne-section-shadow-intensity, 1) * 0.6) calc(28px * var(--ne-section-shadow-intensity, 1) * 0.6) rgba(0, 0, 0, calc(0.32 * var(--ne-section-shadow-intensity, 1) * 0.6));
  }
  .ne-bento__tile--glow {
    box-shadow:
      0 0 calc(28px * var(--ne-section-glow-intensity, 1) * 0.6) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }
  .ne-bento__tile--shadow.ne-bento__tile--glow {
    box-shadow:
      0 calc(8px * var(--ne-section-shadow-intensity, 1) * 0.6) calc(28px * var(--ne-section-shadow-intensity, 1) * 0.6) rgba(0, 0, 0, calc(0.32 * var(--ne-section-shadow-intensity, 1) * 0.6)),
      0 0 calc(28px * var(--ne-section-glow-intensity, 1) * 0.6) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35));
  }

  /* ── Hover effects ─────────────────────────────────────────── */
  .ne-bento__tile--hover-lift:hover {
    transform: translateY(-4px) translateZ(0);
    box-shadow: 0 16px 40px rgba(0, 0, 0, 0.35);
  }
  .ne-bento__tile--hover-zoom:hover .ne-bento__tile-img,
  .ne-bento__tile--hover-zoom:hover .ne-bento__tile-video,
  .ne-bento__tile--hover-zoom:hover .ne-bento__tile-video-embed {
    transform: scale(1.06);
  }
  /* Tilt follows cursor: --tx and --ty CSS vars set by JS on mousemove.
     Cursor-area pressed back (away from camera) — opposite area lifts.
     transform-style: preserve-3d removed from base — it created a 3D
     rendering context on every tilt tile (even idle ones) which broke
     backdrop-filter sampling on glass surfaces section-wide. The
     perspective() function inside the transform value provides the
     3D effect on its own; preserve-3d is only needed for NESTED 3D
     transforms (children rotating in parent's 3D space), which we
     don't have here. */
  .ne-bento__tile--hover-tilt {
    transition: transform 0.1s linear, box-shadow 0.3s ease;
  }
  /* Tilt hover — pure 2D transforms (skewX + skewY + translateY).
     Earlier revisions used `perspective(1000px) rotateX/Y(...)` which
     created a 3D rendering context on the tile. That context caused
     the compositor to evict adjacent compositor layers during rapid
     mousemove transform mutations — visible as other sections' glass
     flickering transparent during bento hover (user report 2026-05-23).
     2D skew preserves the cursor-following responsive feel (the tile
     "leans" toward the cursor) without any 3D compositing context, so
     glass on the tile AND on sibling sections remains stable.
     Skew angles are bounded smaller than rotation angles were (max
     6deg per axis vs 8deg rotation) — heavier skew distorts the tile
     shape visibly into a parallelogram, which doesn't match the
     intended "tilt" feel. */
  .ne-bento__tile--hover-tilt:hover {
    transform:
      skewX(var(--ne-bento-tilt-y, 0deg))
      skewY(var(--ne-bento-tilt-x, 0deg))
      translateY(-2px);
    box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
  }
  @keyframes ne-bento-glow-pulse {
    0%, 100% { box-shadow: 0 0 24px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)); }
    50% { box-shadow: 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.6)); }
  }
  .ne-bento__tile--hover-glow-pulse:hover {
    animation: ne-bento-glow-pulse 1.6s ease-in-out infinite;
  }

  /* ── Media wrapper (image / video / collection / product) ──── */
  .ne-bento__tile-media {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
    border-radius: inherit;
  }
  .ne-bento__tile-img,
  .ne-bento__tile-video,
  .ne-bento__tile-video-embed {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .ne-bento__tile-placeholder {
    width: 100%;
    height: 100%;
    background: var(--ne-scheme-bg-surface, rgba(0, 0, 0, 0.04));
  }
  .ne-bento__tile-placeholder svg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.45;
  }

  .ne-bento__tile-link {
    display: block;
    width: 100%;
    height: 100%;
    text-decoration: none;
    color: inherit;
  }

  /* Image overlay text */
  .ne-bento__tile-overlay-text {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    justify-content: flex-start;
    padding: 20px 24px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.55) 0%, rgba(0, 0, 0, 0) 60%);
    color: #fff;
    pointer-events: none;
    z-index: 2;
  }
  .ne-bento__tile-overlay-text > span {
    font-size: clamp(1rem, 1.6vw, 1.5rem);
    font-weight: 700;
    line-height: 1.2;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
  }

  /* ── Product tile ──────────────────────────────────────────── */
  .ne-bento__tile-product {
    display: flex;
    flex-direction: column;
    height: 100%;
    text-decoration: none;
    color: inherit;
  }
  .ne-bento__tile-product .ne-bento__tile-media {
    flex: 1;
    min-height: 0;
  }
  .ne-bento__tile-product-body {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 12px 14px;
    background: transparent;
  }
  .ne-bento__tile-product-title {
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  .ne-bento__tile-product-price {
    font-size: 13px;
    font-weight: 500;
  }
  .ne-bento__tile-price-sale { color: var(--ne-scheme-accent); font-weight: 600; }
  .ne-bento__tile-price-compare { color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5)); margin-left: 6px; }

  /* Quick-add button: always full-width at bottom inside reserved
     padding area. Body always reserves space so title never gets
     covered. Hover only toggles opacity — no layout shift. */
  .ne-bento__tile-quick-add {
    position: absolute;
    bottom: 12px;
    left: 12px;
    right: 12px;
    z-index: 3;
    padding: 10px 14px;
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: auto;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity 0.2s ease, transform 0.2s ease;
    pointer-events: none;
  }
  .ne-bento__tile--product:hover .ne-bento__tile-quick-add,
  .ne-bento__tile--product:focus-within .ne-bento__tile-quick-add {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }

  /* Title stays at default position. On hover the body expands
     padding-bottom so title moves UP and button slides into the gap.
     No reserved empty space when not hovered. */
  .ne-bento__tile--has-qa .ne-bento__tile-product-body {
    transition: padding-bottom 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
  }
  .ne-bento__tile--has-qa:hover .ne-bento__tile-product-body,
  .ne-bento__tile--has-qa:focus-within .ne-bento__tile-product-body {
    padding-bottom: 56px;
  }

  /* ── Collection tile ───────────────────────────────────────── */
  .ne-bento__tile-collection {
    display: block;
    width: 100%;
    height: 100%;
    text-decoration: none;
    color: inherit;
  }
  .ne-bento__tile-collection-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: flex-start;
    padding: 20px 24px;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.7) 0%, rgba(0, 0, 0, 0.1) 50%, rgba(0, 0, 0, 0) 100%);
    color: #fff;
    z-index: 2;
    text-align: left;
  }
  .ne-bento__tile-collection-title {
    font-size: clamp(1.1rem, 1.6vw, 1.5rem);
    font-weight: 700;
    line-height: 1.2;
    margin: 0;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
  }
  .ne-bento__tile-collection-count {
    font-size: 13px;
    margin-top: 4px;
    opacity: 0.9;
  }

  /* ── Content tile (nested @theme blocks) ───────────────────── */
  .ne-bento__tile-content {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    height: 100%;
    color: var(--ne-scheme-text);
  }
  .ne-bento__tile-content > .shopify-block { display: contents; }

  /* ── Blank + empty placeholder ─────────────────────────────── */
  .ne-bento__tile-blank {
    width: 100%;
    height: 100%;
  }
  .ne-bento__tile-empty {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: 16px;
    color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5));
    font-size: 13px;
    text-align: center;
  }

  /* ── Image-fill content layering (when image fills tile) ───── */
  .ne-bento__tile--has-image-fill .ne-bento__tile-overlay-text {
    z-index: 2;
  }

  /* Glass tile text follows the colour scheme — the glass tint IS the scheme
     surface, so scheme text always contrasts (dark scheme → light text,
     light scheme → dark text). Fixes white-on-light-scheme illegibility. */
  .ne-bento__tile--surface-glass .ne-bento__tile-content,
  .ne-bento__tile--surface-glass .ne-bento__tile-product-body { color: var(--ne-scheme-text); }
  .ne-bento__tile--surface-glass .ne-bento__tile-product-title { color: var(--ne-scheme-heading, var(--ne-scheme-text)); }
  .ne-bento__tile--surface-glass .ne-bento__tile-price-compare { color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5)); }
/* END_BLOCK:bento-tile */

/* START_BLOCK:box (INDEX:74) */
.ne-box {
    position: relative;
    box-sizing: border-box;
    display: flex;
    flex-direction: column;
    padding: var(--ne-box-pad, 0);
    border-radius: var(--ne-box-radius, 0);
    background: transparent;
    min-width: 0;
    overflow: clip;
  }
  /* horizontal align = cross axis of the column */
  .ne-box--halign-start   { align-items: flex-start; }
  .ne-box--halign-center  { align-items: center; }
  .ne-box--halign-end     { align-items: flex-end; }
  .ne-box--halign-stretch { align-items: stretch; }
  /* vertical align = main axis of the column */
  .ne-box--valign-start   { justify-content: flex-start; }
  .ne-box--valign-center  { justify-content: center; }
  .ne-box--valign-end     { justify-content: flex-end; }
  .ne-box--valign-between  { justify-content: space-between; }
  .ne-box > .shopify-block { max-width: 100%; }

  /* Surface: solid paints the (custom or inherited) scheme surface bg */
  .ne-box--solid { background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg)); }
  .ne-box.ne-box[class*="color-scheme-"].ne-box--solid {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg)) !important;
    color: var(--ne-scheme-text);
  }
  /* Direct-colour override: propagate the override text colour to child blocks
     that don't set their own. The scheme rule above only fires with a color-X
     class, which the pure override (no custom scheme) doesn't add. */
  .ne-box.ne-cov { color: var(--ne-scheme-text); }

  /* Glass — Theme Settings → Effects → Glassmorphism vars */
  .ne-box.ne-box--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05)) !important;
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0;
  }

  .ne-box--glow   { box-shadow: 0 0 calc(24px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.4)); }
  .ne-box--shadow { box-shadow: 0 calc(16px * var(--ne-fx-shadow-i, 1)) calc(40px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, calc(0.22 * var(--ne-fx-shadow-i, 1))); }
  .ne-box--glow.ne-box--shadow {
    box-shadow: 0 calc(16px * var(--ne-fx-shadow-i, 1)) calc(40px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, calc(0.22 * var(--ne-fx-shadow-i, 1))), 0 0 calc(24px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.4));
  }
/* END_BLOCK:box */

/* START_BLOCK:brand (INDEX:75) */
.ne-brand-item {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--ne-space-2, 0.5rem);
    flex-shrink: 0;
    transition: transform var(--ne-transition-fast, 150ms), opacity var(--ne-transition-fast, 150ms);
  }
  .ne-brand-item--solo { gap: 0; }
  .ne-brand-item__link {
    display: flex; flex-direction: column; align-items: center;
    gap: inherit; text-decoration: none; color: inherit;
  }
  .ne-brand-item__logo {
    width: auto; max-width: 200px; object-fit: contain;
    opacity: var(--ne-bs-opacity, 0.6);
    transition: opacity var(--ne-transition-base, 250ms),
                transform var(--ne-transition-base, 250ms),
                filter var(--ne-transition-base, 250ms);
  }
  .ne-brand-item:hover .ne-brand-item__logo { opacity: 1; transform: scale(1.08); }

  /* Recolor: SVG/PNG-mask sampled in scheme accent. Inherits hover-color. */
  .ne-brand-item__logo--mask {
    display: inline-block;
    width: 200px;
    background-color: var(--ne-scheme-accent, currentColor);
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
  }
  .ne-brand-item--recolor:hover .ne-brand-item__logo--mask {
    background-color: var(--ne-scheme-accent-hover, var(--ne-scheme-text));
  }

  .ne-brand-item__label {
    font-size: var(--ne-font-size-xs, 0.75rem);
    color: var(--ne-scheme-text-muted);
    white-space: nowrap;
    opacity: 0.8;
  }
  .ne-brand-item__name-only {
    font-size: var(--ne-font-size-lg, 1.125rem);
    color: var(--ne-scheme-text-muted);
    white-space: nowrap;
    opacity: var(--ne-bs-opacity, 0.6);
    transition: all var(--ne-transition-base, 250ms);
  }
  .ne-brand-item:hover .ne-brand-item__name-only {
    color: var(--ne-scheme-text);
    opacity: 1;
  }
/* END_BLOCK:brand */

/* START_BLOCK:button (INDEX:76) */
/* Wrapper must span full available width for alignment to have effect.
     Alignment via CSS var — parent containers (group, stack section,
     ...) can set --ne-bw-justify as a default; the block's own
     modifier class sets the var at element-scope so explicit
     per-block alignment always wins over inherited group defaults. */
  .ne-block-button-wrap {
    display: flex;
    width: 100%;
    justify-content: var(--ne-bw-justify, flex-start);
  }
  .ne-block-button-wrap--left   { --ne-bw-justify: flex-start; }
  .ne-block-button-wrap--center { --ne-bw-justify: center; }
  .ne-block-button-wrap--right  { --ne-bw-justify: flex-end; }
  .ne-block-button-wrap--full   { --ne-bw-justify: stretch; }
  .ne-block-button-wrap--full .ne-block-button { width: 100%; justify-content: center; }

  .ne-block-button {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
  }
  .ne-block-button__icon { flex: 0 0 auto; }
  .ne-block-button--icon-only { aspect-ratio: 1; padding-inline: 0.75em; justify-content: center; }

  /* Mask-painted custom icon — inherits currentColor from button text */
  .ne-block-button__icon--mask {
    display: inline-block;
    background-color: currentColor;
    -webkit-mask-size: contain; mask-size: contain;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center; mask-position: center;
  }

  /* Per-block size overrides (opt-in via toggle) */
  .ne-block-button.ne-btn--sm { padding: 0.5rem 1rem;   font-size: var(--ne-font-size-sm, 0.875rem); }
  .ne-block-button.ne-btn--md { padding: 0.75rem 1.5rem; font-size: var(--ne-font-size-md, 1rem); }
  .ne-block-button.ne-btn--lg { padding: 1rem 2rem;    font-size: var(--ne-font-size-lg, 1.125rem); }

  /* Per-block shape overrides (opt-in via toggle) */
  .ne-block-button.ne-btn--rounded { border-radius: 8px !important; }
  .ne-block-button.ne-btn--pill    { border-radius: 999px !important; }
  .ne-block-button.ne-btn--square  { border-radius: 0 !important; }

  /* Stacked shadow layers — hard + soft + glow can all combine */
  .ne-block-button {
    --ne-btn-fx-hard: 0 0 0 0 transparent;
    --ne-btn-fx-soft: 0 0 0 0 transparent;
    --ne-btn-fx-glow: 0 0 0 0 transparent;
    transition: transform .15s ease, box-shadow .15s ease;
    box-shadow: var(--ne-btn-fx-hard), var(--ne-btn-fx-soft), var(--ne-btn-fx-glow);
  }
  .ne-block-button--fx-shadow { --ne-btn-fx-hard: 0 4px 0 0 rgba(0,0,0,0.85); }
  .ne-block-button--fx-soft   { --ne-btn-fx-soft: 0 6px 18px rgba(0,0,0,0.22); }
  /* Two-layer glow matching the header button (menu.liquid --ne-action-shadow
     at default glow-intensity 0.5): an opaque-accent core + a softer
     accent-glow halo. The opaque core is what gives the header glow its
     punch — the previous single accent-glow@0.3 layer read flat by
     comparison (user 2026-05-24: match button glow to header button). */
  .ne-block-button--fx-glow   { --ne-btn-fx-glow: 0 0 7px var(--ne-scheme-accent, rgba(99,102,241,0.5)), 0 0 14px var(--ne-scheme-accent-glow, rgba(99,102,241,0.3)); }
  /* Hard shadow stays at same offset on hover — only when fx-lift is
     also active does the apparent distance grow (because the button
     lifts up while the shadow stays put). */
  .ne-block-button--fx-lift.ne-block-button--fx-shadow:hover,
  .ne-block-button--fx-lift.ne-block-button--fx-shadow:focus-visible { --ne-btn-fx-hard: 0 7px 0 0 rgba(0,0,0,0.85); }
  .ne-block-button--fx-soft:hover,
  .ne-block-button--fx-soft:focus-visible { --ne-btn-fx-soft: 0 10px 26px rgba(0,0,0,0.32); }
  .ne-block-button--fx-glow:hover,
  .ne-block-button--fx-glow:focus-visible { --ne-btn-fx-glow: 0 0 14px var(--ne-scheme-accent, rgba(99,102,241,0.8)), 0 0 28px var(--ne-scheme-accent-glow, rgba(99,102,241,0.5)); }

  /* 3D lift — independent transform, still combinable with any shadow */
  .ne-block-button--fx-lift { transform: translateY(-4px); }
  .ne-block-button--fx-lift:hover,
  .ne-block-button--fx-lift:focus-visible { transform: translateY(-7px); }
  /* Click press — dips toward the surface on :active, mirroring the header
     3D button's click feedback (user 2026-05-24). Snappy 0.08s transition. */
  .ne-block-button--fx-lift:active { transform: translateY(0); transition: transform 0.08s ease; }
/* END_BLOCK:button */

/* START_BLOCK:cart-items (INDEX:77) */
/* Each row + the header is an independent grid with the same track
     template. Subgrid was tempting (cross-row max-content alignment)
     but it breaks card_padding: padding applied to a subgrid row
     doesn't affect the parent-track-positioned children, so images
     visually overlap the padding area. With independent grids the
     row's own padding insets every child correctly, and cross-row
     alignment is preserved via the shared min-width tokens
     (--ne-ci-qty-min / --ne-ci-total-min). */
  .ne-cart-items {
    --ne-ci-glow-effect: var(--ne-block-glow-effect, 0 0 32px var(--ne-scheme-accent-glow));
    --ne-ci-shadow-effect: var(--ne-block-shadow-effect, 0 8px 24px rgba(0, 0, 0, 0.18));
    --ne-ci-qty-min: 160px;
    --ne-ci-total-min: 96px;
    --ne-ci-track:
      var(--ne-ci-img-size, 80px)
      minmax(0, 1fr)
      minmax(var(--ne-ci-qty-min), max-content)
      minmax(var(--ne-ci-total-min), max-content);
    display: flex;
    flex-direction: column;
    gap: var(--ne-ci-gap, 0);
  }
  /* Custom color-scheme on the items wrapper — suppress critical.css
     auto-bg so glass/transparent surfaces stay intact (Rule 173). */
  .ne-cart-items[class*="color-"] { background: transparent !important; }

  /* Shared column-track between header and item rows so labels sit
     exactly above their columns and quantity/total widgets line up
     vertically across all items. Total uses min-content with a
     min-width fallback so the column doesn't shift per row. */
  .ne-cart-items {
    --ne-ci-track:
      var(--ne-ci-img-size, 80px) /* image */
      minmax(0, 1fr)              /* info */
      minmax(160px, max-content)  /* qty + remove (fixed across rows) */
      minmax(96px, max-content);  /* total — anchored to widest price */
  }

  /* Header uses the same track template as the rows so labels sit
     directly above their respective columns. */
  .ne-cart-items__header {
    display: grid;
    grid-template-columns: var(--ne-ci-track);
    column-gap: var(--ne-space-5);
    padding: 0 var(--ne-ci-padding, 16px) var(--ne-space-3);
    border-bottom: 1px solid var(--ne-scheme-border);
    margin-bottom: var(--ne-space-2);
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
  }
  /* "Product" header label spans image+info columns. */
  .ne-cart-items__header > :nth-child(1) { grid-column: 1 / span 2; }
  .ne-cart-items__header > :nth-child(2) {
    grid-column: 3;
    text-align: left;
    padding-left: 36px;
  }
  .ne-cart-items__header > :nth-child(3) { grid-column: 4; text-align: right; }

  .ne-cart-items__row {
    display: grid;
    grid-template-columns: var(--ne-ci-track);
    column-gap: var(--ne-space-5);
    position: relative;
    padding: var(--ne-ci-padding, 0);
    border-radius: var(--ne-ci-radius, 0);
    align-items: center;
    /* No isolation: isolate — would form backdrop-root and starve the
       glass ::before of a sample. Glass is on the host directly below
       (mirrors announcement-bar pattern). 2026-05-06 fix. */
  }

  .ne-cart-items__row:not(:last-child) {
    border-bottom: 1px solid var(--ne-scheme-border);
  }

  /* AJAX loading + removing states. Section-level .ne-cart--loading dims +
     blocks pointer-events while a /cart/change.js round-trip is in flight.
     Row-level --removing is a quick local cue applied right at click time
     (before the section refetch lands). Both use opacity only — transform
     would shift the layout during the fade. */
  .ne-cart--loading .ne-cart-items {
    pointer-events: none;
    opacity: 0.6;
    transition: opacity 150ms ease;
  }
  .ne-cart-items__row--removing {
    opacity: 0.5;
    pointer-events: none;
    transition: opacity 120ms ease;
  }

  /* Solid: backplate via ::before (Rule 157). */
  .ne-cart-items--surface-solid .ne-cart-items__row::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border-radius: inherit;
    z-index: 0;
  }
  .ne-cart-items--surface-solid .ne-cart-items__row > * { position: relative; z-index: 1; }

  /* Glass: bg + backdrop-filter on the row itself (no ::before so the
     blur isn't trapped inside a backdrop-root). */
  .ne-cart-items--surface-glass .ne-cart-items__row {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border — white glass-border reads as a bottom contour on dark
       bg (user 2026-05-24). */
    border: 0;
  }
  .ne-cart-items--surface-glass.ne-cart-items--glass-tint .ne-cart-items__row {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.08))) !important;
  }

  .ne-cart-items--glow .ne-cart-items__row { box-shadow: var(--ne-ci-glow-effect); }
  .ne-cart-items--shadow .ne-cart-items__row { box-shadow: var(--ne-ci-shadow-effect); }
  .ne-cart-items--glow.ne-cart-items--shadow .ne-cart-items__row {
    box-shadow: var(--ne-ci-shadow-effect), var(--ne-ci-glow-effect);
  }

  .ne-cart-items__media { width: var(--ne-ci-img-size, 80px); }
  .ne-cart-items__img {
    width: var(--ne-ci-img-size, 80px);
    height: var(--ne-ci-img-size, 80px);
    object-fit: cover;
    border-radius: var(--ne-ci-img-radius, var(--ne-radius-md, 8px));
    display: block;
  }

  .ne-cart-items__title {
    display: block;
    text-decoration: none;
    color: var(--ne-scheme-text);
    font-weight: 500;
    font-size: var(--ne-font-size-sm);
    line-height: 1.3;
  }
  .ne-cart-items__title:hover { color: var(--ne-scheme-accent); }
  .ne-cart-items__variant,
  .ne-cart-items__plan,
  .ne-cart-items__unit {
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-xs);
    margin-top: var(--ne-space-1);
  }
  .ne-cart-items__price {
    display: inline-flex;
    align-items: baseline;
    gap: var(--ne-space-2);
    flex-wrap: wrap;
    font-size: var(--ne-font-size-sm);
    margin-top: var(--ne-space-1);
  }
  .ne-cart-items__compare-price {
    color: var(--ne-scheme-fg-muted);
    text-decoration: line-through;
    font-size: var(--ne-font-size-xs);
  }
  .ne-cart-items__sale-price {
    color: var(--ne-scheme-sale, var(--ne-scheme-accent));
    font-weight: 600;
  }
  .ne-cart-items__discount {
    color: var(--ne-scheme-accent);
    font-size: var(--ne-font-size-xs);
    margin-top: var(--ne-space-1);
  }

  .ne-cart-items__qty-cell {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: var(--ne-space-3);
  }

  .ne-cart-items__qty-spinner {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-cart-items__qty-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    background: none;
    border: none;
    color: var(--ne-scheme-text);
    cursor: pointer;
  }
  .ne-cart-items__qty-input {
    width: 40px;
    text-align: center;
    border: none;
    background: none;
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-sm);
    -moz-appearance: textfield;
    appearance: textfield;
  }
  .ne-cart-items__qty-input::-webkit-inner-spin-button,
  .ne-cart-items__qty-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

  .ne-cart-items__qty-select {
    padding: var(--ne-space-2) var(--ne-space-3);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md, 8px);
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-sm);
  }

  .ne-cart-items__remove {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-1);
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-xs);
    cursor: pointer;
    text-decoration: none;
    transition: color 0.15s ease;
  }
  .ne-cart-items__remove:hover { color: var(--ne-scheme-accent); }
  .ne-cart-items__remove--text .ne-cart-items__remove-text,
  .ne-cart-items__remove--icon_text .ne-cart-items__remove-text {
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .ne-cart-items__remove-icon {
    flex-shrink: 0;
    transition: color 0.15s ease;
  }

  .ne-cart-items__total {
    text-align: right;
  }
  .ne-cart-items__total-price {
    font-size: var(--ne-font-size-sm);
    font-weight: 600;
  }

  @media (max-width: 749px) {
    /* Mobile: switch off subgrid entirely — each row is a self-contained
       2-column grid (image left, content right). Subgrid alignment across
       rows is unnecessary on mobile because only one product is visible
       at a time. */
    .ne-cart-items {
      display: flex;
      flex-direction: column;
      grid-template-columns: none;
      gap: var(--ne-ci-gap, 8px);
    }
    .ne-cart-items__header { display: none; }
    .ne-cart-items__row {
      display: grid;
      grid-template-columns: var(--ne-ci-img-size, 64px) minmax(0, 1fr);
      grid-template-rows: auto auto auto;
      grid-template-areas:
        "media info"
        "media qty"
        "media total";
      grid-column: auto;
      column-gap: var(--ne-space-4);
      row-gap: var(--ne-space-2);
      align-items: start;
      /* Card padding is applied as an inset around the entire row so
         the image AND text both move inward together (the image is in
         column 1 of the row's own grid, not the parent's). */
      padding: var(--ne-ci-padding, 16px);
    }
    .ne-cart-items__media { grid-area: media; }
    .ne-cart-items__info { grid-area: info; min-width: 0; }
    .ne-cart-items__qty-cell { grid-area: qty; justify-content: flex-start; }
    .ne-cart-items__total { grid-area: total; text-align: left; }
    .ne-cart-items__img {
      width: var(--ne-ci-img-size, 64px);
      height: var(--ne-ci-img-size, 64px);
    }
    /* Truncate long titles to 2 lines on mobile to keep rows compact */
    .ne-cart-items__title {
      display: -webkit-box;
      -webkit-line-clamp: 2;
      line-clamp: 2;
      -webkit-box-orient: vertical;
      overflow: hidden;
      text-overflow: ellipsis;
    }
  }
/* END_BLOCK:cart-items */

/* START_BLOCK:cart-promo (INDEX:78) */
.ne-cart-promo {
    --ne-cp-glow-effect: var(--ne-block-glow-effect, 0 0 32px var(--ne-scheme-accent-glow));
    --ne-cp-shadow-effect: var(--ne-block-shadow-effect, 0 8px 24px rgba(0, 0, 0, 0.18));
    position: relative;
    padding: var(--ne-space-3) var(--ne-space-4);
    border-radius: var(--ne-radius-md, 8px);
    /* No isolation: isolate (forms backdrop-root → glass blur is
       starved on initial paint). Glass moves to host directly. */
  }
  /* Custom color-scheme — suppress the wrapper background (Rule 173).
     Glass/transparent surfaces handle their own paint via ::before. */
  .ne-cart-promo[class*="color-"] { background: transparent !important; }
  .ne-cart-promo--glow { box-shadow: var(--ne-cp-glow-effect); }
  .ne-cart-promo--shadow { box-shadow: var(--ne-cp-shadow-effect); }
  .ne-cart-promo--glow.ne-cart-promo--shadow {
    box-shadow: var(--ne-cp-shadow-effect), var(--ne-cp-glow-effect);
  }

  .ne-cart-promo__inner {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: var(--ne-space-3);
  }

  .ne-cart-promo__icon-img {
    width: 24px;
    height: 24px;
    object-fit: contain;
    display: block;
  }

  .ne-cart-promo__icon--mask {
    display: inline-block;
    width: 24px;
    height: 24px;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    flex-shrink: 0;
  }

  .ne-cart-promo__message {
    margin: 0;
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text);
  }

  .ne-cart-promo--reached .ne-cart-promo__message {
    /* Use heading color (white in scheme-1) for reliable contrast on
       glass surfaces. Accent (#6366f1) doesn't get reliable WCAG AA
       on semi-transparent backdrops. */
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    font-weight: 500;
  }

  /* Solid: backplate via ::before (Rule 157). */
  .ne-cart-promo--surface-solid::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border);
    border-radius: inherit;
    z-index: 0;
  }

  /* Glass: bg + backdrop-filter on the host directly (no backdrop-root
     trap). Mirrors announcement-bar pattern. */
  .ne-cart-promo--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border — white glass-border reads as a bottom contour on dark
       bg (user 2026-05-24). */
    border: 0;
  }
  .ne-cart-promo--surface-glass::before { content: none; }

  .ne-cart-promo--surface-transparent::before { content: none; }

  .ne-cart-promo__bar {
    position: relative;
    z-index: 1;
    margin-top: var(--ne-space-2);
    height: 4px;
    background: var(--ne-scheme-border, rgba(255, 255, 255, 0.1));
    border-radius: 2px;
    overflow: hidden;
  }

  .ne-cart-promo__bar-fill {
    height: 100%;
    background: var(--ne-scheme-accent);
    border-radius: 2px;
    transition: width 0.3s ease;
  }

  .ne-cart-promo--reached .ne-cart-promo__bar-fill {
    box-shadow: 0 0 8px var(--ne-scheme-accent-glow);
  }
/* END_BLOCK:cart-promo */

/* START_BLOCK:cart-summary (INDEX:79) */
.ne-cart-summary {
    --ne-cs-glow-effect: var(--ne-block-glow-effect, 0 0 32px var(--ne-scheme-accent-glow));
    --ne-cs-shadow-effect: var(--ne-block-shadow-effect, 0 8px 24px rgba(0, 0, 0, 0.18));
    position: relative;
    padding: var(--ne-cs-padding, 24px);
    border-radius: var(--ne-cs-radius, 12px);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4);
    /* No isolation: isolate. Per CSS Backdrop Filter spec, isolation
       forms a backdrop-root for descendants — putting backdrop-filter
       on a ::before pseudo would then sample only the host's (empty)
       content, leaving glass visibly transparent until some unrelated
       repaint forced a re-sample. announcement-bar / .ne-section--glass
       in critical.css already follow this pattern. 2026-05-06 fix. */
  }
  /* Custom color-scheme — suppress the wrapper background paint that
     critical.css applies via .color-scheme-X (Rule 173). Foreground
     vars (text, accent, border) still cascade to children. */
  .ne-cart-summary[class*="color-"] { background: transparent !important; }

  .ne-cart-summary--sticky {
    position: sticky;
    top: 100px;
  }

  /* Solid: backplate via ::before so card-padding doesn't leak the
     section bg through subpixel seams (Rule 157). */
  .ne-cart-summary--surface-solid::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border);
    border-radius: inherit;
    z-index: 0;
  }
  .ne-cart-summary--surface-solid > * { position: relative; z-index: 1; }

  /* Glass: bg + backdrop-filter on the HOST element directly. The host
     has no backdrop-root above it inside cart-section so the blur
     samples the page background correctly from frame 1. */
  .ne-cart-summary--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border — white glass-border reads as a bottom contour on dark
       bg (user 2026-05-24). */
    border: 0;
  }
  .ne-cart-summary--surface-glass::before { content: none; }

  .ne-cart-summary--surface-transparent::before { content: none; }

  .ne-cart-summary--glow { box-shadow: var(--ne-cs-glow-effect); }
  .ne-cart-summary--shadow { box-shadow: var(--ne-cs-shadow-effect); }
  .ne-cart-summary--glow.ne-cart-summary--shadow {
    box-shadow: var(--ne-cs-shadow-effect), var(--ne-cs-glow-effect);
  }

  .ne-cart-summary__note {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
  }
  .ne-cart-summary__note-label {
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
  }
  .ne-cart-summary__note-input {
    width: 100%;
    min-height: 80px;
    padding: var(--ne-space-2) var(--ne-space-3);
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.04));
    border: 1px solid var(--ne-scheme-border);
    /* A multi-line textarea must never become a full pill (input_shape:pill
       sets --ne-radius-input:9999px and the global input/textarea rule wins
       over the block's plain border-radius). Cap at a rounded value; square
       (0) + rounded stay untouched via min(). Same fix as contact-form. */
    border-radius: min(var(--ne-radius-input, 8px), 16px) !important;
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-sm);
    resize: vertical;
  }

  .ne-cart-summary__discount {
    display: flex;
    justify-content: space-between;
    font-size: var(--ne-font-size-sm);
  }
  .ne-cart-summary__discount-amount { color: var(--ne-scheme-accent); }

  .ne-cart-summary__subtotal {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding-top: var(--ne-space-3);
    border-top: 1px solid var(--ne-scheme-border);
  }
  .ne-cart-summary__subtotal-label {
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
  }
  .ne-cart-summary__subtotal-amount {
    font-size: var(--ne-font-size-lg);
    font-weight: 600;
  }

  .ne-cart-summary__estimated {
    display: flex;
    justify-content: space-between;
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-fg-muted);
  }

  .ne-cart-summary__taxnotice {
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-xs);
  }

  .ne-cart-summary__checkout { width: 100%; }

  /* Express-checkout container (Shop Pay / PayPal / Apple Pay etc.).
     Shopify renders these via the additional-checkout-buttons drop.
     The container always renders; if no providers are configured the
     output is whitespace-only — :not(:has(*)) collapses it. */
  .ne-cart-summary__dynamic-checkout {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
  }
  .ne-cart-summary__dynamic-checkout:not(:has(*)) { display: none; }
  /* Match the express/PayPal button shape to the pill Check-out above it. These
     come from `content_for_additional_checkout_buttons` → `.additional-checkout-button`,
     NOT `.shopify-payment-button__button` (which the old rule targeted and which
     only exists on the product form / quick-view). Cover both DOMs. PayPal's
     iframe button may still resist the radius; the wrapper clip is the fallback. */
  /* Custom property pierces the accelerated-checkout shadow DOM; the class/iframe
     clip is the light-DOM fallback (see cart-drawer.liquid for the full rationale). */
  .ne-cart-summary__dynamic-checkout {
    --shopify-accelerated-checkout-button-border-radius: var(--ne-btn-radius, 8px);
  }
  .ne-cart-summary__dynamic-checkout .wallet-button-wrapper,
  .ne-cart-summary__dynamic-checkout .wallet-cart-grid { width: 100%; }
  .ne-cart-summary__dynamic-checkout button[type="submit"],
  .ne-cart-summary__dynamic-checkout .shopify-payment-button__button,
  .ne-cart-summary__dynamic-checkout .wallet-cart-button-container,
  .ne-cart-summary__dynamic-checkout .wallet-cart-button,
  .ne-cart-summary__dynamic-checkout .wallet-cart-button > *,
  .ne-cart-summary__dynamic-checkout iframe {
    border-radius: var(--ne-btn-radius, 8px) !important;
    overflow: hidden !important;
  }

  .ne-cart-summary__express-fallback {
    width: 100%;
    text-align: center;
  }

  .ne-cart-summary__continue {
    display: block;
    text-align: center;
    color: var(--ne-scheme-fg-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
    font-size: var(--ne-font-size-sm);
  }
  .ne-cart-summary__continue:hover { color: var(--ne-scheme-accent); }

  @media (max-width: 989px) {
    /* Disable desktop sticky on mobile but keep position:relative —
       the surface ::before uses position:absolute with inset:0 and
       needs summary itself to be the containing block. With static
       the ::before would inherit the next positioned ancestor (the
       section) and paint over every sibling block. */
    .ne-cart-summary.ne-cart-summary--sticky {
      position: relative !important;
      top: auto !important;
    }
  }

  /* The mobile-sticky bar (<aside data-sticky-cart-bar>) is rendered in
     Liquid alongside the in-flow summary. JS body-mounts it on init at
     mobile viewport and sets data-sticky-mounted="1". On desktop the
     attribute never gets set, so the bar would otherwise show up in
     normal flow as a duplicate summary. Hide it whenever the JS hasn't
     mounted it (initial paint + desktop forever). */
  .ne-cart-summary--mobile-bottom:not([data-sticky-mounted="1"]) {
    display: none !important;
  }

  /* Mobile sticky bottom — JS body-mounts the summary so position:fixed
     works regardless of which ancestor has transform/backdrop-filter/
     will-change (Rule 146). JS also: (a) computes section-width and
     applies it via inline-style if mobile_sticky_full_width=false,
     (b) auto-hides the bar via IntersectionObserver once the natural
     summary anchor (which is the cart-section bottom) scrolls into
     view, so the footer is never overlapped. */
  @media (max-width: 749px) {
    /* Sticky-bar pattern (mirrors snippets/sticky-product-bar.liquid).
       JS body-mounts the bar ONCE on init (Rule 146). It always uses
       position:fixed, and visibility is toggled via [data-sticky-state]
       attribute — NOT by moving DOM in/out. This eliminates the
       duplicate-render flicker that happens when Shopify live-previews
       a setting change. The customer sees a smooth fade.

       In-flow summary remains a separate concern — it's rendered the
       same Liquid block; on mobile-bottom mode, JS hides the body-mounted
       bar when the in-flow summary's natural position is visible. */
    .ne-cart-summary.ne-cart-summary--mobile-bottom[data-sticky-mounted="1"] {
      position: fixed !important;
      bottom: 0 !important;
      top: auto !important;
      /* No !important on left/right/width — JS inline-style controls the
         section-aligned positioning. Edge-to-edge override below uses
         !important to enforce viewport-edges. */
      left: 0;
      right: auto;
      z-index: 30;
      margin: 0 !important;
      isolation: isolate;
      /* Transform-only transition. No opacity/visibility change — element
         stays continuously rendered so backdrop-filter remains computed
         throughout the slide. Eliminates the "glass blooms in late"
         flicker that opacity-fade caused (Rule 218). */
      transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
      border-radius: var(--ne-cs-radius, 12px) var(--ne-cs-radius, 12px) 0 0 !important;
      padding: var(--ne-space-3) var(--ne-space-4) !important;
      gap: var(--ne-space-2) !important;
      will-change: transform;
    }
    /* Hidden state: slide off-screen below the viewport. Element is still
       rendered (no opacity/visibility change), so glass backdrop-filter
       remains active and re-appears instantly on next show. */
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"][data-sticky-state="hidden"] {
      transform: translateY(105%);
      pointer-events: none;
    }
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"][data-sticky-state="visible"] {
      transform: translateY(0);
      pointer-events: auto;
    }
    /* Edge-to-edge mode override — !important to defeat JS inline-style */
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"].ne-cart-summary--mobile-fullwidth {
      left: 0 !important;
      right: 0 !important;
      width: auto !important;
      border-radius: 0 !important;
      border: none !important;
    }
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"].ne-cart-summary--mobile-fullwidth.ne-cart-summary--surface-glass::before {
      border: none !important;
    }
    /* Compact bar — note hidden, subtotal divider dropped */
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"] .ne-cart-summary__note {
      display: none;
    }
    .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"] .ne-cart-summary__subtotal {
      border-top: none;
      padding-top: 0;
    }
    @media (prefers-reduced-motion: reduce) {
      .ne-cart-summary.ne-cart-summary--mobile-bottom[data-sticky-mounted="1"] {
        transition: opacity 0.15s ease;
      }
      .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"][data-sticky-state="hidden"] {
        transform: none;
        opacity: 0;
        pointer-events: none;
      }
      .ne-cart-summary--mobile-bottom[data-sticky-mounted="1"][data-sticky-state="visible"] {
        opacity: 1;
      }
    }
  }
/* END_BLOCK:cart-summary */

/* START_BLOCK:collection-banner (INDEX:80) */
.ne-collection-banner {
    position: relative;
    /* Stacking context to contain the internal layers — via z-index, NOT
       isolation: isolate. isolation forms a *backdrop root*, which would trap
       the whole-banner glass backdrop-filter (it could then only frost the
       box's own background, not the page behind the banner). z-index:0 gives a
       stacking context without a backdrop root, so the frost reaches the page. */
    z-index: 0;
    /* Resolved overlay tint (colour × opacity), shared by the media overlay and
       the extended-plate overlay (surf-full). */
    --ne-cb-ov: color-mix(in srgb, var(--ne-cb-overlay-color, #000) var(--ne-cb-overlay-alpha, 0%), transparent);
    width: 100%;
    border-radius: var(--ne-cb-radius, var(--ne-card-radius, var(--ne-radius-md, 12px)));
    overflow: clip;
    color: var(--ne-cb-text-color, #fff);
  }

  /* Height presets → CSS vars (desktop + mobile), consumed per layout below. */
  .ne-collection-banner--compact { --ne-cb-h: 240px; --ne-cb-h-m: 180px; }
  .ne-collection-banner--medium  { --ne-cb-h: 380px; --ne-cb-h-m: 280px; }
  .ne-collection-banner--large   { --ne-cb-h: 520px; --ne-cb-h-m: 380px; }
  .ne-collection-banner--cover   { --ne-cb-h: 80vh;  --ne-cb-h-m: 60vh;  }

  .ne-collection-banner--no-image {
    background: linear-gradient(135deg,
      color-mix(in srgb, var(--ne-scheme-accent) 60%, transparent),
      color-mix(in srgb, var(--ne-scheme-bg-surface, var(--ne-scheme-bg)) 100%, transparent)
    );
    color: var(--ne-scheme-text);
  }

  .ne-collection-banner__media { z-index: 0; }

  .ne-collection-banner__media picture {
    display: block;
    width: 100%;
    height: 100%;
  }

  .ne-collection-banner__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--ne-cb-focal, 50% 50%);
    display: block;
  }

  .ne-collection-banner--parallax .ne-collection-banner__image {
    will-change: transform;
    transition: transform 0.1s linear;
  }

  /* Colour overlay — the resolved tint (colour × opacity) is exposed as
     --ne-cb-ov, then shaped into a mode (solid / gradient / vignette) below. */
  .ne-collection-banner__overlay {
    position: absolute;
    inset: 0;
    background: var(--ne-cb-ov);
    pointer-events: none;
    z-index: 1;
  }
  .ne-collection-banner--ov-top .ne-collection-banner__overlay {
    background: linear-gradient(to bottom, var(--ne-cb-ov) 0%, transparent 72%);
  }
  .ne-collection-banner--ov-bottom .ne-collection-banner__overlay {
    background: linear-gradient(to top, var(--ne-cb-ov) 0%, transparent 72%);
  }
  .ne-collection-banner--ov-both .ne-collection-banner__overlay {
    background: linear-gradient(to bottom, var(--ne-cb-ov) 0%, transparent 34%, transparent 66%, var(--ne-cb-ov) 100%);
  }
  .ne-collection-banner--ov-vignette .ne-collection-banner__overlay {
    background: radial-gradient(ellipse at center, transparent 42%, var(--ne-cb-ov) 100%);
  }
  .ne-collection-banner--ov-spotlight .ne-collection-banner__overlay {
    background: radial-gradient(ellipse at center, var(--ne-cb-ov) 0%, transparent 60%);
  }
  /* surf-full + BELOW layout only: the plate spans the whole box, but the
     overlay element only covers the image area (it lives in .__media). Extend
     the tint behind the below text via a content-level layer. Scoped to below
     layouts — in OVERLAY layout the content sits over the media (already
     overlaid), so this must NOT fire there (it would double the overlay). */
  .ne-collection-banner--surf-full.ne-collection-banner--d-below .ne-collection-banner__content::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: var(--ne-cb-ov);
    pointer-events: none;
  }

  /* ── Mounted glass: the box is a flat, TRANSLUCENT glass PLATE (scheme
     surface at ~55% so the page behind the banner shows through — real glass,
     optionally accent-tinted, NO blur, NO image copy). The real image is mounted
     crisp + true-colour on top (contained), so the plate shows around it. Plate
     lives on the media so it paints behind the image automatically. */
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass .ne-collection-banner__media {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-tint .ne-collection-banner__media {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
  }
  /* Extend the plate to the WHOLE box (media + content) so below-layout text
     sits on the glass too. Plate + frost move to the box; media goes transparent
     and drops its own frost so it doesn't double-blur. */
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--surf-full {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-tint.ne-collection-banner--surf-full {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--surf-full .ne-collection-banner__media {
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }

  /* Below layout + glass only on the image area (surf-full OFF): the glass is a
     self-contained card, NOT the whole box (the box also holds the below text).
     So move the rounded corners + clip AND the glow/shadow onto the MEDIA (all
     four corners), and strip them from the box — otherwise the box rounds only
     the top of the glass and wraps the glow around the text too. Overlay layout
     (media fills the box) and surf-full (glass IS the box) are excluded. */
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) {
    overflow: visible;
    border-radius: 0;
    box-shadow: none;
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
    border-radius: var(--ne-cb-radius, var(--ne-card-radius, var(--ne-radius-md, 12px)));
    overflow: hidden;
    /* Frost moves to ::before so the media clips it as an ANCESTOR (Firefox does
       not self-clip backdrop-filter to border-radius; an ancestor clip works —
       same reason the overlay layout renders fine, where the box clips). Glow/
       shadow stay on the media (outset box-shadow ignores overflow → still show). */
    background: transparent;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) .ne-collection-banner__media::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    border-radius: inherit;
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    pointer-events: none;
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-tint:not(.ne-collection-banner--surf-full) .ne-collection-banner__media::before {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-glow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
    box-shadow: 0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-shadow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
    box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28);
  }
  .ne-collection-banner--d-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-glow.ne-collection-banner--panel-shadow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
    box-shadow:
      0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28),
      0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }
  /* Crisp image mounted on the plate at its NATURAL aspect (no internal
     letterbox), sized by --ne-cb-img-scale (height-driven) and placed by the
     focal point on BOTH axes via the background-position trick: put the focal%
     point of the image onto the focal% point of the box (left/top = focal, then
     translate back by focal). Works whether the image is smaller than the box
     (free space → pan both axes) or larger (scale > 100% → overflow → pan both,
     clipped by the box). picture:display:contents so the img positions against
     the media directly. */
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass .ne-collection-banner__media picture,
  .ne-collection-banner--fit-fit .ne-collection-banner__media picture {
    display: contents;
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass .ne-collection-banner__image,
  .ne-collection-banner--fit-fit .ne-collection-banner__image {
    position: absolute;
    z-index: 1;
    width: auto;
    height: var(--ne-cb-img-scale, 90%);
    max-width: none;
    max-height: none;
    object-fit: contain;
    left: var(--ne-cb-fx, 50%);
    top: var(--ne-cb-fy, 50%);
    transform: translate(calc(-1 * var(--ne-cb-fx, 50%)), calc(-1 * var(--ne-cb-fy, 50%)));
  }
  /* Colour overlay: by default it darkens only the plate (z:0, below the crisp
     image) so the mounted motif keeps its true colour. Toggle "overlay on image"
     lifts it above the image (z:2) so the gradient/vignette also lies on the
     motif. */
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass .ne-collection-banner__overlay {
    z-index: 0;
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--ov-on-image .ne-collection-banner__overlay {
    z-index: 2;
  }

  /* ── OVERLAY layout: media fills the box (absolute), text sits on top ── */
  .ne-collection-banner--d-overlay {
    display: flex;
    min-height: var(--ne-cb-h, 380px);
  }
  .ne-collection-banner--d-overlay .ne-collection-banner__media {
    position: absolute;
    inset: 0;
  }
  .ne-collection-banner--d-overlay .ne-collection-banner__content {
    position: relative;
    z-index: 2;
    width: 100%;
  }
  .ne-collection-banner--d-overlay.ne-collection-banner--vert-top    { align-items: flex-start; }
  .ne-collection-banner--d-overlay.ne-collection-banner--vert-center { align-items: center; }
  .ne-collection-banner--d-overlay.ne-collection-banner--vert-bottom { align-items: flex-end; }

  /* ── BELOW layout: media in normal flow, text flows underneath ── */
  .ne-collection-banner--d-below { display: block; }
  .ne-collection-banner--d-below .ne-collection-banner__media {
    position: relative;
    height: var(--ne-cb-h, 380px);
  }
  .ne-collection-banner--d-below .ne-collection-banner__content {
    position: relative;
    z-index: 2;
  }

  /* Content wrapper: positioning + max-width + horizontal placement of panel */
  .ne-collection-banner__content {
    padding: clamp(24px, 6vw, 64px);
    display: flex;
  }
  .ne-collection-banner__content > .ne-collection-banner__panel {
    max-width: min(100%, var(--ne-cb-cw, 1200px));
  }
  .ne-collection-banner--align-left   .ne-collection-banner__content { justify-content: flex-start; }
  .ne-collection-banner--align-center .ne-collection-banner__content { justify-content: center; }
  .ne-collection-banner--align-right  .ne-collection-banner__content { justify-content: flex-end; }

  /* Text panel — flex column holding legacy text and/or nested blocks. */
  .ne-collection-banner__panel {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3, 12px);
    border-radius: var(--ne-cb-panel-r, 0);
    padding: var(--ne-cb-panel-p, 0);
    min-width: 0;
    position: relative;
  }
  .ne-collection-banner--align-left   .ne-collection-banner__panel { text-align: left;   align-items: flex-start; }
  .ne-collection-banner--align-center .ne-collection-banner__panel { text-align: center; align-items: center; }
  .ne-collection-banner--align-right  .ne-collection-banner__panel { text-align: right;  align-items: flex-end; }

  /* ── Surface backing (mirrors the section/group glass-solid pattern) ──
     Two targets, chosen by surface_target:
       · surf-panel  → paints the text card (.__panel) only.
       · surf-banner → the box becomes a frosted glass plate (backdrop-filter on
         the media/box) with the crisp image mounted on top; + top shine strip,
         radius/glow/shadow on the box. See the mounted-glass block below. */

  /* Target = TEXT PANEL */
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-solid .ne-collection-banner__panel {
    background: color-mix(in srgb, var(--ne-scheme-bg, #111) var(--ne-cb-panel-a, 100%), transparent);
    color: var(--ne-scheme-text);
  }
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-glass .ne-collection-banner__panel {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-glass.ne-collection-banner--panel-tint .ne-collection-banner__panel {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
  }
  /* Top horizontal shine strip on the glass panel card — same token + look as
     the whole-banner glass. Sits in the panel's top padding, above nothing. */
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-glass .ne-collection-banner__panel::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.20) 1px, rgba(255, 255, 255, 0.06) 2px, transparent 3px));
    pointer-events: none;
  }
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-glow .ne-collection-banner__panel {
    box-shadow: 0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-shadow .ne-collection-banner__panel {
    box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28);
  }
  .ne-collection-banner--surf-panel.ne-collection-banner--panel-glow.ne-collection-banner--panel-shadow .ne-collection-banner__panel {
    box-shadow:
      0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28),
      0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }

  /* Target = WHOLE BANNER, sheen layer. Sits ABOVE the mounted image (z:3) but
     below the content. For GLASS it carries only the top horizontal shine strip
     (the plate surface itself is the media background below the image). For
     SOLID it is an opaque colour band over the media. */
  .ne-collection-banner__sheen {
    position: absolute;
    inset: 0;
    z-index: 3;
    pointer-events: none;
    border-radius: inherit;
  }
  /* Top horizontal shine strip — same gradient + MECHANISM as the glass product
     cards: the shine is a background-IMAGE on the element that carries the
     border-radius (backgrounds always clip to border-radius, in every browser —
     unlike a pseudo-element's paint, which Firefox does not clip to an ancestor
     radius / overflow reliably). So it hugs the rounded corners exactly. */
  .ne-collection-banner--surf-banner .ne-collection-banner__sheen {
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255, 255, 255, 0.25) 0%, rgba(255, 255, 255, 0.20) 1px, rgba(255, 255, 255, 0.06) 2px, transparent 3px));
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-solid .ne-collection-banner__sheen {
    background-color: color-mix(in srgb, var(--ne-scheme-bg, #111) var(--ne-cb-panel-a, 100%), transparent);
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glass .ne-collection-banner__sheen {
    background-color: transparent;
  }
  /* Glow / shadow on the banner box itself (box-shadow renders outside clip). */
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glow {
    box-shadow: 0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-shadow {
    box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28);
  }
  .ne-collection-banner--surf-banner.ne-collection-banner--panel-glow.ne-collection-banner--panel-shadow {
    box-shadow:
      0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28),
      0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
  }

  .ne-collection-banner__heading {
    color: inherit;
    margin: 0;
  }
  .ne-collection-banner__description {
    max-width: 720px;
    color: inherit;
    opacity: 0.92;
  }
  .ne-collection-banner__description :where(p, h2, h3, ul, ol) { color: inherit; }

  /* Nested theme-block wrappers shouldn't break the flex column. */
  .ne-collection-banner__panel > .shopify-block { display: contents; }

  @media (max-width: 749px) {
    /* Custom mobile overrides. NOTE: the desktop --ne-cb-* vars are set INLINE
       on the block, and inline custom properties beat any stylesheet rule — so
       redefining them here does nothing. Instead override the image's actual
       properties to read the mobile (-m) vars. Selectors match/exceed the
       desktop image rules' specificity and sit later in source, so they win. */
    .ne-collection-banner--mobile-custom.ne-collection-banner--surf-banner .ne-collection-banner__image,
    .ne-collection-banner--mobile-custom.ne-collection-banner--fit-fit .ne-collection-banner__image {
      height: var(--ne-cb-img-scale-m, 90%);
      left: var(--ne-cb-fx-m, 50%);
      top: var(--ne-cb-fy-m, 50%);
      transform: translate(calc(-1 * var(--ne-cb-fx-m, 50%)), calc(-1 * var(--ne-cb-fy-m, 50%)));
    }
    /* Fill (cover) mode: focal is object-position, not left/top. */
    .ne-collection-banner--mobile-custom .ne-collection-banner__image {
      object-position: var(--ne-cb-focal-m, 50% 50%);
    }

    /* Mobile heights on the desktop-chosen layout. */
    .ne-collection-banner--d-overlay { min-height: var(--ne-cb-h-m, 280px); }
    .ne-collection-banner--d-below .ne-collection-banner__media { height: var(--ne-cb-h-m, 280px); }

    /* Mobile layout override = BELOW (flips a desktop-overlay banner to stacked). */
    .ne-collection-banner--m-below { display: block; min-height: 0 !important; }
    .ne-collection-banner--m-below .ne-collection-banner__media {
      position: relative;
      inset: auto;
      height: var(--ne-cb-h-m, 280px);
    }
    .ne-collection-banner--m-below .ne-collection-banner__content {
      position: relative;
      z-index: 2;
      width: auto;
    }
    /* surf-full overlay extension when the text drops below on mobile. */
    .ne-collection-banner--surf-full.ne-collection-banner--m-below .ne-collection-banner__content::before {
      content: "";
      position: absolute;
      inset: 0;
      z-index: -1;
      background: var(--ne-cb-ov);
      pointer-events: none;
    }

    /* Mobile-below glass-card treatment (same as the desktop d-below case):
       radius/clip + glow/shadow on the MEDIA, stripped from the box. */
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) {
      overflow: visible;
      border-radius: 0;
      box-shadow: none;
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
      border-radius: var(--ne-cb-radius, var(--ne-card-radius, var(--ne-radius-md, 12px)));
      overflow: hidden;
      background: transparent;
      -webkit-backdrop-filter: none;
      backdrop-filter: none;
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass:not(.ne-collection-banner--surf-full) .ne-collection-banner__media::before {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 0;
      border-radius: inherit;
      background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
      -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
      backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
      pointer-events: none;
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-tint:not(.ne-collection-banner--surf-full) .ne-collection-banner__media::before {
      background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-glow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
      box-shadow: 0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-shadow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
      box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28);
    }
    .ne-collection-banner--m-below.ne-collection-banner--surf-banner.ne-collection-banner--panel-glass.ne-collection-banner--panel-glow.ne-collection-banner--panel-shadow:not(.ne-collection-banner--surf-full) .ne-collection-banner__media {
      box-shadow:
        0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.28),
        0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120, 120, 255, 0.45));
    }

    /* Mobile layout override = OVERLAY (flips a desktop-below banner to layered). */
    .ne-collection-banner--m-overlay { display: flex; min-height: var(--ne-cb-h-m, 280px); }
    .ne-collection-banner--m-overlay .ne-collection-banner__media {
      position: absolute;
      inset: 0;
      height: auto;
    }
    .ne-collection-banner--m-overlay .ne-collection-banner__content {
      position: relative;
      z-index: 2;
      width: 100%;
    }
    .ne-collection-banner--m-overlay.ne-collection-banner--vert-top    { align-items: flex-start; }
    .ne-collection-banner--m-overlay.ne-collection-banner--vert-center { align-items: center; }
    .ne-collection-banner--m-overlay.ne-collection-banner--vert-bottom { align-items: flex-end; }

    /* Mobile text alignment override. */
    .ne-collection-banner--malign-left   .ne-collection-banner__content { justify-content: flex-start; }
    .ne-collection-banner--malign-center .ne-collection-banner__content { justify-content: center; }
    .ne-collection-banner--malign-right  .ne-collection-banner__content { justify-content: flex-end; }
    .ne-collection-banner--malign-left   .ne-collection-banner__panel { text-align: left;   align-items: flex-start; }
    .ne-collection-banner--malign-center .ne-collection-banner__panel { text-align: center; align-items: center; }
    .ne-collection-banner--malign-right  .ne-collection-banner__panel { text-align: right;  align-items: flex-end; }

    /* Disable parallax on mobile (perf + jank). */
    .ne-collection-banner--parallax .ne-collection-banner__image {
      transform: none !important;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-collection-banner--parallax .ne-collection-banner__image {
      transform: none !important;
      transition: none;
    }
  }
/* END_BLOCK:collection-banner */

/* START_BLOCK:collection-card (INDEX:81) */
/* Layout owned by the parent collection-grid (subgrid rows, card
     padding/radius/chrome). This file only styles the inner content +
     the card's own visual link semantics. */
  .ne-collection-card {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span 2;
    text-decoration: none;
    color: var(--ne-scheme-text, currentColor);
    position: relative;
    overflow: hidden;
    border-radius: var(--ne-cg-card-radius, 12px);
    background: transparent;
    padding: var(--ne-cg-card-pad, 0);
    /* Backplate (Rule 157): subgrid cards with direct `background:`
       can leak section-bg through track-boundary subpixel drift.
       ::before paints a solid surface on the full card rect. */
    isolation: isolate;
    /* GPU-layer baseline (Rule 133): forces a stable compositing layer
       so hover transform (translateY) doesn't collapse the layer mid-
       transition — without this the right edge briefly exposes
       section-bg as a 1-px hairline during scale-down on hover-out. */
    transform: translateZ(0);
    backface-visibility: hidden;
    transition: transform .25s ease;
  }
  /* Fallback if parent lacks subgrid context. */
  @supports not (grid-template-rows: subgrid) {
    .ne-collection-card { display: flex; flex-direction: column; }
  }
  .ne-collection-card::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-bg, transparent);
    border-radius: inherit;
    z-index: 0;
    pointer-events: none;
  }
  /* Children stack above backplate (Rule 157). */
  .ne-collection-card > * { position: relative; z-index: 1; }

  /* Backplate always paints with the inherited scheme-bg by default
     (works even when the parent section uses transparent_bg, because
     the scheme class cascade still defines --ne-scheme-bg).
     Truly transparent cards: parent grid sets .ne-cg--cards-transparent
     which forces the backplate transparent. */
  .ne-cg.ne-cg--cards-transparent .ne-collection-card:not(.ne-collection-card--has-scheme)::before {
    background: transparent;
  }

  /* Keep translateZ(0) on hover or the GPU layer collapses (Rule 133). */
  .ne-collection-card:hover { transform: translateY(-3px) translateZ(0); }

  /* Media — aspect-ratio comes from the parent grid via CSS var.
     image_radius:
       0   → only the TOP corners follow the card-radius (image flush
             against info row, Rule 128)
       >0  → all 4 corners rounded with the explicit image_radius
             (use with card_padding > 0 for a framed look). */
  .ne-collection-card__media {
    position: relative;
    width: 100%;
    aspect-ratio: var(--ne-cg-image-ratio, 3/4);
    overflow: hidden;
    border-radius: var(--ne-cg-image-radius, 0);
    background: var(--ne-scheme-bg-surface, rgba(127,127,127,0.08));
  }
  /* When image_radius is 0 (default) AND card has no padding, fall
     back to top-only corners following card-radius. */
  .ne-cg--image-flush .ne-collection-card__media {
    border-radius: var(--ne-cg-card-radius, 12px) var(--ne-cg-card-radius, 12px) 0 0;
  }
  .ne-collection-card__image {
    /* Absolute fill so the image does NOT participate in the media
       wrapper's height calc. With height:100% in-flow + the wrapper's
       aspect-ratio, the browser hits a cyclic dependency and falls back
       to the intrinsic image height — aspect-ratio is silently ignored
       and a square source stays square (Rule 309). Absolute fill lets
       the wrapper's aspect-ratio govern; object-fit:cover crops. */
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Explicit center-anchor — without this, sub-pixel rounding on
       narrow viewports can offset the image to one side when the
       intrinsic image dimensions don't match the container exactly. */
    object-position: 50% 50%;
    display: block;
    /* GPU-layer pinning on the image too (Rule 133) — keeps the
       scale-up/down animation in the same compositing layer as the
       card so AA aligns and there's no edge-flicker mid-transition. */
    transform: translateZ(0);
    backface-visibility: hidden;
    transition: transform .5s ease;
  }
  /* Image zoom only on hover-capable pointers. On touch, :hover sticks while
     scrolling over the image, which zooms it and blocks the scroll gesture. */
  @media (hover: hover) {
    .ne-collection-card:hover .ne-collection-card__image { transform: scale(1.04) translateZ(0); }
  }

  .ne-collection-card__placeholder {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #999;
    background: rgba(127,127,127,0.08);
    padding: 12%;
  }
  .ne-collection-card__placeholder svg {
    width: 100%;
    height: auto;
    opacity: 0.4;
  }

  /* Overlay — gradient bottom fade for overlay-mode titles. Only
     visible when grid is in overlay mode + intensity > 0. */
  .ne-collection-card__overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background: linear-gradient(
      to bottom,
      transparent 0,
      transparent 40%,
      rgba(0,0,0,calc(0.6 * var(--ne-cg-overlay-intensity, 0))) 100%
    );
    opacity: var(--ne-cg-overlay-opacity, 0);
    transition: opacity .25s ease;
  }

  /* Info row — sits beneath the media in stacked mode, overlays the
     media in overlay mode (parent grid switches via class modifier). */
  .ne-collection-card__info {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: var(--ne-cg-info-pad, 14px) var(--ne-cg-info-pad-x, 4px) var(--ne-cg-info-pad, 14px);
    text-align: var(--ne-cg-text-align, left);
    align-items: var(--ne-cg-text-flex-align, flex-start);
  }
  .ne-collection-card__title {
    margin: 0;
    font-family: var(--font-heading--family);
    font-size: var(--ne-cg-title-size, 1.125rem);
    font-weight: var(--ne-cg-title-weight, 600);
    line-height: 1.3;
    color: inherit;
  }
  .ne-collection-card__count {
    margin: 0;
    font-size: var(--ne-cg-count-size, 0.875rem);
    color: inherit;
    opacity: 0.7;
  }

  /* Overlay-mode card layout — drop grid/subgrid, use simple block.
     Media stays in normal flow with its own aspect-ratio so the card
     gets a real intrinsic height (an absolute media would collapse
     the card to 0 because it'd contribute nothing to layout). Info
     absolutely positioned over the media so its content (1- or 2-line
     text) doesn't push the card height — all cards in a row share
     the same column width and therefore the same media height,
     so bottoms always align. */
  .ne-cg--title-overlay .ne-collection-card {
    display: block;
    grid-row: span 1;
    grid-template-rows: none;
    position: relative;
    width: 100%;
  }
  .ne-cg--title-overlay .ne-collection-card__media {
    position: relative;
    width: 100%;
    aspect-ratio: var(--ne-cg-image-ratio, 3/4);
    border-radius: inherit;
  }
  .ne-cg--title-overlay .ne-collection-card__overlay { opacity: 1; }
  .ne-cg--title-overlay .ne-collection-card__info {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    padding: 20px;
    color: #fff;
    text-shadow: 0 1px 8px rgba(0,0,0,0.5);
    z-index: 2;
  }
  .ne-cg--title-overlay .ne-collection-card__count { opacity: 0.85; }
/* END_BLOCK:collection-card */

/* START_BLOCK:collection-grid (INDEX:82) */
.ne-cg {
    width: 100%;
    /* Wrapper paints nothing — cards own their own surface via the
       backplate ::before. Force transparent so an inherited scheme-bg
       doesn't leak onto the wrapper. */
    background: transparent !important;
  }
  .ne-cg.ne-cov { color: var(--ne-scheme-text); }

  /* Theme-block wrapper collapses so each card is a direct grid item
     — required for subgrid (Rule 70 + Rule 127). */
  .ne-cg__grid > .shopify-block,
  .ne-cg__track > .shopify-block { display: contents; }

  /* ─────────────── Grid layout ─────────────── */
  /* Explicit 2-row template (media / info) per card-row. Subgrid
     in the cards reads these rows so titles / bottoms align across
     cards. `grid-auto-rows: auto auto` on top continues the pattern
     when cards wrap to a second row of cards. align-items: stretch
     makes the card cells fill the row height (so subgrid has a
     definite container height to pass through). */
  .ne-cg--grid .ne-cg__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-cg-cols, 3), minmax(0, 1fr));
    grid-template-rows: auto auto;
    grid-auto-rows: auto auto;
    align-items: stretch;
    row-gap: 0;
    column-gap: var(--ne-cg-gap, 20px);
  }

  /* Overlay mode collapses to single row; gap re-engages because cards
     no longer subgrid into shared rows. */
  .ne-cg--title-overlay.ne-cg--grid .ne-cg__grid {
    grid-template-rows: auto;
    grid-auto-rows: auto;
    row-gap: var(--ne-cg-gap, 20px);
  }

  /* ─────────────── Carousel layout ─────────────── */
  .ne-cg--carousel .ne-cg__carousel {
    position: relative;
    overflow-x: clip;
    overflow-y: visible;
    padding-inline: 32px;
  }
  .ne-cg--carousel .ne-cg__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - (var(--ne-cg-cols, 3) - 1) * var(--ne-cg-gap, 20px)) / var(--ne-cg-cols, 3));
    /* Explicit 2-row template — subgrid in cards reads media/info
       rows. With column-flow each card occupies one column × 2 rows;
       the info row's height = max text height across cards, passed
       through subgrid → all bottoms align even with variable titles. */
    grid-template-rows: auto auto;
    grid-auto-rows: auto auto;
    align-items: stretch;
    row-gap: 0;
    column-gap: var(--ne-cg-gap, 20px);
    overflow-x: auto;
    overflow-y: hidden;
    /* proximity (Rule 138) — mandatory amplifies subgrid jitter. */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Rule 205: explicit touch-action so mobile swipe delegates fully.
       pan-y lets a vertical swipe scroll the page instead of being trapped
       by this horizontal carousel. */
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    padding-block: 32px;
    padding-inline: var(--ne-cg-gap, 20px);
    margin-block: -32px;
    scroll-padding-inline: var(--ne-cg-gap, 20px);
    /* No mask-image (Rule 158). */
    -webkit-mask-image: none;
    mask-image: none;
    will-change: scroll-position;
  }
  .ne-cg--carousel.ne-cg--title-overlay .ne-cg__track {
    grid-template-rows: auto;
    grid-auto-rows: auto;
  }
  .ne-cg--carousel .ne-cg__track::-webkit-scrollbar { display: none; }
  .ne-cg--carousel .ne-collection-card { scroll-snap-align: start; }

  /* ─────────────── Card chrome (grid + carousel) ─────────────── */
  /* Composed shadow (Rule 130) — chrome modifiers each set ONE slot
     so glass/glow/shadow stack without specificity conflicts. */
  .ne-cg .ne-collection-card {
    --ne-cg-shadow-outer: 0 0 0 0 transparent;
    --ne-cg-shadow-glow:  0 0 0 0 transparent;
    box-shadow: var(--ne-cg-shadow-outer), var(--ne-cg-shadow-glow);
  }
  /* Hover-reduced shadow values (Rule 159) — keeps cut from
     carousel-edge shadow during card-zoom hover. */
  .ne-cg--shadow .ne-collection-card { --ne-cg-shadow-outer: 0 6px 14px rgba(0,0,0,0.14); }
  .ne-cg--glow   .ne-collection-card { --ne-cg-shadow-glow:  0 0 14px var(--ne-scheme-accent-glow, rgba(120,120,255,0.3)); }
  .ne-cg--shadow .ne-collection-card:hover { --ne-cg-shadow-outer: 0 4px 10px rgba(0,0,0,0.16); }
  .ne-cg--glow   .ne-collection-card:hover { --ne-cg-shadow-glow:  0 0 10px var(--ne-scheme-accent-glow, rgba(120,120,255,0.3)); }

  /* Glass card — Rule 237 pattern: bg + backdrop-filter on the card host
     directly (not on ::before backplate), so card hover-transforms don't
     trap descendant glass. Same theme-effects vars as section/block
     glass for consistent visual intensity (user report 2026-05-23). */
  .ne-cg--glass .ne-collection-card {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border: 1px glass-border reads as a bottom contour line on dark
       bg (same fix as blog/product-card glass, user QA 2026-05-25). */
    border: 0;
    will-change: backdrop-filter;
  }
  .ne-cg--glass.ne-cg--glass-tint .ne-collection-card {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05))) !important;
  }
  .ne-cg--glass .ne-collection-card::before { content: none; }
  /* Transparent card — kill the backplate so section bg shows through. */
  .ne-cg--transparent .ne-collection-card::before {
    background: transparent !important;
  }

  /* ─────────────── Arrows ─────────────── */
  .ne-cg__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--ne-scheme-text, currentColor);
    cursor: pointer;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
  }
  .ne-cg--arrow-button .ne-cg__arrow {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127,127,127,0.25));
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  }
  .ne-cg--arrow-plain .ne-cg__arrow {
    background: transparent;
    border: 0;
    box-shadow: none;
    width: 32px;
    height: 32px;
  }
  .ne-cg--arrow-plain .ne-cg__arrow:hover { color: var(--ne-scheme-accent, currentColor); }

  /* Below-track layout — arrows absolute beneath the track (Rule 154).
     Padding-bottom = vertical-bleed (32) + clearance (16) + arrow-h (40). */
  .ne-cg--carousel:not(.ne-cg--arrows-overlay) .ne-cg__carousel { padding-bottom: 88px; }
  .ne-cg--carousel:not(.ne-cg--arrows-overlay) .ne-cg__arrow { position: absolute; bottom: 0; margin-top: 0; }
  .ne-cg--carousel:not(.ne-cg--arrows-overlay) .ne-cg__arrow--prev { left: calc(50% - 44px); }
  .ne-cg--carousel:not(.ne-cg--arrows-overlay) .ne-cg__arrow--next { left: calc(50% + 4px); }

  /* Overlay mode arrows centered on card vertically. */
  .ne-cg--arrows-overlay .ne-cg__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    margin-top: 0;
  }
  .ne-cg--arrows-overlay .ne-cg__arrow:hover { transform: translateY(-50%) scale(1.05); }
  .ne-cg--arrows-overlay .ne-cg__arrow--prev { left: 4px; }
  .ne-cg--arrows-overlay .ne-cg__arrow--next { right: 4px; }

  /* ─────────────── Responsive ─────────────── */
  @media (max-width: 989px) {
    .ne-cg--grid .ne-cg__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ne-cg--carousel .ne-cg__track { grid-auto-columns: calc((100% - var(--ne-cg-gap, 20px) * 1) / 2); }
  }
  @media (max-width: 599px) {
    .ne-cg--grid .ne-cg__grid {
      grid-template-columns: repeat(var(--ne-cg-cols-mobile, 1), minmax(0, 1fr));
      row-gap: var(--ne-cg-gap, 20px);
    }
    .ne-cg--carousel .ne-cg__carousel { padding-inline: 28px; }
    /* Mobile carousel column-width follows the user-picked mobile
       columns count. 1 → full width (no peek, cards fill the
       viewport), 2 → two-up. */
    .ne-cg--carousel .ne-cg__track {
      grid-auto-columns: calc((100% - (var(--ne-cg-cols-mobile, 1) - 1) * var(--ne-cg-gap, 20px)) / var(--ne-cg-cols-mobile, 1));
    }
  }

  /* Hide arrows when all cards fit without overflow. JS toggles
     .ne-cg--no-overflow on the carousel root. Keeps the layout clean
     for small grids while staying responsive on small viewports. */
  .ne-cg--no-overflow .ne-cg__arrow { display: none; }
  /* When arrows are hidden in below-mode, drop the reserved
     padding-bottom so we don't leave empty space under the track. */
  .ne-cg--carousel:not(.ne-cg--arrows-overlay).ne-cg--no-overflow .ne-cg__carousel {
    padding-bottom: 0;
  }
/* END_BLOCK:collection-grid */

/* START_BLOCK:contact-form (INDEX:83) */
.ne-contact-form {
    width: 100%;
    background: transparent !important;
    border-radius: var(--ne-cf-radius, 16px);
    padding: clamp(20px, 4vw, 32px);
  }

  /* Surface modes */
  .ne-contact-form--solid {
    background: var(--ne-scheme-bg, transparent) !important;
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-contact-form--glass {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(140%);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(140%);
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  /* Accent-tinted glass — opt-in via glass_tint (local class, not global). */
  .ne-contact-form--glass.ne-contact-form--tint {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(127, 127, 127, 0.08))) !important;
  }
  .ne-contact-form--transparent {
    padding: 0;
  }

  .ne-contact-form--shadow {
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.25);
  }
  .ne-contact-form--glow {
    box-shadow: 0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-contact-form--shadow.ne-contact-form--glow {
    box-shadow:
      0 12px 32px rgba(0, 0, 0, 0.25),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }

  .ne-contact-form form {
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .ne-contact-form__row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
  }
  @media (max-width: 599px) {
    .ne-contact-form__row { grid-template-columns: 1fr; }
  }

  .ne-contact-form__field {
    display: flex;
    flex-direction: column;
    gap: 6px;
  }
  .ne-contact-form__field label {
    font-size: 0.85rem;
    font-weight: 500;
    color: var(--ne-scheme-text);
    /* Field labels follow the block's own alignment if set, otherwise
       inherit from the host section's --ne-tp--align (text alignment
       set at the section). */
    text-align: var(--ne-cf-label-align, var(--ne-tp--align, left));
  }
  .ne-contact-form__input {
    width: 100%;
    color: var(--ne-scheme-text);
  }
  .ne-contact-form__textarea {
    resize: vertical;
    min-height: 120px;
    /* A multi-line textarea must never become a full pill (input_shape:pill
       sets --ne-radius-input:999px). Cap at a rounded value; square (0) and
       rounded stay untouched via min(). */
    border-radius: min(var(--ne-radius-input, 8px), 16px) !important;
  }

  .ne-contact-form__success {
    padding: 14px 16px;
    border-radius: 8px;
    background: color-mix(in srgb, var(--ne-scheme-accent, #16a34a) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--ne-scheme-accent, #16a34a) 35%, transparent);
    color: var(--ne-scheme-text);
    font-weight: 500;
    margin: 0 0 4px;
  }
  .ne-contact-form__errors {
    padding: 12px 14px;
    border-radius: 8px;
    background: rgba(220, 38, 38, 0.08);
    border: 1px solid rgba(220, 38, 38, 0.4);
    color: var(--ne-scheme-text);
    margin: 0 0 4px;
  }
  .ne-contact-form__errors ul { margin: 0; padding-left: 20px; }

  .ne-contact-form__submit {
    margin-top: 4px;
    /* Submit button picks its own alignment if the user set one,
       otherwise follows the section's --ne-bw-justify. flex-start
       is the final fallback for blocks used outside a section. */
    align-self: var(--ne-cf-btn-align, var(--ne-bw-justify, flex-start));
  }
  .ne-contact-form__submit--full {
    width: 100%;
    align-self: stretch;
  }
/* END_BLOCK:contact-form */

/* START_BLOCK:countdown-timer (INDEX:84) */
.ne-cd-timer {
    display: flex;
    align-items: stretch;
    justify-content: center;
    gap: 8px;
    flex-wrap: wrap;
    color: var(--ne-scheme-text);
    /* Force transparent — css-variables.liquid paints background on
       any .color-{scheme} element. Without this override the timer
       container fills the gaps between unit cards with the custom
       scheme bg, defeating the per-card paint. */
    background: transparent !important;
  }

  .ne-cd-timer__unit {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 16px 20px;
    min-width: 84px;
    border: 1px solid var(--ne-scheme-border, transparent);
    border-radius: var(--ne-cd-radius, 12px);
    background: var(--ne-scheme-bg, transparent);
  }

  .ne-cd-timer--glass .ne-cd-timer__unit {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border: 1px glass-border reads as a bottom contour line on dark
       bg (same fix as blog/product-card glass, user QA 2026-05-25). */
    border: 0;
  }
  /* Tinted glass (local class, not global ne-glass--tinted) — tint on the
     unit boxes only, never the full-width container. */
  .ne-cd-timer--glass.ne-cd-timer--glass-tint .ne-cd-timer__unit {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(127, 127, 127, 0.08)));
  }
  /* Transparent: kill bg + border so section bg shows through digits. */
  .ne-cd-timer--transparent .ne-cd-timer__unit {
    background: transparent;
    border-color: transparent;
  }

  /* Effects coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-cd-timer--shadow .ne-cd-timer__unit {
    box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(20px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25);
  }
  .ne-cd-timer--glow .ne-cd-timer__unit {
    box-shadow: 0 0 calc(24px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-cd-timer--shadow.ne-cd-timer--glow .ne-cd-timer__unit {
    box-shadow:
      0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(20px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25),
      0 0 calc(24px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }

  .ne-cd-timer__num {
    font-family: var(--ne-cd-digit-font, var(--font-heading--family));
    font-size: clamp(1.75rem, 5vw, 3rem);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--ne-scheme-accent);
  }
  .ne-cd-timer__label {
    font-size: var(--ne-font-size-xs, 0.75rem);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
  }

  .ne-cd-timer__sep {
    align-self: center;
    font-size: clamp(1.5rem, 4vw, 2.25rem);
    font-weight: 300;
    line-height: 1;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.45;
    user-select: none;
  }

  .ne-cd-timer__expired {
    font-family: var(--ne-cd-digit-font, var(--font-heading--family));
    font-size: clamp(1.25rem, 3vw, 1.75rem);
    font-weight: 600;
    color: var(--ne-scheme-text);
    text-align: center;
    margin: 0;
    flex: 1 1 100%;
  }

  @media (max-width: 599px) {
    /* Force all 4 units into one row on narrow viewports (≤375px must
       still fit). flex-wrap: nowrap + min-width: 0 lets cards shrink
       below their min-content; responsive digit/label sizes kick in
       via clamp() so nothing overflows. */
    .ne-cd-timer {
      flex-wrap: nowrap;
      gap: 4px;
    }
    .ne-cd-timer__unit {
      min-width: 0;
      flex: 1 1 0;
      padding: 10px 4px;
    }
    .ne-cd-timer__num {
      font-size: clamp(1.1rem, 5.5vw, 1.75rem);
    }
    .ne-cd-timer__label {
      font-size: clamp(0.55rem, 2vw, 0.7rem);
      letter-spacing: 0.04em;
    }
    .ne-cd-timer__sep { display: none; }
  }
/* END_BLOCK:countdown-timer */

/* START_BLOCK:divider (INDEX:85) */
.ne-block-divider-wrap { display: flex; width: 100%; }
  .ne-block-divider-wrap--left   { justify-content: flex-start; }
  .ne-block-divider-wrap--center { justify-content: center; }
  .ne-block-divider-wrap--right  { justify-content: flex-end; }

  .ne-block-divider {
    border: 0;
    margin: 0;
    background: var(--ne-divider-color, currentColor);
  }
  .ne-block-divider--solid  { background: var(--ne-divider-color, currentColor); }
  .ne-block-divider--dashed {
    background: transparent;
    border-top: var(--ne-divider-thickness, 1px) dashed var(--ne-divider-color, currentColor);
    height: 0 !important;
  }
  .ne-block-divider--dotted {
    background: transparent;
    border-top: var(--ne-divider-thickness, 1px) dotted var(--ne-divider-color, currentColor);
    height: 0 !important;
  }
  .ne-block-divider--gradient {
    background: linear-gradient(90deg,
      transparent 0%,
      var(--ne-divider-color, currentColor) 50%,
      transparent 100%);
  }
  .ne-block-divider--glow {
    background: var(--ne-divider-color, currentColor);
    box-shadow: 0 0 12px var(--ne-divider-color, currentColor),
                0 0 24px var(--ne-divider-color, currentColor);
  }

  /* Match dashed/dotted thickness to range slider */
  .ne-block-divider--dashed,
  .ne-block-divider--dotted {
    border-top-width: max(1px, var(--ne-divider-thickness, 1px));
  }
/* END_BLOCK:divider */

/* START_BLOCK:footer-copyright (INDEX:86) */
.ne-footer-cr {
    display: flex;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 16px;
    background: transparent !important;
    font-size: var(--ne-fcr-size, 13px);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
  }
  .ne-footer-cr--align-center { justify-content: center; text-align: center; }
  .ne-footer-cr--align-right  { justify-content: flex-end; text-align: right; }

  .ne-footer-cr__text { margin: 0; }
  .ne-footer-cr__shop {
    color: inherit;
    text-decoration: none;
    transition: color 0.15s ease;
  }
  .ne-footer-cr__shop:hover { color: var(--ne-scheme-text); }

  .ne-footer-cr__policies {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
  }
  .ne-footer-cr__policy-link {
    color: inherit;
    text-decoration: none;
    transition: color 0.15s ease;
  }
  .ne-footer-cr__policy-link:hover { color: var(--ne-scheme-text); }
/* END_BLOCK:footer-copyright */

/* START_BLOCK:footer-follow-shop (INDEX:87) */
.ne-footer-follow {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: transparent !important;
  }
  .ne-footer-follow--align-center { align-items: center; text-align: center; }
  .ne-footer-follow--align-right  { align-items: flex-end;  text-align: right;  }
  .ne-footer-follow__heading {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }
  /* The Shop button is rendered + styled by Shopify; just give it room. */
  .ne-footer-follow__btn { display: inline-flex; }
/* END_BLOCK:footer-follow-shop */

/* START_BLOCK:footer-legal (INDEX:88) */
details.ne-footer-legal {
    position: relative;
    display: inline-block;
    background: transparent !important;
    z-index: 50;
  }
  /* When the popover is open, lift the whole details container even higher
     so its popover (z-index 10 within) paints above any footer section
     painted by .color-{scheme} background (Rule 173) — solves the case
     where popover opens upward into a sibling footer section that has a
     solid background and would otherwise paint over it. */
  details.ne-footer-legal:hover,
  details.ne-footer-legal:focus-within,
  details.ne-footer-legal[open] {
    z-index: 200;
  }
  .ne-footer-legal--align-center { text-align: center; }
  .ne-footer-legal--align-right  { text-align: right; }

  /* Trigger button (summary) */
  .ne-footer-legal__trigger {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;
    list-style: none;
    user-select: none;
    padding: 6px 0;
    font-family: var(--ne-fl-font, inherit);
    font-size: var(--ne-fl-label-size, 13px);
    font-weight: var(--ne-fl-label-weight, 500);
    text-transform: var(--ne-fl-label-transform, none);
    color: var(--ne-scheme-text);
    transition: color 0.15s ease;
  }
  .ne-footer-legal__trigger:hover { color: var(--ne-scheme-accent, var(--ne-scheme-text)); }
  .ne-footer-legal__trigger::-webkit-details-marker { display: none; }
  .ne-footer-legal__trigger::marker { content: ''; }
  .ne-footer-legal__chevron {
    transition: transform 0.18s ease;
    opacity: 0.7;
  }
  details.ne-footer-legal[open] .ne-footer-legal__chevron {
    transform: rotate(180deg);
  }

  /* Popover — opens upward (above trigger) so it stays visible at
     the bottom of the page. Solid scheme background + subtle shadow.
     Hover-driven: visible on parent :hover or :focus-within. The
     <details>[open] state is overridden so toggle still works for
     touch + keyboard, but desktop users get the natural hover-reveal. */
  /* Popover gets body-mounted by JS (Rule 146), so style targets BOTH
     pre-mount (still inside details) AND post-mount (data-ne-fl-mounted
     on body). Position:fixed with JS-set top/left for body-mounted,
     position:absolute with bottom:100%+6px fallback if JS fails. */
  .ne-footer-legal__popover {
    position: absolute;
    bottom: calc(100% + 6px);
    left: 0;
    margin: 0;
    padding: 8px 0;
    list-style: none;
    min-width: 200px;
    max-width: 320px;
    background: var(--ne-scheme-bg);
    border: 1px solid color-mix(in srgb, var(--ne-scheme-text) 14%, transparent);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18);
    z-index: 9999;
    opacity: 0;
    visibility: hidden;
    transform: translateY(6px);
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
    pointer-events: none;
  }
  /* Body-mounted instance — fixed positioning, JS sets left/top */
  body > .ne-footer-legal__popover[data-ne-fl-mounted] {
    position: fixed;
    bottom: auto;
  }
  /* Open state — toggled by JS via data-ne-fl-open attribute */
  .ne-footer-legal__popover[data-ne-fl-open] {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
  }
  /* Pre-mount fallback (rare race where JS hasn't body-mounted yet):
     CSS :hover/[open] on the host still triggers visibility */
  details.ne-footer-legal[open] > .ne-footer-legal__popover:not([data-ne-fl-mounted]),
  details.ne-footer-legal:hover > .ne-footer-legal__popover:not([data-ne-fl-mounted]),
  details.ne-footer-legal:focus-within > .ne-footer-legal__popover:not([data-ne-fl-mounted]) {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    pointer-events: auto;
  }
  /* Sync chevron rotation with hover/open state too */
  details.ne-footer-legal:hover .ne-footer-legal__chevron,
  details.ne-footer-legal:focus-within .ne-footer-legal__chevron {
    transform: rotate(180deg);
  }
  /* Bridge the 6px gap between trigger and popover so the popover
     stays open while the cursor moves down to the links. */
  details.ne-footer-legal::before {
    content: '';
    position: absolute;
    bottom: 100%;
    left: 0;
    right: 0;
    height: 12px;
    pointer-events: auto;
  }
  details.ne-footer-legal:not(:hover):not(:focus-within):not([open])::before {
    pointer-events: none;
  }
  /* Pre-mount anchor variants — only used until JS body-mounts the
     popover. Once body-mounted, JS computes the absolute viewport X
     from the trigger's rect so anchor-translate isn't needed. */
  .ne-footer-legal--popover-right > .ne-footer-legal__popover:not([data-ne-fl-mounted]) {
    left: auto;
    right: 0;
  }
  .ne-footer-legal--popover-center > .ne-footer-legal__popover:not([data-ne-fl-mounted]) {
    left: 50%;
    transform: translate(-50%, 6px);
  }
  details.ne-footer-legal[open] > .ne-footer-legal--popover-center > .ne-footer-legal__popover:not([data-ne-fl-mounted]) {
    transform: translate(-50%, 0);
  }

  .ne-footer-legal__item { display: block; }
  .ne-footer-legal__link {
    display: block;
    padding: 8px 16px;
    font-family: var(--ne-fl-font, inherit);
    font-size: var(--ne-fl-link-size, 14px);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    text-decoration: none;
    transition: color 0.12s ease, background 0.12s ease;
  }
  .ne-footer-legal__link:hover {
    color: var(--ne-scheme-text);
    background: color-mix(in srgb, var(--ne-scheme-text) 6%, transparent);
  }
/* END_BLOCK:footer-legal */

/* START_BLOCK:footer-localization (INDEX:89) */
.ne-footer-loc {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: transparent !important;
  }
  .ne-footer-loc--align-center { align-items: center; text-align: center; }
  .ne-footer-loc--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-loc--inline {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    gap: 12px;
  }

  .ne-footer-loc__heading {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  .ne-footer-loc__form {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
  }
  .ne-footer-loc--inline .ne-footer-loc__form {
    flex-direction: row;
    align-items: center;
    gap: 8px;
  }

  .ne-footer-loc__label {
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.75;
  }
  .ne-footer-loc--inline .ne-footer-loc__label {
    margin-right: 4px;
  }

  .ne-footer-loc__select {
    appearance: none;
    -webkit-appearance: none;
    background: color-mix(in srgb, var(--ne-scheme-text) 6%, transparent);
    border: 1px solid color-mix(in srgb, var(--ne-scheme-text) 14%, transparent);
    border-radius: 8px;
    padding: 8px 32px 8px 12px;
    font: inherit;
    font-size: 14px;
    color: var(--ne-scheme-text);
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 12px;
    transition: border-color 0.15s ease, background 0.15s ease;
  }
  .ne-footer-loc__select:hover {
    border-color: color-mix(in srgb, var(--ne-scheme-text) 24%, transparent);
  }
  .ne-footer-loc__select:focus {
    outline: 2px solid var(--ne-scheme-accent);
    outline-offset: 2px;
  }
/* END_BLOCK:footer-localization */

/* START_BLOCK:footer-logo (INDEX:90) */
.ne-footer-logo {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: transparent !important;
  }
  .ne-footer-logo--align-center { align-items: center; text-align: center; }
  .ne-footer-logo--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-logo__link { display: inline-block; }
  .ne-footer-logo__shop-name {
    font-size: 18px;
    font-weight: 700;
    color: var(--ne-scheme-text);
    text-decoration: none;
  }
  .ne-footer-logo__tagline {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
    max-width: 28em;
  }

  /* Swap rendering: standard visible by default, inverse hidden;
     [data-ne-mode="alternate"] flips visibility for light-mode. */
  .ne-footer-logo__img--inverse { display: none; }
  [data-ne-mode="alternate"] [data-ne-logo-swap] .ne-footer-logo__img--standard { display: none; }
  [data-ne-mode="alternate"] [data-ne-logo-swap] .ne-footer-logo__img--inverse  { display: block; }
/* END_BLOCK:footer-logo */

/* START_BLOCK:footer-menu (INDEX:91) */
.ne-footer-menu {
    display: flex;
    flex-direction: column;
    gap: 16px;
    background: transparent !important;
  }
  .ne-footer-menu--align-center { align-items: center; text-align: center; }
  .ne-footer-menu--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-menu__heading {
    margin: 0;
    font-family: var(--ne-fm-heading-font, inherit);
    font-size: var(--ne-fm-heading-size, 14px);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  .ne-footer-menu__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--ne-fm-link-gap, 8px);
  }
  /* Horizontal layout: links wrap inline; gap doubles as both row+col gap */
  .ne-footer-menu--horizontal .ne-footer-menu__list {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: calc(var(--ne-fm-link-gap, 8px) * 2);
  }
  /* Two-column grid for tall menus */
  .ne-footer-menu--two-col .ne-footer-menu__list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: 24px;
  }

  .ne-footer-menu__link {
    display: inline-block;
    font-family: var(--ne-fm-link-font, inherit);
    font-size: var(--ne-fm-link-size, 14px);
    font-weight: var(--ne-fm-link-weight, 400);
    text-transform: var(--ne-fm-link-transform, none);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    text-decoration: none;
    line-height: 1.4;
    transition: color 0.15s ease, text-decoration-color 0.15s ease;
  }
  .ne-footer-menu__link:hover,
  .ne-footer-menu__link--active {
    color: var(--ne-scheme-text);
  }
  .ne-footer-menu--hover-underline .ne-footer-menu__link:hover {
    text-decoration: underline;
    text-underline-offset: 4px;
  }

  /* ---- Mobile accordion (only when --accordion modifier is set) ---- */
  details.ne-footer-menu { display: flex; flex-direction: column; gap: 16px; background: transparent !important; }
  details.ne-footer-menu > summary {
    list-style: none;
    cursor: default;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    user-select: none;
  }
  details.ne-footer-menu > summary::-webkit-details-marker { display: none; }
  details.ne-footer-menu > summary::marker { content: ''; }
  .ne-footer-menu__chevron {
    transition: transform 0.2s ease;
    color: currentColor;
    opacity: 0.6;
    display: none; /* Hidden on desktop */
  }
  /* Mobile: enable interactivity + show chevron */
  @media (max-width: 749px) {
    details.ne-footer-menu--accordion > summary {
      cursor: pointer;
      padding-block: 8px;
      justify-content: space-between;
      width: 100%;
      border-bottom: 1px solid color-mix(in srgb, var(--ne-scheme-text) 12%, transparent);
    }
    details.ne-footer-menu--accordion .ne-footer-menu__chevron { display: inline-flex; }
    details.ne-footer-menu--accordion[open] .ne-footer-menu__chevron { transform: rotate(180deg); }
    details.ne-footer-menu--accordion > .ne-footer-menu__list { padding-block: 12px; }
  }
/* END_BLOCK:footer-menu */

/* START_BLOCK:footer-newsletter (INDEX:92) */
.ne-footer-news {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: transparent !important;
  }
  .ne-footer-news--align-center { align-items: center; text-align: center; }
  .ne-footer-news--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-news__heading {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }
  .ne-footer-news__text {
    margin: 0;
    font-size: 14px;
    line-height: 1.5;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
    max-width: 32em;
  }

  .ne-footer-news__fields {
    display: flex;
    gap: 8px;
    width: 100%;
    max-width: 420px;
  }
  .ne-footer-news--align-center .ne-footer-news__fields { margin-inline: auto; }
  .ne-footer-news--align-right  .ne-footer-news__fields { margin-left: auto; }

  .ne-footer-news__input { flex: 1; min-width: 0; }
  .ne-footer-news__btn   { flex-shrink: 0; }

  .ne-footer-news__success {
    margin: 0;
    font-size: 14px;
    color: var(--ne-scheme-accent, #6366f1);
  }
  .ne-footer-news__error {
    margin: 0;
    font-size: 14px;
    color: var(--ne-color-sale, #ef4444);
  }

  @media (max-width: 600px) {
    .ne-footer-news__fields { flex-direction: column; }
  }
/* END_BLOCK:footer-newsletter */

/* START_BLOCK:footer-payment (INDEX:93) */
.ne-footer-payment {
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: transparent !important;
  }
  .ne-footer-payment--align-center { align-items: center; text-align: center; }
  .ne-footer-payment--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-payment__heading {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  .ne-footer-payment__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: row;
    gap: var(--ne-fp-gap, 8px);
    align-items: center;
  }
  .ne-footer-payment--wrap     .ne-footer-payment__list { flex-wrap: wrap; }
  .ne-footer-payment--no-wrap  .ne-footer-payment__list { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: thin; }

  .ne-footer-payment__item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: var(--ne-fp-h, 24px);
    opacity: var(--ne-fp-opacity, 1);
  }
  .ne-footer-payment__svg,
  .ne-footer-payment__img {
    height: var(--ne-fp-h, 24px);
    width: auto;
    display: block;
  }
  .ne-footer-payment__svg { border-radius: 4px; }

  /* Accent-tinted custom icon: the uploaded image becomes a mask filled with the
     scheme accent colour (like the built-in SVG icons use currentColor). */
  .ne-footer-payment__img--accent {
    background-color: var(--ne-scheme-accent);
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-position: center;
    mask-position: center;
  }

  /* Monochrome filter skips accent-tinted icons (grayscale would desaturate them). */
  .ne-footer-payment--mono .ne-footer-payment__svg,
  .ne-footer-payment--mono .ne-footer-payment__img:not(.ne-footer-payment__img--accent) {
    filter: grayscale(1) brightness(1.1) contrast(0.9);
  }
/* END_BLOCK:footer-payment */

/* START_BLOCK:footer-quick-links (INDEX:94) */
.ne-footer-ql {
    display: flex;
    flex-direction: column;
    gap: 16px;
    background: transparent !important;
  }
  .ne-footer-ql--align-center { align-items: center; text-align: center; }
  .ne-footer-ql--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-ql__heading {
    margin: 0;
    font-size: var(--ne-fql-heading-size, 14px);
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  .ne-footer-ql__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    gap: var(--ne-fql-link-gap, 12px);
  }
  .ne-footer-ql--vertical .ne-footer-ql__list { flex-direction: column; }
  .ne-footer-ql--horizontal .ne-footer-ql__list {
    flex-direction: row;
    flex-wrap: wrap;
    column-gap: calc(var(--ne-fql-link-gap, 12px) * 2);
  }

  .ne-footer-ql__link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-family: var(--ne-fql-link-font, inherit);
    font-size: var(--ne-fql-link-size, 14px);
    font-weight: var(--ne-fql-link-weight, 500);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
    text-decoration: none;
    line-height: 1.4;
    transition: color 0.18s ease, opacity 0.18s ease, transform 0.18s ease;
  }
  .ne-footer-ql--icon-right .ne-footer-ql__link { justify-content: flex-start; }

  .ne-footer-ql__link:hover {
    color: var(--ne-scheme-text);
    opacity: 1;
  }
  .ne-footer-ql__link:hover .ne-footer-ql__icon { color: var(--ne-scheme-accent); }
  .ne-footer-ql__link:hover .ne-footer-ql__icon svg { transform: translateX(2px); }
  .ne-footer-ql--icon-left .ne-footer-ql__link:hover .ne-footer-ql__icon svg { transform: translateX(-2px); }

  .ne-footer-ql__icon {
    display: inline-flex;
    color: var(--ne-scheme-text);
    opacity: 0.7;
    transition: color 0.18s ease, opacity 0.18s ease;
  }
  .ne-footer-ql__icon svg { transition: transform 0.18s ease; }

  .ne-footer-ql__label { display: inline-block; }
/* END_BLOCK:footer-quick-links */

/* START_BLOCK:footer-row (INDEX:95) */
.ne-footer-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(var(--ne-fr-min-col, 220px), 1fr));
    gap: var(--ne-fr-gap, 32px);
    align-items: var(--ne-fr-valign, stretch);
    width: 100%;
    background: transparent !important;
  }
  /* Children that are .shopify-block wrappers — let them be flex
     containers so their actual block fills the column box vertically.
     Keeps headings top-aligned + bottom edges of each card visually
     even when content lengths differ. */
  .ne-footer-row > .shopify-block {
    display: flex;
    flex-direction: column;
    min-width: 0;
  }
  .ne-footer-row > .shopify-block > * {
    height: 100%;
  }

  /* ── Distribution: "spread" ─────────────────────────────────────────
     Each visual line distributes its OWN items edge-to-edge, independent of
     any other line's tracks (the grid mode above ties every wrapped line to
     the same auto-fit columns — that's what "row 2 follows row 1" was). With
     space-between: 2 items → left + right · 3 → left + centre + right · more →
     first left, last right, the rest evenly spaced in the middle. Items keep
     their content width (no equal-column stretch). */
  .ne-footer-row--spread {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: var(--ne-fr-valign, flex-start);
    gap: var(--ne-fr-gap, 32px);
  }
  .ne-footer-row--spread > .shopify-block {
    flex: 0 1 auto;
  }

  /* Mobile: choose 1 or 2 columns explicitly via setting */
  @media (max-width: 749px) {
    .ne-footer-row--columns.ne-footer-row--mcols-1 {
      grid-template-columns: 1fr;
    }
    .ne-footer-row--columns.ne-footer-row--mcols-2 {
      grid-template-columns: 1fr 1fr;
    }
    /* Spread mode on mobile: stack (1) or wrap two-per-line (2). Reset the
       desktop space-between so a single stacked item isn't pushed apart. */
    .ne-footer-row--spread.ne-footer-row--mcols-1 {
      flex-direction: column;
      align-items: stretch;
      justify-content: flex-start;
    }
    .ne-footer-row--spread.ne-footer-row--mcols-2 > .shopify-block {
      flex-basis: calc(50% - var(--ne-fr-gap, 32px) / 2);
    }
  }
/* END_BLOCK:footer-row */

/* START_BLOCK:footer-social (INDEX:96) */
.ne-footer-social {
    display: flex;
    flex-direction: column;
    gap: 16px;
    background: transparent !important;
  }
  .ne-footer-social--align-center { align-items: center; text-align: center; }
  .ne-footer-social--align-right  { align-items: flex-end;  text-align: right;  }

  .ne-footer-social__heading {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
  }

  .ne-footer-social__icons {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ne-fs-gap, 8px);
  }

  .ne-footer-social__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--ne-fs-btn-size, 36px);
    height: var(--ne-fs-btn-size, 36px);
    color: var(--ne-scheme-text);
    opacity: 0.75;
    background: transparent;
    text-decoration: none;
    transition: color 0.18s ease, background 0.18s ease, transform 0.18s ease, opacity 0.18s ease, box-shadow 0.18s ease;
  }
  .ne-footer-social--shape-rounded .ne-footer-social__link { border-radius: 8px; }
  .ne-footer-social--shape-circle  .ne-footer-social__link { border-radius: 999px; }
  .ne-footer-social--shape-square  .ne-footer-social__link { border-radius: 0; }

  .ne-footer-social__link:hover {
    opacity: 1;
    color: var(--ne-scheme-text);
  }
  .ne-footer-social--hover-lift  .ne-footer-social__link:hover { transform: translateY(-2px); }
  .ne-footer-social--hover-fade  .ne-footer-social__link:hover { background: color-mix(in srgb, var(--ne-scheme-text) 8%, transparent); }
  .ne-footer-social--hover-glow  .ne-footer-social__link:hover {
    background: color-mix(in srgb, var(--ne-scheme-text) 6%, transparent);
    box-shadow: 0 0 18px var(--ne-scheme-accent-glow, rgba(99,102,241,0.35));
  }
  .ne-footer-social--hover-none .ne-footer-social__link:hover { opacity: 1; }

  /* Custom-uploaded icon images: render as recolorable mask so they
     follow scheme text color, just like the inline SVGs. */
  .ne-footer-social__icon-img {
    width: var(--ne-fs-icon-size, 20px);
    height: var(--ne-fs-icon-size, 20px);
    object-fit: contain;
    display: block;
  }

  .ne-footer-social__link--text {
    width: auto;
    padding: 0 12px;
    font-size: 14px;
  }
/* END_BLOCK:footer-social */

/* START_BLOCK:gallery-grid (INDEX:97) */
.ne-gg {
    width: 100%;
    background: transparent !important;
  }

  /* Theme-block wrapper collapse — image children become direct grid /
     flex items (Rule 70). */
  .ne-gg__grid > .shopify-block,
  .ne-gg__track > .shopify-block { display: contents; }

  /* Item chrome — applied to the image block's wrap inside this grid.
     The wrap itself becomes the visible card; we kill its default
     flex centering so it fills the grid cell. */
  .ne-gg .ne-block-image-wrap {
    width: 100%;
    align-items: stretch;
    margin: 0;
    border-radius: var(--ne-gg-radius, 8px);
    overflow: hidden;
    position: relative;
  }
  .ne-gg .ne-block-image {
    width: 100%;
    border-radius: var(--ne-gg-radius, 8px);
    overflow: hidden;
    /* Force a stacking context so caption-overlay positions against
       this card (not against an ancestor). */
    position: relative;
  }
  /* Image atom may carry its own inline border-radius from its `radius`
     setting; force it to inherit the gallery's radius so the user only
     manages one slider (the parent block). */
  .ne-gg .ne-block-image__img { border-radius: inherit !important; }

  /* ──────────────── Effect chrome (Rule 130 — composed shadow) ─────────────
     Each modifier sets ONE slot of the composed box-shadow so glass +
     glow + shadow stack without conflict. Effects sit on the WRAP
     (outer), not on .ne-block-image, because the wrap also carries
     overflow:hidden for caption-overlay clipping — a box-shadow on
     the inner element would be clipped by the wrap. Box-shadow on the
     wrap renders outside its own box and is unaffected by wrap's
     overflow:hidden. */
  .ne-gg .ne-block-image-wrap {
    --ne-gg-shadow-outer: 0 0 0 0 transparent;
    --ne-gg-shadow-glow:  0 0 0 0 transparent;
    box-shadow: var(--ne-gg-shadow-outer), var(--ne-gg-shadow-glow);
    transition: box-shadow .2s ease, transform .2s ease;
  }
  /* Effects coupled to global block-intensity (Theme-Settings ×0.6).
     Dampened defaults keep the shadow inside the carousel padding-inline
     buffer (~gap), but the multiplier scales them globally. */
  .ne-gg--shadow .ne-block-image-wrap { --ne-gg-shadow-outer: var(--ne-block-shadow-effect, 0 3px 9px rgba(0,0,0,0.14)); }
  .ne-gg--glow   .ne-block-image-wrap { --ne-gg-shadow-glow:  var(--ne-block-glow-effect, 0 0 10px var(--ne-scheme-accent-glow, rgba(120,120,255,0.28))); }
  .ne-gg--shadow .ne-block-image-wrap:hover { --ne-gg-shadow-outer: var(--ne-block-shadow-effect, 0 2px 6px rgba(0,0,0,0.12)); }
  .ne-gg--glow   .ne-block-image-wrap:hover { --ne-gg-shadow-glow:  var(--ne-block-glow-effect, 0 0 7px var(--ne-scheme-accent-glow, rgba(120,120,255,0.24))); }
  .ne-gg--glass  .ne-block-image-wrap {
    background: var(--ne-glass-bg, rgba(255,255,255,0.05)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }

  /* ──────────────── Silhouette mode (transparent-PNG-friendly) ─────────────
     drop-shadow follows the alpha channel of the image, producing a
     contour-shaped glow/shadow instead of a rectangular box-shadow.
     Trade-off: requires NO clipping ancestor — overflow:hidden +
     border-radius on the card would clip the silhouette shadow. So we
     drop both, and force captions to the "below" layout (the overlay
     caption pattern needs overflow:hidden to anchor the gradient).
     The card box itself becomes invisible (transparent bg, no border,
     no card box-shadow), only the image's contour is decorated. */
  /* Suppress the rectangular wrap chrome — effects now sit on the
     wrap (see "Effect chrome" block above), so we have to override
     them here, not on .ne-block-image.

     `:not(.ne-gg--glass)` gate: when glass is on, glass styles win
     and silhouette overrides are skipped (glass needs the wrap's
     bg/border/backdrop-filter to render the frosted look). The image
     drop-shadow filter still applies (silhouette mode coexists with
     glass) — see `.ne-gg--silhouette .ne-block-image__img` below.

     We deliberately keep the wrap's border-radius so user-set corner
     radius still works on non-transparent images; drop-shadow follows
     the (rounded) silhouette either way. */
  .ne-gg--silhouette:not(.ne-gg--glass) .ne-block-image-wrap {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    overflow: visible !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  .ne-gg--silhouette:not(.ne-gg--glass) .ne-block-image {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    overflow: visible !important;
  }
  .ne-gg--silhouette .ne-block-image__img {
    /* Compose drop-shadows: shadow first (further), glow second
       (tighter accent halo). Each modifier toggles its own var. */
    filter:
      var(--ne-gg-drop-shadow, drop-shadow(0 0 0 transparent))
      var(--ne-gg-drop-glow,   drop-shadow(0 0 0 transparent));
    transition: filter .25s ease, transform .25s ease;
  }
  /* Silhouette drop-shadow values scaled by global block-intensity
     multiplier (Theme-Settings → Effects → Section glow/shadow ×0.6).
     calc() in each component lets a single Theme slider tune all
     gallery effects globally. */
  .ne-gg--silhouette.ne-gg--shadow .ne-block-image__img {
    --ne-gg-drop-shadow: drop-shadow(
      0
      calc(6px * var(--ne-block-shadow-intensity, 0.6))
      calc(10px * var(--ne-block-shadow-intensity, 0.6))
      rgba(0,0,0, calc(0.28 * var(--ne-block-shadow-intensity, 0.6)))
    );
  }
  .ne-gg--silhouette.ne-gg--glow .ne-block-image__img {
    --ne-gg-drop-glow: drop-shadow(
      0 0
      calc(8px * var(--ne-block-glow-intensity, 0.6))
      var(--ne-scheme-accent-glow, rgba(120,120,255,0.4))
    );
  }
  /* Hover dampens further so shadow doesn't clip when zoom scales the img. */
  .ne-gg--silhouette.ne-gg--shadow .ne-block-image:hover .ne-block-image__img {
    --ne-gg-drop-shadow: drop-shadow(
      0
      calc(4px * var(--ne-block-shadow-intensity, 0.6))
      calc(7px * var(--ne-block-shadow-intensity, 0.6))
      rgba(0,0,0, calc(0.24 * var(--ne-block-shadow-intensity, 0.6)))
    );
  }
  .ne-gg--silhouette.ne-gg--glow .ne-block-image:hover .ne-block-image__img {
    --ne-gg-drop-glow: drop-shadow(
      0 0
      calc(6px * var(--ne-block-glow-intensity, 0.6))
      var(--ne-scheme-accent-glow, rgba(120,120,255,0.36))
    );
  }
  /* Silhouette mode forces caption-below (overlay needs overflow). */
  .ne-gg--silhouette .ne-block-image__caption {
    position: static !important;
    background: none !important;
    color: inherit !important;
    opacity: 1 !important;
    transform: none !important;
    margin-top: 8px;
    text-align: center;
  }

  /* Uniform aspect ratio — only applied when the user picked a fixed
     aspect at the parent. Skipped on .ne-gg--mixed so per-image
     aspect choices come through unchanged. */
  .ne-gg--uniform .ne-block-image:not(.ne-block-image--ratio) {
    aspect-ratio: var(--ne-gg-aspect, 1/1);
  }
  .ne-gg--uniform .ne-block-image:not(.ne-block-image--ratio) .ne-block-image__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* ──────────────── Grid layout ──────────────── */
  .ne-gg--grid .ne-gg__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-gg-cols, 3), minmax(0, 1fr));
    gap: var(--ne-gg-gap, 12px);
  }
  @media (max-width: 989px) {
    .ne-gg--grid .ne-gg__grid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
    }
  }
  @media (max-width: 599px) {
    .ne-gg--grid .ne-gg__grid {
      grid-template-columns: repeat(var(--ne-gg-cols-mobile, 2), minmax(0, 1fr));
      gap: calc(var(--ne-gg-gap, 12px) * 0.75);
    }
  }

  /* ──────────────── Carousel layout ──────────────── */
  .ne-gg--carousel .ne-gg__carousel {
    position: relative;
    /* `overflow-x: clip` hard-clips the track at the carousel's box
       edge. `overflow-y: visible` keeps vertical shadow bleed.
       padding-inline: 48 reserves an INSET ZONE: items are pushed
       away from the box edge so their shadow renders inside the
       gutter without reaching the visible box border. Arrows
       (left:0 / right:0) sit in this gutter alongside items. */
    overflow-x: clip;
    overflow-y: visible;
    padding-inline: 32px;
  }
  .ne-gg--carousel .ne-gg__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - (var(--ne-gg-cols, 3) - 1) * var(--ne-gg-gap, 12px)) / var(--ne-gg-cols, 3));
    gap: var(--ne-gg-gap, 12px);
    overflow-x: auto;
    overflow-y: hidden;
    /* proximity (not mandatory) — avoids subpixel jitter during smooth
       scroll (Rule 138). */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    scrollbar-width: none;
    /* Rule 205: explicit touch-action so mobile swipe delegates fully.
       pan-y lets a vertical swipe scroll the page instead of being trapped
       by this horizontal carousel. */
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    /* Inline padding = gap creates a shadow-bleed buffer inside the
       track. Items inset by gap-width; their shadow renders into the
       padding zone (within track's border, not clipped by overflow-x).
       Off-screen peek width = padding - gap = 0. */
    padding-block: 32px;
    padding-inline: var(--ne-gg-gap, 18px);
    margin-block: -32px;
    scroll-padding-inline: var(--ne-gg-gap, 18px);
    /* Fade-out at inline edges: shadow softens at track edges instead
       of hard-clipping. Off-screen item peeks are handled by JS
       IntersectionObserver. */
    -webkit-mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--ne-gg-gap, 18px),
      black calc(100% - var(--ne-gg-gap, 18px)),
      transparent 100%
    );
    mask-image: linear-gradient(
      to right,
      transparent 0,
      black var(--ne-gg-gap, 18px),
      black calc(100% - var(--ne-gg-gap, 18px)),
      transparent 100%
    );
    /* Keep a stable compositor layer mid-scroll (Rule 145). */
    will-change: scroll-position;
  }
  /* Glassmorphism + mask-image are mutually exclusive — mask creates
     a stacking context that breaks `backdrop-filter` sampling on
     glass cards (the blur effect doesn't render). When glass is on
     we drop the mask so blur works; the trade-off is no edge fade. */
  .ne-gg--glass.ne-gg--carousel .ne-gg__track {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .ne-gg--carousel .ne-gg__track::-webkit-scrollbar { display: none; }
  .ne-gg--carousel .ne-gg__track > * { scroll-snap-align: start; }

  @media (max-width: 989px) {
    .ne-gg--carousel .ne-gg__track {
      grid-auto-columns: calc((100% - var(--ne-gg-gap, 12px)) / 2);
    }
  }
  @media (max-width: 599px) {
    .ne-gg--carousel .ne-gg__carousel { padding-inline: 28px; }
    .ne-gg--carousel .ne-gg__track {
      grid-auto-columns: calc((100% - var(--ne-gg-gap, 12px) * (var(--ne-gg-cols-mobile, 2) - 1)) / var(--ne-gg-cols-mobile, 2));
    }
  }

  /* Carousel arrows — same chrome as testimonial-list. */
  .ne-gg__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    color: var(--ne-scheme-text, currentColor);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
    padding: 0;
  }
  .ne-gg__arrow:hover { transform: translateY(-50%) scale(1.05); }
  .ne-gg__arrow--prev { left: 12px; }
  .ne-gg__arrow--next { right: 12px; }
  .ne-gg--arrow-button .ne-gg__arrow {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127,127,127,0.25));
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  }
  .ne-gg--arrow-plain .ne-gg__arrow {
    background: transparent;
    border: 0;
    box-shadow: none;
    width: 32px;
    height: 32px;
  }
  .ne-gg--arrow-plain .ne-gg__arrow:hover { color: var(--ne-scheme-accent, currentColor); }

  /* ──────────────── Hover zoom ──────────────── */
  .ne-gg--zoom .ne-block-image__img {
    transition: transform .5s ease;
  }
  .ne-gg--zoom .ne-block-image:hover .ne-block-image__img {
    transform: scale(1.06);
  }

  /* ──────────────── Captions ──────────────── */
  /* "below" — default flow position from the image block. Just a small
     polish so it sits flush under the card. */
  .ne-gg--caption-below .ne-block-image__caption {
    margin-top: 8px;
    text-align: center;
  }

  /* "overlay" / "hover" — caption absolute-positioned over the image's
     bottom edge with a gradient backdrop. The image block emits the
     <figcaption> as a sibling of .ne-block-image, so we re-anchor by
     making the wrap relative + moving the caption inside its bounds. */
  .ne-gg--caption-overlay .ne-block-image-wrap {
    position: relative;
    overflow: hidden;
    border-radius: var(--ne-gg-radius, 8px);
  }
  .ne-gg--caption-overlay .ne-block-image__caption {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    margin: 0;
    padding: 16px 18px;
    background: linear-gradient(to top, rgba(0,0,0,0.78), rgba(0,0,0,0.0));
    color: #fff;
    font-size: 0.9375rem;
    line-height: 1.4;
  }
  .ne-gg--caption-hover .ne-block-image__caption {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity .25s ease, transform .25s ease;
  }
  .ne-gg--caption-hover .ne-block-image-wrap:hover .ne-block-image__caption,
  .ne-gg--caption-hover .ne-block-image-wrap:focus-within .ne-block-image__caption {
    opacity: 1;
    transform: translateY(0);
  }
  .ne-gg--caption-hidden .ne-block-image__caption { display: none; }

  /* ──────────────── Lightbox ──────────────── */
  .ne-gg--lightbox .ne-block-image { cursor: zoom-in; }
  .ne-gg__lightbox {
    position: fixed;
    inset: 0;
    z-index: 9999;
    /* Scheme-bg backdrop + blur — matches the product lightbox so the
       gallery lightbox follows the active colour scheme instead of a
       hardcoded black (user 2026-05-24). */
    background: color-mix(in srgb, var(--ne-scheme-bg, #000) 88%, transparent);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    color: var(--ne-scheme-text, #fff);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(16px, 4vw, 48px);
    opacity: 0;
    transition: opacity .25s ease;
  }
  .ne-gg__lightbox[hidden] { display: none; }
  .ne-gg__lightbox.is-visible { opacity: 1; }
  .ne-gg__lb-figure {
    margin: 0;
    max-width: min(92vw, 1400px);
    max-height: 88vh;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
  }
  .ne-gg__lb-image {
    max-width: 100%;
    max-height: 72vh;
    object-fit: contain;
    background: transparent;
    /* Vertical gestures stay native (close/scroll); horizontal drags are left
       free for the JS swipe-to-paginate handler (Rule 205). Matches the
       product lightbox. */
    touch-action: pan-y;
  }
  .ne-gg__lb-caption {
    color: var(--ne-scheme-text, #fff);
    font-size: 0.9375rem;
    text-align: center;
    max-width: 80ch;
    margin: 0;
  }
  .ne-gg__lb-close,
  .ne-gg__lb-nav {
    position: absolute;
    color: var(--ne-scheme-text, #fff);
    cursor: pointer;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background-color .15s ease, transform .15s ease;
  }
  /* Close button position only — chrome (bg/border/radius) inherited
     from the shared .ne-gg__lb-nav--button|--plain modifiers below. */
  .ne-gg__lb-close { top: 24px; right: 24px; }
  /* Lightbox nav (prev/next) AND close button follow the slider's
     arrow_shape/arrow_icon setting. Button-mode = bg + border;
     plain-mode = icon only. */
  .ne-gg__lb-nav--button {
    background: color-mix(in srgb, var(--ne-scheme-text, #fff) 14%, transparent);
    border: 1px solid color-mix(in srgb, var(--ne-scheme-text, #fff) 22%, transparent);
  }
  .ne-gg__lb-nav--plain {
    background: transparent;
    border: 0;
  }
  .ne-gg__lb-nav--button:hover { background: color-mix(in srgb, var(--ne-scheme-text, #fff) 26%, transparent); }
  .ne-gg__lb-nav--plain:hover { color: var(--ne-scheme-accent, #fff); }
  .ne-gg__lb-nav { top: 50%; transform: translateY(-50%); }
  .ne-gg__lb-nav--prev { left: 24px; }
  .ne-gg__lb-nav--next { right: 24px; }
  .ne-gg__lb-counter {
    position: absolute;
    top: 28px;
    left: 28px;
    color: color-mix(in srgb, var(--ne-scheme-text, #fff) 85%, transparent);
    font-size: 0.875rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.04em;
    pointer-events: none;
    user-select: none;
  }
  /* Thumbnail strip — JS-built, mirrors the product lightbox (user
     2026-05-24). Pinned to the bottom; active thumb ringed in the accent. */
  .ne-gg__lb-thumbs {
    position: absolute;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 8px;
    max-width: min(92vw, 720px);
    overflow-x: auto;
    padding: 4px;
    scrollbar-width: none;
  }
  .ne-gg__lb-thumbs[hidden] { display: none; }
  .ne-gg__lb-thumbs::-webkit-scrollbar { display: none; }
  .ne-gg__lb-thumb {
    flex: 0 0 auto;
    width: 56px;
    height: 56px;
    border: 0;
    padding: 0;
    border-radius: var(--ne-radius-md, 8px);
    overflow: hidden;
    cursor: pointer;
    opacity: 0.5;
    background: none;
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--ne-scheme-text, #fff) 18%, transparent);
    transition: opacity .15s ease, box-shadow .15s ease;
  }
  .ne-gg__lb-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .ne-gg__lb-thumb:hover { opacity: 0.85; }
  .ne-gg__lb-thumb--active {
    opacity: 1;
    box-shadow: 0 0 0 2px var(--ne-scheme-accent, #6366f1);
  }
  @media (max-width: 599px) {
    .ne-gg__lb-thumb { width: 44px; height: 44px; }
    .ne-gg__lb-image { max-height: 64vh; }
  }
  @media (max-width: 599px) {
    .ne-gg__lb-close { top: 12px; right: 12px; width: 38px; height: 38px; }
    .ne-gg__lb-nav { width: 38px; height: 38px; }
    .ne-gg__lb-nav--prev { left: 8px; }
    .ne-gg__lb-nav--next { right: 8px; }
  }
/* END_BLOCK:gallery-grid */

/* START_BLOCK:glass-logo (INDEX:98) */
.ne-glogo {
  position: relative;
  width: 100%;
  height: var(--ne-gl-h, 420px);
  isolation: isolate;
}
.ne-glogo__fallback,
.ne-glogo__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.ne-glogo__fallback {
  object-fit: contain;
  opacity: 1;
  transition: opacity 0.5s ease;
  z-index: 0;
  transform: translate(var(--ne-gl-tx, 0), var(--ne-gl-ty, 0)) scale(var(--ne-gl-fscale, 1));
}
.ne-glogo__canvas {
  display: block;
  opacity: 0;
  transition: opacity 0.6s ease;
  z-index: 1;
  pointer-events: none;
}
/* Overflow mode — canvas grows beyond the block so the motif can be pushed past
   the block / section edge (the section is un-clipped via :has() in critical.css). */
.ne-glogo--overflow { overflow: clip; overflow-clip-margin: 1500px; }
.ne-glogo--overflow .ne-glogo__canvas { inset: -150%; width: 400%; height: 400%; max-width: none; }
@media (max-width: 749px) {
  .ne-glogo { height: var(--ne-gl-h-m, 300px); }
}
/* END_BLOCK:glass-logo */

/* START_BLOCK:group (INDEX:99) */
.ne-group {
    display: flex;
    gap: var(--ne-group-gap, 16px);
    padding: var(--ne-group-padding, 0);
    border-radius: var(--ne-group-radius, 0);
    min-width: 0;
    position: relative;
    background: transparent;
  }
  .ne-group--vertical {
    flex-direction: column;
    align-items: var(--ne-group-align, flex-start);
    justify-content: var(--ne-group-justify, flex-start);
  }
  .ne-group--horizontal {
    flex-direction: row;
    align-items: var(--ne-group-align, center);
    justify-content: var(--ne-group-justify, flex-start);
    flex-wrap: wrap;
  }
  /* Horizontal children share row-space equally by default. Wraps inside
     (.ne-block-button-wrap, .ne-block-text, etc.) declare width:100% so their
     intrinsic basis would otherwise force a full row each — flex:1 1 0
     forces the equal-share layout regardless of intrinsic width. min-width:0
     plus the global .ne-btn wrap rules let labels longer than the cell
     wrap onto multiple lines instead of overflowing the viewport. */
  .ne-group--horizontal > * { flex: 1 1 0; min-width: 0; }
  .ne-group--horizontal .ne-block-button { min-width: 0; }

  /* Overlay (stack): all direct children occupy the SAME grid cell, layered on
     top of each other (instead of flowing vertically/horizontally). Combine
     with per-child Advanced settings (offset + z-index) to nudge + reorder the
     stack. place-items decides where each child sits in the shared cell; each
     child keeps its intrinsic size. The cell sizes to the largest child. */
  .ne-group--overlay {
    display: grid;
    align-items: var(--ne-group-align, start);
    justify-items: var(--ne-group-justify, start);
  }
  .ne-group--overlay > * { grid-area: 1 / 1; min-width: 0; }

  /* Equalize: in addition to the equal-share wraps above, force any button
     INSIDE its wrap to fill 100% AND stretch the row so that one wrapped
     button pulls its sibling along to match heights. Without stretch the
     unwrapped button stays single-line and looks misaligned. */
  .ne-group--horizontal.ne-group--equalize { align-items: stretch; }
  .ne-group--horizontal.ne-group--equalize > * { display: flex; }
  .ne-group--horizontal.ne-group--equalize .ne-block-button {
    width: 100%;
    height: 100%;
    justify-content: center;
  }

  /* Mobile auto-stack: horizontal group flips to a column on narrow screens.
     Children become full-width so buttons / images / etc don't compete for
     row space. Disabled by setting mobile_direction = "horizontal". */
  @media (max-width: 749px) {
    .ne-group--horizontal.ne-group--mstack {
      flex-direction: column;
      align-items: stretch;
    }
    .ne-group--horizontal.ne-group--mstack > * {
      flex: 0 0 auto;
      width: 100%;
    }
    .ne-group--horizontal.ne-group--mstack .ne-block-button {
      width: 100%;
      justify-content: center;
    }
  }

  /* Vertical: align-items only works on items that are NOT forced to 100% — let children shrink to content */
  .ne-group--vertical > * { max-width: 100%; }

  /* A custom scheme recolours the group CONTENT (text via --ne-scheme-text,
     children via the inherited --ne-scheme-* vars) — always, independent of
     whether the box paints a background. So a custom scheme with show_bg OFF
     recolours only the content, box stays transparent. */
  .ne-group.ne-group[class*="color-scheme-"]:not(.ne-group--glass) {
    color: var(--ne-scheme-text);
  }
  /* show_bg paints the box from the ACTIVE scheme (global or custom); decoupled
     from scheme selection, opt-in (default off → no regression). Opacity via
     --ne-group-bg-a (color-mix fades toward transparent). */
  .ne-group.ne-group--bg:not(.ne-group--glass) {
    background: color-mix(in srgb, var(--ne-scheme-bg, transparent) var(--ne-group-bg-a, 100%), transparent) !important;
  }

  /* Direct-colour override: propagate the override text colour to child blocks
     that don't set their own (the scheme rule above only fires with a color-X
     class, which the pure override doesn't add). */
  .ne-group.ne-cov { color: var(--ne-scheme-text); }

  /* Glass — uses Theme Settings → Effects → Glassmorphism vars
     (--ne-glass-bg, --ne-glass-blur, --ne-glass-saturate, --ne-glass-border)
     so the global controls (Glas-Unschärfe / Deckkraft / Sättigung)
     propagate to group blocks. Previously hardcoded blur(14px) /
     saturate(1.1) / rgba(255,255,255,0.08) — that made the settings
     panel non-functional for groups. */
  .ne-group.ne-group--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05)) !important;
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    /* Border removed (user 2026-05-23): on dark page-bg the 1px white
       glass-border was visible as a bottom contour line on group blocks.
       The backdrop blur + bg-tint already define the surface boundary
       visually. */
    border: 0;
  }
  /* Tint with scheme accent — opt-in via the `glass_tint` toggle (which
     emits .ne-glass--tinted). `--ne-glass-bg-tinted` is set per-scheme in
     css-variables.liquid (accent pre-mixed with --ne-glass-bg's alpha).
     Specificity (0,3,0) + !important beats the base .ne-group--glass
     rule (0,2,0) + !important above. User report 2026-05-23: tint toggle
     didn't apply because the previous selector keyed on [class*="color-scheme-"]
     (auto-tint on custom-scheme) instead of the explicit modifier. */
  .ne-group.ne-group--glass.ne-glass--tinted {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05))) !important;
    border-color: var(--ne-glass-border-tinted, var(--ne-glass-border, rgba(255, 255, 255, 0))) !important;
  }

  .ne-group--glow {
    box-shadow: 0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.45));
  }
  .ne-group--shadow {
    box-shadow: 0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.28);
  }
  .ne-group--glow.ne-group--shadow {
    box-shadow:
      0 calc(8px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.28),
      0 0 calc(40px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.45));
  }

  /* Prevent child shopify-block wrappers from breaking flex layout */
  .ne-group > .shopify-block { display: contents; }

  /* Vertical cross-axis propagation — implemented via CSS vars so
     per-block alignment settings can override the group default.
     Group sets the var at group-scope (inherited by descendants);
     atoms that read the var ALSO set it at element-scope from their
     own alignment setting, so element-scope beats inherited ancestor
     and the block's choice wins when the user sets it.

     Vars: --ne-tp--align (heading/text), --ne-bw-justify
     (button-wrap), --ne-iw-align (image/video-wrap).
     No !important anywhere — the var-override chain handles priority. */
  .ne-group--vertical.ne-group--xalign-flex-start {
    --ne-tp--align: left;
    --ne-bw-justify: flex-start;
    --ne-iw-align: flex-start;
  }
  .ne-group--vertical.ne-group--xalign-center {
    --ne-tp--align: center;
    --ne-bw-justify: center;
    --ne-iw-align: center;
  }
  .ne-group--vertical.ne-group--xalign-flex-end {
    --ne-tp--align: right;
    --ne-bw-justify: flex-end;
    --ne-iw-align: flex-end;
  }
  .ne-group--vertical.ne-group--xalign-stretch {
    --ne-tp--align: left;
    --ne-bw-justify: stretch;
    --ne-iw-align: stretch;
  }
/* END_BLOCK:group */

/* START_BLOCK:heading (INDEX:100) */
.ne-block-heading { background: transparent !important; }
/* END_BLOCK:heading */

/* START_BLOCK:hotspot-hero (INDEX:101) */
.ne-hotspot-hero {
    position: absolute;
    z-index: 2;
    display: flex;
    flex-direction: column;
    pointer-events: none;
    padding: 24px;
    max-width: 100%;
  }
  /* Rule 173: .color-{scheme} paints bg by default. We're using the
     class for CSS-var cascade (Rule 201), not for paint — clear it. */
  .ne-hotspot-hero[class*="color-scheme-"] { background: transparent !important; }
  .ne-hotspot-hero.ne-cov { color: var(--ne-scheme-text); }
  /* Inner content can be interactive (buttons etc) */
  .ne-hotspot-hero__inner {
    pointer-events: auto;
    width: 100%;
    max-width: var(--ne-hh-max-w, 480px);
    display: flex;
    flex-direction: column;
    gap: 12px;
    color: #fff;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5);
  }
  .ne-hotspot-hero__inner > .shopify-block { display: contents; }

  /* ── Position anchors ─────────────────────────────────────── */
  .ne-hotspot-hero--top-left { top: 0; left: 0; align-items: flex-start; text-align: left; }
  .ne-hotspot-hero--top-center { top: 0; left: 50%; transform: translateX(-50%); align-items: center; text-align: center; }
  .ne-hotspot-hero--top-right { top: 0; right: 0; align-items: flex-end; text-align: right; }

  .ne-hotspot-hero--center-left { top: 50%; left: 0; transform: translateY(-50%); align-items: flex-start; text-align: left; }
  .ne-hotspot-hero--center { top: 50%; left: 50%; transform: translate(-50%, -50%); align-items: center; text-align: center; }
  .ne-hotspot-hero--center-right { top: 50%; right: 0; transform: translateY(-50%); align-items: flex-end; text-align: right; }

  .ne-hotspot-hero--bottom-left { bottom: 0; left: 0; align-items: flex-start; text-align: left; }
  .ne-hotspot-hero--bottom-center { bottom: 0; left: 50%; transform: translateX(-50%); align-items: center; text-align: center; }
  .ne-hotspot-hero--bottom-right { bottom: 0; right: 0; align-items: flex-end; text-align: right; }

  /* Aligned text & contents within the inner */
  .ne-hotspot-hero--top-center .ne-hotspot-hero__inner,
  .ne-hotspot-hero--center .ne-hotspot-hero__inner,
  .ne-hotspot-hero--bottom-center .ne-hotspot-hero__inner { align-items: center; }
  .ne-hotspot-hero--top-right .ne-hotspot-hero__inner,
  .ne-hotspot-hero--center-right .ne-hotspot-hero__inner,
  .ne-hotspot-hero--bottom-right .ne-hotspot-hero__inner { align-items: flex-end; }

  /* ── Background variants ──────────────────────────────────── */
  .ne-hotspot-hero--bg-transparent { background: transparent; }

  /* Scrim — directional gradient that follows position */
  .ne-hotspot-hero--bg-scrim::before {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: -1;
  }
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--bottom-left::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--bottom-center::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--bottom-right::before {
    background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 100%);
  }
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--top-left::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--top-center::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--top-right::before {
    background: linear-gradient(to bottom, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 100%);
  }
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--center::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--center-left::before,
  .ne-hotspot-hero--bg-scrim.ne-hotspot-hero--center-right::before {
    background: radial-gradient(ellipse at center, rgba(0,0,0,0.45) 0%, rgba(0,0,0,0) 70%);
  }

  /* Glass background — uses theme glass vars from theme settings */
  .ne-hotspot-hero--bg-glass .ne-hotspot-hero__inner {
    padding: 20px 24px;
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.18));
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    border: 1px solid var(--ne-glass-border, transparent);
    border-radius: var(--ne-hh-radius, 12px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);
  }

  /* Scheme-bg — solid backplate using current scheme */
  .ne-hotspot-hero--bg-scheme .ne-hotspot-hero__inner {
    padding: 20px 24px;
    background: var(--ne-scheme-bg);
    color: var(--ne-scheme-text);
    text-shadow: none;
    border-radius: var(--ne-hh-radius, 12px);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
  }

  /* CSS source-order matters: position-derived defaults FIRST, then
     explicit alignment override. Last-wins at equal specificity, so
     when text_alignment is set, it beats the position default. */

  /* Position-derived default --ne-bw-justify when text_alignment is "inherit" */
  .ne-hotspot-hero--top-left .ne-hotspot-hero__inner,
  .ne-hotspot-hero--center-left .ne-hotspot-hero__inner,
  .ne-hotspot-hero--bottom-left .ne-hotspot-hero__inner {
    --ne-bw-justify: flex-start;
  }
  .ne-hotspot-hero--top-center .ne-hotspot-hero__inner,
  .ne-hotspot-hero--center .ne-hotspot-hero__inner,
  .ne-hotspot-hero--bottom-center .ne-hotspot-hero__inner {
    --ne-bw-justify: center;
  }
  .ne-hotspot-hero--top-right .ne-hotspot-hero__inner,
  .ne-hotspot-hero--center-right .ne-hotspot-hero__inner,
  .ne-hotspot-hero--bottom-right .ne-hotspot-hero__inner {
    --ne-bw-justify: flex-end;
  }

  /* Explicit text_alignment override (independent of position).
     Sets --ne-bw-justify CSS-var which button-block reads when its
     own alignment is "Inherit from parent" (Rule 126). */
  .ne-hotspot-hero--align-left .ne-hotspot-hero__inner {
    align-items: flex-start;
    text-align: left;
    --ne-bw-justify: flex-start;
  }
  .ne-hotspot-hero--align-center .ne-hotspot-hero__inner {
    align-items: center;
    text-align: center;
    --ne-bw-justify: center;
  }
  .ne-hotspot-hero--align-right .ne-hotspot-hero__inner {
    align-items: flex-end;
    text-align: right;
    --ne-bw-justify: flex-end;
  }

  /* Glow + shadow effects (apply to glass + scheme bg modes) */
  .ne-hotspot-hero--glow.ne-hotspot-hero--bg-glass .ne-hotspot-hero__inner,
  .ne-hotspot-hero--glow.ne-hotspot-hero--bg-scheme .ne-hotspot-hero__inner {
    box-shadow:
      0 12px 40px rgba(0, 0, 0, 0.3),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-hotspot-hero--shadow.ne-hotspot-hero--bg-glass .ne-hotspot-hero__inner,
  .ne-hotspot-hero--shadow.ne-hotspot-hero--bg-scheme .ne-hotspot-hero__inner {
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3);
  }
  .ne-hotspot-hero--shadow.ne-hotspot-hero--glow.ne-hotspot-hero--bg-glass .ne-hotspot-hero__inner,
  .ne-hotspot-hero--shadow.ne-hotspot-hero--glow.ne-hotspot-hero--bg-scheme .ne-hotspot-hero__inner {
    box-shadow:
      0 18px 60px rgba(0, 0, 0, 0.5),
      0 4px 12px rgba(0, 0, 0, 0.3),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }

  /* Mobile: shrink padding + use full width */
  @media (max-width: 749px) {
    .ne-hotspot-hero { padding: 16px; }
    .ne-hotspot-hero__inner { max-width: 100%; }
    .ne-hotspot-hero--bg-glass .ne-hotspot-hero__inner,
    .ne-hotspot-hero--bg-scheme .ne-hotspot-hero__inner {
      padding: 16px 18px;
    }

    /* Mobile flow-mode: JS moves hero out of __media into __stage as
       sibling (above/below). Hero loses overlay positioning, takes
       full width in section flow. Hotspots stay inside __media so
       their position-anchors remain relative to image bounds. */
    .ne-hotspot-image__stage[data-hero-flow="1"] {
      display: flex;
      flex-direction: column;
    }
    .ne-hotspot-hero[data-flow-mode="1"] {
      position: static !important;
      transform: none !important;
      inset: auto !important;
      width: 100%;
      max-width: 100%;
      padding: 16px;
      pointer-events: auto;
    }
    .ne-hotspot-hero[data-flow-mode="1"][data-flow-order="-1"] { order: -1; }
    .ne-hotspot-hero[data-flow-mode="1"][data-flow-order="1"] { order: 1; }
    /* Scrim gradient irrelevant in flow-mode */
    .ne-hotspot-hero[data-flow-mode="1"].ne-hotspot-hero--bg-scrim::before { display: none; }
    .ne-hotspot-hero[data-flow-mode="1"] .ne-hotspot-hero__inner {
      max-width: 100%;
      width: 100%;
    }
  }
/* END_BLOCK:hotspot-hero */

/* START_BLOCK:hotspot (INDEX:102) */
/* Position via calc() instead of transform to avoid creating a
     containing block for fixed-positioned descendants (Rule 146).
     The mobile bottom-sheet card uses position:fixed and must anchor
     to viewport, not to a transformed ancestor. */
  .ne-hotspot {
    position: absolute;
    left: calc(var(--ne-hotspot-x, 50%) - var(--ne-hotspot-dot-size, 32px) / 2);
    top: calc(var(--ne-hotspot-y, 50%) - var(--ne-hotspot-dot-size, 32px) / 2);
    width: var(--ne-hotspot-dot-size, 32px);
    height: var(--ne-hotspot-dot-size, 32px);
    z-index: 4;
    pointer-events: auto;
  }
  /* Lift the open hotspot above its siblings. Every .ne-hotspot is its own
     stacking context at z-index 4, so without this the open card competes
     at the same level as neighbouring hotspots and later-DOM dots/cards
     paint over it — stealing hover + click from the card's controls
     (Choose options, swatches). Only one card is open at a time. */
  .ne-hotspot[data-hotspot-active] { z-index: 20; }

  /* ── Dot base ──────────────────────────────────────────────── */
  .ne-hotspot__dot {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--ne-hotspot-dot-size, 32px);
    height: var(--ne-hotspot-dot-size, 32px);
    padding: 0;
    border: 2px solid rgba(255, 255, 255, 0.85);
    background: var(--ne-hotspot-dot-color, var(--ne-scheme-accent, #6366f1));
    color: #fff;
    font-size: calc(var(--ne-hotspot-dot-size, 32px) * 0.42);
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    box-shadow:
      0 2px 12px rgba(0, 0, 0, 0.4),
      0 0 0 4px rgba(255, 255, 255, 0.15);
    transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
    z-index: 1;
  }
  .ne-hotspot__dot:hover,
  .ne-hotspot__dot:focus-visible {
    transform: scale(1.15);
    box-shadow:
      0 4px 20px rgba(0, 0, 0, 0.5),
      0 0 0 6px rgba(255, 255, 255, 0.25),
      0 0 24px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5));
    outline: none;
  }
  .ne-hotspot__dot-core {
    position: relative;
    z-index: 2;
    pointer-events: none;
  }

  /* ── Shape modifiers ───────────────────────────────────────── */
  /* Rule 144: critical.css `button:not(.ne-btn):not(...)` reset has
     (0,6,1)+!important. Our descendant rules need !important to win. */
  .ne-hotspot--shape-circle .ne-hotspot__dot { border-radius: 50% !important; }
  .ne-hotspot--shape-square .ne-hotspot__dot { border-radius: 0 !important; }
  .ne-hotspot--shape-rounded-square .ne-hotspot__dot { border-radius: 25% !important; }

  /* ── Pulse animation ───────────────────────────────────────── */
  .ne-hotspot__dot-pulse {
    position: absolute;
    inset: -4px;
    border-radius: inherit;
    background: var(--ne-hotspot-dot-color, var(--ne-scheme-accent, #6366f1));
    opacity: 0.45;
    z-index: 0;
    animation: ne-hotspot-pulse 2s ease-out infinite;
    pointer-events: none;
  }
  /* Pulse matches dot shape — explicit !important so we don't depend
     on `inherit` cascading from a critical.css-overridden parent. */
  .ne-hotspot--shape-circle .ne-hotspot__dot-pulse { border-radius: 50% !important; }
  .ne-hotspot--shape-square .ne-hotspot__dot-pulse { border-radius: 0 !important; }
  .ne-hotspot--shape-rounded-square .ne-hotspot__dot-pulse { border-radius: 25% !important; }

  @keyframes ne-hotspot-pulse {
    0% { transform: scale(1); opacity: 0.5; }
    70% { transform: scale(1.8); opacity: 0; }
    100% { transform: scale(1.8); opacity: 0; }
  }

  /* ── Color modifiers ───────────────────────────────────────── */
  .ne-hotspot--color-accent .ne-hotspot__dot,
  .ne-hotspot--color-accent .ne-hotspot__dot-pulse {
    background: var(--ne-scheme-accent, #6366f1);
  }
  .ne-hotspot--color-bg .ne-hotspot__dot,
  .ne-hotspot--color-bg .ne-hotspot__dot-pulse {
    background: var(--ne-scheme-bg, #fff);
  }
  .ne-hotspot--color-bg .ne-hotspot__dot {
    color: var(--ne-scheme-text, #000);
    border-color: var(--ne-scheme-text, rgba(0, 0, 0, 0.85));
  }

  /* ── Glass toggle: uses theme glass vars from theme settings ─── */
  .ne-hotspot--dot-glass .ne-hotspot__dot {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.18));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.5));
    box-shadow:
      0 4px 16px rgba(0, 0, 0, 0.35),
      inset 0 0 0 1px rgba(255, 255, 255, 0.2),
      inset 0 1px 0 rgba(255, 255, 255, 0.4);
  }
  /* Inner accent core when glass + non-numbered */
  .ne-hotspot--dot-glass:not(.ne-hotspot--dot-numbered) .ne-hotspot__dot::before {
    content: "";
    position: absolute;
    inset: 30%;
    border-radius: inherit;
    background: var(--ne-hotspot-dot-color, var(--ne-scheme-accent, #6366f1));
    box-shadow: 0 0 8px var(--ne-hotspot-dot-color, var(--ne-scheme-accent, #6366f1));
    z-index: 1;
  }
  .ne-hotspot--shape-circle.ne-hotspot--dot-glass:not(.ne-hotspot--dot-numbered) .ne-hotspot__dot::before { border-radius: 50% !important; }
  .ne-hotspot--shape-square.ne-hotspot--dot-glass:not(.ne-hotspot--dot-numbered) .ne-hotspot__dot::before { border-radius: 0 !important; }
  .ne-hotspot--shape-rounded-square.ne-hotspot--dot-glass:not(.ne-hotspot--dot-numbered) .ne-hotspot__dot::before { border-radius: 25% !important; }

  /* Glass + pulse: pulse becomes more subtle */
  .ne-hotspot--dot-glass .ne-hotspot__dot-pulse {
    background: rgba(255, 255, 255, 0.5);
  }

  /* Active state */
  [data-hotspot-active] .ne-hotspot__dot {
    transform: scale(1.2);
    box-shadow:
      0 4px 20px rgba(0, 0, 0, 0.5),
      0 0 0 6px rgba(255, 255, 255, 0.3),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.6));
  }
  [data-hotspot-active] .ne-hotspot__dot-pulse { animation: none; opacity: 0; }

  /* ── Card ──────────────────────────────────────────────────────
     Architecture (Rule 215+):
     • Card transform = base (position-anchor) + effect-offset (animation).
       Position rules set tx-base/ty-base, effect rules set tx-offset/
       ty-offset/scale. Independent vars → no source-order conflicts.
     • CSS Custom Properties don't transition by default (discrete-step).
       For transform animation we use @property registration on the
       offset vars so they interpolate as <length> / <number>.
     • Glass uses data-glass attr on card itself (works regardless of
       body-mount on mobile, where wrapper-class ancestors are absent).
     • Effect controls what animates: fade=opacity, slide=transform,
       scale=transform, none=instant. Glass+fade uses keyframe to
       force per-frame backdrop-filter compute.
  */
  @property --ne-h-tx-offset {
    syntax: "<length>";
    inherits: false;
    initial-value: 0px;
  }
  @property --ne-h-ty-offset {
    syntax: "<length>";
    inherits: false;
    initial-value: 0px;
  }
  @property --ne-h-scale {
    syntax: "<number>";
    inherits: false;
    initial-value: 1;
  }

  .ne-hotspot__card {
    /* Position-anchor base (per position-modifier override below) */
    --ne-h-tx-base: -50%;
    --ne-h-ty-base: 0px;
    /* Effect offsets — closed-state values (open-state below resets) */
    --ne-h-tx-offset: 0px;
    --ne-h-ty-offset: 0px;
    --ne-h-scale: 1;

    position: absolute;
    left: 50%;
    top: 100%;
    margin-top: 16px;
    transform: translate(
      calc(var(--ne-h-tx-base) + var(--ne-h-tx-offset)),
      calc(var(--ne-h-ty-base) + var(--ne-h-ty-offset))
    ) scale(var(--ne-h-scale));
    width: 280px;
    max-width: calc(100vw - 32px);
    background: var(--ne-scheme-bg, #fff);
    color: var(--ne-scheme-text, #000);
    border-radius: var(--ne-radius-lg, 16px);
    overflow: hidden;
    opacity: 1;
    visibility: hidden;
    pointer-events: none;
    /* Transition CSS-var properties directly (works because @property
       registered them with concrete syntax types). transform inherits
       from var changes, but transitioning the vars themselves is more
       reliable than transitioning the calc-derived transform.
       Close timings (this rule applies during close): use shorter
       duration so card disappears quickly after transform settles. */
    transition:
      --ne-h-tx-offset 0.18s cubic-bezier(0.4, 0, 1, 0.6),
      --ne-h-ty-offset 0.18s cubic-bezier(0.4, 0, 1, 0.6),
      --ne-h-scale 0.18s cubic-bezier(0.4, 0, 1, 0.6),
      transform 0.18s cubic-bezier(0.4, 0, 1, 0.6),
      opacity 0.18s ease,
      visibility 0s linear 0.18s;
    z-index: 5;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25);
  }
  .ne-hotspot__card[aria-hidden="false"] {
    visibility: visible;
    pointer-events: auto;
    transition:
      --ne-h-tx-offset 0.3s cubic-bezier(0.22, 1, 0.36, 1),
      --ne-h-ty-offset 0.3s cubic-bezier(0.22, 1, 0.36, 1),
      --ne-h-scale 0.3s cubic-bezier(0.22, 1, 0.36, 1),
      transform 0.3s cubic-bezier(0.22, 1, 0.36, 1),
      opacity 0.3s ease,
      visibility 0s linear 0s;
    /* Effect offsets and opacity are NOT touched here. They fall back
       to base values (offsets=0, opacity=1) because the closed-state
       effect rules below are scoped to [aria-hidden="true"] and stop
       matching once the card opens. Transition fires on the value
       change (24px→0, 0.85→1, 0→1) and animates smoothly. */
  }

  /* ── Position modifiers (set base translate vars) ─────────────── */
  /* card-top: same horizontal centering as default base, only top/bottom
     swap. tx/ty base values stay at default. */
  .ne-hotspot--card-top .ne-hotspot__card {
    top: auto; bottom: 100%; margin-top: 0; margin-bottom: 16px;
  }
  .ne-hotspot--card-left .ne-hotspot__card {
    top: 50%; left: auto; right: 100%; margin-top: 0; margin-right: 16px;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: -50%;
  }
  .ne-hotspot--card-right .ne-hotspot__card {
    top: 50%; left: 100%; margin-top: 0; margin-left: 16px;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: -50%;
  }

  /* ── Corner positions: explicit edge anchoring, no centering ─── */
  .ne-hotspot--card-top-left .ne-hotspot__card {
    top: auto; bottom: 100%; left: auto; right: 100%;
    margin: 0 12px 12px 0;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: 0px;
  }
  .ne-hotspot--card-top-right .ne-hotspot__card {
    top: auto; bottom: 100%; left: 100%; right: auto;
    margin: 0 0 12px 12px;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: 0px;
  }
  .ne-hotspot--card-bottom-left .ne-hotspot__card {
    top: 100%; bottom: auto; left: auto; right: 100%;
    margin: 12px 12px 0 0;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: 0px;
  }
  .ne-hotspot--card-bottom-right .ne-hotspot__card {
    top: 100%; bottom: auto; left: 100%; right: auto;
    margin: 12px 0 0 12px;
    --ne-h-tx-base: 0px;
    --ne-h-ty-base: 0px;
  }

  /* ── Anchor modifiers: align card edge to dot ─────────────────── */
  .ne-hotspot--card-left.ne-hotspot--anchor-top .ne-hotspot__card,
  .ne-hotspot--card-right.ne-hotspot--anchor-top .ne-hotspot__card {
    top: 0;
    --ne-h-ty-base: 0px;
  }
  .ne-hotspot--card-left.ne-hotspot--anchor-bottom .ne-hotspot__card,
  .ne-hotspot--card-right.ne-hotspot--anchor-bottom .ne-hotspot__card {
    top: auto; bottom: 0;
    --ne-h-ty-base: 0px;
  }
  .ne-hotspot--card-top.ne-hotspot--anchor-start .ne-hotspot__card,
  .ne-hotspot--card-bottom.ne-hotspot--anchor-start .ne-hotspot__card {
    left: 0;
    --ne-h-tx-base: 0px;
  }
  .ne-hotspot--card-top.ne-hotspot--anchor-end .ne-hotspot__card,
  .ne-hotspot--card-bottom.ne-hotspot--anchor-end .ne-hotspot__card {
    left: auto; right: 0;
    --ne-h-tx-base: 0px;
  }

  /* ── Hover-bridge: invisible hit-area between dot and card ────
     Pseudo-element on card extends past its edge into the gap so
     cursor traveling from dot to card stays within hover zone.
     40px bridge ensures even fast/diagonal cursor moves stay locked. */
  .ne-hotspot__card::after {
    content: "";
    position: absolute;
    pointer-events: none;
    /* Sit BEHIND the card content. Corner card-positions stretch the bridge
       across the whole card (top/bottom:-32px) which, with pointer-events:auto
       when open, otherwise covers the card's own controls (Choose options) and
       swallows every click — only edge positions (left/right) escaped. In the
       real dot↔card gap there is no card content above it, so it still wins the
       hit-test there and keeps the hover-zone (verified headless 2026-06-02). */
    z-index: -1;
    /* Bridge 64px + 32px overflow on each end for diagonal cursor moves */
    top: -64px;
    left: -32px;
    right: -32px;
    height: 68px;
  }
  .ne-hotspot__card[aria-hidden="false"]::after { pointer-events: auto; }

  .ne-hotspot--card-top .ne-hotspot__card::after {
    top: auto; bottom: -64px; left: -32px; right: -32px; height: 68px; width: auto;
  }
  .ne-hotspot--card-left .ne-hotspot__card::after {
    top: -32px; bottom: -32px; left: auto; right: -64px; width: 68px; height: auto;
  }
  .ne-hotspot--card-right .ne-hotspot__card::after {
    top: -32px; bottom: -32px; left: -64px; right: auto; width: 68px; height: auto;
  }
  /* Bridge for corner-positioned cards: diagonal hover-zone */
  .ne-hotspot--card-top-left .ne-hotspot__card::after,
  .ne-hotspot--card-top-right .ne-hotspot__card::after,
  .ne-hotspot--card-bottom-left .ne-hotspot__card::after,
  .ne-hotspot--card-bottom-right .ne-hotspot__card::after {
    top: -32px; left: -32px; right: -32px; bottom: -32px;
    width: auto; height: auto;
  }

  /* ── Open-effect modifiers (data-effect on card) ─────────────────
     Effect rules are scoped to [aria-hidden="true"] (closed state) only.
     Once card opens (aria-hidden="false"), these rules stop matching →
     offsets/opacity revert to base values (offsets=0, opacity=1) →
     transition fires on the value change → smooth animation.

     This avoids the source-order tie between effect rules and the
     [aria-hidden="false"] open-state rule (both 0,2,0 specificity).
     Position modifiers use a different CSS-var (--ne-h-*-base) so they
     never collide with effect offsets. */

  /* All non-instant effects: card opacity fades on close so close-button
     and shadow vanish in lockstep with the transform animation (without
     this, the close button stays opaque while card slides/scales away).
     For slide/scale this is in addition to the transform animation —
     opacity transitions 1→0 simultaneously, hitting 0 right at
     visibility-hidden delay end. */
  .ne-hotspot__card[data-effect="fade"][aria-hidden="true"],
  .ne-hotspot__card[data-effect="slide"][aria-hidden="true"],
  .ne-hotspot__card[data-effect="scale"][aria-hidden="true"] {
    opacity: 0;
  }

  /* slide — directional 24px offset closed; reverts to 0 on open */
  .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] { --ne-h-ty-offset: 24px; }
  .ne-hotspot--card-top .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] { --ne-h-ty-offset: -24px; }
  .ne-hotspot--card-left .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: -24px;
    --ne-h-ty-offset: 0px;
  }
  .ne-hotspot--card-right .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: 24px;
    --ne-h-ty-offset: 0px;
  }
  .ne-hotspot--card-top-left .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: -16px; --ne-h-ty-offset: -16px;
  }
  .ne-hotspot--card-top-right .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: 16px; --ne-h-ty-offset: -16px;
  }
  .ne-hotspot--card-bottom-left .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: -16px; --ne-h-ty-offset: 16px;
  }
  .ne-hotspot--card-bottom-right .ne-hotspot__card[data-effect="slide"][aria-hidden="true"] {
    --ne-h-tx-offset: 16px; --ne-h-ty-offset: 16px;
  }

  /* scale — grow 0.85 → 1.0 (base scale=1 on open) */
  .ne-hotspot__card[data-effect="scale"][aria-hidden="true"] { --ne-h-scale: 0.85; }

  /* none — instant, skip all transitions/animations both states.
     Force visibility to flip in lockstep with aria-hidden so the card
     (incl. close button) disappears at the same frame instead of
     lingering through any inherited transition delay. */
  .ne-hotspot__card[data-effect="none"],
  .ne-hotspot__card[data-effect="none"][aria-hidden="false"],
  .ne-hotspot__card[data-effect="none"][aria-hidden="true"] {
    transition: none !important;
    animation: none !important;
  }
  .ne-hotspot__card[data-effect="none"][aria-hidden="true"] {
    visibility: hidden !important;
    opacity: 0 !important;
  }
  .ne-hotspot__card[data-effect="none"][aria-hidden="false"] {
    visibility: visible !important;
    opacity: 1 !important;
  }

  /* ── Card glass / glow / shadow (data-attrs on card) ─────────────
     data-glass/shadow/glow live directly on the card element so
     selectors work both when card is in DOM (desktop) and body-mounted
     (mobile bottom-sheet, Rule 146 — wrapper-class ancestors lost). */

  /* Glass: backdrop-filter directly on card (no ::before) so opacity
     transitions on the card aren't multiplied with a child's opacity.
     For data-effect="fade" + glass, a keyframe animates opacity AND
     backdrop-filter together — forcing per-frame backdrop compute
     (browsers skip backdrop-filter render during plain opacity
     transitions). For slide/scale the card opacity stays 1, so
     backdrop-filter renders at full strength throughout the animation. */
  .ne-hotspot__card[data-glass="1"] {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.18));
    backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    /* No border: 1px glass-border reads as a bottom contour line on dark
       bg (same fix as blog/product-card glass, user QA 2026-05-25 #25). */
    border: 0;
    color: #fff;
  }
  /* Accent-tinted glass card — opt-in via card_glass_tint. */
  .ne-hotspot__card[data-glass="1"][data-glass-tint="1"] {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.18))) !important;
  }
  .ne-hotspot__card[data-glass="1"] .ne-hotspot__card-body {
    color: #fff;
    text-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
  }
  .ne-hotspot__card[data-glass="1"] .ne-hotspot__card-price-compare {
    color: rgba(255, 255, 255, 0.6);
  }
  /* will-change keeps the backdrop-filter compositing layer alive
     during transform animations so blur renders every frame. */
  .ne-hotspot__card[data-glass="1"][data-effect="slide"],
  .ne-hotspot__card[data-glass="1"][data-effect="scale"] {
    will-change: transform;
  }

  /* fade + glass: keyframe animates opacity 0→1 while backdrop-filter
     stays at FULL strength throughout. Including backdrop-filter in
     the animation (with constant value) forces the browser to keep its
     compositing layer alive for every frame — bypassing the optimization
     that suppresses backdrop-filter compute during plain opacity
     transitions. Result: glass blur is visible from frame 1, fading in
     proportional to opacity rather than appearing only at animation end. */
  .ne-hotspot__card[data-glass="1"][data-effect="fade"][aria-hidden="false"] {
    animation: ne-hotspot-fade-glass-in 0.3s ease forwards;
  }
  @keyframes ne-hotspot-fade-glass-in {
    from {
      opacity: 0;
      backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
      -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    }
    to {
      opacity: 1;
      backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
      -webkit-backdrop-filter: blur(var(--ne-glass-blur-heavy, 28px)) saturate(var(--ne-glass-saturate, 1.6));
    }
  }

  /* Shadow / glow on card directly (data-attrs survive body-mount) */
  .ne-hotspot__card[data-shadow="1"] {
    box-shadow: 0 18px 60px rgba(0, 0, 0, 0.45), 0 4px 12px rgba(0, 0, 0, 0.25);
  }
  .ne-hotspot__card[data-glow="1"] {
    box-shadow:
      0 12px 40px rgba(0, 0, 0, 0.25),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.45));
  }
  .ne-hotspot__card[data-shadow="1"][data-glow="1"] {
    box-shadow:
      0 18px 60px rgba(0, 0, 0, 0.45),
      0 4px 12px rgba(0, 0, 0, 0.25),
      0 0 32px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.45));
  }

  /* Close button always above glass-card content (z-index above body) */
  .ne-hotspot__card[data-glass="1"] .ne-hotspot__close { z-index: 4; }

  .ne-hotspot__close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 28px;
    height: 28px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(0, 0, 0, 0.5);
    border: 0;
    color: #fff;
    cursor: pointer;
    z-index: 3;
    transition: background 0.2s ease;
  }
  .ne-hotspot__close:hover { background: rgba(0, 0, 0, 0.75); }

  /* Close button shape modifiers */
  .ne-hotspot--close-circle .ne-hotspot__close { border-radius: 50%; }
  .ne-hotspot--close-rounded .ne-hotspot__close { border-radius: 6px; }
  .ne-hotspot--close-square .ne-hotspot__close { border-radius: 0; }
  .ne-hotspot--close-none .ne-hotspot__close { display: none; }
  /* Bare: icon only, no background, no border */
  .ne-hotspot--close-bare .ne-hotspot__close {
    background: transparent;
    color: var(--ne-scheme-text, #fff);
    text-shadow: 0 1px 4px rgba(0,0,0,0.4);
    filter: drop-shadow(0 1px 2px rgba(0,0,0,0.5));
  }
  .ne-hotspot--close-bare .ne-hotspot__close:hover {
    background: transparent;
    transform: scale(1.15);
  }
  /* Bare on glass card → light icon (data-glass survives body-mount) */
  .ne-hotspot--close-bare .ne-hotspot__card[data-glass="1"] .ne-hotspot__close { color: #fff; }

  .ne-hotspot__card-media {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    border-radius: var(--ne-radius-lg, 16px) var(--ne-radius-lg, 16px) 0 0;
  }
  .ne-hotspot__card-img,
  .ne-hotspot__card-placeholder {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  .ne-hotspot__card-body {
    padding: 14px 16px 16px;
    display: flex;
    flex-direction: column;
    gap: 8px;
  }
  .ne-hotspot__card-body--custom { padding-top: 36px; }
  /* When link-image media precedes the body, reset to normal padding */
  .ne-hotspot__card-media + .ne-hotspot__card-body--custom { padding-top: 14px; }
  /* When close button is hidden or bare, no need for top clearance */
  .ne-hotspot--close-none .ne-hotspot__card-body--custom,
  .ne-hotspot--close-bare .ne-hotspot__card-body--custom { padding-top: 14px; }

  /* Custom body alignment (heading + text + link) — button always
     sized to content with min-width tap-target, alignment via flex
     align-items so left/center/right anchor consistently. */
  .ne-hotspot__card-body--align-left { text-align: left; align-items: flex-start; }
  .ne-hotspot__card-body--align-center { text-align: center; align-items: center; }
  .ne-hotspot__card-body--align-right { text-align: right; align-items: flex-end; }
  /* All alignments: button auto-width with sensible min so tap target stays comfortable */
  .ne-hotspot__card-body--custom .ne-hotspot__card-atc {
    width: auto;
    min-width: 140px;
  }

  /* Heading font roles */
  .ne-hotspot__card-title--font-heading { font-family: var(--ne-font-heading, inherit); }
  .ne-hotspot__card-title--font-body { font-family: var(--ne-font-body, inherit); }
  .ne-hotspot__card-title--font-subheading { font-family: var(--ne-font-subheading, inherit); }
  .ne-hotspot__card-title--font-accent { font-family: var(--ne-font-accent, inherit); }
  .ne-hotspot__card-body--empty {
    padding: 24px 16px;
    text-align: center;
    color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5));
    font-size: 14px;
  }

  .ne-hotspot__card-title {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.3;
    margin: 0;
    color: inherit;
    text-decoration: none;
  }
  a.ne-hotspot__card-title:hover { color: var(--ne-scheme-accent); }

  .ne-hotspot__card-text {
    font-size: 13px;
    margin: 0;
    color: inherit;
    opacity: 0.85;
    line-height: 1.5;
  }

  .ne-hotspot__card-price { font-size: 14px; font-weight: 500; }
  .ne-hotspot__card-price-sale { color: var(--ne-scheme-accent); font-weight: 600; }
  .ne-hotspot__card-price-compare { color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5)); margin-left: 6px; }

  .ne-hotspot__card-soldout {
    color: var(--ne-scheme-text-muted, rgba(0, 0, 0, 0.5));
    font-size: 13px;
    font-style: italic;
  }

  /* ── On-card colour swatches (card_show_swatches) ─────────────── */
  .ne-hotspot__card-swatches {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--ne-space-1, 4px);
    margin-top: 6px;
  }
  .ne-hotspot__card-body--align-center .ne-hotspot__card-swatches { justify-content: center; }
  .ne-hotspot__card-body--align-right .ne-hotspot__card-swatches { justify-content: flex-end; }
  .ne-hotspot__card-swatch {
    width: 14px;
    height: 14px;
    border-radius: var(--ne-radius-full, 999px);
    /* Soft inset ring (mirrors product-card swatch) — a hard 1px border
       antialiases poorly on a 14px circle. */
    border: 0;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
    flex: 0 0 auto;
  }
  .ne-hotspot__card-swatch--image {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  .ne-hotspot__card-swatch-more { font-size: var(--ne-font-size-xs, 11px); opacity: 0.7; }

  /* Quick-add popover: the shared .ne-quick-add-popover anchors itself
     absolutely to the product-card's .ne-product-card__atc-wrap (bottom:100%
     drop-up). The hotspot card has no such wrapper, so the popover collapsed
     to a sliver over the image. Anchor it to the card body and let it flow
     as a normal dropdown directly below the "Choose options" button. */
  .ne-hotspot__card-body { position: relative; }
  .ne-hotspot__card-body .ne-quick-add-popover {
    position: static;
    inset: auto;
    margin-top: var(--ne-space-2, 8px);
  }

  .ne-hotspot__card-atc {
    margin-top: 4px;
    width: 100%;
    text-align: center;
    text-decoration: none;
  }

  /* ── Mobile bottom-sheet ─────────────────────────────────────── */
  @media (max-width: 749px) {
    /* Universal override: all card positions + effects collapse into a
       slide-up bottom-sheet on mobile. The bare `.ne-hotspot__card`
       selector matches whether the card is in DOM (desktop fallback)
       or body-mounted (Rule 146 — wrapper-class ancestors absent).
       !important defeats specific position/effect transforms. */
    .ne-hotspot__card {
      position: fixed !important;
      left: 0 !important;
      right: 0 !important;
      bottom: 0 !important;
      top: auto !important;
      margin: 0 !important;
      width: 100% !important;
      max-width: 100% !important;
      max-height: 85vh !important;
      overflow-y: auto !important;
      -webkit-overflow-scrolling: touch;
      border-radius: var(--ne-radius-lg, 16px) var(--ne-radius-lg, 16px) 0 0 !important;
      transform: translateY(100%) !important;
      z-index: 9998;
      /* Mobile sheet uses translate-only motion. Disable fade-effect's
         opacity animation + glass keyframe so blur stays full strength
         throughout the slide-up (no "transparent during animation"). */
      opacity: 1 !important;
      animation: none !important;
    }
    .ne-hotspot__card[aria-hidden="false"] {
      transform: translateY(0) !important;
    }

    .ne-hotspot__card-media {
      /* Drop fixed aspect-ratio on mobile — let image keep natural
         proportions so portraits don't get top/bottom-cropped. */
      aspect-ratio: auto;
      max-height: 50vh;
      border-radius: var(--ne-radius-lg, 16px) var(--ne-radius-lg, 16px) 0 0;
    }
    .ne-hotspot__card-img { max-height: 50vh; object-fit: contain; }
    .ne-hotspot__close { top: 12px; right: 12px; width: 36px; height: 36px; }
    /* iOS safe-area: respect notch/home-indicator */
    .ne-hotspot__card { padding-bottom: env(safe-area-inset-bottom, 0) !important; }

    /* Smaller dots on mobile — clamped between 20-32px regardless of user setting */
    .ne-hotspot__dot {
      width: clamp(20px, calc(var(--ne-hotspot-dot-size, 32px) * 0.75), 32px) !important;
      height: clamp(20px, calc(var(--ne-hotspot-dot-size, 32px) * 0.75), 32px) !important;
    }

    body.ne-hotspot-sheet-open::before {
      content: "";
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.55);
      z-index: 9997;
      pointer-events: none;
    }
  }

  /* ── Slide-in side panel (card_display_mode: slide-in) ──────────────
     Desktop: the card body-mounts and becomes a full-height side drawer
     (quick-view look, merchant-selectable left/right). Mobile keeps the
     bottom-sheet above — its bare-selector !important rules win there.
     Marker lives on the card element (data-display-mode) so it survives
     the body-mount, where wrapper-class ancestors are gone (Rule 146). */
  @media (min-width: 750px) {
    /* Floating side panel — NOT full height (vertically centred, content-sized
       up to a cap, scrolls inside). Sits a little in from the edge, all corners
       rounded → clearly distinct from the dot-anchored popover card. */
    .ne-hotspot__card[data-display-mode="slide-in"] {
      position: fixed !important;
      top: 50% !important;
      bottom: auto !important;
      /* Height follows the content — no internal scroll. overflow:hidden only
         clips the rounded corners (nothing is cut, since height is auto). */
      height: auto !important;
      max-height: none !important;
      margin: 0 !important;
      width: var(--ne-hotspot-slide-width, 460px) !important;
      max-width: 92vw !important;
      overflow: hidden !important;
      border-radius: var(--ne-radius-lg, 16px) !important;
      z-index: 9998;
      opacity: 1 !important;
      animation: none !important;
      transition: transform 0.38s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-side="right"] {
      right: 24px !important;
      left: auto !important;
      transform: translate(calc(100% + 32px), -50%) !important;
    }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-side="left"] {
      left: 24px !important;
      right: auto !important;
      transform: translate(calc(-100% - 32px), -50%) !important;
    }
    .ne-hotspot__card[data-display-mode="slide-in"][aria-hidden="false"] {
      transform: translate(0, -50%) !important;
    }
    /* Hero image — merchant-selectable aspect ratio (slide_media_ratio).
       Fixed ratios define the media-box height and the image fills it (cover);
       "natural" keeps the intrinsic ratio with a height cap. */
    .ne-hotspot__card[data-display-mode="slide-in"] .ne-hotspot__card-media {
      display: block;
      width: 100%;
      overflow: hidden;
    }
    .ne-hotspot__card[data-display-mode="slide-in"] .ne-hotspot__card-img {
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="square"] .ne-hotspot__card-media { aspect-ratio: 1 / 1; }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="portrait"] .ne-hotspot__card-media { aspect-ratio: 3 / 4; }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="landscape"] .ne-hotspot__card-media { aspect-ratio: 4 / 3; }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="wide"] .ne-hotspot__card-media { aspect-ratio: 16 / 9; }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="natural"] .ne-hotspot__card-media { aspect-ratio: auto; max-height: 280px; }
    .ne-hotspot__card[data-display-mode="slide-in"][data-slide-ratio="natural"] .ne-hotspot__card-img { height: auto; max-height: 280px; }

    /* Thumbnail gallery (slide-in only — rendered in Liquid only here). */
    .ne-hotspot__card-gallery {
      display: flex;
      gap: 8px;
      /* Vertical padding: overflow-x:auto forces overflow-y to compute to `auto`,
         which would clip the active thumb's accent border / focus ring at the
         edge. Padding gives it room inside the padding box. */
      padding: 10px 14px 5px;
      overflow-x: auto;
      -webkit-overflow-scrolling: touch;
    }
    .ne-hotspot__card-thumb {
      flex: 0 0 auto;
      width: 56px;
      height: 56px;
      padding: 0;
      border: 2px solid transparent;
      border-radius: var(--ne-radius-md, 10px);
      overflow: hidden;
      cursor: pointer;
      background: none;
      opacity: 0.7;
      transition: opacity 0.15s, border-color 0.15s;
    }
    .ne-hotspot__card-thumb.is-active,
    .ne-hotspot__card-thumb:hover {
      opacity: 1;
      border-color: var(--ne-scheme-accent, currentColor);
    }
    .ne-hotspot__card-thumb img {
      width: 100%;
      height: 100%;
      object-fit: cover;
      display: block;
    }

    /* Description excerpt (slide-in only). Line count is merchant-controlled
       via --ne-hs-desc-lines (set inline from card_desc_lines). */
    .ne-hotspot__card-desc {
      margin-top: 8px;
      font-size: 0.86rem;
      line-height: 1.5;
      color: var(--ne-scheme-text-muted, currentColor);
      opacity: 0.85;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: var(--ne-hs-desc-lines, 4);
      line-clamp: var(--ne-hs-desc-lines, 4);
      overflow: hidden;
    }

    /* Options expanded by default: hide the toggle, render the popover inline
       as a padded, subtly-framed panel (distinct from the popover card). */
    .ne-hotspot__card[data-display-mode="slide-in"] [data-quick-add-toggle] {
      display: none !important;
    }
    /* Expanded (always-open) options panel — scoped to the OPEN card only
       ([aria-hidden="false"]); the unscoped `visibility/display !important` used to
       leak the panel through a CLOSED slide-in card (which is only visibility:hidden,
       overridable by a descendant's explicit visible), showing the variant box while
       the hotspot was shut. */
    .ne-hotspot__card[data-display-mode="slide-in"][aria-hidden="false"] .ne-quick-add-popover {
      display: flex !important;
      flex-direction: column;
      gap: 16px;
      position: static !important;
      visibility: visible !important;
      opacity: 1 !important;
      transform: none !important;
      box-shadow: none !important;
      background: color-mix(in srgb, var(--ne-scheme-text, #000) 4%, transparent) !important;
      border: 1px solid color-mix(in srgb, var(--ne-scheme-text, #000) 12%, transparent) !important;
      border-radius: var(--ne-radius-md, 10px) !important;
      padding: 16px !important;
      margin-top: 12px !important;
      min-width: 0 !important;
    }
    /* A touch more air between the option label and its swatch row too. */
    .ne-hotspot__card[data-display-mode="slide-in"] .ne-quick-add-popover__label {
      margin-bottom: 8px;
    }

    /* Dimming overlay while a slide-in panel is open (desktop-only — on
       desktop the body class is set exclusively for slide-in mode). */
    body.ne-hotspot-sheet-open::before {
      content: "";
      position: fixed;
      inset: 0;
      background: rgba(0, 0, 0, 0.5);
      z-index: 9997;
      pointer-events: none;
    }
  }
/* END_BLOCK:hotspot */

/* START_BLOCK:icon (INDEX:103) */
.ne-block-icon {
    display: flex;
    width: 100%;
    line-height: 0;
  }
  .ne-block-icon--align-left   { justify-content: flex-start; }
  .ne-block-icon--align-center { justify-content: center; }
  .ne-block-icon--align-right  { justify-content: flex-end; }

  /* Custom color scheme should NOT paint bg on the outer row */
  .ne-block-icon[class*="color-scheme-"] { background: transparent; color: var(--ne-scheme-accent); }

  .ne-block-icon__frame {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--ne-block-icon-size, 48px);
    height: var(--ne-block-icon-size, 48px);
    box-sizing: content-box;
    color: inherit;
  }
  .ne-block-icon__frame > .ne-icon,
  .ne-block-icon__frame > .ne-block-icon__mask,
  .ne-block-icon__frame > .ne-block-icon__img {
    display: block;
    width: var(--ne-block-icon-size, 48px);
    height: var(--ne-block-icon-size, 48px);
  }
  .ne-block-icon__frame > .ne-block-icon__img {
    object-fit: contain;
  }

  /* Mask-based custom icon — inherits color via currentColor */
  .ne-block-icon__mask {
    background-color: currentColor;
    -webkit-mask-size: contain;
            mask-size: contain;
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
  }

  /* Background shapes — applied to the frame (guaranteed square) */
  .ne-block-icon--bg-circle .ne-block-icon__frame,
  .ne-block-icon--bg-square .ne-block-icon__frame,
  .ne-block-icon--bg-glass .ne-block-icon__frame {
    padding: var(--ne-block-icon-pad, 12px);
    background: var(--ne-scheme-card-bg, rgba(255,255,255,0.06));
  }
  .ne-block-icon--bg-circle .ne-block-icon__frame { border-radius: 999px; }
  .ne-block-icon--bg-square .ne-block-icon__frame { border-radius: var(--ne-radius-md, 8px); }
  .ne-block-icon--bg-glass .ne-block-icon__frame {
    border-radius: var(--ne-radius-md, 8px);
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, var(--ne-scheme-border, rgba(255,255,255,0.1)));
  }
/* END_BLOCK:icon */

/* START_BLOCK:image (INDEX:105) */
/* Alignment via CSS var — parent containers (group, etc.) can set
     --ne-iw-align as a default; the block's own modifier class sets it
     at element-scope so explicit per-block alignment wins. */
  .ne-block-image-wrap {
    display: flex;
    width: 100%;
    flex-direction: column;
    align-items: var(--ne-iw-align, center);
  }
  .ne-block-image-wrap--left   { --ne-iw-align: flex-start; }
  .ne-block-image-wrap--center { --ne-iw-align: center; }
  .ne-block-image-wrap--right  { --ne-iw-align: flex-end; }

  /* Vertical position inside the parent's full height. Stretches the wrap to
     the parent height (needs a definite-height parent, e.g. a bento cell in
     fill mode) and positions the image on the column's main axis. */
  .ne-block-image-wrap--vcenter { height: 100%; justify-content: center; }
  .ne-block-image-wrap--vend    { height: 100%; justify-content: flex-end; }

  /* Fit-to-parent (fill) — image covers the full parent tile as a
     background layer. z-index falls back to -1 (BEHIND sibling blocks) but
     RESPECTS the merchant's "Ebene" (--ne-adv-z) when set, so several fill
     images + a heading can be interleaved at custom z-levels inside one cell
     (user 2026-07-12 — a fixed -1 collapsed every fill layer onto one plane,
     so nothing could sit BETWEEN them). Absolute inset:0 is robust where
     height:100% collapses (indefinite-height grid parent). Needs a positioned
     parent — .ne-bcell__content is position:relative. */
  .ne-block-image-wrap--fill {
    position: absolute;
    inset: 0;
    z-index: var(--ne-adv-z, -1);
    display: block;
    width: auto;
    height: auto;
  }
  .ne-block-image-wrap--fill .ne-block-image {
    width: 100%;
    height: 100%;
    max-width: none;
    aspect-ratio: auto;
    overflow: hidden;
  }
  .ne-block-image-wrap--fill .ne-block-image__img {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Center-overlay — absolutely centered over the parent (e.g. the hero),
     BEHIND sibling content (z-index:-1 = over the hero background, under the
     headline). The wrap fills the parent (inset:0) and flex-centres the image;
     the image keeps its aspect ratio (width = % of the hero, height = auto) so
     a frame of the parent shows on ALL sides (no crop). Rule 453b/454. Needs a
     positioned ancestor — .ne-hero__content-wrap is position:relative. The wrap
     inlines position/z-index (see _wrap_style) so it wins when the block also
     carries .ne-block-adv; the .ne-block-adv transform then fine-tunes on top. */
  .ne-block-image-wrap--overlay {
    position: absolute;
    inset: 0;
    z-index: var(--ne-adv-z, -1);
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .ne-block-image-wrap--overlay .ne-block-image {
    width: var(--ne-ov-w, 80%);
    max-width: none;
    height: auto;
  }
  .ne-block-image-wrap--overlay .ne-block-image__img { width: 100%; height: auto; object-fit: contain; }
  /* Overlay contour (Rule 454): the wrap fills the hero (so --ne-ov-w measures
     against it), but the visible image is only ovw% and centred — so the box
     contour must hug the IMAGE box, not the hero-sized wrap edge. The advanced
     border's --ne-adv-b* vars are set inline on the wrap and inherit down, so
     kill the outline on the wrap and re-emit it on .ne-block-image (which is
     exactly the image box, radius included). */
  .ne-block-image-wrap--overlay.ne-block-adv--bordered { outline-width: 0; }
  .ne-block-image-wrap--overlay.ne-block-adv--bordered .ne-block-image {
    outline-width: var(--ne-adv-bw-d, 0);
    outline-style: var(--ne-adv-bs-d, solid);
    outline-color: var(--ne-adv-bc-d, transparent);
    outline-offset: var(--ne-adv-bo-d, 0px);
  }
  @media (max-width: 749px) {
    .ne-block-image-wrap--overlay.ne-block-adv--bordered.ne-block-adv--mobilecustom .ne-block-image {
      outline-width: var(--ne-adv-bw-m, var(--ne-adv-bw-d, 0));
      outline-style: var(--ne-adv-bs-m, var(--ne-adv-bs-d, solid));
      outline-color: var(--ne-adv-bc-m, var(--ne-adv-bc-d, transparent));
      outline-offset: var(--ne-adv-bo-m, var(--ne-adv-bo-d, 0px));
    }
  }

  .ne-block-image { position: relative; }
  /* Colour overlay / scrim — sits over the <img>, under the caption/shine,
     inset:0 with border-radius:inherit so it follows the image's rounded
     corners in every mode (natural, ratio, fill, overlay-center). background +
     opacity are inlined per style (solid / directional gradient / vignette). */
  .ne-block-image__scrim {
    position: absolute;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    border-radius: inherit;
  }
  .ne-block-image--ratio { width: 100%; overflow: hidden; }
  .ne-img-sil-svg { position: absolute; width: 0; height: 0; overflow: hidden; }
  .ne-block-image__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Image silhouette contour (target=image) — none unless --ne-img-sil set */
    filter: var(--ne-img-sil, none);
    /* Inherit container radius so corners clip in natural mode where the
       container can't use overflow:hidden (would cut drop-shadow filters,
       Rule 105). Ratio mode also inherits — same result either way. */
    border-radius: inherit;
  }
  .ne-block-image--ratio .ne-block-image__img {
    position: absolute;
    inset: 0;
  }
  .ne-block-image__link { display: block; }

  /* Placeholder SVG — neutral mid-grey + faint tint background so the
     icon stays visible on both light and dark color schemes (otherwise
     it inherits scheme-text and disappears against same-tone backgrounds). */
  .ne-block-image__img--placeholder {
    color: #999;
    background: rgba(127, 127, 127, 0.08);
    object-fit: contain;
    padding: 8%;
  }

  /* Natural mode: drop-shadow on img follows alpha channel (silhouette for transparent PNG/SVG,
     identical to box-shadow for solid rectangles). */
  .ne-block-image:not(.ne-block-image--ratio).ne-block-image--shadow .ne-block-image__img {
    filter: drop-shadow(0 calc(10px * var(--ne-fx-shadow-i, 1)) calc(18px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.35));
  }
  .ne-block-image:not(.ne-block-image--ratio).ne-block-image--glow .ne-block-image__img {
    filter: drop-shadow(0 0 calc(10px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.55)))
            drop-shadow(0 0 calc(18px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.45)));
  }
  .ne-block-image:not(.ne-block-image--ratio).ne-block-image--shadow.ne-block-image--glow .ne-block-image__img {
    filter: drop-shadow(0 calc(10px * var(--ne-fx-shadow-i, 1)) calc(18px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.35))
            drop-shadow(0 0 calc(10px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.55)))
            drop-shadow(0 0 calc(18px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.45)));
  }

  /* Ratio mode: container is always rectangular (object-fit: cover) — use box-shadow.
     Coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-block-image--ratio.ne-block-image--shadow { box-shadow: 0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.25); }
  .ne-block-image--ratio.ne-block-image--glow   { box-shadow: 0 0 calc(28px * var(--ne-fx-glow-i, 1)) calc(2px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.5)); }

  /* Glass-shine top highlight — opt-in glossy edge over the image. Image
     blocks have no glass surface (a blurred bg would be hidden under the
     image), so this exposes the VISIBLE part of the shine standalone
     (user 2026-05-24). Uses the global shine gradient when present, else a
     literal fallback so it works without theme glass-shine enabled. */
  /* Surface options. Glass = frosted panel BEHIND the image — for images
     with a transparent background (PNG) the glass + global shine show
     through the transparent areas, so the subject appears to float on glass
     (user 2026-05-24). The --surface-glass class also carries "glass" so the
     global glass-shine paints its full-width top highlight automatically. */
  .ne-block-image--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border-radius: var(--ne-radius-lg, 14px);
    overflow: hidden;
  }
  /* Empty editor placeholder: the SVG is square (object-fit: contain) but the
     glass panel can stretch taller than it, leaving a glass strip below the
     icon that reads as a stray band (user QA 2026-05-25 #1). Pin the panel to
     a square so it hugs the placeholder. Real images (object-fit: cover) fill
     the box and don't need this. */
  .ne-block-image--surface-glass:not(.ne-block-image--ratio):has(.ne-block-image__img--placeholder) {
    aspect-ratio: 1 / 1;
  }
  .ne-block-image--surface-transparent { background: transparent; }

  .ne-block-image--shine { position: relative; }
  /* Full-size overlay (inset:0) + border-radius:inherit so the rounded
     corners are honoured at the element's true size — the shine gradient
     (visible only in the top ~3px) is then clipped to the rounded top
     corners and follows them exactly. A 3px-tall ::after with a large
     corner radius clamps the radius to ~1.5px, making the bar look too
     narrow / overshoot at the corners (user QA 2026-05-25). */
  .ne-block-image--shine::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.20) 1px, rgba(255,255,255,0.06) 2px, transparent 3px));
    pointer-events: none;
    z-index: 3;
    border-radius: inherit;
  }
  .ne-block-image--ratio.ne-block-image--shadow.ne-block-image--glow {
    box-shadow:
      0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.25),
      0 0 calc(28px * var(--ne-fx-glow-i, 1)) calc(2px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.5));
  }

  .ne-block-image__caption {
    margin-top: 0.5rem;
    font-size: 0.875rem;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
/* END_BLOCK:image */

/* START_BLOCK:newsletter-form (INDEX:109) */
/* Wrap is the alignment carrier (Rule 126). Reads --ne-bw-justify
     from the parent unless the block sets its own modifier. */
  .ne-block-newsletter-form {
    display: flex;
    width: 100%;
    justify-content: var(--ne-bw-justify, center);
  }
  .ne-block-newsletter-form--left   { --ne-bw-justify: flex-start; }
  .ne-block-newsletter-form--center { --ne-bw-justify: center; }
  .ne-block-newsletter-form--right  { --ne-bw-justify: flex-end; }
  .ne-block-newsletter-form--full   { --ne-bw-justify: stretch; }

  .ne-block-newsletter-form__form {
    width: 100%;
    max-width: var(--ne-nf-max-w, 480px);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2, 0.5rem);
  }
  .ne-block-newsletter-form--full .ne-block-newsletter-form__form { max-width: none; }

  .ne-block-newsletter-form__field {
    display: flex;
    gap: var(--ne-space-2, 0.5rem);
    width: 100%;
  }
  .ne-block-newsletter-form__form--stacked .ne-block-newsletter-form__field {
    flex-direction: column;
  }

  .ne-block-newsletter-form__input {
    flex: 1;
    min-width: 0;
    padding: 0.75rem 1rem;
    background: var(--ne-scheme-input-bg, rgba(255,255,255,0.08));
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border, rgba(255,255,255,0.2)));
    border-radius: var(--ne-radius-input, 8px);
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    font: inherit;
    transition: border-color .15s ease, box-shadow .15s ease, background-color .15s ease;
  }
  .ne-block-newsletter-form__input::placeholder {
    color: var(--ne-scheme-input-placeholder, var(--ne-scheme-text));
    opacity: 0.55;
  }
  .ne-block-newsletter-form__input:focus-visible {
    outline: none;
    border-color: var(--ne-scheme-accent);
    box-shadow: var(--ne-input-focus-shadow, none);
  }
  .ne-block-newsletter-form__input[aria-invalid="true"] {
    border-color: var(--ne-color-error, #ef4444);
  }

  .ne-block-newsletter-form__submit {
    flex-shrink: 0;
    white-space: nowrap;
  }
  .ne-block-newsletter-form__form--stacked .ne-block-newsletter-form__submit {
    width: 100%;
  }

  .ne-block-newsletter-form__success,
  .ne-block-newsletter-form__error {
    margin: 0;
    font-size: var(--ne-font-size-sm, 0.875rem);
    line-height: 1.4;
  }
  .ne-block-newsletter-form__success { color: var(--ne-color-success, #10b981); }
  .ne-block-newsletter-form__error   { color: var(--ne-color-error, #ef4444); }

  .ne-visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }

  /* Merged inline mode — input + submit render as ONE conjoined pill.
     gap: 0 collapses the join seam; the input gets only the left half of
     the radius, the submit only the right half. The outer radii pick up
     the theme's --ne-radius-input (Theme-Settings → Buttons & Inputs →
     Input shape) so pill mode (999px) gives a true capsule. Border of
     the input on the right edge is removed to avoid a double-stroke at
     the seam. User request 2026-05-23. */
  .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__field {
    gap: 0;
    position: relative;
  }
  .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__input {
    border-top-right-radius: 0 !important;
    border-bottom-right-radius: 0 !important;
    border-right-width: 0 !important;
  }
  .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__submit {
    border-top-left-radius: 0 !important;
    border-bottom-left-radius: 0 !important;
  }

  /* Glass-button toggle also tints the merged-mode input so input + button
     read as one glass capsule (user request 2026-05-23). */
  body[data-ne-glass-buttons="1"] .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__input {
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(var(--ne-glass-saturate, 1.4));
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.02) 100%);
    background-blend-mode: overlay;
    border-color: rgba(255, 255, 255, 0.28) !important;
    box-shadow: var(--ne-glass-btn-bevel, inset 0 1px 0 rgba(255, 255, 255, 0.35));
  }

  /* Focus-glow PER CHILD via clip-path-masked box-shadow. Each child
     (input + submit) carries its own box-shadow halo that follows its
     OWN border-radius perfectly — no parent-field halo, no asymmetric-
     radius approximation needed. clip-path then clips the shadow on
     the SEAM side so the two halos don't overlap into each other's
     interior:
       • Input clip: inset(-4px 0 -4px -4px)  →  shadow visible
         left/top/bottom, hidden on right (at the seam)
       • Submit clip: inset(-4px -4px -4px 0) →  shadow visible
         right/top/bottom, hidden on left (at the seam)
     The two clipped halos meet flush at the seam with no doubled glow
     line. Each follows its child's actual border-radius (input radius
     left, button radius right) — guaranteed alignment regardless of
     shape combo (pill+pill, rounded+pill, etc.). User report 2026-05-23
     after several halo iterations: this is the only approach that
     hugs the visible silhouette regardless of mixed shapes. */
  .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__field:has(.ne-block-newsletter-form__input:focus-visible) .ne-block-newsletter-form__input {
    box-shadow: var(--ne-input-focus-shadow, none);
    clip-path: inset(-4px 0 -4px -4px);
    outline: none;
  }
  .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__field:has(.ne-block-newsletter-form__input:focus-visible) .ne-block-newsletter-form__submit {
    box-shadow: var(--ne-input-focus-shadow, none);
    clip-path: inset(-4px -4px -4px 0);
  }

  /* Mobile: stack regardless of desktop setting. Keeps input + button
     comfortably tappable on small screens. Merged-mode falls back to
     normal stacked layout — a stacked pill seam would look wrong. */
  @media (max-width: 599px) {
    .ne-block-newsletter-form__field { flex-direction: column; gap: var(--ne-space-2, 0.5rem) !important; }
    .ne-block-newsletter-form__form--inline .ne-block-newsletter-form__submit { width: 100%; }
    .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__input,
    .ne-block-newsletter-form__form--merged .ne-block-newsletter-form__submit {
      border-radius: var(--ne-radius-input, 8px) !important;
      border-right-width: 1px !important;
    }
  }
/* END_BLOCK:newsletter-form */

/* START_BLOCK:product-buy-buttons (INDEX:110) */
.ne-product-buy {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }
  /* Low-stock counter */
  .ne-product-stock {
    display: flex;
    align-items: center;
    gap: 0.5em;
    margin: 0;
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
    color: var(--ne-scheme-text);
  }
  .ne-product-stock--hidden { display: none; }
  .ne-product-stock__dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--ne-scheme-sale, #e0443e);
    box-shadow: 0 0 0 0 var(--ne-scheme-sale, #e0443e);
    animation: ne-stock-pulse 2s ease-out infinite;
  }
  @keyframes ne-stock-pulse {
    0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--ne-scheme-sale, #e0443e) 55%, transparent); }
    70% { box-shadow: 0 0 0 7px transparent; }
    100% { box-shadow: 0 0 0 0 transparent; }
  }
  @media (prefers-reduced-motion: reduce) {
    .ne-product-stock__dot { animation: none; }
  }
  .ne-product-buy form {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }

  /* Stacked mode (default): qty + ATC + dynamic each on their own row.
     The qty group is content-width (~120px) so the spinner doesn't
     stretch across the column. align-self stops the parent's default
     flex stretching. */
  .ne-product-buy--qty-stacked .ne-product-buy__qty       { align-self: flex-start; }
  .ne-product-buy--qty-stacked.ne-product-buy--full-width .ne-product-buy__qty { align-self: stretch; }

  /* Inline mode: qty + ATC side-by-side via grid. max-content keeps
     the qty cell tight to its intrinsic width; ATC fills the rest.
     Express checkout is placed in COL 2 only (not full row) so it
     shares the exact same cell width as ATC — when buttons_width is
     less than 100%, both buttons end up the same absolute pixel
     width because their parent grid cell is identical. */
  .ne-product-buy--qty-inline form {
    display: grid;
    grid-template-columns: max-content 1fr;
    gap: var(--ne-space-3);
    align-items: stretch;
  }
  .ne-product-buy--qty-inline .ne-product-buy__qty          { grid-row: 1; grid-column: 1; }
  .ne-product-buy--qty-inline .ne-product-buy__qty-label    { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0; }
  .ne-product-buy--qty-inline .ne-product-buy__qty-wrap     { height: 100%; }
  .ne-product-buy--qty-inline .ne-product-buy__atc          { grid-row: 1; grid-column: 2; }
  .ne-product-buy--qty-inline .ne-product-buy__recipient    { grid-row: 1; grid-column: 1 / -1; }
  .ne-product-buy--qty-inline .ne-product-buy__dyn-checkout { grid-column: 1 / -1; }
  .ne-product-buy--qty-inline .ne-product-buy__installments { grid-column: 1 / -1; }
  /* When a gift-card recipient form is present it occupies row 1; push qty/ATC to row 2. */
  .ne-product-buy--qty-inline:has(.ne-product-buy__recipient) .ne-product-buy__qty { grid-row: 2; }
  .ne-product-buy--qty-inline:has(.ne-product-buy__recipient) .ne-product-buy__atc { grid-row: 2; }

  /* ── Gift-card recipient form ─────────────────────────────────────────── */
  .ne-product-buy__recipient {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
  }
  .ne-product-buy__recipient-toggle {
    display: flex;
    align-items: center;
    gap: var(--ne-space-2);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text);
    cursor: pointer;
  }
  .ne-product-buy__recipient-toggle input { accent-color: var(--ne-scheme-accent); width: 16px; height: 16px; flex-shrink: 0; }
  .ne-product-buy__recipient-fields {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
    padding: var(--ne-space-3);
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border));
    border-radius: var(--ne-radius-md, 8px);
    background: var(--ne-scheme-input-bg, var(--ne-glass-bg, rgba(255,255,255,0.04)));
  }
  .ne-product-buy__recipient-fields[hidden] { display: none; }
  .ne-product-buy__recipient-field { display: flex; flex-direction: column; gap: var(--ne-space-1); }
  .ne-product-buy__recipient-field label { font-size: var(--ne-font-size-xs); font-weight: 500; color: var(--ne-scheme-fg-muted); }
  .ne-product-buy__recipient-input {
    width: 100%;
    padding: var(--ne-space-2) var(--ne-space-3);
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border));
    border-radius: min(var(--ne-radius-input, 8px), 16px);
    background: var(--ne-scheme-input-bg, transparent);
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    font-size: var(--ne-font-size-sm);
    font-family: inherit;
  }
  .ne-product-buy__recipient-input:focus { outline: none; border-color: var(--ne-scheme-accent); box-shadow: var(--ne-input-focus-shadow, none); }
  /* Single-line fields follow the theme input shape (pill on Hyprism). The
     multi-line message textarea looks wrong as a pill, so force a moderate
     rounded radius — higher specificity + !important beats the global
     `input,textarea,select { border-radius: var(--ne-radius-input) !important }`
     in critical.css. */
  .ne-product-buy__recipient-fields textarea.ne-product-buy__recipient-input {
    resize: vertical;
    border-radius: var(--ne-radius-md, 12px) !important;
  }
  .ne-product-buy__recipient-hint { font-size: var(--ne-font-size-xs); color: var(--ne-scheme-fg-muted); }

  /* ── Shop Pay Installments banner ─────────────────────────────────────── */
  .ne-product-buy__installments:empty { display: none; }
  .ne-product-buy__installments { font-size: var(--ne-font-size-sm); }

  /* ── Per-control glow / shadow (Effects) ──────────────────────────────
     Each control composes: optional glass bevel (when Theme → glass-buttons
     is on, --ne-glass-btn-bevel inherits from body) + optional glow +
     optional drop shadow. Glow = two-layer accent-core + halo matching the
     header button; --*-glow-button switches the core to the control's button
     colour. Vars default to transparent so the box-shadow list stays valid.
     !important so these win over the glass-buttons box-shadow + the native
     .ne-btn--primary:hover glow. */
  .ne-product-buy--qty-glow   { --ne-qty-glow: 0 0 14px var(--ne-qty-glow-c, var(--ne-scheme-accent)), 0 0 28px color-mix(in srgb, var(--ne-qty-glow-c, var(--ne-scheme-accent)) 45%, transparent); }
  .ne-product-buy--qty-shadow { --ne-qty-shadow: 0 8px 24px rgba(0, 0, 0, 0.20); }
  .ne-product-buy--qty-glow-button { --ne-qty-glow-c: var(--ne-scheme-input-bg, var(--ne-scheme-btn-bg, var(--ne-scheme-accent))); }
  .ne-product-buy--qty-glow .ne-product-buy__qty-wrap,
  .ne-product-buy--qty-shadow .ne-product-buy__qty-wrap {
    box-shadow:
      var(--ne-glass-btn-bevel, 0 0 0 transparent),
      var(--ne-qty-glow, 0 0 0 transparent),
      var(--ne-qty-shadow, 0 0 0 transparent) !important;
  }

  .ne-product-buy--atc-glow   { --ne-atc-glow: 0 0 14px var(--ne-atc-glow-c, var(--ne-scheme-accent)), 0 0 28px color-mix(in srgb, var(--ne-atc-glow-c, var(--ne-scheme-accent)) 45%, transparent); }
  .ne-product-buy--atc-shadow { --ne-atc-shadow: 0 8px 24px rgba(0, 0, 0, 0.20); }
  .ne-product-buy--atc-glow-button { --ne-atc-glow-c: var(--ne-scheme-btn-bg, var(--ne-scheme-accent)); }
  .ne-product-buy--atc-glow .ne-product-buy__atc,
  .ne-product-buy--atc-shadow .ne-product-buy__atc,
  .ne-product-buy--atc-glow .ne-product-buy__atc:hover,
  .ne-product-buy--atc-shadow .ne-product-buy__atc:hover {
    box-shadow:
      var(--ne-glass-btn-bevel, 0 0 0 transparent),
      var(--ne-atc-glow, 0 0 0 transparent),
      var(--ne-atc-shadow, 0 0 0 transparent) !important;
  }

  .ne-product-buy--buynow-glow   { --ne-buynow-glow: 0 0 14px var(--ne-buynow-glow-c, var(--ne-scheme-accent)), 0 0 28px color-mix(in srgb, var(--ne-buynow-glow-c, var(--ne-scheme-accent)) 45%, transparent); }
  .ne-product-buy--buynow-shadow { --ne-buynow-shadow: 0 8px 24px rgba(0, 0, 0, 0.20); }
  /* Buy-it-now "button colour" = the wallet button's ACTUAL rendered
     colour, read by JS into --ne-buynow-actual-c (Shopify controls that
     button's colour; it isn't theme-readable in CSS). Falls back to the
     theme button colour until/if JS can't read it. */
  /* Fallback = transparent (not btn-bg) so there's NO violet glow flash on
     load before the JS reads the wallet's real colour into
     --ne-buynow-actual-c; the glow simply fades in once read (user
     2026-05-24). */
  .ne-product-buy--buynow-glow-button { --ne-buynow-glow-c: var(--ne-buynow-actual-c, transparent); }
  .ne-product-buy--buynow-glow .ne-product-buy__dyn-checkout .shopify-payment-button__button,
  .ne-product-buy--buynow-shadow .ne-product-buy__dyn-checkout .shopify-payment-button__button {
    box-shadow:
      var(--ne-buynow-glow, 0 0 0 transparent),
      var(--ne-buynow-shadow, 0 0 0 transparent) !important;
  }

  .ne-product-buy__qty {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1);
  }
  .ne-product-buy__qty-label {
    font-size: var(--ne-font-size-xs);
    font-weight: 500;
    color: var(--ne-scheme-fg-muted);
  }
  .ne-product-buy__qty-wrap {
    display: inline-flex;
    align-items: stretch;
    /* Default content-width — keeps the spinner narrow regardless of
       parent layout. Full-width modifier expands to 100%. */
    width: max-content;
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border));
    /* Follow theme Buttons & Inputs → Eckenradius for visual cohesion */
    border-radius: var(--ne-btn-radius, var(--ne-radius-input, var(--ne-radius-md, 8px)));
    background: var(--ne-scheme-input-bg, var(--ne-glass-bg, rgba(255,255,255,0.04)));
    overflow: hidden;
  }
  .ne-product-buy__qty-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    min-height: 40px;
    background: none;
    border: none;
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    cursor: pointer;
    transition: background var(--ne-transition-fast);
    flex-shrink: 0;
  }
  .ne-product-buy__qty-btn:hover { background: var(--ne-glass-bg, rgba(255,255,255,0.08)); }
  .ne-product-buy__qty-input {
    flex: 0 0 auto;
    width: 50px;
    text-align: center;
    border: none;
    background: transparent;
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
    appearance: textfield;
    -moz-appearance: textfield;
  }
  /* Full-width: input grows to fill, wrap stretches to row width. */
  .ne-product-buy--full-width .ne-product-buy__qty-input { flex: 1; width: auto; }
  /* Keep a visible focus state (matches the theme's other inputs) — bare
     outline:none removed the only keyboard-focus indicator (WCAG 2.4.7). */
  .ne-product-buy__qty-input:focus { outline: none; border-color: var(--ne-scheme-accent); }
  .ne-product-buy__qty-input::-webkit-inner-spin-button,
  .ne-product-buy__qty-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }

  .ne-product-buy__atc {
    width: 100%;
    padding: var(--ne-btn-padding-y, var(--ne-space-3)) var(--ne-btn-padding-x, var(--ne-space-6));
  }
  /* Buttons width applied at the FORM level — not on individual
     buttons. This makes the math clean for inline mode:
       Form width = buttons_width %  of info-col
       Row 1 = qty (max-content) + ATC (1fr remainder)
       Row 2 = Express (full form width)
     Result: qty + ATC combined = Express width. Qty stays its own
     intrinsic width — only ATC adapts to fill the rest. */
  .ne-product-buy form {
    width: var(--ne-pd-btn-width, 100%);
    max-width: 100%;
  }
  .ne-product-buy__atc { width: 100%; }
  .ne-product-buy__dyn-checkout { width: 100%; }
  .ne-product-buy__dyn-checkout :is(.shopify-payment-button, .shopify-payment-button__button) {
    width: 100% !important;
  }

  /* Quantity label alignment — follows the alignment setting. The label
     is a block element above the qty wrap; text-align controls its
     visual position within its row. */
  .ne-product-buy--align-left .ne-product-buy__qty-label   { text-align: left; }
  .ne-product-buy--align-center .ne-product-buy__qty-label { text-align: center; }
  .ne-product-buy--align-right .ne-product-buy__qty-label  { text-align: right; }

  /* Alignment — drives where ATC, Express, and qty sit within the form.
     For qty: only applies in STACKED mode. In inline-mode the qty cell
     must stay grid-cell-stretched so its height matches the ATC button
     — applying align-self there would collapse the cell height. */
  .ne-product-buy--align-left .ne-product-buy__atc,
  .ne-product-buy--align-left .ne-product-buy__dyn-checkout {
    align-self: flex-start !important;
  }
  .ne-product-buy--align-center .ne-product-buy__atc,
  .ne-product-buy--align-center .ne-product-buy__dyn-checkout {
    align-self: center !important;
  }
  .ne-product-buy--align-right .ne-product-buy__atc,
  .ne-product-buy--align-right .ne-product-buy__dyn-checkout {
    align-self: flex-end !important;
  }
  .ne-product-buy--align-left.ne-product-buy--qty-stacked .ne-product-buy__qty   { align-self: flex-start !important; }
  .ne-product-buy--align-center.ne-product-buy--qty-stacked .ne-product-buy__qty { align-self: center !important; }
  .ne-product-buy--align-right.ne-product-buy--qty-stacked .ne-product-buy__qty  { align-self: flex-end !important; }

  /* Qty match button-width — both the qty container AND its wrap must
     be wide enough. Container stretches to form width so the wrap's
     percentage is calculated against the same reference as ATC width. */
  .ne-product-buy--qty-match-btn .ne-product-buy__qty {
    align-self: stretch !important;
    width: 100% !important;
  }
  .ne-product-buy--qty-match-btn .ne-product-buy__qty-wrap {
    width: var(--ne-pd-btn-width, 100%);
    justify-content: space-between;
  }
  .ne-product-buy--qty-match-btn .ne-product-buy__qty-input {
    flex: 1;
    width: auto;
  }

  /* Full-width modifier: stretch the WRAP edge-to-edge AND space buttons
     out so the visual matches "spinner takes full row" expectation. */
  .ne-product-buy--full-width .ne-product-buy__qty-wrap {
    display: flex;
    width: 100%;
    justify-content: space-between;
  }

  .ne-product-buy__dyn-checkout :is(.shopify-payment-button, .shopify-payment-button__button) {
    width: 100%;
  }
  .ne-product-buy__dyn-checkout .shopify-payment-button__button {
    border-radius: var(--ne-btn-radius, var(--ne-radius-input, 8px)) !important;
    min-height: calc(var(--ne-btn-padding-y, var(--ne-space-3)) * 2 + 1.5em) !important;
    padding: var(--ne-btn-padding-y, var(--ne-space-3)) var(--ne-btn-padding-x, var(--ne-space-6)) !important;
    font-size: var(--ne-font-size-sm) !important;
    font-weight: var(--ne-btn-font-weight, 600) !important;
    font-family: inherit !important;
    text-transform: var(--ne-btn-text-transform, none) !important;
    transition: opacity var(--ne-transition-fast) !important;
  }
  .ne-product-buy__dyn-checkout .shopify-payment-button__button:not([disabled]):hover { opacity: 0.88 !important; }

  /* Empty :empty fallback so layout doesn't push when no providers */
  .ne-product-buy__dyn-checkout:empty { display: none; }

  @media (max-width: 749px) {
    .ne-product-buy--qty-inline form {
      grid-template-columns: 1fr;
    }
    /* Reset the desktop inline row placement (qty + ATC both sit on grid-row 1,
       cols 1/2). A single-column mobile grid needs the row reset too — only
       changing grid-column leaves both items in row 1 col 1, so they overlap.
       grid-row: auto lets them auto-flow into separate rows in DOM order. */
    .ne-product-buy--qty-inline .ne-product-buy__qty,
    .ne-product-buy--qty-inline .ne-product-buy__atc,
    .ne-product-buy--qty-inline .ne-product-buy__dyn-checkout,
    .ne-product-buy--qty-inline .ne-product-buy__recipient,
    .ne-product-buy--qty-inline:has(.ne-product-buy__recipient) .ne-product-buy__qty,
    .ne-product-buy--qty-inline:has(.ne-product-buy__recipient) .ne-product-buy__atc {
      grid-column: 1;
      grid-row: auto;
    }
  }
/* END_BLOCK:product-buy-buttons */

/* START_BLOCK:product-collapsible (INDEX:111) */
.ne-product-collapsible {
    border-bottom: 1px solid var(--ne-scheme-border);
  }
  .ne-product-collapsible__summary {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--ne-space-3) 0;
    cursor: pointer;
    list-style: none;
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
    user-select: none;
  }
  .ne-product-collapsible__summary::-webkit-details-marker { display: none; }
  .ne-product-collapsible__heading {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-2);
  }
  /* Icon color — defaults to scheme accent, custom color via inline style
     (with !important) on the SVG element. SVG strokes/fills use
     `currentColor` so any color set on the host element propagates. */
  .ne-product-collapsible__icon {
    flex-shrink: 0;
    color: var(--ne-scheme-accent);
    fill: none;
    stroke: currentColor;
  }
  .ne-product-collapsible__icon--custom {
    /* For custom uploaded image icons — apply color via mask so PNG/SVG
       become tintable. */
    -webkit-mask-image: var(--ne-pd-collapsible-mask, none);
    mask-image: var(--ne-pd-collapsible-mask, none);
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    background-color: currentColor;
  }
  .ne-product-collapsible__chevron { transition: transform var(--ne-transition-fast); flex-shrink: 0; }
  .ne-product-collapsible[open] .ne-product-collapsible__chevron { transform: rotate(180deg); }
  .ne-product-collapsible__body {
    padding-bottom: var(--ne-space-4);
    color: var(--ne-scheme-fg-muted, var(--ne-scheme-text-muted));
    font-size: var(--ne-font-size-sm);
    line-height: 1.7;
  }
  .ne-product-collapsible__body p:first-child { margin-top: 0; }
  .ne-product-collapsible__body p:last-child  { margin-bottom: 0; }

  /* Rich-text media compat — mirror blocks/product-description.liquid so that
     images inserted via Shopify's Polaris editor (e.g. a row of certification
     / trust badges) flow inline instead of stacking. Counters the global
     `img { display: block }` reset (critical.css) and makes solo-image
     paragraph/div wrappers inline-block so adjacent badges sit side by side
     and wrap at the viewport edge. */
  .ne-product-collapsible__body p:has(> img:only-child),
  .ne-product-collapsible__body p:has(> a:only-child > img:only-child),
  .ne-product-collapsible__body p:has(> span:only-child > img:only-child),
  .ne-product-collapsible__body div:has(> img:only-child),
  .ne-product-collapsible__body div:has(> a:only-child > img:only-child),
  .ne-product-collapsible__body div:has(> span:only-child > img:only-child) {
    display: inline-block;
    vertical-align: middle;
    margin: 0 var(--ne-space-2) var(--ne-space-2) 0;
  }
  .ne-product-collapsible__body img {
    display: inline-block;
    vertical-align: middle;
    margin-block: 0;
  }
  /* Standalone image source (e.g. a size-chart metafield) — block-level and
     scales to the body width. Higher specificity than the inline-block rule
     above so the chart isn't forced inline. */
  .ne-product-collapsible__body img.ne-product-collapsible__image {
    display: block;
    margin: var(--ne-space-2) 0;
    border-radius: var(--ne-radius-md);
  }
/* END_BLOCK:product-collapsible */

/* START_BLOCK:product-description (INDEX:112) */
.ne-product-description {
    color: var(--ne-scheme-text);
    line-height: 1.7;
    font-size: var(--ne-font-size-sm);
  }
  .ne-product-description p { margin: 0 0 var(--ne-space-2); }

  /* =============================================================================
     Polaris-RTE COMPAT — defensive layout for Shopify's modern rich-text editor
     =============================================================================
     Shopify's Polaris-based product-description editor (used in Admin since
     ~2023) serializes EACH inserted media block (image, embed, button) as its
     own block-level wrapper <div class="Polaris-Text--root"> or <p>. When a
     merchant inserts 3-5 certification/trust badges side-by-side in the visual
     editor, they get saved as N adjacent block-level divs, which then stack
     vertically on render — even though the editor preview shows them in a row.

     Defensive rule: detect adjacent paragraph/div blocks that contain ONLY an
     image (single child = <img>, or single child = <span>/<a> wrapping an
     <img>) and force them to inline-block + reset the global .rte img margin
     so they flow naturally in a row, wrapping at viewport edge.

     Single image content (e.g. one full-width hero in the description) is
     unaffected — the inline-block change only matters when adjacent inline-
     blocks share a line. A solo image gets a full row to itself either way.

     :has() support: Chrome/Edge 105+, Safari 15.4+, Firefox 121+ — universal
     modern browsers (Hyprism already uses :has() throughout per Rules 190,
     208, 213). */
  .ne-product-description p:has(> img:only-child),
  .ne-product-description p:has(> a:only-child > img:only-child),
  .ne-product-description p:has(> span:only-child > img:only-child),
  .ne-product-description div:has(> img:only-child),
  .ne-product-description div:has(> a:only-child > img:only-child),
  .ne-product-description div:has(> span:only-child > img:only-child) {
    display: inline-block;
    vertical-align: middle;
    margin: 0 var(--ne-space-2) var(--ne-space-2) 0;
  }
  /* Reset the global .rte img margin-block (critical.css line 1229) for
     images inside the image-only wrappers — otherwise the inherited
     16px top+bottom margin pushes them out of the inline-block row. */
  .ne-product-description p:has(> img:only-child) > img,
  .ne-product-description p:has(> a:only-child > img:only-child) > a > img,
  .ne-product-description p:has(> span:only-child > img:only-child) > span > img,
  .ne-product-description div:has(> img:only-child) > img,
  .ne-product-description div:has(> a:only-child > img:only-child) > a > img,
  .ne-product-description div:has(> span:only-child > img:only-child) > span > img {
    margin: 0;
    vertical-align: middle;
  }

  /* Multi-image paragraphs — when merchants paste several images into a
     single <p> (typical for a row of certification/trust badges, e.g.
     <p><img><img>&nbsp;<img>&nbsp;<img></p>), the global reset
     `img { display: block }` (critical.css line 46) stacks them vertically
     even though the markup intends a row. Counter that for any image inside
     the description so they flow inline horizontally and wrap at viewport
     edge. Images keep their intrinsic width (HTML width attr or natural
     size). The :only-child rules above still apply on top — they make the
     wrapping <p> itself inline-block so adjacent solo-image <p>s also flow
     side-by-side. */
  .ne-product-description img {
    display: inline-block;
    vertical-align: middle;
    margin-block: 0;
  }
  .ne-product-description--font-body       { font-family: var(--font-body--family); }
  .ne-product-description--font-subheading { font-family: var(--font-subheading--family); }
  .ne-product-description--font-heading    { font-family: var(--font-heading--family); }
  .ne-product-description--font-accent     { font-family: var(--font-accent--family); }

  /* Show-more mode: line-clamp closed → button reveals full text */
  .ne-product-description--truncate .ne-product-description__body {
    display: -webkit-box;
    -webkit-line-clamp: var(--ne-pd-clamp-lines, 4);
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .ne-product-description--truncate[data-expanded="1"] .ne-product-description__body {
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
  }
  .ne-product-description__toggle {
    margin-top: var(--ne-space-2);
    background: none;
    border: none;
    padding: 0;
    color: var(--ne-scheme-accent);
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    text-decoration: underline;
    text-underline-offset: 3px;
    font-family: inherit;
  }
  .ne-product-description__toggle-hide { display: none; }
  .ne-product-description--truncate[data-expanded="1"] .ne-product-description__toggle-show { display: none; }
  .ne-product-description--truncate[data-expanded="1"] .ne-product-description__toggle-hide { display: inline; }

  /* Scroll mode: max-height = preview_lines * line-height + internal scroll */
  .ne-product-description--scroll {
    position: relative;
  }
  .ne-product-description--scroll .ne-product-description__body {
    max-height: calc(var(--ne-pd-clamp-lines, 4) * 1.7em);
    overflow-y: auto;
    padding-right: var(--ne-space-2);
    padding-bottom: var(--ne-space-3);
    scrollbar-width: thin;
    scrollbar-color: var(--ne-scheme-border) transparent;
    /* Soft fade at bottom so text dissolves into the chevron hint */
    -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 24px), transparent 100%);
    mask-image: linear-gradient(to bottom, #000 calc(100% - 24px), transparent 100%);
  }
  /* Hide the mask once the user has scrolled to the bottom (JS sets attr) */
  .ne-product-description--scroll[data-scroll-bottom="1"] .ne-product-description__body {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .ne-product-description--scroll .ne-product-description__body::-webkit-scrollbar { width: 4px; }
  .ne-product-description--scroll .ne-product-description__body::-webkit-scrollbar-track { background: transparent; }
  .ne-product-description--scroll .ne-product-description__body::-webkit-scrollbar-thumb {
    background: var(--ne-scheme-border);
    border-radius: 2px;
  }
  /* Chevron-down hint — positioned at the bottom-center of the wrap,
     bounce-pulse animation. Hidden once scrolled to bottom. */
  .ne-product-description--scroll::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 4px;
    width: 18px;
    height: 18px;
    transform: translateX(-50%);
    background-color: var(--ne-scheme-accent);
    -webkit-mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    mask-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
    -webkit-mask-size: contain;
    mask-size: contain;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
    pointer-events: none;
    animation: ne-pd-scroll-bounce 1.6s ease-in-out infinite;
    opacity: 0.85;
  }
  .ne-product-description--scroll[data-scroll-bottom="1"]::after {
    opacity: 0;
    animation: none;
  }
  @keyframes ne-pd-scroll-bounce {
    0%, 100% { transform: translate(-50%, 0); }
    50%      { transform: translate(-50%, 4px); }
  }
/* END_BLOCK:product-description */

/* START_BLOCK:product-grid (INDEX:113) */
/* Grid is always transparent — cards float directly on the section
     bg. Scheme class still applies but we kill its painted bg. */
  .ne-pg { width: 100%; background: transparent !important; }

  /* Grid — subgrid row count adapts to which card rows are actually
     rendered (media + title + price always; swatches / ATC only when
     the toggle is on). Parent owns `--ne-pg-rows-tpl` (computed in
     Liquid), cards inherit the template. */
  .ne-pg__grid {
    display: grid;
    /* minmax(0, 1fr) stops tracks from shrinking to content-width
       when images have intrinsic sizes, and gives the browser a
       deterministic basis for subpixel rounding — prevents the tiny
       asymmetry where one column renders 1px narrower/wider than
       another. */
    grid-template-columns: repeat(var(--ne-pg-columns, 4), minmax(0, 1fr));
    grid-template-rows: var(--ne-pg-rows-tpl, auto 1fr auto auto auto);
    grid-auto-rows: var(--ne-pg-rows-tpl, auto 1fr auto auto auto);
    /* align-items: stretch makes each card cell fill the row height,
       giving the subgrid in the card a definite container to pass
       through. Without it, narrow viewports (mobile 2-col) can show
       cards with mismatched bottoms because the subgrid receives no
       explicit row-end to align to. */
    align-items: stretch;
    /* Row-gap matches column-gap so multi-row grids breathe. Cards
       in subgrid mode would inherit this row-gap and leak section-bg
       through the media→title→price seam — `.ne-product-card` itself
       declares `row-gap: 0` (snippets/product-card.liquid) which the
       subgrid spec honours: a child's explicit row-gap wins over the
       inherited value. So the parent can set whatever row-gap it
       likes for inter-card spacing. */
    row-gap: var(--ne-pg-gap, 16px);
    column-gap: var(--ne-pg-gap, 16px);
    /* Padding removed (user 2026-05-23: cards should align flush with
       the block edge). Previously had 8px h-padding + 8/16px v-padding
       as breathing room — now the cards extend to the grid's exterior.
       The section_padding_x setting at the containing section gives
       merchants global control over the inset, so this internal padding
       is redundant. Vertical padding kept as 0 — section padding-top/
       bottom handles that side. */
    padding: 0;
  }
  /* Card row-span per row-count class (can't use var() inside
     `grid-row: span X` — browsers require a literal integer). */
  .ne-pg--rows-3 .ne-pg__grid > .ne-product-card,
  .ne-pg--rows-3 .ne-pg__slide > .ne-product-card { grid-row: span 3; }
  .ne-pg--rows-4 .ne-pg__grid > .ne-product-card,
  .ne-pg--rows-4 .ne-pg__slide > .ne-product-card { grid-row: span 4; }
  .ne-pg--rows-5 .ne-pg__grid > .ne-product-card,
  .ne-pg--rows-5 .ne-pg__slide > .ne-product-card { grid-row: span 5; }
  /* Each card spans the 5 rows of its own row in the grid
     (subgrid applied to .ne-product-card itself — see product-card.liquid).
     Safari note: supports subgrid since 16.0. */

  @media (max-width: 989px) {
    .ne-pg__grid { grid-template-columns: repeat(3, 1fr); }
  }
  @media (max-width: 749px) {
    .ne-pg__grid {
      grid-template-columns: repeat(var(--ne-pg-columns-mobile, 2), 1fr);
      column-gap: calc(var(--ne-pg-gap, 16px) * 0.75);
      /* Vertical breathing room between card-rows on mobile. The card's
         own grid (snippets/product-card.liquid line 278) declares
         `row-gap: 0` so the parent's row-gap does NOT leak into the
         subgrid card-internal seams (media→title→price). Subgrid spec:
         a child's explicit row-gap overrides the inherited gap. */
      row-gap: var(--ne-pg-gap, 16px);
    }
  }

  /* Carousel — track is a CSS grid with column-flow so cards align
     on shared rows (subgrid pass-through: track → slide → card).
     This keeps title / price / swatches / ATC on the same baseline
     across the carousel just like the grid layout (Rule 127).
     `display: grid + grid-auto-flow: column + overflow-x: auto`
     scroll-snaps natively. */
  /* `overflow-x: clip` hard-clips the track's inline bleed at the
     carousel's box edge — off-screen scroll-buffer cards never leak
     past. `overflow-y: visible` keeps the track's vertical shadow
     bleed (padding-block + margin-block: -32) visible past the
     carousel's top/bottom edges. clip (not hidden) is required so
     overflow-y can stay visible.

     padding-inline reserves an INSET ZONE on the carousel: cards are
     pushed away from the box edge so their box-shadow can render
     inside the carousel without reaching the visible box border. For
     a typical 30px shadow blur, 40px inset gives the shadow full
     room to fade. The track inside still has its own padding/margin
     trick for vertical bleed; horizontal it sits in the carousel's
     content-area, naturally constrained. */
  .ne-pg__carousel {
    position: relative;
    overflow-x: clip;
    overflow-y: visible;
    padding-inline: 32px;
  }
  /* Track has overflow-x: auto for horizontal scroll. The shadow
     clip happens at the TRACK's border-edge (not at the box edge),
     so we add an inline padding equal to `--ne-pg-gap` — that's the
     "shadow-bleed buffer" inside the track. Cards sit inside this
     padding (inset by gap-width); their box-shadow renders into the
     padding zone (within track's border-box) and is visible up to
     gap-distance past the card edge. Off-screen scroll-buffer cards
     start exactly at gap-distance into the padding zone, which means
     padding == gap → off-screen peek width = padding - gap = 0.
     scroll-padding-inline aligns scroll-snap targets to the content-
     box start (cards don't snap into the padding zone). */
  .ne-pg__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - var(--ne-pg-gap, 16px) * (var(--ne-pg-columns, 4) - 1)) / var(--ne-pg-columns, 4));
    grid-template-rows: var(--ne-pg-rows-tpl, auto 1fr auto auto auto);
    /* Same as grid-mode: stretch cards to row height so subgrid has
       a definite container — without this, mobile 2-col layouts can
       show cards with misaligned bottoms. */
    align-items: stretch;
    /* row-gap MUST be 0 — see .ne-pg__grid for full reasoning. Subgrid
       children inherit the parent's row-gap, leaking section-bg through
       the seam between media/title/price as a 1-2px hairline. */
    row-gap: 0;
    column-gap: var(--ne-pg-gap, 16px);
    overflow-x: auto;
    overflow-y: hidden;
    /* proximity (not mandatory) — Rule 138: mandatory amplifies
       subgrid jitter on smooth-scroll. */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    scrollbar-width: none;
    -ms-overflow-style: none;
    /* Rule 205: explicit touch-action so mobile swipe delegates fully to
       this scroll container — without this the swipe stops mid-card and
       can't continue past the block edge. pan-y is included so a vertical
       swipe scrolls the PAGE instead of being trapped by this horizontal
       carousel (touching a card image previously blocked page scroll). */
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    padding-block: 32px;
    padding-inline: var(--ne-pg-gap, 16px);
    margin-block: -32px;
    scroll-padding-inline: var(--ne-pg-gap, 16px);
    /* mask-image disabled — even narrow fade-slivers create a mask
       compositing layer on the track that subtly fades alpha across
       the entire track, exposing section-bg through subgrid track
       boundaries inside cards as variable horizontal hairlines (only
       in live preview, not in the editor's iframe sandbox). The
       trade-off: shadow edges hard-clip at the carousel border
       instead of fading. Acceptable; the carousel padding-inline +
       overflow-x: clip keeps shadows visually inside the box. */
    -webkit-mask-image: none;
    mask-image: none;
    will-change: scroll-position;
  }
  /* Glassmorphism + mask-image are mutually exclusive — mask creates
     a stacking context that breaks `backdrop-filter` sampling on
     glass cards (the blur effect doesn't render). When glass is on
     we drop the mask so blur works; the trade-off is no edge fade. */
  .ne-pg--glass-on .ne-pg__track {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .ne-pg__track::-webkit-scrollbar { display: none; }
  /* Slide passes the track's row template through to the card via
     nested subgrid (track → slide → card). Each slide spans N rows
     based on the rows-N modifier (literal integer needed; Rule 129). */
  .ne-pg__slide {
    display: grid;
    grid-template-rows: subgrid;
    /* Slide subgrid inherits track's row-gap unless explicitly
       overridden — without this, even with row-gap: 0 on the track,
       the slide's nested-subgrid layer can still leak section-bg
       through the seams between media/title/price as hairlines on
       hover (subpixel layer-shift exposes the gap). Setting to 0
       at every subgrid level eliminates the leak path entirely. */
    row-gap: 0;
    scroll-snap-align: start;
  }
  .ne-pg--rows-3 .ne-pg__slide { grid-row: span 3; }
  .ne-pg--rows-4 .ne-pg__slide { grid-row: span 4; }
  .ne-pg--rows-5 .ne-pg__slide { grid-row: span 5; }

  @media (max-width: 989px) {
    .ne-pg--carousel .ne-pg__track {
      grid-auto-columns: calc((100% - var(--ne-pg-gap, 16px) * 2) / 3);
    }
  }
  @media (max-width: 749px) {
    .ne-pg--carousel .ne-pg__track {
      grid-auto-columns: calc((100% - var(--ne-pg-gap, 16px) * (var(--ne-pg-columns-mobile, 2) - 1)) / var(--ne-pg-columns-mobile, 2));
    }
  }

  /* Arrows — base reset; per-style chrome below */
  .ne-pg__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--ne-scheme-text, currentColor);
    cursor: pointer;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
  }
  .ne-pg--arrow-button .ne-pg__arrow {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127,127,127,0.25));
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  }
  .ne-pg--arrow-plain .ne-pg__arrow {
    background: transparent;
    border: 0;
    box-shadow: none;
    width: 32px;
    height: 32px;
  }
  .ne-pg--arrow-plain .ne-pg__arrow:hover { color: var(--ne-scheme-accent, currentColor); }

  /* Below-track layout — arrows positioned ABSOLUTELY beneath the
     track (no flex on the carousel). Avoids flex-wrap interactions
     with the track's negative margin (which would otherwise shift
     items around when the track's margin-box doesn't fill the flex
     line). padding-bottom = track's vertical bleed (32) + clearance
     (16) + arrow height (40) = 88. */
  .ne-pg--carousel:not(.ne-pg--arrows-overlay) .ne-pg__carousel {
    padding-bottom: 88px;
  }
  .ne-pg--carousel:not(.ne-pg--arrows-overlay) .ne-pg__arrow {
    position: absolute;
    bottom: 0;
    margin-top: 0;
  }
  .ne-pg--carousel:not(.ne-pg--arrows-overlay) .ne-pg__arrow--prev {
    left: calc(50% - 44px);
  }
  .ne-pg--carousel:not(.ne-pg--arrows-overlay) .ne-pg__arrow--next {
    left: calc(50% + 4px);
  }

  /* Overlay mode — arrows centered on the *card* vertically (Rule:
     user-requested cardmitte alignment). Carousel is `position: relative`,
     arrows absolute at top: 50%. */
  .ne-pg--arrows-overlay .ne-pg__carousel {
    position: relative;
  }
  .ne-pg--arrows-overlay .ne-pg__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 3;
    margin-top: 0;
  }
  .ne-pg--arrows-overlay .ne-pg__arrow:hover { transform: translateY(-50%) scale(1.05); }
  .ne-pg--arrows-overlay .ne-pg__arrow--prev { left: 4px; }
  .ne-pg--arrows-overlay .ne-pg__arrow--next { right: 4px; }

  /* Coverflow — the focus card lies free in front; cards to its left and
     right are pushed together into overlapping stacks (scaled down + turned
     back in 3D). JS sets --d (signed distance from active) + --ad (abs) per
     card; the transform reads them, sign of --d picks left/right side + 3D
     turn. Container height = tallest card (JS, stable). Focus alignment
     shifts the anchor (--ne-cf-origin); overflow:hidden clips the off-side. */
  .ne-pg__coverflow {
    position: relative;
    width: 100%;
    height: var(--ne-cf-h, 460px);
    overflow: visible;          /* spill (default); clip handled at section/viewport */
    /* No `perspective`/`isolation` here — both form a backdrop-root that kills
       the glass cards' backdrop-filter (Rule 49). Perspective is applied
       per-card inside the transform() instead, so the card itself carries it. */
    --ne-cf-origin: 50%;
  }
  .ne-pg__coverflow--align-left  { --ne-cf-origin: calc(var(--ne-cf-card-w, 340px) * 0.5 + 28px); }
  .ne-pg__coverflow--align-right { --ne-cf-origin: calc(100% - var(--ne-cf-card-w, 340px) * 0.5 - 28px); }
  /* Viewport carries the optional fade mask; chevrons live outside it (siblings
     of the viewport) so the mask never fades them. Spill (default) = visible, the
     SECTION clips at its own edge in clip mode (critical.css drops the spill-guard
     for --clip). Only the fade needs the viewport to clip + mask. */
  .ne-pg__cf-viewport {
    position: absolute;
    inset: 0;
    overflow: visible;
    border-radius: inherit;
  }
  .ne-pg__coverflow--clip.ne-pg__coverflow--fade .ne-pg__cf-viewport { overflow: hidden; }
  .ne-pg__coverflow--clip.ne-pg__coverflow--style-stack.ne-pg__coverflow--fade .ne-pg__cf-viewport,
  .ne-pg__coverflow--clip.ne-pg__coverflow--style-fan.ne-pg__coverflow--fade .ne-pg__cf-viewport {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-cf-fade, 0px), #000 calc(100% - var(--ne-cf-fade, 0px)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-cf-fade, 0px), #000 calc(100% - var(--ne-cf-fade, 0px)), transparent 100%);
  }
  /* Stage wraps the cards (not the arrows). Every coverflow optic shifts +
     scales the whole composition so it always fits the coverflow width and
     stays centred — no outer card clips at the block edge. */
  .ne-pg__cf-stage { position: absolute; inset: 0; }
  .ne-pg__coverflow .ne-pg__cf-stage {
    transform: translateX(var(--ne-cf-drum-shift, 0px)) scale(var(--ne-cf-drum-fit, 1));
    transform-origin: 50% 50%;
    transition: transform .55s cubic-bezier(.22, 1, .36, 1);
  }
  .ne-pg--no-trans .ne-pg__cf-stage,
  .ne-pg--no-trans .ne-pg__cf-card { transition: none !important; }
  /* Paging gestures (coverflow_nav). */
  .ne-pg__coverflow--nav-drag { cursor: grab; touch-action: pan-y; }
  .ne-pg__coverflow--nav-drag.ne-pg__coverflow--grabbing { cursor: grabbing; }
  .ne-pg__cf-card {
    position: absolute;
    top: 50%;
    left: var(--ne-cf-origin, 50%);
    width: min(var(--ne-cf-card-w, 320px), 82%);
    /* Radius on every surface (glass already had it) so the solid back panel +
       overflow clip round the same as the glass cards — no sharp/overhanging
       corners on the reversed cards (user 2026-06-25). */
    border-radius: var(--ne-cf-card-radius, 12px);
    transform-origin: 50% 50%;
    --d: 0;
    --ad: 0;
    /* Compact stagger: a smaller lateral step (% of card width) keeps many
       side cards visible as overlapping turned wings; rotateY turns them,
       translateZ pushes back, scale shrinks with distance. */
    transform:
      perspective(3000px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--d) * var(--ne-cf-step, 23%)))
      rotateY(calc(var(--d) * -5deg))
      translateZ(0px)
      scale(calc(1 - var(--ad) * 0.035));
    z-index: calc(100 - var(--ad) * 5);
    /* Stack geometry dialled in via the stack tuner (2026-06-25): a flat, subtle
       fanned coverflow — gentle turn, no depth, light shrink, full opacity. */
    opacity: 1;
    transition: transform .55s cubic-bezier(.22,1,.36,1), opacity .55s ease, box-shadow .35s ease;
    pointer-events: auto;
    cursor: pointer;
    will-change: transform;
    /* Glow/shadow compose here (moved off the inner card, which is transparent
       in glass mode + clipped by overflow). Inset = glass highlight. */
    box-shadow: var(--ne-cf-shadow-inset, 0 0 0 transparent), var(--ne-cf-shadow-outer, 0 0 0 transparent);
  }
  .ne-pg__cf-card[data-active] { cursor: default; }
  /* Equalise card heights across the coverflow — products with vs without colour
     swatches render at different heights; force them all to the tallest
     (--ne-cf-h, measured by JS) so the silhouettes line up (user 2026-06-25). */
  .ne-pg__cf-card,
  .ne-pg__cf-card .ne-product-card { min-height: var(--ne-cf-h); }
  /* ── Coverflow optics ──────────────────────────────────────────────
     The base .ne-pg__cf-card transform above is the "stack" optic. Fan +
     drum override only `transform` (drum also hides backfaces), reading the
     same JS-set --d / --ad, so the JS stays optic-agnostic. */
  /* Fan — cards tilt in-plane (rotateZ) and arc up toward the edges, like a
     hand of cards opening upward. Flat (no perspective). overflow:visible +
     extra height give the arc headroom so the lifted edges aren't clipped. */
  .ne-pg__coverflow--style-fan {
    height: calc(var(--ne-cf-h, 460px) + 150px);
    overflow: visible;
  }
  .ne-pg__coverflow--style-fan .ne-pg__cf-card {
    transform:
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--gd, var(--d)) * 54%))
      translateY(calc(var(--cad, var(--ad)) * var(--cad, var(--ad)) * -12px))
      rotate(calc(var(--cd, var(--d)) * 9deg))
      scale(calc(1 - var(--ad) * 0.04));
    /* keep the fan's own subtle edge fade (the stack base is now opacity:1) */
    opacity: max(0.88, calc(1 - var(--ad) * 0.045));
  }
  /* Solid surface = genuinely opaque cards, so don't dim the back ones. */
  .ne-pg__coverflow--style-fan .ne-pg__cf-card--surface-solid:not([data-hidden]) { opacity: 1; }
  /* Drum — ASYMMETRIC, like a card pulled from a rolodex/dispenser. The active
     card is extracted flat at the focus side; every OTHER card piles on the
     opposite side (uses --ad, so past + future cards stack as one drum body),
     turning + receding into the distance and fading toward the edge. Direction
     follows the focus position via --ne-cf-drum-dir (left/right only — centre
     has no meaningful asymmetry). */
  /* Drum — a real slide-carousel/Kodak cylinder. Every non-focus card sits on
     a vertical RING (rotateY around the axis + constant translateZ = radius),
     all piling to ONE side via --ad * drum-dir so the ring curves away from the
     focus and fades into the back (only the front half reads). The focus card
     is EJECTED forward out of the ring (translateZ + slight lift), like a slide
     lifted from the tray. Paging re-renders --d/--ad → the ring rotates and the
     next card ejects. */
  /* Drum — a slide magazine: the FOCUS card stands alone on one side, the drum
     (a fanned stack of heavily-overlapping cards, each turned a touch more than
     the last — face-on at the front, edge-on toward the back, fading at the
     section edge so only ~a quarter shows) sits SEPARATELY on the other side.
     Paging pushes the focus card into the drum + lifts the next one out.
     The drum's pivot (--ne-cf-origin) is offset to the drum side; the focus
     card is translated across to the opposite (focus) side. */
  .ne-pg__coverflow--style-drum {
    /* Tall enough to contain the 1.26×-scaled focus card (× the fit-scale) so it
       never overlaps the heading above / button below — the box reserves the
       height; cards stay vertically centred inside it. */
    height: calc(var(--ne-cf-h, 460px) * 1.28 * var(--ne-cf-drum-fit, 1) + 30px);
    overflow: visible;
    --ne-cf-origin: 49%;
  }
  .ne-pg__coverflow--style-drum.ne-pg__coverflow--align-right { --ne-cf-origin: 51%; }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card {
    /* Full drum seen from the side: cards run face-on (focus side) → edge-on (a
       thin line at the front) → on around the back, where their reversed faces
       show (view from behind) → fade out at the far edge. Uses --di (the whole
       sequence) so the whole drum lines up; backs stay visible (no backface
       hide); images keep 100% opacity. */
    transform-origin: 50% 50%;
    transform:
      perspective(2600px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--ne-cf-drum-dir, 1) * var(--di, var(--ad)) * 13%))
      rotateY(calc(var(--ne-cf-drum-dir, 1) * var(--di, var(--ad)) * 15deg))
      scale(1.12);
    /* Stacking peaks at the edge-on card (~90° ≈ di 5.8, the thin line at the
       front) and falls off to BOTH sides: face-on cards recede on the focus
       side, the reversed backs recede on the far side — symmetric, so the back
       half is no longer stacked the wrong way round (user 2026-06-25).
       Geometry values dialled in via the drum tuner (2026-06-25). */
    z-index: calc(90 - abs(var(--di, var(--ad)) - 5.6) * 2);
    opacity: 1;
  }
  /* Focus card: stands alone on the opposite side, flat, on top. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card[data-active] {
    transform-origin: 50% 50%;
    transform:
      perspective(2600px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--ne-cf-drum-dir, 1) * -130%))
      rotateY(0deg)
      scale(1.26);
    opacity: 1;
    z-index: 300;
  }
  /* Hover-lift on the CF-CARD (via --ne-cf-lift inside every optic's transform)
     so the box-shadow lifts WITH the card — no gap between card and shadow on
     any surface (the gap only showed on solid, where the inner card was the
     visible face). The inner card itself never lifts. (user 2026-06-25) */
  .ne-pg__coverflow .ne-pg__cf-card .ne-product-card:hover { transform: none !important; }
  .ne-pg__coverflow .ne-pg__cf-card:hover { --ne-cf-lift: -12px; }
  /* Drum: only the focus card lifts — overlapping rotated cards stay put. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]):hover { --ne-cf-lift: 0px; }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card[data-active]:hover {
    --ne-cf-lift: -16px;
    --ne-cf-shadow-outer: 0 24px 48px rgba(0, 0, 0, 0.42);
  }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]) .ne-product-card--has-secondary:hover .ne-product-card__image--primary { opacity: 1 !important; }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]) .ne-product-card--has-secondary:hover .ne-product-card__image--secondary { opacity: 0 !important; }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]) .ne-product-card--hover-zoom:hover .ne-product-card__image--primary { transform: none !important; }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]) .ne-product-card__quick-view { display: none !important; }
  /* Back cards (past the edge-on point ~di 5.6) show their reversed face — frost
     just the CONTENT (image + text) so it reads as "this is the back". The card
     EDGE stays sharp (overflow:hidden clips it), all backs get the SAME blur
     (step, not progressive), oriented at the glass blur (user 2026-06-25). */
  /* Render the reversed face (the grid default hides it for GPU/hairline
     reasons) so the back of the card is visible in the drum, not see-through. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card .ne-product-card {
    backface-visibility: visible;
  }
  /* Clip the blur + back panel to the card edge on the side/back cards only —
     the focus card stays overflow:visible so its hover-lift isn't clipped. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card:not([data-active]) {
    overflow: hidden;
  }
  /* Glass backs frost their content (image + text); solid backs are a plain
     panel (handled below) so they get no blur. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card--surface-glass .ne-product-card {
    filter: blur(calc(min(1, max(0, (var(--di, var(--ad)) - 5.6) * 100)) * 5px));
  }
  /* Back-face treatment — an overlay that fades in only on the reversed cards
     (di past the edge-on point). SOLID → opaque card colour, so the back reads
     "completely solid"; GLASS + tint → the accent tint laid over the back image
     (user 2026-06-25). It paints above the content (z 7), clipped to the radius;
     opacity is the same step as the blur so the front faces are untouched. */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    z-index: 7;
    pointer-events: none;
    background: transparent;
    opacity: calc(min(1, max(0, (var(--di, var(--ad)) - 5.6) * 100)));
  }
  .ne-pg__coverflow--style-drum .ne-pg__cf-card--surface-glass.ne-pg__cf-card--tint::before {
    background: var(--ne-glass-bg-tinted, rgba(99, 102, 241, 0.18));
  }
  /* SOLID back: a plain panel in the card colour covering the reversed face, so
     the mirrored image/text don't show through — it reads like the blank back of
     a card (no blur needed). Stacked so it stays opaque even if the scheme card
     colour has alpha (user 2026-06-25). */
  .ne-pg__coverflow--style-drum .ne-pg__cf-card--surface-solid::before {
    background:
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      var(--ne-cf-cbg);
  }
  /* SOLID surface in a coverflow: the cards overlap (and the drum flips them),
     so a scheme "Card background" colour with alpha reads as see-through. Stack
     the card colour so the solid surface is genuinely opaque — in the card's own
     colour (no-op when it's already opaque). Fixes both the front and, via the
     reversed face, the back (user 2026-06-25). */
  .ne-pg__coverflow .ne-pg__cf-card--surface-solid {
    --ne-cf-cbg: var(--ne-scheme-card-bg, var(--ne-scheme-bg, var(--ne-scheme-bg-surface, #1a1a28)));
  }
  .ne-pg__coverflow .ne-pg__cf-card--surface-solid .ne-product-card::before {
    background:
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      var(--ne-cf-cbg);
  }
  /* Card chrome modifiers — all plug into --ne-cf-shadow-outer. */
  .ne-pg__cf-card--shadow { --ne-cf-shadow-outer: 0 8px 22px rgba(0, 0, 0, 0.28); }
  .ne-pg__cf-card--shadow:hover { --ne-cf-shadow-outer: 0 14px 32px rgba(0, 0, 0, 0.38); }
  .ne-pg__cf-card--glow:hover { --ne-cf-shadow-outer: 0 0 32px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.45)); }
  .ne-pg__cf-card--shadow.ne-pg__cf-card--glow:hover {
    --ne-cf-shadow-outer: 0 0 30px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.4)), 0 12px 28px rgba(0, 0, 0, 0.32);
  }
  .ne-pg__cf-card[data-hidden] { opacity: 0; pointer-events: none; }
  /* Coverflow glass — PROGRESSIVE ENHANCEMENT:
     • Default (Chrome / Edge / Safari): real backdrop-filter blur on every
       card. These engines render backdrop-filter on transformed elements.
     • Firefox (html.ne-ff, set by JS): Firefox can't render backdrop-filter on
       transformed elements → fall back to a mock-glass frosted gradient.
     The inner card is rendered transparent so the glass surface shows through. */
  .ne-pg__cf-card--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06)) !important;
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border-radius: var(--ne-cf-card-radius, 12px);
    overflow: hidden;
    --ne-cf-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    will-change: transform, backdrop-filter;
  }
  /* Tint — a translucent accent wash over the frosted base (opt-in via
     card_glass_tint). NOT the theme's --ne-glass-bg-tinted (accent @ 50% alpha),
     which on a small card over imagery reads as solid (user 2026-06-24).
     color-mix keeps it see-through so the backdrop blur still shows; using
     --ne-scheme-accent directly resolves reliably in this context. */
  /* Strength is driven by Theme Settings → Effects → Glass tint intensity
     (--ne-glass-bg-tinted = accent @ that intensity). The tint is an image
     layer (a solid-colour gradient) OVER the frosted background-colour, so the
     backdrop blur shows through at low/mid intensity and goes solid at 100% —
     and it stays valid CSS (two bare colours in the `background` SHORTHAND is
     invalid → the whole declaration drops, leaving the card un-tinted; that was
     the "no difference" / "not solid at 100%" bug, user 2026-06-25). */
  .ne-pg__cf-card--glass.ne-pg__cf-card--tint {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.06)) !important;
    background-image: linear-gradient(
      var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5)),
      var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5))) !important;
  }
  /* Firefox mock-glass: tint over a frost gradient (no backdrop blur to thin
     it, so the white frost keeps glass character). */
  html.ne-ff .ne-pg__cf-card--glass.ne-pg__cf-card--tint {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.10)) !important;
    background-image:
      linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.02) 55%, rgba(255, 255, 255, 0.07) 100%),
      linear-gradient(
        var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5)),
        var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5))) !important;
  }
  html.ne-ff .ne-pg__cf-card--glass {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background:
      linear-gradient(135deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.05) 38%, rgba(255, 255, 255, 0.02) 62%, rgba(255, 255, 255, 0.12) 100%),
      var(--ne-glass-bg, rgba(255, 255, 255, 0.07)) !important;
    --ne-cf-shadow-inset:
      inset 0 1px 0 rgba(255, 255, 255, 0.24),
      inset 0 -1px 0 rgba(0, 0, 0, 0.06),
      inset 0 0 0 1px rgba(255, 255, 255, 0.06);
  }
  /* Glass shine — top light-line over the card (Rule 371/LG1). The generic
     [class*="glass"] bg-image shine lands BEHIND the inner card (rendered
     transparent in glass mode) so it never reads; an inset:0 ::after with the
     shared --ne-glass-shine-bg paints it on top, clipped to the radius. Works
     in both real-glass (Chromium) and mock-glass (Firefox) — it's independent
     of backdrop-filter. The cf-card is excluded from the generic bg-image
     shine in critical.css so this stays the single source. */
  body[data-ne-glass-shine="1"] .ne-pg__cf-card--glass::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    border-radius: inherit;
    background: var(--ne-glass-shine-bg);
  }
  /* Arrows sit ON the sides, vertically centred, above all cards. */
  .ne-pg__coverflow .ne-pg__arrow {
    position: absolute;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    z-index: 300;
  }
  .ne-pg__coverflow .ne-pg__arrow--prev { left: 8px; right: auto; }
  .ne-pg__coverflow .ne-pg__arrow--next { right: 8px; left: auto; }
  /* ── Mobile coverflow ─────────────────────────────────────────────
     Stack + fan: a bigger focus card with only a sliver of the neighbours
     (the JS also hides cards beyond ±2). Drum: the focus card sits ABOVE
     the ring — a narrow phone has no room for the side-by-side split.
     Values dialled visually; tune freely. */
  @media (max-width: 749px) {
    /* Stack + fan + drum — big focus card (the JS no longer shrinks-to-fit on
       mobile, so this is the real focus size). */
    .ne-pg__coverflow--style-stack .ne-pg__cf-card,
    .ne-pg__coverflow--style-fan .ne-pg__cf-card,
    .ne-pg__coverflow--style-drum .ne-pg__cf-card {
      width: min(var(--ne-cf-card-w, 320px), 94%);
    }
    /* Stack — neighbours pulled in + turned + pushed back + shrunk so they tuck
       BEHIND the focus (only slim slivers peek), and stay inside the block so
       nothing hard-clips at the edge. overflow:visible kills the rectangular cut. */
    .ne-pg__coverflow--style-stack { overflow: visible; }
    .ne-pg__coverflow--style-stack .ne-pg__cf-card {
      transform:
        perspective(2600px)
        translate(-50%, -50%)
        translateY(var(--ne-cf-lift, 0px))
        translateX(calc(var(--d) * 30%))
        rotateY(calc(var(--d) * -10deg))
        translateZ(calc(var(--ad) * -120px))
        scale(calc(1 - var(--ad) * 0.12));
    }
    /* Fan — smaller horizontal spread so the arc edges stay in the block while
       the upward arc keeps its headroom; overflow:visible (no clip). */
    .ne-pg__coverflow--style-fan { overflow: visible; }
    .ne-pg__coverflow--style-fan .ne-pg__cf-card {
      transform:
        translate(-50%, -50%)
        translateY(var(--ne-cf-lift, 0px))
        translateX(calc(var(--gd, var(--d)) * 30%))
        translateY(calc(var(--cad, var(--ad)) * var(--cad, var(--ad)) * -16px))
        rotate(calc(var(--cd, var(--d)) * 11deg))
        scale(calc(1 - var(--ad) * 0.06));
    }

    /* Drum — vertical: large focus card on top, a tight one-directional ring
       (same --di fan as desktop, so it's always a clean full ring regardless of
       which card is active) centred directly below it. The JS keeps a short
       contiguous ring (di 0..4), centres it horizontally under the focus via
       --ne-cf-ring-shift, and sits the chevrons on the ring. fit stays 1 so
       nothing is squashed/elongated. */
    .ne-pg__coverflow--style-drum {
      height: calc(var(--ne-cf-h, 460px) * 1.7 * var(--ne-cf-drum-fit, 1) + 40px);
      overflow: visible;
      --ne-cf-origin: 50%;
      --ne-cf-ring-shift: 0px;
    }
    .ne-pg__coverflow--style-drum.ne-pg__coverflow--align-right { --ne-cf-origin: 50%; }
    /* Geometry dialled in via the mobile-drum tuner (2026-06-26). */
    .ne-pg__coverflow--style-drum .ne-pg__cf-card {
      transform:
        perspective(2400px)
        translate(-50%, -50%)
        translateY(calc(var(--ne-cf-h, 460px) * 0.48))
        translateX(calc(var(--di, var(--ad)) * 13% + var(--ne-cf-ring-shift, 0px)))
        rotateY(calc(var(--di, var(--ad)) * -18deg))
        scale(0.66);
      /* Left card frontmost, each one to the right a layer deeper. */
      z-index: calc(90 - var(--di, var(--ad)) * 2);
    }
    /* Focus card lifts above the ring, flat + large (no ring-shift). */
    .ne-pg__coverflow--style-drum .ne-pg__cf-card[data-active] {
      transform:
        perspective(2400px)
        translate(-50%, -50%)
        translateY(calc(var(--ne-cf-h, 460px) * -0.39 + var(--ne-cf-lift, 0px)))
        rotateY(0deg)
        scale(1);
    }
    /* Mobile drum: cards take their natural content height (no forced
       --ne-cf-h min) so the focus card isn't stretched with empty body
       space below the price. Ring sits behind, uneven heights invisible. */
    .ne-pg__coverflow--style-drum .ne-pg__cf-card,
    .ne-pg__coverflow--style-drum .ne-pg__cf-card .ne-product-card { min-height: 0; }
  }
  @media (prefers-reduced-motion: reduce) {
    .ne-pg__cf-card { transition: opacity .2s ease; }
  }
/* END_BLOCK:product-grid */

/* START_BLOCK:product-pickup (INDEX:114) */
.ne-pickup { display: block; }
  /* Hide the wrapper entirely when there is no pickup content (empty node). */
  .ne-pickup:not(:has(.ne-pickup__preview)) { display: none; }

  .ne-pickup__preview {
    display: flex;
    align-items: flex-start;
    gap: var(--ne-space-2);
    color: var(--ne-scheme-text);
  }
  .ne-pickup__icon { color: var(--ne-scheme-accent); flex-shrink: 0; margin-top: 2px; }
  .ne-pickup__preview-text { display: flex; flex-direction: column; gap: 2px; }
  .ne-pickup__status { margin: 0; font-size: var(--ne-font-size-sm); font-weight: 600; }
  .ne-pickup__time { margin: 0; font-size: var(--ne-font-size-xs); color: var(--ne-scheme-fg-muted); }

  .ne-pickup__toggle {
    margin-top: var(--ne-space-2);
    padding: 0;
    background: none;
    border: none;
    color: var(--ne-scheme-link, var(--ne-scheme-accent));
    font-size: var(--ne-font-size-sm);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
  }
  .ne-pickup__toggle:hover { opacity: 0.8; }

  .ne-pickup__list {
    margin-top: var(--ne-space-3);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
    padding: var(--ne-space-3);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md, 8px);
  }
  .ne-pickup__list[hidden] { display: none; }
  .ne-pickup__loc-name { margin: 0; font-size: var(--ne-font-size-sm); font-weight: 600; color: var(--ne-scheme-text); }
  .ne-pickup__loc-time { margin: 2px 0 0; font-size: var(--ne-font-size-xs); color: var(--ne-scheme-fg-muted); }
  .ne-pickup__loc-address { margin: var(--ne-space-1) 0 0; font-style: normal; font-size: var(--ne-font-size-xs); color: var(--ne-scheme-fg-muted); line-height: 1.5; }
/* END_BLOCK:product-pickup */

/* START_BLOCK:product-price (INDEX:115) */
.ne-product-price {
    display: flex;
    align-items: baseline;
    gap: var(--ne-space-2);
    flex-wrap: wrap;
    color: var(--ne-scheme-text);
    font-weight: 600;
    line-height: 1.2;
  }
  .ne-product-price--sm  { font-size: 0.875rem; }
  .ne-product-price--md  { font-size: 1.125rem; }
  .ne-product-price--lg  { font-size: 1.5rem; }
  .ne-product-price--xl  { font-size: 2rem; }
  .ne-product-price--2xl { font-size: 2.75rem; }
  .ne-product-price--3xl { font-size: 3.5rem; }
  .ne-product-price--left   { justify-content: flex-start; text-align: left; }
  .ne-product-price--center { justify-content: center; text-align: center; }
  .ne-product-price--right  { justify-content: flex-end; text-align: right; }
  .ne-product-price--on-sale .ne-product-price__current { color: var(--ne-scheme-sale, var(--ne-scheme-accent)); }
  .ne-product-price__compare {
    color: var(--ne-scheme-fg-muted);
    font-weight: 400;
  }
  .ne-product-price__compare[hidden] { display: none; }
  .ne-product-price__unit,
  .ne-product-price__tax {
    flex-basis: 100%;
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-xs);
    font-weight: 400;
  }
/* END_BLOCK:product-price */

/* START_BLOCK:product-selling-plan (INDEX:116) */
.ne-product-selling-plan {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }
  .ne-product-selling-plan__heading {
    margin: 0;
    font-size: var(--ne-font-size-sm);
    font-weight: 600;
    color: var(--ne-scheme-text);
  }
  .ne-product-selling-plan__group-name {
    margin: 0 0 var(--ne-space-2);
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-fg-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }
  .ne-product-selling-plan__options {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
  }
  .ne-product-selling-plan__option {
    display: flex;
    align-items: center;
    gap: var(--ne-space-3);
    padding: var(--ne-space-3);
    background: var(--ne-glass-bg, transparent);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md);
    cursor: pointer;
    transition: border-color var(--ne-transition-fast), background var(--ne-transition-fast);
  }
  .ne-product-selling-plan__option:hover { border-color: var(--ne-scheme-accent); }
  .ne-product-selling-plan__option--active {
    border-color: var(--ne-scheme-accent);
    background: var(--ne-scheme-accent-glow, rgba(99,102,241,0.1));
  }
  .ne-product-selling-plan__option-name {
    flex: 1;
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text);
  }
  .ne-product-selling-plan__savings {
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-accent);
    font-weight: 600;
  }
/* END_BLOCK:product-selling-plan */

/* START_BLOCK:product-share (INDEX:117) */
.ne-product-share {
    display: flex;
    flex-wrap: wrap;
    gap: var(--ne-space-3);
    align-items: center;
  }
  .ne-product-share--left   { justify-content: flex-start; }
  .ne-product-share--center { justify-content: center; }
  .ne-product-share--right  { justify-content: flex-end; }
  /* Uniform circular icon buttons — accent-color icon, no background by
     default. Hover fills with accent. Same visual language across native
     share + FB / X / Pinterest / WhatsApp / Email. */
  .ne-product-share__btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ne-space-2);
    width: 38px;
    height: 38px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: var(--ne-radius-full);
    color: var(--ne-scheme-accent);
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    transition: color var(--ne-transition-fast), transform var(--ne-transition-fast);
    position: relative;
    text-decoration: none;
    overflow: hidden;
  }
  .ne-product-share__btn:hover {
    color: var(--ne-scheme-text);
    transform: translateY(-1px);
  }
  .ne-product-share__icon { flex-shrink: 0; }

  /* Icon+text / text-only mode: expand horizontally to fit the text
     label, but keep the same accent color + transparent background. */
  .ne-product-share:not(.ne-product-share--icon) .ne-product-share__btn:has(.ne-product-share__label) {
    width: auto;
    height: auto;
    padding: var(--ne-space-2) var(--ne-space-3);
    border-radius: var(--ne-btn-radius, 999px);
  }

  .ne-product-share__copied {
    position: absolute;
    inset: 0;
    display: none;
    align-items: center;
    justify-content: center;
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-radius: inherit;
    padding: 0 var(--ne-space-2);
    font-size: var(--ne-font-size-xs);
  }
  .ne-product-share__btn[data-copied="1"] .ne-product-share__copied { display: flex; }
/* END_BLOCK:product-share */

/* START_BLOCK:product-size-chart (INDEX:118) */
.ne-product-size-chart__trigger {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-2);
    background: none;
    border: none;
    padding: 0;
    color: var(--ne-scheme-fg-muted, var(--ne-scheme-text-muted));
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    transition: color var(--ne-transition-fast);
  }
  .ne-product-size-chart__trigger:hover { color: var(--ne-scheme-accent); }
  .ne-product-size-chart--trigger-link .ne-product-size-chart__trigger {
    text-decoration: underline;
    text-underline-offset: 3px;
  }
  .ne-product-size-chart--trigger-button .ne-product-size-chart__trigger {
    padding: var(--ne-space-2) var(--ne-space-4);
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md);
  }

  .ne-product-size-chart__modal {
    position: fixed;
    inset: 0;
    margin: auto;
    width: min(640px, 90vw);
    max-height: 80vh;
    padding: 0;
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-lg, 12px);
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg));
    color: var(--ne-scheme-text);
    overflow: hidden;
  }
  .ne-product-size-chart__modal::backdrop {
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(8px);
  }
  .ne-product-size-chart__modal-inner {
    display: flex;
    flex-direction: column;
    max-height: 80vh;
  }
  .ne-product-size-chart__modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: var(--ne-space-5) var(--ne-space-6);
    border-bottom: 1px solid var(--ne-scheme-border);
  }
  .ne-product-size-chart__modal-heading {
    margin: 0;
    font-size: var(--ne-font-size-lg);
    font-weight: 600;
  }
  .ne-product-size-chart__modal-close {
    background: none;
    border: none;
    color: var(--ne-scheme-text);
    cursor: pointer;
    padding: var(--ne-space-1);
    display: flex;
  }
  .ne-product-size-chart__modal-body {
    padding: var(--ne-space-6);
    overflow-y: auto;
  }
  .ne-product-size-chart__modal-body table {
    width: 100%;
    border-collapse: collapse;
  }
  .ne-product-size-chart__modal-body th,
  .ne-product-size-chart__modal-body td {
    padding: var(--ne-space-2) var(--ne-space-3);
    border: 1px solid var(--ne-scheme-border);
    text-align: left;
    font-size: var(--ne-font-size-sm);
  }
/* END_BLOCK:product-size-chart */

/* START_BLOCK:product-tabs (INDEX:119) */
.ne-product-tabs {
    display: flex;
    flex-direction: column;
  }
  .ne-product-tabs__nav {
    display: flex;
    gap: 0;
  }
  .ne-product-tabs__btn {
    background: none;
    border: none;
    padding: var(--ne-space-3) var(--ne-space-4);
    color: var(--ne-scheme-fg-muted);
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    transition: all var(--ne-transition-fast);
    font-family: inherit;
  }
  .ne-product-tabs__btn--active { color: var(--ne-scheme-text); }

  /* Underline style */
  .ne-product-tabs--underline .ne-product-tabs__nav { border-bottom: 1px solid var(--ne-scheme-border); }
  .ne-product-tabs--underline .ne-product-tabs__btn {
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
  }
  .ne-product-tabs--underline .ne-product-tabs__btn--active { border-bottom-color: var(--ne-scheme-accent); }

  /* Pill style — radius forced via inline-style (Rule 175) */
  .ne-product-tabs--pill .ne-product-tabs__nav { gap: var(--ne-space-2); }
  .ne-product-tabs--pill .ne-product-tabs__btn {
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
    border: 1px solid var(--ne-scheme-border);
  }
  .ne-product-tabs--pill .ne-product-tabs__btn--active {
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-color: var(--ne-scheme-accent);
  }

  /* Square style — sharp corners, simple bordered button */
  .ne-product-tabs--square .ne-product-tabs__nav { gap: var(--ne-space-2); }
  .ne-product-tabs--square .ne-product-tabs__btn {
    background: transparent;
    border: 1px solid var(--ne-scheme-border);
  }
  .ne-product-tabs--square .ne-product-tabs__btn--active {
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-color: var(--ne-scheme-accent);
  }

  .ne-product-tabs__panels { padding-top: var(--ne-space-4); }
  .ne-product-tabs__panel {
    color: var(--ne-scheme-fg-muted, var(--ne-scheme-text-muted));
    font-size: var(--ne-font-size-sm);
    line-height: 1.7;
  }
  .ne-product-tabs__panel p:first-child { margin-top: 0; }
  .ne-product-tabs__panel p:last-child  { margin-bottom: 0; }

  /* Rich-text media compat — mirror blocks/product-description.liquid so that
     images inserted via Shopify's Polaris editor (e.g. a row of certification
     / trust badges) flow inline instead of stacking. Counters the global
     `img { display: block }` reset (critical.css) and makes solo-image
     paragraph/div wrappers inline-block so adjacent badges sit side by side
     and wrap at the viewport edge. */
  .ne-product-tabs__panel p:has(> img:only-child),
  .ne-product-tabs__panel p:has(> a:only-child > img:only-child),
  .ne-product-tabs__panel p:has(> span:only-child > img:only-child),
  .ne-product-tabs__panel div:has(> img:only-child),
  .ne-product-tabs__panel div:has(> a:only-child > img:only-child),
  .ne-product-tabs__panel div:has(> span:only-child > img:only-child) {
    display: inline-block;
    vertical-align: middle;
    margin: 0 var(--ne-space-2) var(--ne-space-2) 0;
  }
  .ne-product-tabs__panel img {
    display: inline-block;
    vertical-align: middle;
    margin-block: 0;
  }
  /* Standalone image source (e.g. a size-chart metafield) — block-level and
     scales to the panel width. Higher specificity than the inline-block rule
     above so the chart isn't forced inline. */
  .ne-product-tabs__panel img.ne-product-tabs__image {
    display: block;
    margin: var(--ne-space-2) 0;
    border-radius: var(--ne-radius-md);
  }
/* END_BLOCK:product-tabs */

/* START_BLOCK:product-title (INDEX:120) */
.ne-product-title {
    margin: 0;
    color: var(--ne-scheme-text);
    word-break: break-word;
    overflow-wrap: anywhere;
    hyphens: auto;
  }
  .ne-product-title--left   { text-align: left; }
  .ne-product-title--center { text-align: center; }
  .ne-product-title--right  { text-align: right; }
  .ne-product-title--font-body       { font-family: var(--font-body--family); }
  .ne-product-title--font-subheading { font-family: var(--font-subheading--family); }
  .ne-product-title--font-heading    { font-family: var(--font-heading--family); }
  .ne-product-title--font-accent     { font-family: var(--font-accent--family); }
/* END_BLOCK:product-title */

/* START_BLOCK:product-variant-picker (INDEX:121) */
.ne-product-variants {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }

  .ne-product-variants__option {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
    width: 100%;
  }
  /* Select sits at content-width — alignment-modifier drives where it
     sits in the option column. Swatches/btns use justify-content (set
     in the alignment block above), select uses align-self. */
  .ne-product-variants--left   .ne-product-variants__select { align-self: flex-start; }
  .ne-product-variants--center .ne-product-variants__select { align-self: center; }
  .ne-product-variants--right  .ne-product-variants__select { align-self: flex-end; }
  .ne-product-variants__label {
    display: flex;
    gap: var(--ne-space-1);
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
    color: var(--ne-scheme-text);
  }
  .ne-product-variants__value {
    color: var(--ne-scheme-fg-muted);
    font-weight: 400;
  }

  /* Alignment — drives label text-align AND swatch/btn justify-content
     so it actually has a visible effect (Rule 1 of layout — when items
     fill the parent width, align-items on the parent is a no-op). */
  .ne-product-variants--left   .ne-product-variants__label { justify-content: flex-start; text-align: left; }
  .ne-product-variants--center .ne-product-variants__label { justify-content: center;     text-align: center; }
  .ne-product-variants--right  .ne-product-variants__label { justify-content: flex-end;   text-align: right; }
  .ne-product-variants--left   .ne-product-variants__swatches,
  .ne-product-variants--left   .ne-product-variants__btns   { justify-content: flex-start; }
  .ne-product-variants--center .ne-product-variants__swatches,
  .ne-product-variants--center .ne-product-variants__btns   { justify-content: center; }
  .ne-product-variants--right  .ne-product-variants__swatches,
  .ne-product-variants--right  .ne-product-variants__btns   { justify-content: flex-end; }

  /* Hide native radio circles — selected state is shown via the
     swatch/button --active modifier instead. Keep accessible to
     screen readers via off-screen positioning, not display:none. */
  .ne-product-variants input[type="radio"] {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
    appearance: none;
  }

  /* Color swatches */
  .ne-product-variants__swatches {
    display: flex;
    gap: var(--ne-space-2);
    flex-wrap: wrap;
  }
  .ne-product-variants__swatch { cursor: pointer; }
  .ne-product-variants__swatch-color {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: var(--ne-radius-full);
    border: 2px solid var(--ne-scheme-variant-border, var(--ne-scheme-border));
    transition: border-color var(--ne-transition-fast), box-shadow var(--ne-transition-fast);
    /* Glass-bead optic (theme → Glass colour swatches). transparent default
       so it's a no-op when off. Composed in every state below so the bead
       coexists with the ring / variant glow. */
    box-shadow: var(--ne-swatch-glass-fx, 0 0 0 transparent);
  }
  .ne-product-variants__swatch-color--image {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  .ne-product-variants__swatch--active .ne-product-variants__swatch-color {
    border-color: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent));
    box-shadow:
      var(--ne-swatch-glass-fx, 0 0 0 transparent);
  }

  /* Button swatches */
  .ne-product-variants__btns {
    display: flex;
    gap: var(--ne-space-2);
    flex-wrap: wrap;
  }
  .ne-product-variants__btn { cursor: pointer; }
  .ne-product-variants__btn-label {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: var(--ne-space-2) var(--ne-space-3);
    /* Circle minimum: width AND height = 2.5em minimum. Combined with
       pill border-radius (999px) single-char labels (XS, S, M, L)
       render as PERFECT CIRCLES (square box + pill radius), longer
       labels (XL, XXL, 3XL) expand horizontally into pills.
       Without min-height the natural padding gave height ~36px while
       min-width forced 40px → oval rather than circle (user report
       2026-05-23). 2.5em on both axes guarantees circle for short
       text. line-height: 1 keeps the text from inflating height. */
    min-width: 2.5em;
    min-height: 2.5em;
    line-height: 1;
    background: var(--ne-scheme-variant-bg, transparent);
    color: var(--ne-scheme-variant-text, var(--ne-scheme-text));
    border: 1px solid var(--ne-scheme-variant-border, var(--ne-scheme-border));
    /* Follows theme Buttons & Inputs → Variant button shape (pill /
       square / rounded). Falls back to main btn-radius. */
    border-radius: var(--ne-variant-btn-radius, var(--ne-btn-radius, var(--ne-radius-md, 6px)));
    font-size: var(--ne-font-size-sm);
    transition: all var(--ne-transition-fast);
  }
  .ne-product-variants__btn--active .ne-product-variants__btn-label {
    background: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent));
    color: var(--ne-scheme-variant-text-selected, var(--ne-scheme-btn-text));
    border-color: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent));
  }

  /* ── Selected-variant glow / shadow (Effects) ─────────────────────────
     Applies ONLY to the selected swatch / button. Glow = two-layer accent
     core + halo (matches the header button); --glow-variant switches the
     core colour to the selected-variant colour. Composes the glass bevel
     (when Theme → glass-buttons is on) so it co-exists with glass mode.
     Vars default to transparent so the box-shadow list stays valid. */
  .ne-product-variants--glow   { --ne-pv-glow: 0 0 12px var(--ne-pv-glow-c, var(--ne-scheme-accent)), 0 0 24px color-mix(in srgb, var(--ne-pv-glow-c, var(--ne-scheme-accent)) 45%, transparent); }
  .ne-product-variants--shadow { --ne-pv-shadow: 0 6px 16px rgba(0, 0, 0, 0.20); }
  .ne-product-variants--glow-variant { --ne-pv-glow-c: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent)); }

  .ne-product-variants--glow .ne-product-variants__btn--active .ne-product-variants__btn-label,
  .ne-product-variants--shadow .ne-product-variants__btn--active .ne-product-variants__btn-label {
    box-shadow:
      var(--ne-glass-btn-bevel, 0 0 0 transparent),
      var(--ne-pv-glow, 0 0 0 transparent),
      var(--ne-pv-shadow, 0 0 0 transparent) !important;
  }
  /* Colour swatch — source=variant: glow uses the swatch's OWN colour
     (--ne-sw-c, emitted inline per swatch). Overriding --ne-pv-glow-c on the
     swatch makes --ne-pv-glow (defined on the root, referencing it) resolve
     to the swatch colour at use-time. Button swatches inherit the root's
     --ne-pv-glow-c (= selected-variant colour) since they carry no --ne-sw-c. */
  /* Glow active (accent source / default): accent glow + glass bead, NO ring.
     Reset the border-color back to the default (the active rule set it to the
     accent colour = the thin violet ring the user saw, user 2026-05-24). */
  .ne-product-variants--glow .ne-product-variants__swatch--active .ne-product-variants__swatch-color {
    border-color: transparent;
    box-shadow:
      var(--ne-pv-glow, 0 0 0 transparent),
      var(--ne-swatch-glass-fx, 0 0 0 transparent),
      var(--ne-pv-shadow, 0 0 0 transparent);
  }
  /* Variant source: glow in the swatch's OWN colour, used DIRECTLY here
     (no --ne-pv-glow indirection — that resolved against the root's
     --ne-pv-glow-c, not the per-swatch one, so it fell back to accent;
     user 2026-05-24). --ne-sw-c is emitted inline per swatch. Lightened
     ~28% toward white so dark swatches (charcoal) still glow visibly.
     Specificity (0,4,0) beats the accent rule above. */
  .ne-product-variants--glow.ne-product-variants--glow-variant .ne-product-variants__swatch--active .ne-product-variants__swatch-color {
    border-color: transparent;
    box-shadow:
      0 0 12px color-mix(in srgb, var(--ne-sw-c, var(--ne-scheme-accent)) 72%, white),
      0 0 24px color-mix(in srgb, var(--ne-sw-c, var(--ne-scheme-accent)) 40%, transparent),
      var(--ne-swatch-glass-fx, 0 0 0 transparent),
      var(--ne-pv-shadow, 0 0 0 transparent);
  }
  /* Shadow only (no glow): keep the accent selection ring + shadow. */
  .ne-product-variants--shadow:not(.ne-product-variants--glow) .ne-product-variants__swatch--active .ne-product-variants__swatch-color {
    box-shadow:
      var(--ne-swatch-glass-fx, 0 0 0 transparent),
      var(--ne-pv-shadow, 0 0 0 transparent);
  }

  /* Select — content-width based on the longest option, capped to
     parent. min-width keeps single-letter sizes (S/M/L) readable. */
  .ne-product-variants__select {
    padding: var(--ne-space-2) var(--ne-space-3);
    background: var(--ne-scheme-input-bg, var(--ne-glass-bg, rgba(255,255,255,0.05)));
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border));
    border-radius: var(--ne-btn-radius, var(--ne-radius-input, var(--ne-radius-md, 8px)));
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    font-size: var(--ne-font-size-sm);
    width: auto;
    min-width: 120px;
    max-width: 100%;
  }
  .ne-product-variants__select:focus {
    outline: none;
    border-color: var(--ne-scheme-accent);
    box-shadow: 0 0 0 3px var(--ne-scheme-accent-glow, var(--ne-scheme-accent-subtle));
  }
/* END_BLOCK:product-variant-picker */

/* START_BLOCK:product-vendor (INDEX:122) */
.ne-product-vendor {
    margin: 0;
    color: var(--ne-scheme-fg-muted, var(--ne-scheme-text-muted));
    line-height: 1.4;
  }
  .ne-product-vendor--xs { font-size: 0.75rem; }
  .ne-product-vendor--sm { font-size: 0.875rem; }
  .ne-product-vendor--md { font-size: 1rem; }
  .ne-product-vendor--lg { font-size: 1.25rem; }
  .ne-product-vendor--xl { font-size: 1.5rem; }
  .ne-product-vendor--2xl { font-size: 2rem; }
  .ne-product-vendor--left   { text-align: left; }
  .ne-product-vendor--center { text-align: center; }
  .ne-product-vendor--right  { text-align: right; }
  .ne-product-vendor--uppercase { text-transform: uppercase; letter-spacing: 0.05em; }
  .ne-product-vendor__link {
    color: inherit;
    text-decoration: none;
    transition: color var(--ne-transition-fast);
  }
  .ne-product-vendor__link:hover { color: var(--ne-scheme-accent); }
/* END_BLOCK:product-vendor */

/* START_BLOCK:product-wishlist (INDEX:123) */
.ne-product-wishlist {
    display: flex;
  }
  .ne-product-wishlist--left   { justify-content: flex-start; }
  .ne-product-wishlist--center { justify-content: center; }
  .ne-product-wishlist--right  { justify-content: flex-end; }

  .ne-product-wishlist__btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-2);
    padding: var(--ne-space-2) var(--ne-space-3);
    background: transparent;
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-btn-radius, var(--ne-radius-md, 8px));
    color: var(--ne-scheme-text);
    font-family: inherit;
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    transition: color var(--ne-transition-fast), border-color var(--ne-transition-fast), background var(--ne-transition-fast);
  }
  .ne-product-wishlist__btn:hover {
    color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
    border-color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
  }
  .ne-product-wishlist__btn.ne-wishlist-btn--active {
    color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
    border-color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
  }
  .ne-product-wishlist__btn.ne-wishlist-btn--active .ne-product-wishlist__icon {
    fill: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
  }

  .ne-product-wishlist__icon { flex-shrink: 0; }

  /* Icon-only variant: square button with just the heart */
  .ne-product-wishlist--icon .ne-product-wishlist__btn {
    width: 38px;
    height: 38px;
    padding: 0;
    justify-content: center;
    border-radius: var(--ne-radius-full);
  }
  /* Text-only: hide icon */
  .ne-product-wishlist--text .ne-product-wishlist__icon { display: none; }

  /* Sync mode: read --ne-pd-btn-width from the section ancestor, set
     by the buy-buttons block. Result: wishlist matches ATC width
     exactly without any per-block setting. */
  .ne-product-wishlist--sync-buy .ne-product-wishlist__btn {
    width: var(--ne-pd-btn-width, 100%);
    justify-content: center;
    padding: var(--ne-btn-padding-y, var(--ne-space-3)) var(--ne-btn-padding-x, var(--ne-space-6));
  }
  /* Independent width mode: --ne-pd-wl-width emitted on the block via
     inline style (only when sync is OFF). */
  .ne-product-wishlist:not(.ne-product-wishlist--sync-buy) .ne-product-wishlist__btn {
    width: var(--ne-pd-wl-width, 100%);
    justify-content: center;
    padding: var(--ne-btn-padding-y, var(--ne-space-3)) var(--ne-btn-padding-x, var(--ne-space-6));
  }
/* END_BLOCK:product-wishlist */

/* START_BLOCK:recently-viewed-grid (INDEX:124) */
.ne-rv-grid {
    width: 100%;
    background: transparent !important;
  }
  .ne-rv-grid--hide-empty:not([data-rv-state="loaded"]) {
    display: none;
  }
  .ne-rv-grid--hide-empty[data-rv-state="empty"] {
    display: none;
  }

  .ne-rv-grid__track {
    display: grid;
    grid-template-columns: repeat(var(--ne-rv-cols, 4), minmax(0, 1fr));
    gap: var(--ne-rv-gap, 16px);
    /* Inset matches product-grid's .ne-pg__grid so card shadows /
       glow / hover-zoom have the same breathing room around the
       outer card edges. */
    padding: var(--ne-space-2, 8px) var(--ne-space-2, 8px) var(--ne-space-4, 16px);
  }
  @media (max-width: 749px) {
    .ne-rv-grid__track {
      grid-template-columns: repeat(var(--ne-rv-cols-m, 2), minmax(0, 1fr));
    }
  }

  .ne-rv-grid__empty {
    margin: 0;
    padding: 24px 0;
    text-align: center;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.7;
    font-size: 0.9rem;
  }

  /* Card — own namespace (no .ne-product-card collisions).
     overflow: hidden clips the media to the card's border-radius so
     image top corners follow card corners (mirrors product-card
     behaviour). align-items: stretch on the parent grid + a bottom
     margin: auto on the price pushes price rows to align across cards
     in the same grid row even when titles wrap differently.
     Hover transform keeps translateZ via translate3d so the GPU
     compositing layer doesn't collapse mid-frame (Rule 162). */
  .ne-rv-grid .ne-rv-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--ne-rv-title-price-gap, 8px);
    padding: var(--ne-rv-pad, 0);
    border-radius: var(--ne-rv-radius, 12px);
    overflow: hidden;
    isolation: isolate;
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
    height: 100%;
    transition: transform var(--ne-transition-base, 0.2s ease), box-shadow var(--ne-transition-base, 0.2s ease);
  }
  .ne-rv-grid .ne-rv-card:hover {
    transform: translate3d(0, -4px, 0);
  }
  .ne-rv-grid--surface-solid .ne-rv-card {
    background: var(--ne-scheme-bg, transparent);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-rv-grid--surface-glass .ne-rv-card {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.12));
    will-change: backdrop-filter;
  }
  /* Accent-tinted glass cards (same --ne-glass-bg-tinted var as everywhere). */
  .ne-rv-grid--surface-glass.ne-rv-grid--glass-tint .ne-rv-card {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
    border-color: var(--ne-glass-border-tinted, var(--ne-glass-border, rgba(255, 255, 255, 0.12)));
  }

  /* Card shadow / glow — mirrors product-card snippet:
     Shadow visible at rest, slightly stronger on hover.
     Glow lights up only on hover (so cards aren't perma-glowing). */
  .ne-rv-grid--shadow .ne-rv-card {
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
  }
  .ne-rv-grid--shadow .ne-rv-card:hover {
    box-shadow: 0 5px 14px rgba(0, 0, 0, 0.16);
  }
  .ne-rv-grid--glow .ne-rv-card:hover {
    box-shadow: 0 0 16px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.22));
  }
  /* Both: hover prefers glow chip, resting state keeps shadow */
  .ne-rv-grid--shadow.ne-rv-grid--glow .ne-rv-card {
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.12);
  }
  .ne-rv-grid--shadow.ne-rv-grid--glow .ne-rv-card:hover {
    box-shadow:
      0 0 14px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.22)),
      0 4px 12px rgba(0, 0, 0, 0.14);
  }

  .ne-rv-grid .ne-rv-card__link {
    color: inherit;
    text-decoration: none;
    display: contents;
  }

  .ne-rv-grid .ne-rv-card__media {
    position: relative;
    width: 100%;
    border-radius: var(--ne-rv-img-radius, 0);
    overflow: clip;
    /* No bg here — a dark scheme-bg-muted shows as a thin black band
       at image edges if the image doesn't perfectly cover. Card itself
       provides the surface (transparent / solid / glass). */
  }
  .ne-rv-grid--ratio-square .ne-rv-card__media    { aspect-ratio: 1 / 1; }
  .ne-rv-grid--ratio-portrait .ne-rv-card__media  { aspect-ratio: 3 / 4; }
  .ne-rv-grid--ratio-landscape .ne-rv-card__media { aspect-ratio: 4 / 3; }
  .ne-rv-grid--ratio-natural .ne-rv-card__media   { aspect-ratio: auto; min-height: 200px; }

  .ne-rv-grid .ne-rv-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
    display: block;
    transition: opacity 0.25s ease, transform 0.4s ease;
    /* Permanent GPU layer so hover-scale doesn't trigger layer-create
       flicker (mirrors product-card.liquid Rule 162). */
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
  }

  /* Image hover-zoom (mirrors product-card snippet for visual consistency).
     Single-image cards: primary scales to 1.06.
     Cards with secondary: primary fades to 0, secondary fades to 1
     and slightly scales (1.03) for a parallax feel. */
  .ne-rv-grid .ne-rv-card:not(.ne-rv-card--has-secondary):hover .ne-rv-card__img--primary {
    transform: scale3d(1.06, 1.06, 1);
  }
  .ne-rv-grid .ne-rv-card__img--secondary {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
  }
  .ne-rv-grid .ne-rv-card--has-secondary:hover .ne-rv-card__img--primary {
    opacity: 0;
  }
  .ne-rv-grid .ne-rv-card--has-secondary:hover .ne-rv-card__img--secondary {
    opacity: 1;
    transform: scale3d(1.03, 1.03, 1);
  }

  /* Position via global theme-settings vars (Layout → Product cards).
     Visual treatment lives in critical.css (.ne-badge--card / --sale / --sold-out / --new).
     Flex with gap to support multiple badges side-by-side (Sale + New). */
  .ne-rv-grid .ne-rv-card__badge-pos {
    position: absolute;
    top:    var(--ne-badge-top, 8px);
    right:  var(--ne-badge-right, auto);
    bottom: var(--ne-badge-bottom, auto);
    left:   var(--ne-badge-left, 8px);
    z-index: 2;
    display: flex;
    gap: 4px;
  }

  /* Typography mirrors snippets/product-card.liquid so cards look
     identical to those rendered by the static product-grid block. */
  .ne-rv-grid .ne-rv-card__vendor {
    display: block;
    /* Vendor→title gap follows the row-gap slider (mirrors
       .ne-product-card behaviour so all card variants stay in sync). */
    margin: 0 0 var(--ne-rv-title-price-gap, 2px);
    padding: 0 var(--ne-space-3, 12px);
    font-size: var(--ne-font-size-xs, 0.75rem);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
    color: var(--ne-scheme-text);
  }

  .ne-rv-grid .ne-rv-card__title {
    margin: 0;
    padding: var(--ne-space-3, 12px) var(--ne-space-3, 12px) 0;
    font-family: var(--ne-rv-title-font, inherit);
    font-size: var(--ne-font-size-sm, 0.875rem);
    font-weight: 500;
    line-height: 1.3;
    color: var(--ne-scheme-text);
    display: -webkit-box;
    -webkit-line-clamp: var(--ne-rv-title-lines, 2);
    -webkit-box-orient: vertical;
    overflow: hidden;
    transition: color 0.18s ease, opacity 0.18s ease;
  }
  .ne-rv-grid--title-bold .ne-rv-card__title {
    font-weight: 700;
  }
  .ne-rv-grid--title-italic .ne-rv-card__title {
    font-style: italic;
  }
  .ne-rv-grid--title-no-clamp .ne-rv-card__title {
    -webkit-line-clamp: unset;
    display: block;
    overflow: visible;
  }
  .ne-rv-grid .ne-rv-card:hover .ne-rv-card__title {
    opacity: 0.75;
    color: var(--ne-scheme-text);
  }
  .ne-rv-grid .ne-rv-card:hover .ne-rv-card__title {
    color: var(--ne-scheme-accent);
  }

  /* Price sits at card bottom — margin-top: auto pushes it down
     within the flex column. Combined with align-items: stretch on
     the parent grid (default) + height: 100% on the card, prices in
     the same grid row line up at the same y, even when titles wrap
     to different lengths. */
  .ne-rv-grid .ne-rv-card__price {
    margin-top: auto;
    padding: var(--ne-rv-title-price-gap, 8px) var(--ne-space-3, 12px) var(--ne-space-3, 12px);
    font-size: var(--ne-font-size-sm, 0.875rem);
    font-weight: 500;
    color: var(--ne-scheme-text);
    display: flex;
    gap: var(--ne-space-2, 8px);
    align-items: center;
  }
  .ne-rv-grid .ne-rv-card__compare {
    text-decoration: line-through;
    opacity: 0.55;
    font-weight: 400;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
  }
  /* Sale-price reads --ne-scheme-sale directly (the per-scheme token)
     instead of the --ne-color-sale alias at :root — the alias path
     resolves against scheme-1 in some cascade contexts and the card's
     sale-price stays default-red even when the active scheme has a
     custom sale_price colour. */
  .ne-rv-grid .ne-rv-card__price--sale {
    color: var(--ne-scheme-sale, #f87171);
    font-weight: 600;
  }
/* END_BLOCK:recently-viewed-grid */

/* START_BLOCK:related-products-grid (INDEX:125) */
.ne-rp-grid {
    width: 100%;
    background: transparent !important;
    display: grid;
    grid-template-columns: repeat(var(--ne-rp-cols, 4), minmax(0, 1fr));
    gap: var(--ne-rp-gap, 16px);
    /* Outer inset matches product-grid's .ne-pg__grid so card shadows
       / glow / hover-lift have the same breathing room. */
    padding: var(--ne-space-2, 8px) var(--ne-space-2, 8px) var(--ne-space-4, 16px);
    /* Cards align via stretch — product-card has internal 1fr title
       row that absorbs height differences so prices line up. */
    align-items: stretch;
    /* Shared row tracks so the card subgrid (product-card.liquid) can
       align media/title/price/swatches/atc ACROSS cards — without this,
       mixing products with and without swatches drifted the price row
       (user 2026-07-23). Implicit rows repeat `auto` for row 2+. */
    grid-template-rows: auto 1fr auto auto auto;
    grid-auto-rows: auto;
  }
  .ne-rp-grid > .ne-product-card { grid-row: span 5; }
  @media (max-width: 989px) {
    .ne-rp-grid { grid-template-columns: repeat(3, 1fr); }
  }
  @media (max-width: 749px) {
    .ne-rp-grid {
      grid-template-columns: repeat(var(--ne-rp-cols-m, 2), 1fr);
    }
  }

  /* Hide-when-empty: hide if the fallback grid emitted no children
     AND JS hasn't loaded recommendations yet (or loaded none). The
     [data-rp-state="empty"] is set by JS when fetch returns 0. */
  .ne-rp-grid--hide-empty:empty,
  .ne-rp-grid--hide-empty[data-rp-state="empty"] {
    display: none;
  }
/* END_BLOCK:related-products-grid */

/* START_BLOCK:scramble-heading (INDEX:126) */
.ne-scramble-heading {
    margin: 0;
    line-height: 1.1;
    letter-spacing: -0.01em;
    text-wrap: balance;
    background: transparent !important;
  }
/* END_BLOCK:scramble-heading */

/* START_BLOCK:shader-logo (INDEX:127) */
.ne-shlogo {
  display: block;
  max-width: 100%;
}
.ne-shlogo__stage {
  position: relative;
  width: 100%;
  height: var(--ne-shlogo-h, 360px);
}
.ne-shlogo__canvas,
.ne-shlogo__fallback {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}
/* Static shape: a solid gradient of the shader colours, shown when the live
   canvas is hidden (reduce-motion / no-WebGL) or before it boots. */
.ne-shlogo__fallback {
  z-index: 0;
  background: linear-gradient(135deg,
    var(--ne-sh-a, #0d0d0f) 0%,
    var(--ne-sh-b, #f7931a) 55%,
    var(--ne-sh-c, var(--ne-sh-b, #f7931a)) 100%);
}
.ne-shlogo__canvas {
  z-index: 1;
  display: block;
}
.ne-shlogo__sr {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0);
  white-space: nowrap; border: 0;
}
@media (max-width: 749px) {
  .ne-shlogo__stage { height: var(--ne-shlogo-hm, 260px); }
}
/* END_BLOCK:shader-logo */

/* START_BLOCK:shape (INDEX:129) */
.ne-shape {
    position: relative;
    box-sizing: border-box;
    display: block;
    aspect-ratio: var(--ne-shape-aspect, 1 / 1);
    min-width: 0;
    max-width: 100%;
    background: transparent !important;
  }
  /* Fill-parent (aspect = "parent"): cover the parent tile edge-to-edge. Uses
     ABSOLUTE positioning, not flex/height:100% — a grid item's height is not
     "definite" for percentage-height resolution, so a flex-basis-0 / height:100%
     child collapses to 0 (verified). position:absolute + inset:0 sizes against the
     containing block's used box, which is robust. The bento cell content box is
     position:relative, so the shape fills it (padding area included → true full
     bleed, padding need not be zeroed). Meant for a shape used as a tile fill. */
  .ne-shape--fill {
    position: absolute;
    inset: 0;
    width: auto;
    height: auto;
    max-width: none;
    aspect-ratio: auto;
  }
  .ne-shape.ne-cov { color: var(--ne-scheme-text); }
  /* A color-scheme class paints a full-box background (Rule 173). The Shape is
     only the clipped fill, so the box itself must stay transparent — the
     cut-away corners show the page behind, never a square background. */
  .ne-shape[class*="color-scheme-"] { background: transparent !important; }
  .ne-shape__fill {
    position: absolute;
    inset: 0;
    clip-path: var(--ne-shape-clip, none);
    -webkit-clip-path: var(--ne-shape-clip, none);
    overflow: hidden;
    background: transparent;
  }
  .ne-shape__fill--solid { background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg)); }
  .ne-shape[class*="color-scheme-"] .ne-shape__fill--solid {
    background: var(--ne-scheme-bg-surface, var(--ne-scheme-bg)) !important;
  }
  .ne-shape__fill--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  .ne-shape__shader {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    pointer-events: none;
  }
  .ne-shape__img {
    position: absolute;
    /* JS sizes the box to the polygon's bounding box (image fits the shape at
       100%). Until then, fall back to filling the block. */
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transform-origin: center;
    transform: translate(var(--ne-shape-img-tx, 0%), var(--ne-shape-img-ty, 0%)) scale(var(--ne-shape-img-scale, 1));
    filter: blur(var(--ne-shape-img-blur, 0px));
    opacity: var(--ne-shape-img-opacity, 1);
  }
  .ne-shape__overlay {
    position: absolute;
    inset: 0;
    pointer-events: none;
  }
  /* Editor-only corner handles (freeform) */
  .ne-shape__handles {
    position: absolute;
    inset: 0;
    pointer-events: none;
    z-index: 5;
  }
  .ne-shape__handle {
    position: absolute;
    transform: translate(-50%, -50%);
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 11px;
    font-weight: 700;
    line-height: 1;
    color: #fff;
    background: var(--ne-scheme-accent, #6366f1);
    border: 2px solid #fff;
    border-radius: 999px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.45);
  }
  .ne-shape__clipsvg { position: absolute; width: 0; height: 0; pointer-events: none; }
  .ne-shape__pts { position: absolute; width: 0; height: 0; }
/* END_BLOCK:shape */

/* START_BLOCK:slide (INDEX:130) */
/* Layout: parent section's track is a flex row with scroll-snap.
     Slide block is a flex item that fills its track-cell. Internal
     __media + __content layers sit absolutely inside the slide. */
  .ne-slide {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
  }

  /* Media — full-bleed; user-chosen object-position keeps the focal
     point regardless of crop. */
  .ne-slide__media {
    position: absolute;
    inset: 0;
    overflow: hidden;
  }
  .ne-slide__image,
  .ne-slide__video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: var(--ne-slide-img-pos, 50% 50%);
    display: block;
  }
  .ne-slide__placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ne-scheme-bg-surface, rgba(127,127,127,0.08));
    color: #999;
    padding: 8%;
  }
  .ne-slide__placeholder svg {
    width: 100%;
    max-width: 480px;
    height: auto;
    opacity: 0.4;
  }
  .ne-slide__overlay {
    position: absolute;
    inset: 0;
    background: #000;
    opacity: var(--ne-slide-overlay, 0.3);
    pointer-events: none;
  }

  /* Content layer — absolutely positioned over the media using the
     v_align / h_align flex props so the card lands at any of the
     3×3 positions without explicit top/left math. */
  .ne-slide__content {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: var(--ne-slide-v-align, center);
    justify-content: var(--ne-slide-h-align, center);
    padding: clamp(24px, 5vw, 64px);
    pointer-events: none;
  }
  .ne-slide__card {
    pointer-events: auto;
    max-width: var(--ne-slide-content-max, 640px);
    text-align: var(--ne-slide-text-align, center);
    border-radius: var(--ne-slide-content-radius, 0);
    padding: var(--ne-slide-content-pad, 24px);
    display: flex;
    flex-direction: column;
    gap: var(--ne-slide-content-gap, 16px);
    align-items: var(--ne-slide-h-align, center);
  }

  /* Full-width content (slide setting "Full width"): the card fills the slide
     content area (minus the content layer's padding) instead of the centered
     max-width box — for text-only slides that should span the whole width.
     Specificity (0,2,0) beats the base .ne-slide__card max-width. */
  .ne-slide--content-full .ne-slide__card {
    max-width: none;
    width: 100%;
  }

  /* Theme-block wrapper collapses (Rule 70) so child blocks behave as
     direct children of the card flex layout. */
  .ne-slide__card > .shopify-block { display: contents; }
  /* No blocks on the slide → don't render an empty padded glass/scheme box
     (user QA 2026-05-25 #29). */
  .ne-slide__card:not(:has(> *)) { display: none; }

  /* Glass content card — when enabled, adds a backdrop-filtered
     surface behind the text. Tinted variant uses the scheme accent. */
  .ne-slide--glass .ne-slide__card {
    background: var(--ne-glass-bg, rgba(0,0,0,0.35));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-slide--glass-tinted .ne-slide__card {
    background: color-mix(in srgb, var(--ne-scheme-accent, #88a) 25%, rgba(0,0,0,0.3));
    border-color: color-mix(in srgb, var(--ne-scheme-accent, #88a) 35%, rgba(255,255,255,0.18));
  }

  @media (max-width: 749px) {
    .ne-slide__content { padding: clamp(20px, 6vw, 32px); }
    .ne-slide__card { max-width: 100%; }
  }
/* END_BLOCK:slide */

/* START_BLOCK:slider (INDEX:131) */
.ne-slider {
    position: relative;
    overflow: hidden;
    width: 100%;
  }
  .ne-slider--fade {
    mask-image: linear-gradient(to right, transparent, black 6%, black 94%, transparent);
    -webkit-mask-image: linear-gradient(to right, transparent, black 6%, black 94%, transparent);
  }
  .ne-slider__track {
    display: flex;
    align-items: center;
    gap: var(--ne-bs-gap, 48px);
    width: max-content;
    will-change: transform;
  }

  /* Infinite marquee — CSS keyframes. `--ne-bs-set-width` is set by JS
     to the measured width of ONE original set (sum of item widths + one
     gap each). When the keyframe reaches translateX(-set-width) the next
     iteration jumps back to translateX(0) — visually seamless because
     clones are pixel-identical copies of the originals. */
  @keyframes ne-slider-left {
    from { transform: translateX(0); }
    to   { transform: translateX(calc(-1 * var(--ne-bs-set-width, 0px))); }
  }
  @keyframes ne-slider-right {
    from { transform: translateX(calc(-1 * var(--ne-bs-set-width, 0px))); }
    to   { transform: translateX(0); }
  }
  /* Schema labels are swapped historically (ltr = content-moves-left was the
     JS convention). Keep behaviour, readable names via direction classes. */
  .ne-slider--dir-ltr .ne-slider__track {
    animation: ne-slider-left var(--ne-bs-duration, 20s) linear infinite;
  }
  .ne-slider--dir-rtl .ne-slider__track {
    animation: ne-slider-right var(--ne-bs-duration, 20s) linear infinite;
  }
  .ne-slider--pause-hover:hover .ne-slider__track {
    animation-play-state: paused;
  }

  /* Theme-block wrappers collapse so brand items become flex children. */
  .ne-slider .shopify-block { display: contents; }

  /* Logo height: var set on .ne-slider from default_logo_height;
     per-brand override sets the same var on .ne-brand-item (closest
     ancestor wins via cascade). */
  .ne-slider .ne-brand-item__logo {
    height: var(--ne-bs-logo-height, 50px);
  }

  /* Grayscale — apply filter to the WHOLE brand-item (parent of <img>).
     This is more robust than targeting the <img> directly: filter cascades
     visually to the entire subtree as one composited layer, so even if some
     descendant rule sets its own filter, the parent's grayscale wins at
     paint time. Recolor items handle their own desaturation via
     background-color swap below. */
  .ne-slider--grayscale .ne-brand-item:not(.ne-brand-item--recolor) {
    filter: grayscale(1);
    transition: filter 250ms;
  }
  .ne-slider--grayscale .ne-brand-item:not(.ne-brand-item--recolor):hover {
    filter: grayscale(0);
  }
  /* Recolor variants: swap background-color (filter would have no effect on
     a flat-painted <span>). */
  .ne-slider--grayscale .ne-brand-item--recolor .ne-brand-item__logo--mask {
    background-color: var(--ne-scheme-text-muted, currentColor) !important;
  }
  .ne-slider--grayscale .ne-brand-item--recolor:hover .ne-brand-item__logo--mask {
    background-color: var(--ne-scheme-accent, currentColor) !important;
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-slider__track {
      animation: none !important;
      flex-wrap: wrap;
      justify-content: center;
      width: auto;
      padding: 0 var(--page-margin, 20px);
    }
    .ne-slider__track > [aria-hidden="true"] { display: none; }
  }
  @media (max-width: 749px) {
    .ne-slider .ne-brand-item__logo {
      height: calc(var(--ne-bs-logo-height, 50px) * 0.75);
    }
    .ne-slider__track { gap: calc(var(--ne-bs-gap, 48px) * 0.6); }
  }
/* END_BLOCK:slider */

/* START_BLOCK:spacer (INDEX:132) */
.ne-block-spacer {
    display: block;
    width: 100%;
    height: var(--ne-spacer-h, 32px);
    pointer-events: none;
  }
  @media (max-width: 990px) {
    .ne-block-spacer { height: var(--ne-spacer-ht, var(--ne-spacer-h, 32px)); }
  }
  @media (max-width: 640px) {
    .ne-block-spacer { height: var(--ne-spacer-hm, var(--ne-spacer-ht, var(--ne-spacer-h, 32px))); }
  }
/* END_BLOCK:spacer */

/* START_BLOCK:store-map (INDEX:133) */
.ne-store-map {
    position: relative;
    width: 100%;
    background: transparent !important;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    /* Picks up the host section's "Gap between blocks" inline var
       (sections set --ne-map-gap on the section element). Falls back
       to 16px when the block is used outside the map section. */
    gap: var(--ne-map-gap, 16px);
  }

  /* Map area wraps embed + overlay info card so the card can be
     absolute-positioned relative to the embed area only. On mobile
     the wrapper switches to flex-column so the overlay card flows
     under the map instead. */
  .ne-store-map__map-area {
    position: relative;
    width: 100%;
  }

  /* Sibling layouts — info card lives next to the map. */
  .ne-store-map--info-pos-right {
    flex-direction: row;
    align-items: stretch;
  }
  .ne-store-map--info-pos-left {
    flex-direction: row-reverse;
    align-items: stretch;
  }
  .ne-store-map--info-pos-right .ne-store-map__map-area,
  .ne-store-map--info-pos-left  .ne-store-map__map-area {
    flex: 1 1 60%;
    min-width: 0;
  }

  .ne-store-map__embed {
    position: relative;
    width: 100%;
    height: var(--ne-store-map-height, 400px);
    border-radius: var(--ne-store-map-radius, 12px);
    overflow: clip;
    isolation: isolate;
    background: var(--ne-scheme-bg-muted, rgba(127, 127, 127, 0.06));
  }

  /* Shadow + glow apply to BOTH the map embed AND the info card so
     the two surfaces match. The default info-card shadow defined
     further down is overridden when these toggles are on. */
  .ne-store-map--shadow .ne-store-map__embed,
  .ne-store-map--shadow .ne-store-map__info-card {
    box-shadow: 0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25);
  }
  .ne-store-map--glow .ne-store-map__embed,
  .ne-store-map--glow .ne-store-map__info-card {
    box-shadow: 0 0 calc(32px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }
  .ne-store-map--shadow.ne-store-map--glow .ne-store-map__embed,
  .ne-store-map--shadow.ne-store-map--glow .ne-store-map__info-card {
    box-shadow:
      0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0, 0, 0, 0.25),
      0 0 calc(32px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.4));
  }

  .ne-store-map__iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
  }

  /* Map invert filter — cheap CSS-filter trick that re-tints Google's
     light-theme map tiles for a dark scheme. Imperfect on photos /
     satellite, fine for standard road map.
       on   = always inverted
       off  = never inverted
       auto = follows the global dark/light toggle (data-ne-mode on
              <html>: "primary" usually maps to the dark scheme). */
  .ne-store-map--invert-on .ne-store-map__iframe {
    filter: invert(90%) hue-rotate(180deg);
  }
  [data-ne-mode="primary"] .ne-store-map--invert-auto .ne-store-map__iframe {
    filter: invert(90%) hue-rotate(180deg);
  }
  [data-ne-mode="alternate"] .ne-store-map--invert-auto .ne-store-map__iframe {
    filter: none;
  }

  /* Info-card overlay — absolutely positioned in any of 4 corners.
     Sits above the iframe (z-index: 2). No default shadow here —
     overlay layout adds a subtle one separately so it reads against
     the map; sibling cards stay flat unless the user toggles shadow. */
  .ne-store-map__info-card {
    position: absolute;
    z-index: 2;
    max-width: min(280px, calc(100% - 32px));
    padding: 14px 16px;
    border-radius: 12px;
    color: var(--ne-scheme-text);
    font-size: 0.85rem;
    line-height: 1.45;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  /* Subtle default shadow ONLY for overlay cards (need separation
     from the map). Sibling cards stay flat at default. :where()
     drops the rule's specificity to (0,1,0) so the user's --shadow
     / --glow toggle (specificity (0,2,0)) wins regardless of source
     order. */
  :where(.ne-store-map--info-overlay) .ne-store-map__info-card {
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
  }
  .ne-store-map__info-card--solid {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-store-map__info-card--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.65));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }

  /* Corner-overlay positions — absolute relative to __map-area
     (which is position: relative). Only apply in overlay layout;
     sibling resets to static further down. */
  .ne-store-map--info-overlay .ne-store-map__info-card--top-left     { top: 16px; left: 16px; }
  .ne-store-map--info-overlay .ne-store-map__info-card--top-right    { top: 16px; right: 16px; }
  .ne-store-map--info-overlay .ne-store-map__info-card--bottom-left  { bottom: 16px; left: 16px; }
  .ne-store-map--info-overlay .ne-store-map__info-card--bottom-right { bottom: 16px; right: 16px; }

  /* Sibling layout — card flows in normal block layout next to the
     map. Reset overlay-specific positioning; rows shrink to their
     content (so icon + text sit together) and the row group aligns
     left / center / right inside the side column. NOTE: no
     `box-shadow: none` reset here — that was clobbering the
     --shadow / --glow toggle below since this rule comes later in
     source order. The shadow toggle now controls both card surfaces. */
  .ne-store-map--info-sibling .ne-store-map__info-card {
    position: static;
    max-width: none;
    width: 100%;
    align-items: var(--ne-info-rows-align, center);
  }
  .ne-store-map--info-sibling .ne-store-map__info-row {
    flex: 0 0 auto;
    width: auto;
    max-width: 100%;
  }
  .ne-store-map--info-sibling .ne-store-map__info-row .ne-rte,
  .ne-store-map--info-sibling .ne-store-map__info-row a {
    flex: 0 0 auto;
  }
  .ne-store-map--info-align-left   { --ne-info-rows-align: flex-start; }
  .ne-store-map--info-align-center { --ne-info-rows-align: center; }
  .ne-store-map--info-align-right  { --ne-info-rows-align: flex-end; }
  .ne-store-map--info-align-left   .ne-store-map__info-row .ne-rte { text-align: left; }
  .ne-store-map--info-align-center .ne-store-map__info-row .ne-rte { text-align: center; }
  .ne-store-map--info-align-right  .ne-store-map__info-row .ne-rte { text-align: right; }
  /* CTA always centered in sibling layouts — independent of info_align
     (info_align only affects the address rows). */
  .ne-store-map--info-sibling .ne-store-map__cta {
    display: flex;
    justify-content: center;
  }


  .ne-store-map__info-row {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    color: var(--ne-scheme-text);
  }
  .ne-store-map__info-row svg {
    flex-shrink: 0;
    margin-top: 3px;
    color: var(--ne-scheme-accent, currentColor);
  }
  .ne-store-map__info-row .ne-rte,
  .ne-store-map__info-row a {
    flex: 1;
    min-width: 0;
  }
  .ne-store-map__info-row a {
    color: var(--ne-scheme-text);
    text-decoration: none;
  }
  .ne-store-map__info-row a:hover {
    color: var(--ne-scheme-accent);
  }
  .ne-store-map__info-row .ne-rte p {
    margin: 0;
  }
  .ne-store-map__info-row .ne-rte p + p {
    margin-top: 4px;
  }

  @media (max-width: 749px) {
    /* Sibling row layouts collapse to column on mobile. Reset all
       desktop flex sizing so children take their intrinsic height
       and the gap between map and address card equals exactly the
       outer --ne-map-gap (no stretching adds phantom space). */
    .ne-store-map--info-pos-right,
    .ne-store-map--info-pos-left {
      flex-direction: column;
    }
    .ne-store-map--info-pos-right .ne-store-map__map-area,
    .ne-store-map--info-pos-left  .ne-store-map__map-area,
    .ne-store-map--info-pos-right .ne-store-map__side,
    .ne-store-map--info-pos-left  .ne-store-map__side {
      flex: 0 0 auto;
      align-self: stretch;
    }
    .ne-store-map--info-sibling .ne-store-map__side {
      justify-content: flex-start;
    }

    /* Overlay positions also drop to under-the-map flow on mobile —
       the corner overlay style cramps the small viewport. The card
       leaves absolute positioning and stacks below the embed inside
       __map-area, which switches to flex-column with the same
       --ne-map-gap as the section. */
    .ne-store-map--info-overlay .ne-store-map__map-area {
      display: flex;
      flex-direction: column;
      gap: var(--ne-map-gap, 16px);
    }
    .ne-store-map--info-overlay .ne-store-map__info-card {
      position: static;
      top: auto;
      right: auto;
      bottom: auto;
      left: auto;
      max-width: none;
      width: 100%;
    }
  }
  @media (max-width: 599px) {
    .ne-store-map__info-card {
      padding: 12px 14px;
      font-size: 0.8rem;
    }
  }

  /* Sibling layout wrapper — vertically stacks info-card + directions
     CTA into a single column next to the map. Internal gap matches
     the section's "Gap between blocks" so the address card sits as
     close to the CTA as the user configured. */
  .ne-store-map__side {
    display: flex;
    flex-direction: column;
    gap: var(--ne-map-gap, 16px);
    justify-content: center;
  }
  .ne-store-map--info-pos-right .ne-store-map__side,
  .ne-store-map--info-pos-left  .ne-store-map__side {
    flex: 0 0 280px;
    align-self: stretch;
  }

  .ne-store-map__placeholder {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    height: 100%;
    padding: 24px;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.7;
    text-align: center;
    border: 1px dashed var(--ne-scheme-border, rgba(127, 127, 127, 0.3));
    border-radius: inherit;
  }
  .ne-store-map__placeholder p {
    margin: 0;
    font-size: 0.9rem;
  }

  .ne-store-map__cta {
    display: flex;
    justify-content: var(--ne-bw-justify, flex-start);
  }
/* END_BLOCK:store-map */

/* START_BLOCK:tab (INDEX:134) */
.ne-tab__panel {
    width: 100%;
    color: var(--ne-scheme-text);
    line-height: 1.65;
  }
  .ne-tab__panel > .shopify-block { display: contents; }
  .ne-tab__panel > :first-child { margin-top: 0; }
  .ne-tab__panel > :last-child { margin-bottom: 0; }
/* END_BLOCK:tab */

/* START_BLOCK:testimonial-list (INDEX:135) */
.ne-tl {
    --ne-tl-row-span: 3;
    --ne-tl-inner-gap: 12px;
    width: 100%;
    /* The list is a layout wrapper, not a scheme surface — cards paint
       their own bg via --ne-scheme-bg cascading from list.color-scheme-X.
       Force transparent so inherited scheme-bg doesn't leak onto the
       wrapper itself. */
    background: transparent !important;
  }
  .ne-tl.ne-cov { color: var(--ne-scheme-text); }

  /* Theme-block wrapper collapses so each testimonial becomes a direct
     grid item — required for subgrid to work (Rule 70 + Rule 127). */
  .ne-tl__grid > .shopify-block,
  .ne-tl__track > .shopify-block { display: contents; }

  /* ──────────────── Grid layout ──────────────── */
  .ne-tl--grid .ne-tl__grid {
    display: grid;
    grid-template-columns: repeat(var(--ne-tl-cols, 3), minmax(0, 1fr));
    grid-template-rows: repeat(var(--ne-tl-cols, 3), auto);
    gap: var(--ne-tl-gap, 20px);
    /* Row template = 3 rows per card: rating / quote (1fr) / author.
       Each card spans 3 rows → subgrid aligns content across cards. */
    grid-auto-rows: auto 1fr auto;
  }
  .ne-tl--grid .ne-testimonial {
    /* Already set in the child stylesheet but repeat for specificity
       when multiple stylesheets load in different orders. */
    grid-template-rows: subgrid;
    grid-row: span 3;
  }

  /* Stack layout — one column, cards stack vertically. */
  .ne-tl--stack .ne-tl__grid {
    display: flex;
    flex-direction: column;
    gap: var(--ne-tl-gap, 20px);
  }

  /* ──────────────── Slider layout ──────────────── */
  /* padding-inline reserves space for the arrows alongside the cards.
     Track (inside carousel) re-computes grid-auto-columns against the
     reduced width — cards shrink, arrows sit in the gutter with a
     small overlap onto the card edge. */
  .ne-tl--slider .ne-tl__carousel {
    position: relative;
    /* `overflow-x: clip` hard-clips the track's inline bleed at the
       carousel's box edge. `overflow-y: visible` keeps the track's
       vertical shadow bleed visible past the carousel's top/bottom.
       padding-inline: 48 reserves an INSET ZONE: cards are pushed
       away from the box edge so their shadow renders inside the
       gutter without reaching the visible box border. Arrows
       (left:0 / right:0) sit in this gutter alongside cards. */
    overflow-x: clip;
    overflow-y: visible;
    padding-inline: 32px;
  }
  .ne-tl--slider .ne-tl__track {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: calc((100% - (var(--ne-tl-cols, 3) - 1) * var(--ne-tl-gap, 20px)) / var(--ne-tl-cols, 3));
    grid-auto-rows: auto 1fr auto;
    gap: var(--ne-tl-gap, 20px);
    overflow-x: auto;
    overflow-y: hidden;
    /* proximity (not mandatory) — mandatory aggressively re-snaps on
       every sub-pixel drift during smooth scroll, amplifying subgrid
       row jitter; proximity only snaps near rest positions. */
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    scrollbar-width: none;
    /* Rule 205: explicit touch-action so mobile swipe delegates fully to
       this scroll container — without this the swipe stops mid-card.
       pan-y lets a vertical swipe scroll the page instead of being trapped
       by this horizontal carousel. */
    touch-action: pan-x pan-y;
    -webkit-overflow-scrolling: touch;
    /* Track padding-inline = gap (Rule 166). Eliminates next-card peek
       at the right edge: peek = padding-inline − gap; setting them
       equal makes card-(N+1) start exactly at the track-border-right
       (= invisible). Shadow-bleed buffer reduces from 32px → gap
       (typically 20px), which still covers default 24px-blur shadow
       (alpha fades at outer edges, 4px clip imperceptible). At very
       high shadow-intensity (200% global, 48px blur) the outermost
       shadow-edge clips by ~28px — acceptable trade-off for the
       always-clean no-peek visual. padding-block stays at 32px for
       vertical shadow-bleed (unrelated to peek). */
    padding-block: 32px;
    padding-inline: var(--ne-tl-gap, 20px);
    margin-block: -32px;
    scroll-padding-inline: var(--ne-tl-gap, 20px);
    /* Edge-aware fade: only fade on the side(s) where there's
       off-screen content. data-edge is set on the carousel by the
       generic edge-detector in hyprism-features.js. Default state
       (middle / no attr) fades both sides. */
    will-change: scroll-position;
  }
  /* Edge-fade abandoned — mask-image clips shadow/glow (Rule 158);
     gradient overlays look too dark against the section bg.
     Cards swipe in/out without a soft fade — shadow/glow visible,
     centered geometry preserved. */
  /* Glassmorphism + mask-image are mutually exclusive — mask creates
     a stacking context that breaks `backdrop-filter` sampling on
     glass cards (the blur effect doesn't render). When glass is on
     we drop the mask so blur works; the trade-off is no edge fade. */
  .ne-tl--glass.ne-tl--slider .ne-tl__track {
    -webkit-mask-image: none;
    mask-image: none;
  }
  .ne-tl--slider .ne-tl__track::-webkit-scrollbar { display: none; }
  .ne-tl--slider .ne-testimonial {
    scroll-snap-align: start;
    grid-template-rows: subgrid;
    grid-row: span 3;
  }
  .ne-tl__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 40px;
    height: 40px;
    color: var(--ne-scheme-text, currentColor);
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 2;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
    padding: 0;
  }
  .ne-tl__arrow:hover { transform: translateY(-50%) scale(1.05); }
  /* Arrow sits at the inner edge of the carousel's padding — ~4px
     overlap onto the card (arrow width 40px > padding 36px). */
  .ne-tl__arrow--prev { left: 6px; }
  .ne-tl__arrow--next { right: 6px; }

  /* Button style — bg + border + shadow. border-radius lives inline
     on the element (Rule 24 — critical.css button-reset wins
     specificity fights). */
  .ne-tl--arrow-button .ne-tl__arrow {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127,127,127,0.25));
    box-shadow: 0 4px 12px rgba(0,0,0,0.12);
  }

  /* Plain style — just the arrow icon, no background/border. */
  .ne-tl--arrow-plain .ne-tl__arrow {
    background: transparent;
    border: 0;
    box-shadow: none;
    width: 32px;
    height: 32px;
  }
  .ne-tl--arrow-plain .ne-tl__arrow:hover { color: var(--ne-scheme-accent, currentColor); }

  /* ──────────────── Card chrome ──────────────── */
  .ne-tl .ne-testimonial {
    padding: var(--ne-tl-card-pad, 24px);
    border-radius: var(--ne-tl-card-radius, 16px);
    background: transparent;
    border: 1px solid var(--ne-scheme-border, rgba(127,127,127,0.2));
    /* No isolation/transform transitions — both force additional
       compositing layers that shake during smooth-scroll with
       mandatory snap / subgrid row reflow (observed as stars jitter). */
    transition: box-shadow .2s ease;
  }
  /* Opt-in: when the list carries a card_scheme, every card paints
     its BG using the nearest scheme in the cascade — list's scheme by
     default, or the card's own scheme when it overrides. */
  .ne-tl--has-card-scheme .ne-testimonial {
    background: var(--ne-scheme-bg);
    border-color: var(--ne-scheme-border, rgba(127,127,127,0.2));
  }

  /* Chrome modifiers on the LIST set vars the cards read. Composed
     shadow (Rule 130) — glass/glow/shadow stack without conflicts. */
  .ne-tl .ne-testimonial {
    --ne-tl-shadow-outer: 0 0 0 0 transparent;
    --ne-tl-shadow-glow:  0 0 0 0 transparent;
    box-shadow: var(--ne-tl-shadow-outer), var(--ne-tl-shadow-glow);
  }
  /* Effects coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-tl--shadow .ne-testimonial { --ne-tl-shadow-outer: var(--ne-block-shadow-effect, 0 8px 24px rgba(0,0,0,0.12)); }
  .ne-tl--glow   .ne-testimonial { --ne-tl-shadow-glow:  var(--ne-block-glow-effect, 0 0 24px var(--ne-scheme-accent-glow, rgba(120,120,255,0.35))); }
  .ne-tl--glass  .ne-testimonial {
    background: var(--ne-glass-bg, rgba(255,255,255,0.05)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    /* No border: 1px glass-border reads as a bottom contour line on dark
       bg (same fix as blog/product-card glass, user QA 2026-05-25). */
    border: 0;
  }
  /* Accent-tinted glass — opt-in via card_glass_tint (uses per-scheme
     --ne-glass-bg-tinted, same mechanism as the card-glass sweep). */
  .ne-tl--glass.ne-tl--glass-tint .ne-testimonial {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255,255,255,0.05))) !important;
  }

  /* ──────────────── Coverflow layout ──────────────── */
  /* Cards are wrapped in .ne-cf__card by the JS on init. The wrapper owns
     glass/tint/glow/shadow (Rule 49: its transform makes a backdrop-root and
     clips the inner card's box-shadow), so the inner .ne-testimonial drops its
     own chrome and just fills the wrapper. */
  /* The JS wraps each content_for block (a .shopify-block holding the
     .ne-testimonial) in a .ne-cf__card. display:contents lets the snippet's
     `.ne-cf__card > *` rules + the card sizing reach the testimonial. */
  .ne-tl--coverflow .ne-cf__card > .shopify-block { display: contents; }
  .ne-tl--coverflow .ne-testimonial {
    min-height: var(--ne-cf-h);
    grid-row: auto;
    margin: 0;
    border: 0;
    --ne-tl-shadow-outer: 0 0 0 0 transparent !important;
    --ne-tl-shadow-glow: 0 0 0 0 transparent !important;
    box-shadow: none !important;
  }
  /* Solid surface — the wrapper only paints the drum BACK panel, so the inner
     card paints the opaque front face from the same colour (--ne-cf-cbg is
     defined on the .ne-cf__card--surface-solid wrapper and inherits down). */
  .ne-tl--coverflow:not(.ne-tl--glass) .ne-testimonial {
    background: var(--ne-cf-cbg, var(--ne-scheme-card-bg, var(--ne-scheme-bg, #1a1a28))) !important;
  }
  /* Glass surface — inner transparent so the wrapper's backdrop-filter shows. */
  .ne-tl--coverflow.ne-tl--glass .ne-testimonial {
    background: transparent !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }

  /* Decorative giant quote mark before the quote text. */
  .ne-tl--decorated .ne-testimonial__quote::before {
    content: "\201C";
    display: block;
    font-family: var(--font-heading--family);
    font-size: 3rem;
    line-height: 0.8;
    color: var(--ne-scheme-accent, currentColor);
    opacity: 0.4;
    margin-bottom: 8px;
  }

  /* ──────────────── Responsive ──────────────── */
  /* Tablet (600-989px): 2-up slider. Excluded ≤599 so the mobile rules
     below don't fight this for cascade priority. */
  @media (min-width: 600px) and (max-width: 989px) {
    .ne-tl--grid .ne-tl__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .ne-tl--slider .ne-tl__track { grid-auto-columns: calc((100% - var(--ne-tl-gap, 20px)) / 2); }
  }
  @media (max-width: 599px) {
    .ne-tl--grid .ne-tl__grid { grid-template-columns: 1fr; }
    /* Mobile carousel: 0 peek at rest + 32px glow buffer all sides.
       Key math: card 2's visible peek at scrollLeft=0 equals
       (track-padding-inline) − (track-gap). Setting them equal at
       32px each makes peek = 0. Card 1 fills the visible area exactly;
       card 2 starts precisely at the padding-box right edge (clipped
       by overflow-x: auto). When swiping, card 2 enters smoothly.
       - carousel padding-inline: 0 → track uses full carousel width
       - track padding-inline: 32px → 32px glow-buffer each side
       - track gap (column-gap): 32px → matches padding so no peek
       - card grid-auto-columns: 100% = content-box width = T-64
       - card 1 in scroll-port from x=32 to x=T-32 (centered) ✓
       - card 2 starts at scroll-port x=T → off-screen, clipped ✓
       - Center-snap mathematically perfect for all positions. */
    .ne-tl--slider .ne-tl__carousel { padding-inline: 0 !important; }
    .ne-tl--slider .ne-tl__track {
      grid-auto-columns: 100% !important;
      padding-inline: var(--ne-tl-gap, 20px) !important;
      scroll-padding-inline: var(--ne-tl-gap, 20px) !important;
      column-gap: 32px !important;
      gap: 32px !important;
    }
    .ne-tl--slider .ne-testimonial { scroll-snap-align: center !important; }
  }
/* END_BLOCK:testimonial-list */

/* START_BLOCK:testimonial (INDEX:136) */
/* Layout owned by the parent testimonial-list (subgrid rows,
     card padding / radius / chrome). This file only styles the
     inner content. */
  .ne-testimonial {
    display: grid;
    grid-template-rows: subgrid;
    grid-row: span var(--ne-tl-row-span, 3);
    gap: var(--ne-tl-inner-gap, 12px);
    align-content: start;
  }
  /* Fallback if the parent doesn't provide subgrid context (e.g. stack
     layout). Stacks normally with flex-like gap via row gap on grid. */
  @supports not (grid-template-rows: subgrid) {
    .ne-testimonial { display: flex; flex-direction: column; }
  }

  .ne-testimonial__rating {
    display: inline-flex;
    gap: 2px;
    color: var(--ne-color-warning, #f5b042);
    line-height: 0;
  }
  .ne-testimonial__star--empty { color: var(--ne-scheme-border, rgba(127,127,127,0.3)); }

  .ne-testimonial__quote {
    margin: 0;
    padding: 0;
    border: 0;
    color: var(--ne-scheme-text);
    font-family: var(--font-body--family);
    font-size: var(--ne-font-size-md, 1rem);
    line-height: 1.6;
    /* Quote stretches to fill the middle subgrid row so author always
       anchors at the card bottom (Rule 127). */
    align-self: start;
  }
  .ne-testimonial__quote p { margin: 0; }
  .ne-testimonial__quote p + p { margin-top: 0.5em; }

  .ne-testimonial__author {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: auto;
  }

  .ne-testimonial__avatar {
    width: var(--ne-tl-avatar-size, 48px);
    height: var(--ne-tl-avatar-size, 48px);
    flex-shrink: 0;
    overflow: hidden;
    border-radius: var(--ne-tl-avatar-radius, 999px);
    background: var(--ne-scheme-border, rgba(127,127,127,0.15));
  }
  .ne-testimonial__avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  .ne-testimonial__author-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
  }
  .ne-testimonial__name {
    font-style: normal;
    font-weight: 600;
    font-size: var(--ne-font-size-sm, 0.9375rem);
    color: var(--ne-scheme-text);
  }
  .ne-testimonial__title {
    font-size: var(--ne-font-size-xs, 0.8125rem);
    color: var(--ne-scheme-text);
    opacity: 0.7;
  }

  /* Per-card scheme override paints its own background when the card
     has a background declared by the parent. `ne-testimonial--has-scheme`
     flag lets the parent opt into bg-painting when a scheme is set. */
  .ne-testimonial--has-scheme { background-color: var(--ne-scheme-bg); }
/* END_BLOCK:testimonial */

/* START_BLOCK:text (INDEX:137) */
.ne-block-text { background: transparent !important; }
  .ne-block-text > :first-child { margin-top: 0; }
  .ne-block-text > :last-child { margin-bottom: 0; }

  /* Paragraph spacing — global `* { margin: 0 }` reset (critical.css)
     strips browser-default <p> margins, so back-to-back paragraphs
     stack tight. Restore visible separation between <p> blocks via
     the user-controllable `--ne-text-para-gap` (paragraph_spacing
     setting, em-based so it scales with font size). */
  .ne-block-text {
    --ne-text-para-gap: 14px;
    line-height: var(--ne-text-line-height, 1.6);
  }
  .ne-block-text p { margin-block: 0 var(--ne-text-para-gap); }
  .ne-block-text p:last-child { margin-bottom: 0; }

  /* Empty-paragraph spacer — Shopify's richtext editor preserves empty
     <p></p> and <p><br></p> when the user presses Enter on a blank
     line. Render them as a visible blank line so authors can create
     extra spacing without a second text-block (user request 2026-05-23).
     Multiple consecutive empty paragraphs stack — each contributes its
     own 1em height plus the paragraph gap. */
  .ne-block-text p:empty,
  .ne-block-text p:has(> br:only-child) {
    min-height: 1em;
    margin-block: 0 var(--ne-text-para-gap);
  }

  /* Standalone <br> inside a non-empty paragraph (shift+enter line-break) —
     give it the same baseline height the line-height already provides;
     consecutive <br>s stack and create visible vertical space (matches
     the editor's WYSIWYG behavior even when Shopify collapses adjacent
     empty <p>s on save). */
  .ne-block-text br { display: block; content: ""; margin-block-start: 0; }

  /* Lists + blockquote: also restore breathing room. */
  .ne-block-text ul,
  .ne-block-text ol,
  .ne-block-text blockquote { margin-block: 0 var(--ne-text-para-gap); }
  .ne-block-text ul,
  .ne-block-text ol { padding-inline-start: 1.4em; }

  .ne-block-text--drop-cap > p:first-of-type::first-letter {
    float: left;
    font-size: 3.5em;
    line-height: 0.9;
    font-weight: 700;
    padding: 0.1em 0.1em 0 0;
    color: var(--ne-scheme-accent);
  }

  .ne-block-text--highlight mark,
  .ne-block-text--highlight em,
  .ne-block-text--highlight strong {
    background: linear-gradient(180deg, transparent 55%, var(--ne-scheme-accent-glow, rgba(120,120,255,0.3)) 55%);
    padding: 0 0.1em;
    border-radius: 2px;
  }
  .ne-block-text--highlight mark {
    color: inherit;
  }
/* END_BLOCK:text */

/* START_BLOCK:video (INDEX:138) */
/* CSS-var based alignment — parent can set --ne-iw-align (same var
     as image-wrap); block's modifier sets it at element-scope so
     per-block setting wins over inherited group default. */
  .ne-block-video-wrap {
    display: flex;
    width: 100%;
    flex-direction: column;
    align-items: var(--ne-iw-align, center);
  }
  .ne-block-video-wrap--left   { --ne-iw-align: flex-start; }
  .ne-block-video-wrap--center { --ne-iw-align: center; }
  .ne-block-video-wrap--right  { --ne-iw-align: flex-end; }

  .ne-block-video {
    position: relative;
    width: 100%;
    overflow: hidden;
    background: #000;
  }
  /* Surface — for videos with a transparent background (alpha webm/mov).
     transparent = video sits directly on the section bg; glass = frosted
     panel behind it, visible through the video's transparent areas. The
     --surface-glass class carries "glass" so the global glass-shine paints
     its top highlight automatically (user 2026-05-25). */
  .ne-block-video--surface-transparent { background: transparent; }
  .ne-block-video--surface-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  /* Glass-shine top highlight — opt-in glossy edge (mirrors image block). */
  .ne-block-video--shine { position: relative; }
  .ne-block-video--shine::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    background-image: var(--ne-glass-shine-bg, linear-gradient(180deg, rgba(255,255,255,0.25) 0%, rgba(255,255,255,0.20) 1px, rgba(255,255,255,0.06) 2px, transparent 3px));
    pointer-events: none;
    z-index: 3;
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
  }
  /* Embeds need a fallback aspect-ratio when 'auto' is chosen — they have
     no intrinsic size like an upload does. */
  .ne-block-video:not([style*="aspect-ratio"]):has(iframe) { aspect-ratio: 16 / 9; }
  /* YouTube Shorts auto-detected → 9:16. Must be more specific than the iframe rule
     above (adding .ne-block-video selector for higher specificity). User's max_width
     slider controls the width — no CSS cap here. */
  .ne-block-video.ne-block-video--shorts:not([style*="aspect-ratio"]):has(iframe) { aspect-ratio: 9 / 16; }
  /* Empty state (no video, no URL) — show placeholder at 16:9 so editors see the block. */
  .ne-block-video:not([style*="aspect-ratio"]):has(.ne-block-video__placeholder) {
    aspect-ratio: 16 / 9;
    background: var(--ne-scheme-card-bg, var(--ne-scheme-bg, #1a1a1a));
    border: 1px dashed var(--ne-scheme-border, rgba(255,255,255,0.15));
  }
  /* With a glass/transparent surface the dashed empty-state outline + card
     bg fight the glass look — keep the surface clean (user QA #27). The
     dashed hint is editor-only (empty block) anyway. */
  .ne-block-video--surface-glass:not([style*="aspect-ratio"]):has(.ne-block-video__placeholder),
  .ne-block-video--surface-transparent:not([style*="aspect-ratio"]):has(.ne-block-video__placeholder) {
    border: 0 !important;
  }
  .ne-block-video--surface-transparent:not([style*="aspect-ratio"]):has(.ne-block-video__placeholder) {
    background: transparent;
  }
  .ne-block-video__media {
    width: 100%;
    height: auto;
    display: block;
    border: 0;
  }
  /* When an aspect-ratio is forced on the container, fill it. */
  .ne-block-video[style*="aspect-ratio"] .ne-block-video__media,
  .ne-block-video:has(iframe) .ne-block-video__media {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .ne-block-video__placeholder {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    opacity: 0.6;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text, rgba(255,255,255,0.7)));
  }
  .ne-block-video__placeholder .placeholder-svg { max-width: 40%; height: auto; fill: currentColor; }

  /* Effects coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-block-video--shadow { box-shadow: 0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.3); }
  .ne-block-video--glow   { box-shadow: 0 0 calc(36px * var(--ne-fx-glow-i, 1)) calc(6px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.55)); }
  .ne-block-video--shadow.ne-block-video--glow {
    box-shadow:
      0 calc(12px * var(--ne-fx-shadow-i, 1)) calc(32px * var(--ne-fx-shadow-i, 1)) rgba(0,0,0,0.3),
      0 0 calc(36px * var(--ne-fx-glow-i, 1)) calc(6px * var(--ne-fx-glow-i, 1)) var(--ne-scheme-accent-glow, rgba(120,120,255,0.55));
  }
/* END_BLOCK:video */

/* START_BLOCK:wishlist-grid (INDEX:139) */
.ne-wl-grid {
    width: 100%;
    background: transparent !important;
    display: flex;
    flex-direction: column;
    gap: 16px;
  }

  .ne-wl-grid__count {
    font-size: var(--ne-font-size-sm, 0.875rem);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.75;
  }
  .ne-wl-grid__count-label {
    margin-left: 4px;
  }

  .ne-wl-grid__track {
    display: grid;
    grid-template-columns: repeat(var(--ne-wl-cols, 4), minmax(0, 1fr));
    gap: var(--ne-wl-gap, 16px);
    padding: var(--ne-space-2, 8px) var(--ne-space-2, 8px) var(--ne-space-4, 16px);
    align-items: stretch;
  }
  @media (max-width: 989px) {
    .ne-wl-grid__track { grid-template-columns: repeat(3, 1fr); }
  }
  @media (max-width: 749px) {
    .ne-wl-grid__track {
      grid-template-columns: repeat(var(--ne-wl-cols-m, 2), 1fr);
    }
  }

  .ne-wl-grid__empty {
    display: none;
    flex-direction: column;
    align-items: center;
    gap: 16px;
    padding: 48px 16px;
    text-align: center;
    color: var(--ne-scheme-text);
  }
  .ne-wl-grid__empty p {
    margin: 0;
    font-size: 1.05rem;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    opacity: 0.85;
  }

  /* Card — own namespace, mirrors .ne-rv-card visual treatment so
     wishlist cards look like recently-viewed cards. */
  .ne-wl-grid .ne-wl-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--ne-wl-title-price-gap, 8px);
    padding: var(--ne-wl-pad, 0);
    border-radius: var(--ne-wl-radius, 12px);
    /* overflow:visible (was hidden) so the absolutely-positioned quick-add
       cart button's shadow/glow isn't clipped at the card edge. The card
       background still rounds itself (border-radius clips backgrounds), and
       the image is clipped by .ne-wl-card__media's own overflow:clip — in
       flush mode its top corners follow the card radius (rule below). */
    overflow: visible;
    isolation: isolate;
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
    height: 100%;
    transition: transform var(--ne-transition-base, 0.2s ease), box-shadow var(--ne-transition-base, 0.2s ease);
  }
  .ne-wl-grid .ne-wl-card:hover {
    transform: translate3d(0, -4px, 0);
  }
  .ne-wl-grid--surface-solid .ne-wl-card {
    background: var(--ne-scheme-bg, transparent);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-wl-grid--surface-glass .ne-wl-card {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.12));
    will-change: backdrop-filter;
  }
  .ne-wl-grid--surface-glass.ne-wl-grid--glass-tint .ne-wl-card {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05)));
    border-color: var(--ne-glass-border-tinted, var(--ne-glass-border, rgba(255, 255, 255, 0.12)));
  }

  /* Effects coupled to global block-intensity (Theme-Settings ×0.6). */
  .ne-wl-grid--shadow .ne-wl-card { box-shadow: var(--ne-block-shadow-effect, 0 8px 22px rgba(0, 0, 0, 0.22)); }
  .ne-wl-grid--glow .ne-wl-card   { box-shadow: var(--ne-block-glow-effect, 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35))); }
  .ne-wl-grid--shadow.ne-wl-grid--glow .ne-wl-card {
    box-shadow:
      var(--ne-block-shadow-effect, 0 8px 22px rgba(0, 0, 0, 0.22)),
      var(--ne-block-glow-effect, 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35)));
  }

  .ne-wl-grid .ne-wl-card__media {
    position: relative;
    width: 100%;
    border-radius: var(--ne-wl-img-radius, 0);
    overflow: clip;
  }
  /* Flush mode (card_padding == 0): the card no longer clips (overflow:
     visible), so the media must round its own TOP corners to the card
     radius — otherwise a square image corner pokes past the rounded card.
     max() keeps any explicit image_radius if it's larger. Bottom corners
     stay flush against the body row. */
  .ne-wl-grid--flush .ne-wl-card__media {
    border-top-left-radius: max(var(--ne-wl-img-radius, 0px), var(--ne-wl-radius, 12px));
    border-top-right-radius: max(var(--ne-wl-img-radius, 0px), var(--ne-wl-radius, 12px));
  }
  .ne-wl-grid--ratio-square .ne-wl-card__media    { aspect-ratio: 1 / 1; }
  .ne-wl-grid--ratio-portrait .ne-wl-card__media  { aspect-ratio: 3 / 4; }
  .ne-wl-grid--ratio-landscape .ne-wl-card__media { aspect-ratio: 4 / 3; }
  .ne-wl-grid--ratio-natural .ne-wl-card__media   { aspect-ratio: auto; min-height: 200px; }

  .ne-wl-grid .ne-wl-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 50% 50%;
    display: block;
  }

  .ne-wl-grid .ne-wl-card__link {
    color: inherit;
    text-decoration: none;
    display: contents;
  }

  .ne-wl-grid .ne-wl-card__vendor {
    display: block;
    /* Vendor→title gap follows the row-gap slider so all card rows
       share rhythm (matches .ne-product-card behaviour). */
    margin: 0 0 var(--ne-wl-title-price-gap, 2px);
    padding: 0 var(--ne-space-3, 12px);
    font-size: var(--ne-font-size-xs, 0.75rem);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
    color: var(--ne-scheme-text);
  }

  .ne-wl-grid .ne-wl-card__title {
    margin: 0;
    padding: var(--ne-space-3, 12px) var(--ne-space-3, 12px) 0;
    font-family: var(--ne-wl-title-font, inherit);
    font-size: var(--ne-font-size-sm, 0.875rem);
    font-weight: 500;
    line-height: 1.3;
    color: var(--ne-scheme-text);
    display: -webkit-box;
    -webkit-line-clamp: var(--ne-wl-title-lines, 2);
    -webkit-box-orient: vertical;
    overflow: hidden;
    transition: color 0.18s ease, opacity 0.18s ease;
  }
  .ne-wl-grid--title-bold .ne-wl-card__title { font-weight: 700; }
  .ne-wl-grid--title-italic .ne-wl-card__title { font-style: italic; }
  .ne-wl-grid--title-no-clamp .ne-wl-card__title {
    -webkit-line-clamp: unset;
    display: block;
    overflow: visible;
  }
  .ne-wl-grid .ne-wl-card:hover .ne-wl-card__title { opacity: 0.75; }

  .ne-wl-grid .ne-wl-card__price {
    margin-top: auto;
    /* Top padding = row gap above (title→price); bottom is 0 so the
       inter-row gap to the remove button comes from the merchant's
       row-gap slider alone (matches .ne-product-card pattern). */
    padding: var(--ne-wl-title-price-gap, 8px) var(--ne-space-3, 12px) 0;
    font-size: var(--ne-font-size-sm, 0.875rem);
    font-weight: 500;
    color: var(--ne-scheme-text);
  }

  .ne-wl-grid .ne-wl-card__remove {
    align-self: flex-start;
    /* Top margin = row gap above (price→remove). Bottom margin keeps
       the card's edge spacing. */
    margin: var(--ne-wl-title-price-gap, 8px) var(--ne-space-3, 12px) var(--ne-space-3, 12px);
    padding: 4px 0;
    background: transparent;
    border: none;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    font-size: var(--ne-font-size-xs, 0.75rem);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
    opacity: 0.7;
    transition: color 0.18s ease, opacity 0.18s ease;
  }
  .ne-wl-grid .ne-wl-card__remove:hover {
    color: var(--ne-color-sale, #ef4444);
    opacity: 1;
  }

  /* Quick-add cart button — circular icon button anchored bottom-right
     of the card. Sits above all card content via z-index 3. */
  .ne-wl-grid .ne-wl-card__add-cart {
    position: absolute;
    bottom: 8px;
    right: 8px;
    width: 36px;
    height: 36px;
    border-radius: 50% !important;
    background: var(--ne-scheme-accent, #6366f1);
    color: var(--ne-scheme-btn-text, #fff);
    display: grid;
    place-items: center;
    border: none;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
    transition: transform 0.18s ease, background 0.18s ease, box-shadow 0.18s ease;
    z-index: 3;
    padding: 0;
  }
  .ne-wl-grid .ne-wl-card__add-cart:hover {
    transform: scale(1.08);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.32),
                0 0 16px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5));
  }
  .ne-wl-grid .ne-wl-card__add-cart svg {
    width: 18px;
    height: 18px;
  }
  .ne-wl-grid .ne-wl-card__add-cart[data-state="loading"] {
    pointer-events: none;
    opacity: 0.7;
  }
  .ne-wl-grid .ne-wl-card__add-cart[data-state="success"] {
    background: var(--ne-color-success, #16a34a);
  }

  /* Variant popup — fixed overlay; card-styled inner panel. */
  .ne-wl-popup {
    position: fixed;
    inset: 0;
    z-index: 1000;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .ne-wl-popup[hidden] { display: none; }
  .ne-wl-popup__backdrop {
    /* Visually identical to the cart drawer's overlay: same fixed-cover,
       same alpha + blur (read from :root via cart-drawer settings), and
       the same fade-in transition tied to a .ne-wl-popup--open class. */
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, var(--ne-drawer-overlay-alpha, 0.5));
    backdrop-filter: blur(var(--ne-drawer-overlay-blur, 4px));
    -webkit-backdrop-filter: blur(var(--ne-drawer-overlay-blur, 4px));
    opacity: 0;
    transition: opacity var(--ne-transition-base, 0.3s);
  }
  .ne-wl-popup--open .ne-wl-popup__backdrop { opacity: 1; }
  /* Match the drawer's panel slide-in feel — popup card fades + scales. */
  .ne-wl-popup__card {
    opacity: 0;
    transform: translateY(8px);
    transition: opacity var(--ne-transition-base, 0.3s), transform var(--ne-transition-base, 0.3s);
  }
  .ne-wl-popup--open .ne-wl-popup__card { opacity: 1; transform: none; }
  .ne-wl-popup__card {
    position: relative;
    z-index: 1;
    width: min(420px, calc(100% - 32px));
    max-height: calc(100vh - 64px);
    overflow-y: auto;
    border-radius: var(--ne-wl-radius, 12px);
    /* Card padding cascades 1:1 from .ne-wl-grid wrapper so the popup
       mirrors the card spacing exactly (image edge-to-edge when
       card_padding=0, framed when card_padding>0). Inner content
       (title/price/options/button) gets its own breathing room via
       per-element padding below. */
    padding: var(--ne-wl-pad, 0px);
    color: var(--ne-scheme-text);
    display: flex;
    flex-direction: column;
    gap: 12px;
  }
  /* When the card runs with padding 0, give title/price/options/button
     their own inset so they don't touch the popup edges — the image
     stays edge-to-edge as on the card. */
  .ne-wl-popup__title,
  .ne-wl-popup__price,
  .ne-wl-popup__options,
  .ne-wl-popup__submit,
  .ne-wl-popup__error {
    margin-left: max(0px, calc(20px - var(--ne-wl-pad, 0px)));
    margin-right: max(0px, calc(20px - var(--ne-wl-pad, 0px)));
  }
  .ne-wl-popup__title {
    margin-top: max(12px, calc(20px - var(--ne-wl-pad, 0px)));
  }
  /* Bottom breathing room — last visible element gets the same inset
     as the sides so the submit/error button doesn't kiss the card edge
     when card_padding = 0. */
  .ne-wl-popup__submit,
  .ne-wl-popup__error:last-child {
    margin-bottom: max(0px, calc(20px - var(--ne-wl-pad, 0px)));
  }
  /* Mirror the card shadow / glow on the popup panel so it inherits
     the same chrome as the wishlist cards. */
  .ne-wl-popup.ne-wl-grid--shadow .ne-wl-popup__card {
    box-shadow: var(--ne-block-shadow-effect, 0 8px 22px rgba(0, 0, 0, 0.22));
  }
  .ne-wl-popup.ne-wl-grid--glow .ne-wl-popup__card {
    box-shadow: var(--ne-block-glow-effect, 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35)));
  }
  .ne-wl-popup.ne-wl-grid--shadow.ne-wl-grid--glow .ne-wl-popup__card {
    box-shadow:
      var(--ne-block-shadow-effect, 0 8px 22px rgba(0, 0, 0, 0.22)),
      var(--ne-block-glow-effect, 0 0 22px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.35)));
  }
  /* Inherit surface treatment from the grid's surface class on the
     popup wrapper. Mirrors .ne-wl-card exactly — same paint, same
     border treatment, same overflow clip so the media bleeds into
     the radius corners. */
  .ne-wl-popup__card {
    overflow: clip;
  }
  .ne-wl-popup.ne-wl-grid--surface-solid .ne-wl-popup__card {
    background: var(--ne-scheme-bg);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }
  .ne-wl-popup.ne-wl-grid--surface-glass .ne-wl-popup__card {
    background: var(--ne-glass-bg, rgba(127, 127, 127, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-wl-popup.ne-wl-grid--surface-glass.ne-wl-grid--glass-tint .ne-wl-popup__card {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(127, 127, 127, 0.08)));
  }
  .ne-wl-popup.ne-wl-grid--surface-transparent .ne-wl-popup__card {
    /* Truly transparent — content reads against the dark backdrop
       blur. Matches the card optic of transparent-surface cards. */
    background: transparent;
    border: none;
  }

  .ne-wl-popup__close {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 32px;
    height: 32px;
    border-radius: 50% !important;
    border: none;
    background: transparent;
    color: var(--ne-scheme-text);
    cursor: pointer;
    display: grid;
    place-items: center;
    opacity: 0.7;
    transition: opacity 0.18s ease, background 0.18s ease;
    padding: 0;
  }
  .ne-wl-popup__close:hover {
    opacity: 1;
    background: var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
  }

  .ne-wl-popup__media {
    /* Mirror .ne-wl-card__media: own image-radius applied on all four
       corners, sits inside the popup padding (so card_padding spaces
       it from the card edge just like on a card), no media-fill. */
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: var(--ne-wl-img-radius, 0);
    overflow: clip;
    background: transparent;
  }
  .ne-wl-popup__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }

  .ne-wl-popup__title {
    /* Use individual margins so the inset rules (margin-left/right
       further up) keep working — `margin: 0` shorthand would reset
       them and pin the title flush to the card edge. */
    margin-top: 0;
    margin-bottom: 0;
    font-size: 1rem;
    font-weight: 500;
    /* Title typography mirrors .ne-wl-card__title — inherits font, bold,
       italic from the grid's title settings via cascading vars/classes. */
    font-family: var(--ne-wl-title-font, inherit);
    color: var(--ne-scheme-text);
  }
  .ne-wl-grid--title-bold .ne-wl-popup__title   { font-weight: 700; }
  .ne-wl-grid--title-italic .ne-wl-popup__title { font-style: italic; }
  .ne-wl-popup__price {
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--ne-scheme-text);
    /* Match card's title→price spacing. */
    margin-top: calc(var(--ne-wl-title-price-gap, 8px) - 12px);
  }

  .ne-wl-popup__options {
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .ne-wl-popup__option-label {
    display: block;
    font-size: 0.8rem;
    font-weight: 500;
    margin-bottom: 6px;
    color: var(--ne-scheme-text);
  }
  .ne-wl-popup__option-values {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
  }
  .ne-wl-popup__option-value {
    padding: 6px 12px;
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.25));
    border-radius: 6px;
    background: transparent;
    color: var(--ne-scheme-text);
    cursor: pointer;
    font-size: 0.85rem;
    transition: border-color 0.15s ease, background 0.15s ease;
  }
  .ne-wl-popup__option-value:hover {
    border-color: var(--ne-scheme-accent);
  }
  .ne-wl-popup__option-value[aria-pressed="true"] {
    background: var(--ne-scheme-accent);
    color: var(--ne-scheme-btn-text, #fff);
    border-color: var(--ne-scheme-accent);
  }
  .ne-wl-popup__option-value[disabled] {
    opacity: 0.4;
    cursor: not-allowed;
    text-decoration: line-through;
  }

  .ne-wl-popup__submit {
    /* Flex-column children stretch by default — explicit width:100%
       combined with margin-left/right inset would overflow the card,
       so let auto-stretch handle width and use individual margins. */
    margin-top: 4px;
  }
  .ne-wl-popup__error {
    /* Individual margins so the inset margin-left/right rules above
       still apply (margin shorthand would reset them). */
    margin-top: 0;
    margin-bottom: 0;
    color: var(--ne-color-sale, #ef4444);
    font-size: 0.85rem;
    text-align: center;
  }
/* END_BLOCK:wishlist-grid */

/* CSS from snippet stylesheet tags */
/* START_SNIPPET:arc-text (INDEX:143) */
.ne-arctext { position: relative; display: block; margin-inline: auto; }
  .ne-arctext__ch {
    position: absolute;
    top: 0;
    left: 50%;
    transform-origin: 50% 50%;
    white-space: pre;
    will-change: transform;
  }
  .ne-arctext--fade .ne-arctext__ch { opacity: 0; transition: opacity 0.45s ease; }
  .ne-arctext--fade.ne-arctext--in .ne-arctext__ch { opacity: 1; }
/* END_SNIPPET:arc-text */

/* START_SNIPPET:back-to-top (INDEX:144) */
.ne-back-to-top {
    position: fixed;
    z-index: 45;
    width: 46px;
    height: 46px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    cursor: pointer;
    opacity: 0;
    transform: translateY(12px);
    pointer-events: none;
    transition: opacity 0.25s ease, transform 0.25s ease, background 0.2s ease, bottom 0.32s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .ne-back-to-top.is-visible {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
  }
  .ne-back-to-top:hover { transform: translateY(-2px); }
  .ne-back-to-top:focus-visible {
    outline: 2px solid var(--ne-scheme-accent, #6366f1);
    outline-offset: 3px;
  }

  /* Position — horizontal (centre uses the `translate` property so it composes
     with the transform-based show/hide animation, Rule 232) */
  .ne-back-to-top--h-left { left: var(--ne-btt-x, 24px); right: auto; }
  .ne-back-to-top--h-right { right: var(--ne-btt-x, 24px); left: auto; }
  .ne-back-to-top--h-center { left: 50%; right: auto; translate: -50% 0; }
  /* Position — vertical */
  .ne-back-to-top--v-top { top: var(--ne-btt-y, 24px); bottom: auto; }
  .ne-back-to-top--v-bottom { bottom: var(--ne-btt-y, 24px); top: auto; }
  /* When the sticky product bar is visible, a bottom-anchored button lifts to
     clear it — but only if its own offset doesn't already sit above the bar
     (max() keeps a generously-padded button where it is). Synced to the bar's
     slide timing via the `bottom` transition above. */
  html[data-sticky-bar-visible] .ne-back-to-top--v-bottom {
    bottom: max(var(--ne-btt-y, 24px), calc(var(--ne-sticky-bar-h, 0px) + 12px));
  }

  /* Surface — solid */
  .ne-back-to-top--solid {
    background: var(--ne-scheme-btn-bg, var(--ne-scheme-accent, #6366f1));
    color: var(--ne-scheme-btn-text, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(255, 255, 255, 0.14));
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.35);
  }
  /* Surface — glass (border:0 → no bottom hairline, Rule LG3) */
  .ne-back-to-top--glass {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 18px)) saturate(var(--ne-glass-saturate, 140%));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 18px)) saturate(var(--ne-glass-saturate, 140%));
    color: var(--ne-scheme-text, #fff);
    border: 0;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.30);
  }
  /* Glass-buttons toggle → the round button gets the BUTTON-glass optic
     (full-height gradient + inset bevel, same recipe as .ne-btn in
     critical.css) instead of the flat 5px card shine-bar — the straight
     bar read as a dirty chip on the circle (user QA 2026-07-23; the
     global shine rule excludes .ne-back-to-top for the same reason).
     Drop shadow is kept in the composed box-shadow list. */
  body[data-ne-glass-buttons="1"] .ne-back-to-top--glass {
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.02) 100%);
    background-blend-mode: overlay;
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.35),
      inset 0 -1px 0 rgba(0, 0, 0, 0.12),
      0 8px 24px rgba(0, 0, 0, 0.30);
  }
  /* Surface — transparent (icon only, drop-shadow keeps it legible on any bg) */
  .ne-back-to-top--transparent {
    background: transparent;
    border: 0;
    color: var(--ne-scheme-text, #fff);
    box-shadow: none;
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.55)) drop-shadow(0 0 4px rgba(0, 0, 0, 0.35));
  }

  @media (prefers-reduced-motion: reduce) {
    .ne-back-to-top { transition: opacity 0.2s ease; transform: none; }
    .ne-back-to-top.is-visible { transform: none; }
    .ne-back-to-top:hover { transform: none; }
  }
/* END_SNIPPET:back-to-top */

/* START_SNIPPET:coverflow (INDEX:154) */
.ne-cf {
    position: relative;
    width: 100%;
    height: var(--ne-cf-h, 460px);
    /* The cards are clipped/faded by .ne-cf__viewport (a non-transformed inset:0
       layer), NOT by .ne-cf itself — so the chevron arrows (direct children of
       .ne-cf, siblings of the viewport) stay outside the clip + fade mask, and
       the fan's upward card-arc can bleed vertically past the block top/bottom. */
    overflow: visible;
    /* The root carries the consumer's color-{scheme} class only so the cards
       inherit the scheme CSS vars via cascade — it must NOT paint that scheme's
       background itself (.color-scheme-X paints background-color, Rule 173).
       Same as product-grid's `.ne-pg { background: transparent !important }`. */
    background: transparent !important;
    /* No `perspective`/`isolation` here — both form a backdrop-root that kills
       the glass cards' backdrop-filter (Rule 49). Perspective is applied
       per-card inside the transform() instead, so the card itself carries it. */
    --ne-cf-origin: 50%;
  }
  /* Viewport: a non-transformed inset:0 layer that clips the cards horizontally
     (overflow-x:clip = block/section edges) while letting the fan's vertical arc
     bleed (overflow-y:visible). The fade mask + clip live here, NOT on .ne-cf, so
     the chevrons stay above the fade. Drum extends its ring sideways → no clip. */
  .ne-cf__viewport {
    position: absolute;
    inset: 0;
    overflow: visible;          /* spill (default) — cards bleed past the edge */
    border-radius: inherit;
  }
  /* Clip mode (coverflow_overflow = clip): the SECTION clips at its own edge
     (critical.css drops the spill-guard for .ne-cf--clip), so cards reach the
     section edge then stop — not the narrower block edge. The viewport stays
     overflow:visible. Only the optional fade needs the viewport to clip+mask. */
  .ne-cf--clip.ne-cf--fade .ne-cf__viewport { overflow: hidden; }
  /* Optional edge fade-out (clip + coverflow_edge_fade > 0). A horizontal mask
     softens the cut instead of a hard line. Mask = a backdrop-root, so it
     suppresses glass-card backdrop-filter (Rule 49/356); fade is therefore a
     solid/transparent-card treatment. Drum (auto-fit + recentred) is excluded. */
  .ne-cf--clip.ne-cf--style-stack.ne-cf--fade .ne-cf__viewport,
  .ne-cf--clip.ne-cf--style-fan.ne-cf--fade .ne-cf__viewport {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-cf-fade, 0px), #000 calc(100% - var(--ne-cf-fade, 0px)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--ne-cf-fade, 0px), #000 calc(100% - var(--ne-cf-fade, 0px)), transparent 100%);
  }
  .ne-cf--align-left  { --ne-cf-origin: calc(var(--ne-cf-card-w, 340px) * 0.5 + 28px); }
  .ne-cf--align-right { --ne-cf-origin: calc(100% - var(--ne-cf-card-w, 340px) * 0.5 - 28px); }
  /* Stage wraps the cards (not the arrows). Every coverflow optic shifts +
     scales the whole composition so it always fits the coverflow width and
     stays centred — no outer card clips at the block edge. */
  .ne-cf__stage { position: absolute; inset: 0; }
  .ne-cf .ne-cf__stage {
    transform: translateX(var(--ne-cf-drum-shift, 0px)) scale(var(--ne-cf-drum-fit, 1));
    transform-origin: 50% 50%;
    transition: transform .55s cubic-bezier(.22, 1, .36, 1);
  }
  .ne-cf--no-trans .ne-cf__stage,
  .ne-cf--no-trans .ne-cf__card { transition: none !important; }
  /* Paging gestures. */
  .ne-cf--nav-drag { cursor: grab; touch-action: pan-y; }
  .ne-cf--nav-drag.ne-cf--grabbing { cursor: grabbing; }
  .ne-cf__card {
    position: absolute;
    top: 50%;
    left: var(--ne-cf-origin, 50%);
    width: min(var(--ne-cf-card-w, 320px), 82%);
    border-radius: var(--ne-cf-card-radius, 12px);
    transform-origin: 50% 50%;
    --d: 0;
    --ad: 0;
    transform:
      perspective(3000px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--d) * var(--ne-cf-step, 23%)))
      rotateY(calc(var(--d) * -5deg))
      translateZ(0px)
      scale(calc(1 - var(--ad) * 0.035));
    z-index: calc(100 - var(--ad) * 5);
    opacity: 1;
    transition: transform .55s cubic-bezier(.22,1,.36,1), opacity .55s ease, box-shadow .35s ease;
    pointer-events: auto;
    cursor: pointer;
    will-change: transform;
    box-shadow: var(--ne-cf-shadow-inset, 0 0 0 transparent), var(--ne-cf-shadow-outer, 0 0 0 transparent);
  }
  .ne-cf__card[data-active] { cursor: default; }
  /* Equalise card heights across the coverflow — force them all to the tallest
     (--ne-cf-h, measured by JS) so the silhouettes line up. Generic: any inner. */
  .ne-cf__card,
  .ne-cf__card > * { min-height: var(--ne-cf-h); }
  /* ── Optics ── base .ne-cf__card transform is "stack"; fan + drum override
     only `transform`, reading the same JS-set --d / --ad, so the JS stays
     optic-agnostic. */
  .ne-cf--style-fan {
    height: calc(var(--ne-cf-h, 460px) + 150px);
    overflow: visible;
  }
  .ne-cf--style-fan .ne-cf__card {
    transform:
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--gd, var(--d)) * 54%))
      translateY(calc(var(--cad, var(--ad)) * var(--cad, var(--ad)) * -12px))
      rotate(calc(var(--cd, var(--d)) * 9deg))
      scale(calc(1 - var(--ad) * 0.04));
    opacity: max(0.88, calc(1 - var(--ad) * 0.045));
  }
  /* Solid surface = genuinely opaque cards, so don't dim the back ones. */
  .ne-cf--style-fan .ne-cf__card--surface-solid:not([data-hidden]) { opacity: 1; }
  /* Drum — slide magazine: focus card stands alone on one side; the rest pile on
     a turned ring on the other side, face-on → edge-on → reversed backs → fade. */
  .ne-cf--style-drum {
    height: calc(var(--ne-cf-h, 460px) * 1.28 * var(--ne-cf-drum-fit, 1) + 30px);
    overflow: visible;
    --ne-cf-origin: 49%;
  }
  .ne-cf--style-drum.ne-cf--align-right { --ne-cf-origin: 51%; }
  .ne-cf--style-drum .ne-cf__card {
    transform-origin: 50% 50%;
    transform:
      perspective(2600px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--ne-cf-drum-dir, 1) * var(--di, var(--ad)) * 13%))
      rotateY(calc(var(--ne-cf-drum-dir, 1) * var(--di, var(--ad)) * 15deg))
      scale(1.12);
    z-index: calc(90 - abs(var(--di, var(--ad)) - 5.6) * 2);
    opacity: 1;
  }
  .ne-cf--style-drum .ne-cf__card[data-active] {
    transform-origin: 50% 50%;
    transform:
      perspective(2600px)
      translate(-50%, -50%)
      translateY(var(--ne-cf-lift, 0px))
      translateX(calc(var(--ne-cf-drum-dir, 1) * -130%))
      rotateY(0deg)
      scale(1.26);
    opacity: 1;
    z-index: 300;
  }
  /* Hover-lift on the CF-CARD (via --ne-cf-lift inside every optic's transform)
     so the box-shadow lifts WITH the card. The inner card never lifts. */
  .ne-cf .ne-cf__card > *:hover { transform: none !important; }
  .ne-cf .ne-cf__card:hover { --ne-cf-lift: -12px; }
  .ne-cf--style-drum .ne-cf__card:not([data-active]):hover { --ne-cf-lift: 0px; }
  .ne-cf--style-drum .ne-cf__card[data-active]:hover {
    --ne-cf-lift: -16px;
    --ne-cf-shadow-outer: 0 24px 48px rgba(0, 0, 0, 0.42);
  }
  /* Product-card-specific hover suppressions in the drum (no-op for other cards). */
  .ne-cf--style-drum .ne-cf__card:not([data-active]) .ne-product-card--has-secondary:hover .ne-product-card__image--primary { opacity: 1 !important; }
  .ne-cf--style-drum .ne-cf__card:not([data-active]) .ne-product-card--has-secondary:hover .ne-product-card__image--secondary { opacity: 0 !important; }
  .ne-cf--style-drum .ne-cf__card:not([data-active]) .ne-product-card--hover-zoom:hover .ne-product-card__image--primary { transform: none !important; }
  .ne-cf--style-drum .ne-cf__card:not([data-active]) .ne-product-card__quick-view { display: none !important; }
  /* Render the reversed face so the back of the card is visible in the drum. */
  .ne-cf--style-drum .ne-cf__card > * { backface-visibility: visible; }
  /* Clip the blur + back panel to the card edge on side/back cards only — the
     focus card stays overflow:visible so its hover-lift isn't clipped. */
  .ne-cf--style-drum .ne-cf__card:not([data-active]) { overflow: hidden; }
  /* Glass backs frost their content; solid backs are a plain panel (below). */
  .ne-cf--style-drum .ne-cf__card--surface-glass > * {
    filter: blur(calc(min(1, max(0, (var(--di, var(--ad)) - 5.6) * 100)) * 5px));
  }
  /* Back-face treatment — overlay that fades in only on the reversed cards. */
  .ne-cf--style-drum .ne-cf__card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    z-index: 7;
    pointer-events: none;
    background: transparent;
    opacity: calc(min(1, max(0, (var(--di, var(--ad)) - 5.6) * 100)));
  }
  .ne-cf--style-drum .ne-cf__card--surface-glass.ne-cf__card--tint::before {
    background: var(--ne-glass-bg-tinted, rgba(99, 102, 241, 0.18));
  }
  .ne-cf--style-drum .ne-cf__card--surface-solid::before {
    background:
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      var(--ne-cf-cbg);
  }
  /* SOLID surface in a coverflow: stack the card colour so it's genuinely opaque
     (a scheme "Card background" with alpha would read see-through when cards
     overlap / the drum flips them). */
  .ne-cf .ne-cf__card--surface-solid {
    --ne-cf-cbg: var(--ne-scheme-card-bg, var(--ne-scheme-bg, var(--ne-scheme-bg-surface, #1a1a28)));
  }
  .ne-cf .ne-cf__card--surface-solid .ne-product-card::before {
    background:
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      linear-gradient(var(--ne-cf-cbg), var(--ne-cf-cbg)),
      var(--ne-cf-cbg);
  }
  /* Card chrome modifiers — all plug into --ne-cf-shadow-outer. */
  .ne-cf__card--shadow { --ne-cf-shadow-outer: 0 8px 22px rgba(0, 0, 0, 0.28); }
  .ne-cf__card--shadow:hover { --ne-cf-shadow-outer: 0 14px 32px rgba(0, 0, 0, 0.38); }
  .ne-cf__card--glow:hover { --ne-cf-shadow-outer: 0 0 32px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.45)); }
  .ne-cf__card--shadow.ne-cf__card--glow:hover {
    --ne-cf-shadow-outer: 0 0 30px var(--ne-scheme-accent-glow, rgba(127, 127, 127, 0.4)), 0 12px 28px rgba(0, 0, 0, 0.32);
  }
  .ne-cf__card[data-hidden] { opacity: 0; pointer-events: none; }
  /* Coverflow glass — PROGRESSIVE ENHANCEMENT (real backdrop-filter on
     Chromium/WebKit; mock-glass frosted gradient on Firefox via html.ne-ff). */
  .ne-cf__card--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.06)) !important;
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border-radius: var(--ne-cf-card-radius, 12px);
    overflow: hidden;
    --ne-cf-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.18), inset 0 0 0 1px rgba(255, 255, 255, 0.06);
    will-change: transform, backdrop-filter;
  }
  .ne-cf__card--glass.ne-cf__card--tint {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.06)) !important;
    background-image: linear-gradient(
      var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5)),
      var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5))) !important;
  }
  html.ne-ff .ne-cf__card--glass.ne-cf__card--tint {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.10)) !important;
    background-image:
      linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.02) 55%, rgba(255, 255, 255, 0.07) 100%),
      linear-gradient(
        var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5)),
        var(--ne-glass-bg-tinted, rgba(89, 140, 140, 0.5))) !important;
  }
  html.ne-ff .ne-cf__card--glass {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    background:
      linear-gradient(135deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.05) 38%, rgba(255, 255, 255, 0.02) 62%, rgba(255, 255, 255, 0.12) 100%),
      var(--ne-glass-bg, rgba(255, 255, 255, 0.07)) !important;
    --ne-cf-shadow-inset:
      inset 0 1px 0 rgba(255, 255, 255, 0.24),
      inset 0 -1px 0 rgba(0, 0, 0, 0.06),
      inset 0 0 0 1px rgba(255, 255, 255, 0.06);
  }
  /* Glass shine — top light-line over the card (Rule 371/LG1). */
  body[data-ne-glass-shine="1"] .ne-cf__card--glass::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 6;
    pointer-events: none;
    border-radius: inherit;
    background: var(--ne-glass-shine-bg);
  }
  /* Chevrons — self-contained (button-style circle), sit ON the sides. */
  .ne-cf__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    color: var(--ne-scheme-text, currentColor);
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(127, 127, 127, 0.25));
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
    border-radius: 50%;
    cursor: pointer;
    transition: transform .15s ease, box-shadow .15s ease, background-color .15s ease;
  }
  .ne-cf .ne-cf__arrow {
    position: absolute;
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    z-index: 300;
  }
  .ne-cf .ne-cf__arrow:hover { transform: translateY(-50%) scale(1.05); }
  .ne-cf .ne-cf__arrow--prev { left: 8px; right: auto; }
  .ne-cf .ne-cf__arrow--next { right: 8px; left: auto; }
  .ne-cf__arrow svg { width: 18px; height: 18px; }
  /* ── Mobile coverflow ── */
  @media (max-width: 749px) {
    .ne-cf--style-stack .ne-cf__card,
    .ne-cf--style-fan .ne-cf__card,
    .ne-cf--style-drum .ne-cf__card {
      width: min(var(--ne-cf-card-w, 320px), 94%);
    }
    .ne-cf--style-stack { overflow: visible; }
    .ne-cf--style-stack .ne-cf__card {
      transform:
        perspective(2600px)
        translate(-50%, -50%)
        translateY(var(--ne-cf-lift, 0px))
        translateX(calc(var(--d) * 30%))
        rotateY(calc(var(--d) * -10deg))
        translateZ(calc(var(--ad) * -120px))
        scale(calc(1 - var(--ad) * 0.12));
    }
    .ne-cf--style-fan { overflow: visible; }
    .ne-cf--style-fan .ne-cf__card {
      transform:
        translate(-50%, -50%)
        translateY(var(--ne-cf-lift, 0px))
        translateX(calc(var(--gd, var(--d)) * 30%))
        translateY(calc(var(--cad, var(--ad)) * var(--cad, var(--ad)) * -16px))
        rotate(calc(var(--cd, var(--d)) * 11deg))
        scale(calc(1 - var(--ad) * 0.06));
    }
    .ne-cf--style-drum {
      height: calc(var(--ne-cf-h, 460px) * 1.7 * var(--ne-cf-drum-fit, 1) + 40px);
      overflow: visible;
      --ne-cf-origin: 50%;
      --ne-cf-ring-shift: 0px;
    }
    .ne-cf--style-drum.ne-cf--align-right { --ne-cf-origin: 50%; }
    .ne-cf--style-drum .ne-cf__card {
      transform:
        perspective(2400px)
        translate(-50%, -50%)
        translateY(calc(var(--ne-cf-h, 460px) * 0.48))
        translateX(calc(var(--di, var(--ad)) * 13% + var(--ne-cf-ring-shift, 0px)))
        rotateY(calc(var(--di, var(--ad)) * -18deg))
        scale(0.66);
      z-index: calc(90 - var(--di, var(--ad)) * 2);
    }
    .ne-cf--style-drum .ne-cf__card[data-active] {
      transform:
        perspective(2400px)
        translate(-50%, -50%)
        translateY(calc(var(--ne-cf-h, 460px) * -0.39 + var(--ne-cf-lift, 0px)))
        rotateY(0deg)
        scale(1);
    }
    .ne-cf--style-drum .ne-cf__card,
    .ne-cf--style-drum .ne-cf__card > * { min-height: 0; }
  }
  @media (prefers-reduced-motion: reduce) {
    .ne-cf__card { transition: opacity .2s ease; }
  }
/* END_SNIPPET:coverflow */

/* START_SNIPPET:header-styles-2 (INDEX:159) */
.ne-header--left .ne-header__nav-item--flip-v > .ne-header__dropdown,
  .ne-header--left .ne-header__nav-item--flip-v > .ne-header__megamenu,
  .ne-header--right .ne-header__nav-item--flip-v > .ne-header__dropdown,
  .ne-header--right .ne-header__nav-item--flip-v > .ne-header__megamenu {
    top: auto;
    bottom: 0;
    transform: none;
  }
  .ne-header--right .ne-header__dropdown::before,
  .ne-header--right .ne-header__megamenu::before {
    top: 0;
    bottom: 0;
    left: 100%;
    right: auto;
    height: auto;
    width: calc(var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px) + 4px);
  }
  .ne-header--right.ne-header--dropdown-slide .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-slide .ne-header__megamenu {
    transform: translateX(18px) translateY(-50%);
  }
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--right.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateX(0) translateY(-50%);
  }
  /* Sidebar-right + scale: preserve translateY(-50%); scale from right edge */
  .ne-header--right.ne-header--dropdown-scale .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-scale .ne-header__megamenu {
    transform: translateY(-50%) scale(0.85);
    transform-origin: right center;
  }
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--right.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateY(-50%) scale(1);
  }

  /* When header has glow effect, dropdown mirrors it subtly.
     No --ne-shadow-lg here — mixing shadow into the glow rule produced a
     visible dark shadow that made "glow" look like "shadow+glow". Glow should
     be pure accent-colored bloom. */
  .ne-header--glow .ne-header__dropdown {
    box-shadow: 0 0 calc(20px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent-glow, rgba(99,102,241,0.25));
  }

  /* Header shadow effect also mirrors to dropdown + megamenu so the visual
     language stays consistent across header and its flyouts. */
  .ne-header--shadow .ne-header__dropdown,
  .ne-header--shadow .ne-header__megamenu {
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4), 0 1px 4px rgba(0, 0, 0, 0.2);
  }

  /* ── MERGE: header-effect glow/shadow must NOT cross the seam ─────────────
     The DIRECTIONAL merged-PANEL glow/shadow (per position, off-seam) lives in
     menu-styles.liquid (~1291-1346) — the top-header open glow there was the one
     symmetric leftover that bloomed up over the bar; fixed in that file. Here we
     only redirect the header BAR's OWN effect (below), which menu-styles doesn't
     touch. The panel-facing halo the merchant saw (img #40) surfaced only with the
     GLOBAL header config (header_effect = glow; the section config used = none). */

  /* The header BAR's own effect (.ne-header--glow / --shadow, header-styles.liquid
     ~867) is a spreadless halo blooming in ALL directions — including DOWN into the
     content mouth where the merged panel emerges, so a glow ring hugs the seam even
     with the panel glow fixed above. When merged, re-cast the bar effect to spill
     only AWAY from the mouth (top → up; left → left; right → right), so the seam edge
     stays clean. (0,3,0)+!important beats the base effect (0,1,0). */
  .ne-header--top.ne-header--menu-merge.ne-header--glow {
    box-shadow:
      0 calc(-8px * var(--ne-header-glow-intensity, 0.5)) calc(24px * var(--ne-header-glow-intensity, 0.5)) -4px var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      0 calc(-14px * var(--ne-header-glow-intensity, 0.5)) calc(60px * var(--ne-header-glow-intensity, 0.5)) -8px var(--ne-scheme-accent, rgba(99,102,241,0.25)) !important;
  }
  .ne-header--left.ne-header--menu-merge.ne-header--glow {
    box-shadow:
      calc(-8px * var(--ne-header-glow-intensity, 0.5)) 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) -4px var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      calc(-14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(60px * var(--ne-header-glow-intensity, 0.5)) -8px var(--ne-scheme-accent, rgba(99,102,241,0.25)) !important;
  }
  .ne-header--right.ne-header--menu-merge.ne-header--glow {
    box-shadow:
      calc(8px * var(--ne-header-glow-intensity, 0.5)) 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) -4px var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      calc(14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(60px * var(--ne-header-glow-intensity, 0.5)) -8px var(--ne-scheme-accent, rgba(99,102,241,0.25)) !important;
  }

  /* When the dropdown is fully transparent (transparent-on-top header, glass
     off, not scrolled) a glow/shadow would render as a floating outline around
     an invisible panel — suppress it so "fully transparent" really is. Once
     scrolled into the solid state the effect returns via the rules above.
     Specificity (0,4,0) beats the (0,2,0) effect rules. */
  .ne-header--initial-transparent:not(.ne-header--scrolled):not(.ne-glass) .ne-header__dropdown,
  .ne-header--initial-transparent:not(.ne-header--scrolled):not(.ne-glass) .ne-header__megamenu {
    box-shadow: none;
  }

  /* Animation: Fade — visibility delays on hide so opacity can transition out first */
  .ne-header--dropdown-fade .ne-header__dropdown {
    transition: opacity var(--ne-transition-base), visibility 0s var(--ne-transition-base);
  }

  .ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown {
    transition: opacity var(--ne-transition-base), visibility 0s 0s;
  }

  /* Animation: Slide down — larger offset (18px) so the motion is clearly distinct
     from Fade. Subtle 8px values looked identical to Fade in QA. */
  .ne-header--dropdown-slide:not(.ne-header--left):not(.ne-header--right) .ne-header__dropdown {
    transform: translateX(-50%) translateY(-18px);
    transition: opacity var(--ne-transition-base), visibility var(--ne-transition-base), transform var(--ne-transition-base);
  }

  /* Animation: Scale — stronger scale down (0.85) + spring easing so the pop is obvious.
     0.95 was imperceptible against the Fade baseline. */
  .ne-header--dropdown-scale:not(.ne-header--left):not(.ne-header--right) .ne-header__dropdown {
    transform: translateX(-50%) scale(0.85);
    transform-origin: top center;
    transition: opacity var(--ne-transition-base), visibility var(--ne-transition-base), transform var(--ne-transition-spring);
  }

  /* Mega menu: multi-column layout */
  .ne-header__dropdown--mega {
    left: 0;
    transform: none;
    min-width: auto;
    width: max-content;
    max-width: 800px;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
    gap: var(--ne-space-4);
  }

  /* Show dropdown on hover/focus (CSS fallback + JS is-open class) */
  .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header__nav-item.is-open > .ne-header__dropdown {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }
  /* Megamenu open fallback — mirror of the dropdown rule above. Megamenus
     otherwise open ONLY via the glass/transparent-gated slide/fade/scale rules,
     the sidebar sb-mega-panel rule, or the left/right rules — there is no plain
     (non-glass, non-sidebar) reveal. A TOP header that is NOT glass therefore left
     promoted megamenus stuck at visibility:hidden even when open. This is exposed
     by menu-merge (which forces dropdown-slide): a non-glass merged top header never
     opened its megamenus. Give them the same non-gated reveal the dropdown has. */
  .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header__nav-item.is-open > .ne-header__megamenu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  .ne-header--dropdown-slide:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--dropdown-slide:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--dropdown-slide:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item.is-open > .ne-header__dropdown {
    transform: translateX(-50%) translateY(0);
  }

  .ne-header--dropdown-scale:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--dropdown-scale:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--dropdown-scale:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item.is-open > .ne-header__dropdown {
    transform: translateX(-50%) scale(1);
  }

  /* Mega menu: correct initial transforms (no translateX since left:0) — magnitudes
     matched to the dropdown values above so all animations read identically. */
  .ne-header--dropdown-slide .ne-header__dropdown--mega {
    transform: translateY(-18px);
    transform-origin: top left;
  }

  .ne-header--dropdown-scale .ne-header__dropdown--mega {
    transform: scale(0.85);
    transform-origin: top left;
  }

  .ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__dropdown--mega,
  .ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__dropdown--mega,
  .ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__dropdown--mega {
    transform: translateY(0);
  }

  .ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__dropdown--mega,
  .ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__dropdown--mega,
  .ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__dropdown--mega {
    transform: scale(1);
  }

  .ne-header__dropdown-group-title {
    font-size: var(--ne-font-size-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--ne-scheme-text-muted);
    padding: var(--ne-space-1) var(--ne-space-3);
    margin-bottom: var(--ne-space-1);
  }

  .ne-header__dropdown-link {
    display: block;
    padding: var(--ne-space-2) var(--ne-space-3);
    border-radius: var(--ne-radius-md);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
    text-decoration: none;
    transition: all var(--ne-transition-fast);
    line-height: 1.4;
    /* Long labels (e.g. "Crew Sock — Merino Cushion") wrapped to two lines /
       widened the chip unevenly. Truncate with an ellipsis instead, bounded by
       the panel max-width below (user 2026-06-23). The has-submenu trigger is
       display:flex; its label span handles ellipsis (rule further down). */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* Cap the panel width so nowrap links actually truncate rather than stretch
     the panel; non-megamenu only (megamenus manage their own width). */
  .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    max-width: 300px;
  }
  /* Ellipsis inside the flex submenu-trigger: the label must shrink + clip,
     the chevron stays fixed. */
  .ne-header__dropdown-link-label {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .ne-header__dropdown-link:hover {
    color: var(--ne-scheme-text);
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.06));
  }

  /* ── Panel dropdown links inherit the nav Button-Stil / Hover-Stil ─────────
     menu.liquid mirrors the merchant's choice onto the header element:
       • Resting button style    → .ne-header--nav-btn-{rounded|pill|square}
       • Hover style (style=none) → .ne-header--hover-style-{…} / --hover-underline / --hover-bold
     The two groups are mutually exclusive (the hover group is only emitted
     when nav_button_style == 'none'), so these blocks never both apply.
     Until 2026-05-27 both groups targeted only .ne-header__nav-link +
     .ne-header__action — the panel links kept their own plain hover. */

  /* Resting button style → each panel link becomes a button chip. Same plain
     bg/border/text token chain as .ne-header__nav-link--btn-{shape}. The 2px
     vertical gap keeps stacked chips from touching. */
  .ne-header--nav-btn-rounded .ne-header__dropdown-link,
  .ne-header--nav-btn-pill .ne-header__dropdown-link,
  .ne-header--nav-btn-square .ne-header__dropdown-link {
    border: 1px solid var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255, 255, 255, 0)));
    background: var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08))));
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-text)));
    margin-block: 2px;
  }
  .ne-header--nav-btn-rounded .ne-header__dropdown-link { border-radius: var(--ne-btn-radius, var(--ne-radius-md)); }
  .ne-header--nav-btn-pill .ne-header__dropdown-link { border-radius: var(--ne-radius-full); }
  .ne-header--nav-btn-square .ne-header__dropdown-link { border-radius: 0; }

  /* Button-optik chips need a symmetric inset. Side/merge panels zero their
     horizontal padding (header-styles.liquid ~2004: full-width plain rows so the
     hover hitbox reaches the panel edge) — which is right for plain links but
     leaves button chips flush against the panel's L/R edges while the T/B panel
     padding remains, so chips look glued to the sides (user 2026-07-09). When the
     links are styled as buttons, restore the horizontal padding so the chips inset
     equally on all four sides. (0,3,1) beats the (0,2,1) padding-inline:0 rule. */
  .ne-header.ne-header--nav-btn-rounded .ne-header__dropdown:not(.ne-header__dropdown--mega),
  .ne-header.ne-header--nav-btn-pill .ne-header__dropdown:not(.ne-header__dropdown--mega),
  .ne-header.ne-header--nav-btn-square .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    padding-inline: var(--ne-space-3);
  }

  /* More-panel row PARITY — the overflow "More" panel mixes row types: simple entries
     render as .ne-header__dropdown-link (which DO get the button-chip optik above),
     submenu/mega entries as cloned .ne-header__nav-item--more-child carrying a plain
     .ne-header__nav-link (which did NOT get the chip fill → transparent bg + muted text
     + a taller 36px fixed height → looked unstyled AND spaced differently, user 2026-07-09).
     Give the cloned parent rows the EXACT same chip treatment as .ne-header__dropdown-link
     so every row in the panel — and its child flyout — reads identically. (0,3,1) beats the
     base more-child rule (0,2,1). */
  /* ⚠️ !important is REQUIRED here: the spine clone-reset (header-styles.liquid ~520,
     `.ne-header--sb-spine .ne-header__dropdown .ne-header__nav-link`) forces these cloned
     nav-links to `background:transparent!important; border:0!important; color:muted!important`
     so they read flat "like plain rows". That was right BEFORE button-optik; with button chips
     the simple rows (dropdown-link) ARE filled, so the flat clone looked unstyled + off-rhythm.
     `.ne-header` prefix → (0,4,0) beats the (0,3,0) spine rule; !important beats its !important. */
  .ne-header.ne-header--nav-btn-rounded .ne-header__nav-item--more-child > .ne-header__nav-link,
  .ne-header.ne-header--nav-btn-pill .ne-header__nav-item--more-child > .ne-header__nav-link,
  .ne-header.ne-header--nav-btn-square .ne-header__nav-item--more-child > .ne-header__nav-link {
    border: 1px solid var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255, 255, 255, 0))) !important;
    background: var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08)))) !important;
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-text))) !important;
    height: auto;                                    /* match dropdown-link content height */
    padding: var(--ne-space-2) var(--ne-space-3);    /* identical to .ne-header__dropdown-link */
    margin-block: 2px;
  }
  .ne-header.ne-header--nav-btn-rounded .ne-header__nav-item--more-child > .ne-header__nav-link { border-radius: var(--ne-btn-radius, var(--ne-radius-md)); }
  .ne-header.ne-header--nav-btn-pill .ne-header__nav-item--more-child > .ne-header__nav-link { border-radius: var(--ne-radius-full); }
  .ne-header.ne-header--nav-btn-square .ne-header__nav-item--more-child > .ne-header__nav-link { border-radius: 0; }
  .ne-header.ne-header--nav-btn-rounded .ne-header__nav-item--more-child > .ne-header__nav-link:hover,
  .ne-header.ne-header--nav-btn-pill .ne-header__nav-item--more-child > .ne-header__nav-link:hover,
  .ne-header.ne-header--nav-btn-square .ne-header__nav-item--more-child > .ne-header__nav-link:hover {
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-btn-text-hover, var(--ne-scheme-btn-text, var(--ne-scheme-text)))) !important;
    background: var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent))) !important;
    border-color: var(--ne-hbtn-hover-border, var(--ne-scheme-btn-border-hover, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent)))) !important;
  }

  /* UNIFORM panel-row geometry at EVERY level (user 2026-07-09) — the 3 different
     "gaps" the merchant saw were actually 3 different ROW HEIGHTS at a constant 2px gap:
     simple/child rows ≈ 37.6px, but cloned submenu-parent nav-links inherited
     `min-height: var(--ne-nav-btn-length)` from the spine equal-width rule (~43px), and
     deeper submenu triggers differed again. Force ONE fixed height + one block margin on
     every clickable row — simple dropdown-links, cloned more-child nav-links, and nested
     submenu links — so the More panel, its child flyouts and deeper submenus share one
     rhythm. Tweakable via --ne-panel-row-h / --ne-panel-row-gap. (0,4,0)+!important beats
     the spine equal-width min-height (0,3,0) and the clone-reset (0,3,0)!important. */
  .ne-header.ne-header--nav-btn-rounded .ne-header__dropdown-link,
  .ne-header.ne-header--nav-btn-pill .ne-header__dropdown-link,
  .ne-header.ne-header--nav-btn-square .ne-header__dropdown-link,
  .ne-header.ne-header--nav-btn-rounded .ne-header__nav-item--more-child > .ne-header__nav-link,
  .ne-header.ne-header--nav-btn-pill .ne-header__nav-item--more-child > .ne-header__nav-link,
  .ne-header.ne-header--nav-btn-square .ne-header__nav-item--more-child > .ne-header__nav-link {
    height: var(--ne-panel-row-h, 40px) !important;
    min-height: 0 !important;
    margin-block: var(--ne-panel-row-gap, 3px) !important;
    display: flex !important;
    align-items: center !important;
    box-sizing: border-box !important;
  }

  /* Resting-button hover → flip to the button hover tokens (mirrors
     .ne-header__nav-link--btn:hover). Specificity (0,3,0) beats the base
     dropdown-link:hover (0,2,0) — no !important needed. */
  .ne-header--nav-btn-rounded .ne-header__dropdown-link:hover,
  .ne-header--nav-btn-pill .ne-header__dropdown-link:hover,
  .ne-header--nav-btn-square .ne-header__dropdown-link:hover {
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-btn-text-hover, var(--ne-scheme-btn-text, var(--ne-scheme-text))));
    background: var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent)));
    border-color: var(--ne-hbtn-hover-border, var(--ne-scheme-btn-border-hover, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent))));
  }

  /* ── Sub-items inherit the nav-button 3D / glass optic ─────────────────────
     The fill/border/text chain above already mirrors the nav buttons; the
     panel chips looked "flat" only because the 3D depth + glass were missing
     (so a cloned submenu-trigger nav-link-btn stood out). Add them here via the
     SAME --ne-scheme-accent-glow / --ne-action-* / --ne-hbtn-* / --ne-glass-*
     tokens the nav buttons read (inherited, not hardcoded). DEPTH via box-shadow
     only — NO translateY lift: the lift uses `transform`, which the dropdown
     open animation needs, so the chips keep animating. */
  .ne-header.ne-header--nav-btn-3d .ne-header__dropdown-link {
    box-shadow:
      0 0 14px var(--ne-scheme-accent-glow, rgba(120,120,255,0.5)),
      var(--ne-action-shadow);
  }
  .ne-header.ne-header--nav-btn-3d .ne-header__dropdown-link:hover {
    box-shadow:
      0 0 22px var(--ne-scheme-accent-glow, rgba(120,120,255,0.7)),
      var(--ne-action-shadow-hover);
  }
  .ne-header.ne-header--nav-btn-glass .ne-header__dropdown-link {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, rgba(255,255,255,0.06)));
    background-blend-mode: overlay;
    border-color: var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255,255,255,0.12)));
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.4);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.4);
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.35),
      inset 0 -1px 0 rgba(0,0,0,0.12),
      var(--ne-action-shadow);
  }
  /* Header itself is glass → descendant backdrop-filter is neutered (Rule 49),
     suppress the wasted GPU work; the gradient + inset highlights carry the look. */
  .ne-header.ne-glass.ne-header--nav-btn-glass .ne-header__dropdown-link {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Glass dropdown-chip HOVER — without this, glass chips show NO hover state:
     the glass resting bg above (0,3,0) beats the generic chip hover (2480,
     0,2,1), and there was no glass-specific dropdown-link hover (unlike the nav
     buttons, which have a dedicated glass hover at 1607). Mirror that nav recipe
     — same gradient, swap the base to the hover bg token. The pseudo bumps this
     to (0,3,1) so it wins over the resting glass bg without !important. One rule
     covers BOTH parent dropdown chips and submenu chips (both are dropdown-links). */
  .ne-header.ne-header--nav-btn-glass .ne-header__dropdown-link:hover {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-btn-bg, var(--ne-action-bg-hover))));
    background-blend-mode: overlay;
    border-color: var(--ne-hbtn-hover-border, var(--ne-scheme-btn-border-hover, var(--ne-scheme-btn-border, var(--ne-glass-border, rgba(255,255,255,0.12)))));
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-btn-text-hover, var(--ne-scheme-btn-text, var(--ne-scheme-text))));
  }

  /* "More" submenu items keep their nav-link (so the submenu + connector trace
     survive in the clone), but a nav-link carries the nav/accent FONT (e.g.
     Michroma 11px) — override it to the dropdown row font so the cloned row
     reads like the plain dropdown rows. Inherit = same body font the
     dropdown-links use; size from the shared --ne-font-size-sm token. */
  .ne-header__nav-item--more-child > .ne-header__nav-link,
  .ne-header__nav-item--more-child > .ne-header__nav-link > span {
    font-family: inherit !important;
    font-size: var(--ne-font-size-sm, 0.875rem) !important;
    font-weight: inherit !important;
    letter-spacing: normal !important;
    text-transform: none !important;
  }

  /* Hover style (button-style = none) → hover-only shape + underline + bold,
     mirroring the nav-link hover treatment. The base dropdown-link:hover bg
     already supplies the highlight; the shape just sets its radius. */
  .ne-header--hover-style-rounded .ne-header__dropdown-link:hover { border-radius: var(--ne-btn-radius, var(--ne-radius-md, 8px)); }
  .ne-header--hover-style-pill .ne-header__dropdown-link:hover { border-radius: var(--ne-radius-full, 9999px); }
  .ne-header--hover-style-square .ne-header__dropdown-link:hover { border-radius: 0; }
  :is(.ne-header--ul-static, .ne-header--ul-sweep, .ne-header--ul-fade) .ne-header__dropdown-link:hover {
    text-decoration: underline;
    text-underline-offset: 4px;
    text-decoration-thickness: 2px;
  }
  .ne-header--hover-bold .ne-header__dropdown-link:hover {
    -webkit-text-stroke: 0.4px currentColor;
  }
  /* Underline/bold hover WITHOUT a hover-shape → text cue is the whole
     affordance, so drop the default panel hover-highlight block (matches the
     top-level nav-links, which show no background for underline/bold-only
     hover — the block only appears in hover-shape mode). The :not() guard
     keeps the shaped highlight when a hover shape IS chosen. Specificity
     (0,4,0) beats the base dropdown-link:hover (0,2,0). */
  :is(.ne-header--ul-static, .ne-header--ul-sweep, .ne-header--ul-fade):not([class*="hover-style-"]) .ne-header__dropdown-link:hover,
  .ne-header--hover-bold:not([class*="hover-style-"]) .ne-header__dropdown-link:hover {
    background: transparent;
  }

  .ne-header__dropdown-link--view-all {
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-accent);
    font-weight: 500;
  }

  /* Cascading submenu (OS-style flyout) — sub-links appear as a nested panel
     beside the parent row instead of inline below it. */
  .ne-header__dropdown-item {
    position: relative;
    /* BFC, damit die 3px-Chip-margin-block NICHT durch den Wrapper hinaus
       kollabiert (wie im Multicol-Kontext der --more-child-Zeilen). Sonst
       Wrapper 40px hier vs. 46px dort → ungleicher Zeilen-Pitch im Mega. */
    display: flow-root;
  }
  .ne-header__dropdown-item--has-submenu > .ne-header__dropdown-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ne-space-2);
  }
  .ne-header__dropdown-chevron {
    flex: 0 0 auto;
    opacity: 0.7;
    transition: transform var(--ne-transition-fast);
  }
  .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown-link .ne-header__dropdown-chevron,
  .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown-link .ne-header__dropdown-chevron {
    transform: translateX(2px);
    opacity: 1;
  }
  .ne-header__dropdown--submenu {
    /* Reset parent dropdown's top-offset — submenu is not under the header.
       Positioning parent is .ne-header__dropdown-item (position: relative),
       whose 100% width equals the item's width. The parent .ne-header__dropdown
       has padding (--ne-space-3) around the item, so 100% sits INSIDE the
       parent dropdown's right padding. Add that padding to the left offset
       so the submenu clears the parent dropdown's right border cleanly,
       then add --ne-header-dropdown-gap — same visual gap pattern used
       between header and first dropdown. */
    top: 0;
    left: calc(100% + var(--ne-space-3, 12px) + var(--ne-header-dropdown-gap, 10px));
    transform: none;
    min-width: 220px;
  }
  /* Viewport-aware submenu (JS toggles via initSubmenuFlip, same logic as the
     More-flyout): flip left on right overflow, anchor-bottom (open up) on bottom
     clip, cap height + scroll when neither fits. (0,3,0) beats the base (0,1,0);
     transform is untouched so the open animation still runs. */
  .ne-header__dropdown-item--has-submenu.is-flyout-left > .ne-header__dropdown--submenu {
    left: auto;
    right: calc(100% + var(--ne-space-3, 12px) + var(--ne-header-dropdown-gap, 10px));
  }
  .ne-header__dropdown-item--has-submenu.is-flyout-up > .ne-header__dropdown--submenu {
    top: auto;
    bottom: 0;
  }
  .ne-header__dropdown-item--has-submenu.is-flyout-cap > .ne-header__dropdown--submenu {
    max-height: var(--ne-more-flyout-maxh, 70vh);
    overflow-y: auto;
    overflow-x: hidden;
  }
  /* Side hover-bridge follows the flip: default bridge is on the left (right:100%,
     see below); when flipped left the submenu sits left of the item, so the
     bridge moves to the right side. */
  .ne-header__dropdown-item--has-submenu.is-flyout-left > .ne-header__dropdown--submenu::before {
    right: auto;
    left: 100%;
  }
  /* Submenu uses the SAME ::after bg layer as the parent dropdown, so it
     inherits the parent's FULL optic automatically — base scheme bg, header
     glass (1975), custom-optics opacity/blur/tint (1937-1953), custom
     color-scheme (2183) AND the soft-edge fade (1905). Every one of those rules
     targets .ne-header__dropdown::after, which the submenu IS (compound class).
     The previous approach painted bg on the ELEMENT + disabled ::after, which
     made the soft-edge mask a no-op (the mask sits on ::after; masking the
     element would clip the links too — Rule 49) → the submenu showed no fade
     while the parent did (user 2026-06-23). Keep the element transparent; the
     base ::after (inset:0; z-index:-1; bg = --ne-scheme-bg, defined at 1858)
     sits behind the links and carries every optic the parent has. */
  .ne-header__dropdown--submenu {
    background-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* Bridge becomes horizontal for the submenu — spans parent padding + gap */
  .ne-header__dropdown--submenu::before {
    top: 0;
    bottom: 0;
    right: 100%;
    left: auto;
    height: auto;
    width: calc(var(--ne-space-3, 12px) + var(--ne-header-dropdown-gap, 10px) + 4px);
  }
  .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: none;
  }
  /* Slide variant: submenu slides in from the left */
  .ne-header--dropdown-slide .ne-header__dropdown--submenu {
    transform: translateX(-8px);
  }
  .ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transform: translateX(0);
  }
  .ne-header--dropdown-scale .ne-header__dropdown--submenu {
    transform: scale(0.96);
    transform-origin: top left;
  }
  .ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transform: scale(1);
  }
  /* Fade variant — submenu flicker fix. Base fade uses `visibility 0s X` which
     keeps the element `visibility: hidden` during the opacity fade, then flips
     to visible at the end → the submenu pops in abruptly instead of fading.
     The top-level dropdown has the same override via `.ne-header__nav-item:hover`,
     but that selector doesn't match submenus (parent is .ne-header__dropdown-item,
     not .ne-header__nav-item) so the inherited base transition wins. Re-declare
     with `visibility 0s 0s` so the fade is visible from the start. */
  .ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transition: opacity var(--ne-transition-base), visibility 0s 0s;
  }
  /* Right-sidebar header: flip submenu to the left of the parent so it
     doesn't collide with the already left-placed dropdown. */
  .ne-header--right .ne-header__dropdown--submenu {
    left: auto;
    right: calc(100% + var(--ne-space-3, 12px) + var(--ne-header-dropdown-gap, 10px));
  }
  .ne-header--right .ne-header__dropdown--submenu::before {
    right: auto;
    left: 100%;
  }

  /* ==========================================================================
     HEADER - MEGAMENU (block-driven, Horizon-style foundation)
     ========================================================================== */

  /* Megamenu: full-width panel anchored to header bottom edge.
     Uses the same visual language as dropdown (glass, radius, border) but spans
     the full header width for multi-column layouts with columns + promo tiles. */
  /* Megamenu uses position: fixed so it centers to the viewport (or to the
     transformed header ancestor in floating/sticky mode) — not to the narrow
     nav-item li, which would shift the panel hard to one side when the
     trigger isn't near the viewport's horizontal center. */
  .ne-header__megamenu {
    position: fixed;
    /* --ne-header-bottom is published by menu.liquid's JS (getBoundingClientRect
       on .ne-header). Falls back to header_height setting + a generous buffer
       if JS hasn't run yet. Using actual bottom is critical when a taller logo
       pushes the real header height past the configured --ne-header-height. */
    top: calc(var(--ne-header-bottom, calc(var(--ne-header-height, 60px) + 10px)) + var(--ne-header-dropdown-gap, 10px));
    left: 50%;
    transform: translateX(-50%);
    /* Width coupled to --content-width formula from critical.css §4 so the
       megamenu panel always matches the section grid's content column. */
    width: min(
      calc(var(--page-width, 90rem) - var(--ne-section-margin, var(--page-margin, 20px)) * 2),
      calc(100vw - var(--ne-section-margin, var(--page-margin, 20px)) * 2)
    );
    padding: var(--ne-space-4) var(--ne-space-5) var(--ne-space-5);
    /* Same radius-resolution chain as .ne-header__dropdown (line ~1975) so the
       megamenu and dropdowns always share one corner radius — --ne-dropdown-radius
       first (future dropdown-specific override), then the header radius setting. */
    border-radius: var(--ne-dropdown-radius, var(--ne-header-radius, var(--ne-radius-lg)));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    /* Glass + bg live on ::after pseudo — avoids backdrop-root leakage into descendants. */
    background-color: transparent;
    box-shadow: none;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    z-index: var(--ne-z-overlay);
  }
  /* Top frame active: anchor the megamenu width to the VIEWPORT edges minus the
     frame width (offset + per-side padding) PLUS 2px — a CONSTANT clearance to the
     frame at ANY window width (independent of page-width / section-margin, which
     made the gap width-dependent before). Stays centred via the base translateX(-50%),
     so both edges sit exactly frame+2px inside the viewport. Sidebar megas are handled
     separately. */
  .ne-header--top.ne-header--has-frame .ne-header__megamenu {
    width: calc(100vw
      - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px) + 2px)
      - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px) + 2px)) !important;
  }
  .ne-header__megamenu::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    /* Base = solid scheme bg, matching the dropdown. Was a dark heavy-glass
       (--ne-glass-bg-heavy + blur) that always rendered as "blackened glass"
       on a solid header regardless of the glass/transparent setting. The glass
       rule (.ne-header.ne-glass …::after) and the transparent-on-top rule above
       now add / remove the frosted look in lockstep with the dropdown. */
    background-color: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    pointer-events: none;
  }
  /* Top header (not constrained to page width) — widen the megamenu to the
     header's content bound. The header bar spans the full viewport (minus its
     own padding-x), but the megamenu was capped at --page-width, so it sat
     narrower than the nav above it (cramped left column). Floating &
     constrained headers already align (their content is inset to page-width),
     so this only targets .ne-header--top:not(.ne-header--constrained).
     Specificity (0,3,0) beats the base .ne-header__megamenu (0,1,0). */
  .ne-header--top:not(.ne-header--constrained) .ne-header__megamenu {
    width: calc(100vw - var(--ne-header-padding-x, 20px) * 2);
  }

  /* Hover-bridge for the megamenu — same purpose as the dropdown's ::before */
  .ne-header__megamenu::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: calc(-1 * (var(--ne-header-dropdown-gap, 10px) + 4px));
    height: calc(var(--ne-header-dropdown-gap, 10px) + 4px);
    background: transparent;
    pointer-events: auto;
  }

  .ne-header--dropdown-fade .ne-header__megamenu {
    transition: opacity var(--ne-transition-base), visibility 0s var(--ne-transition-base);
  }

  .ne-header--dropdown-slide .ne-header__megamenu {
    transform: translateX(-50%) translateY(-18px);
    transition: opacity var(--ne-transition-base), visibility var(--ne-transition-base), transform var(--ne-transition-base);
  }

  .ne-header--dropdown-scale .ne-header__megamenu {
    transform: translateX(-50%) scale(0.85);
    transform-origin: top center;
    transition: opacity var(--ne-transition-base), visibility var(--ne-transition-base), transform var(--ne-transition-spring);
  }

  .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header__nav-item.is-open > .ne-header__megamenu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  /* Fade open-state — override base `visibility 0s base` delay with `0s 0s`
     so the panel is already visible when the opacity transition starts
     (otherwise it pops in abruptly when the delay elapses = flicker). */
  .ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu {
    transition: opacity var(--ne-transition-base), visibility 0s 0s;
  }

  .ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateX(-50%) translateY(0);
  }

  .ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateX(-50%) scale(1);
  }

  /* ─── Button-menu (dropdown_style = buttons) animation modifiers ───
     btn-menu was previously ignoring dropdown_animation completely — its
     base transition was opacity-only and per-mode modifiers (.ne-header--
     dropdown-fade/slide/scale) only targeted .ne-header__dropdown and
     .ne-header__megamenu. Result: user picks "scale" but sees the staggered
     opacity-fade (which visually looks like slide-down due to per-button
     stagger). Now: each animation mode applies appropriate transform on
     btn-menu wrapper. Opacity stays asymmetric (Rule 236) to avoid backdrop-
     filter bloom-in-late on inner buttons. Per-button stagger remains
     visible inside the wrapper as a sub-animation. */
  .ne-header--dropdown-none .ne-header__btn-menu,
  .ne-header--dropdown-none .ne-header__nav-item:hover > .ne-header__btn-menu,
  .ne-header--dropdown-none .ne-header__nav-item:focus-within > .ne-header__btn-menu,
  .ne-header--dropdown-none .ne-header__nav-item.is-open > .ne-header__btn-menu {
    transition: none;
  }
  /* Slide: wrapper translates down 10px on open. Origin top so motion
     comes from the trigger direction. */
  .ne-header--dropdown-slide .ne-header__btn-menu {
    transform: translateY(-10px);
    transition: opacity var(--ne-transition-base), visibility 0s linear var(--ne-transition-base), transform var(--ne-transition-base);
  }
  .ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__btn-menu,
  .ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__btn-menu,
  .ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__btn-menu {
    transform: translateY(0);
    transition: opacity 0s linear 0s, visibility 0s linear 0s, transform var(--ne-transition-base);
  }
  /* Scale: wrapper scales 0.95→1 with top-left transform-origin (matches
     btn-menu's anchor position at top:100% left:0 of trigger). */
  .ne-header--dropdown-scale .ne-header__btn-menu {
    transform: scale(0.95);
    transform-origin: top left;
    transition: opacity var(--ne-transition-base), visibility 0s linear var(--ne-transition-base), transform var(--ne-transition-base);
  }
  .ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__btn-menu,
  .ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__btn-menu,
  .ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__btn-menu {
    transform: scale(1);
    transition: opacity 0s linear 0s, visibility 0s linear 0s, transform var(--ne-transition-base);
  }
  /* Flip-left variant: scale transform-origin moves to top-right since
     menu now anchors at top:100% right:0 instead of left:0. */
  .ne-header--dropdown-scale .ne-header__btn-menu.ne-header__btn-menu--flip-left {
    transform-origin: top right;
  }
  /* Fade: relies on Bug-B-fix base behavior (opacity asymmetric snap-on-
     open, fade-on-close). No explicit modifier rule needed — opacity-only
     animation matches the "fade" intent without transform interference. */

  /* Per-button stagger override per mode. The btn-menu's inner buttons
     have `transition-delay: calc(var(--i) * 50ms)` which creates a top-
     to-bottom cascade of buttons appearing one-by-one. This stagger is
     the DOMINANT visual signature regardless of what the wrapper does —
     scale/fade wrapper animations are barely noticeable next to the
     button-cascade. To make each animation mode visually distinct:
     - slide: keep stagger (the cascade IS the slide-down character)
     - scale: disable stagger → buttons fade simultaneously, wrapper scales
     - fade: disable stagger but EXTEND duration + add subtle translateY
       on children (not wrapper, to avoid backdrop bloom) for a clearly
       perceptible fade-in distinct from "none"
     - none: disable stagger → instant snap (matches wrapper transition:none) */
  /* none mode: instant snap — kill the cascade animation + any transition. */
  .ne-header--dropdown-none .ne-header__btn-menu__btn {
    transition: none !important;
    animation: none !important;
  }
  /* fade / slide / scale all share the per-button cascade reveal defined on
     the open-state rule further below (@keyframes ne-btn-menu-item-in):
       • fade  = cascade only (no wrapper transform)
       • slide = cascade + wrapper translateY (rules above)
       • scale = cascade + wrapper scale (rules above)
     No fade-specific transform here. A transition-based opacity fade SILENTLY
     FAILS for the buttons: the wrapper snaps visibility:hidden→visible
     (anti-bloom), so the child opacity transition has no rendered baseline to
     animate from and jumps straight to 1. And nav_btn_3d puts
     `transform: translateY(-1px) !important` on the buttons, overriding any
     fade translateY. The keyframe animation on open fires reliably regardless
     of the visibility snap, and being opacity-only it coexists with the 3D
     transform (user report 2026-05-24: fade not visible + stagger wanted on
     all modes). */

  .ne-header--glow .ne-header__megamenu {
    box-shadow: 0 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent-glow, rgba(99,102,241,0.25));
  }

  /* Megamenu panel heading — sits above the inner columns grid. Optional;
     block renders the wrapper only when heading or subheading is set. */
  .ne-header__megamenu-header {
    padding-bottom: var(--ne-space-4);
    margin-bottom: var(--ne-space-4);
    border-bottom: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-header__megamenu-heading {
    font-size: var(--ne-font-size-lg);
    font-weight: 700;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    line-height: 1.2;
  }
  .ne-header__megamenu-subheading {
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text));
    margin-top: var(--ne-space-1);
  }

  /* Megamenu-inner: two-column grid when aside present (links | products/promo),
     single column otherwise. Uses 1fr for links so they expand to fill available
     space; aside is intrinsic width. */
  .ne-header__megamenu-inner {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--ne-space-6);
    align-items: start;
  }
  .ne-header__megamenu--has-aside .ne-header__megamenu-inner {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  /* ── Sidebar mega — PANEL variant (T5.2) ──────────────────────────────────
     A sidebar is narrow + tall. Stack the mega: link columns on TOP, a products
     ROW on the BOTTOM. The panel is a FIXED, frame-clamped card anchored to the
     sidebar's INNER edge (not the individual nav-item) — so:
       · consistent position + the dropdown-gap reference is the sidebar edge
         (fixes: gap didn't seem to apply on sidebars),
       · full frame-height headroom → flyout submenus never need internal scroll,
       · it can't overflow the viewport on short/narrow screens (top, bottom AND
         right edges all stay frame+2px inside).
     Drawer variant (T5.3) overrides this. */
  .ne-header--sb-mega-panel .ne-header__megamenu {
    position: fixed !important;
    /* Fallback vertical anchor; JS (updateSidebarFlip) overrides `top` inline with
       the item-centered value, clamped to the frame band — so the panel sits next
       to the hovered item and only shifts to the edge when it would overflow. */
    top: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + 2px);
    bottom: auto !important;
    /* Glass-safe reveal (Rule 231): keep opacity CONSTANT (any 0→1 fade re-samples
       the glass backdrop per frame → "puff-in" flicker). Show/hide is INSTANT via
       visibility (no transition) — an animated close made the content linger after
       the cursor left ("delayed fade-out"). JS sets `top`. */
    transform: none !important;
    visibility: hidden;
    width: min(720px, calc(100vw
      - var(--ne-header-sidebar-width, 88px)
      - var(--ne-space-2, 8px) - var(--ne-header-dropdown-gap, 10px)
      - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px) + 2px))) !important;
    /* Fallback cap; JS (place) sets an inline max-height from the MEASURED band so
       it can override this — hence NO !important here (inline loses to !important,
       which silently defeated the JS clamp in the Customizer's tall iframe). */
    max-height: calc(100vh
      - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + 2px)
      - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-bottom, 0px) + 2px));
    /* Scroll lives on the INNER block, not the fixed panel: Firefox does not
       reliably clip overflow on a position:fixed element that is capped by
       max-height and holds flex/grid content (the content escaped the box).
       Canonical fix — panel is a flex column that just clips; the inner grid
       flexes to fill and scrolls. Works in Chromium + Firefox. */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    opacity: 1 !important;
    transition: none !important;
  }
  .ne-header--sb-mega-panel .ne-header__megamenu-inner {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
  }
  .ne-header--left.ne-header--sb-mega-panel .ne-header__megamenu {
    left: calc(var(--ne-header-sidebar-width, 88px) + var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px)) !important;
    right: auto !important;
  }
  .ne-header--right.ne-header--sb-mega-panel .ne-header__megamenu {
    right: calc(var(--ne-header-sidebar-width, 88px) + var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px)) !important;
    left: auto !important;
  }
  .ne-header--sb-mega-panel .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--sb-mega-panel .ne-header__nav-item.is-open > .ne-header__megamenu,
  .ne-header--sb-mega-panel .ne-header__nav-item:focus-within > .ne-header__megamenu {
    visibility: visible !important;
  }
  /* When NOT open, force the panel + its whole subtree hidden INSTANTLY. The base
     dropdown effect (equal specificity, later in source) kept visibility for its
     fade duration and ran a per-item stagger, so the sub-items lingered after the
     panel background was already gone. This wins and kills that trailing fade. */
  .ne-header--sb-mega-panel .ne-header__nav-item:not(:hover):not(:focus-within):not(.is-open) > .ne-header__megamenu {
    visibility: hidden !important;
  }
  .ne-header--sb-mega-panel .ne-header__nav-item:not(:hover):not(:focus-within):not(.is-open) > .ne-header__megamenu,
  .ne-header--sb-mega-panel .ne-header__nav-item:not(:hover):not(:focus-within):not(.is-open) > .ne-header__megamenu * {
    transition: none !important;
    animation-duration: 0s !important;
  }
  /* Keep the glass scrim/border pseudos in LOCKSTEP with the panel on hide: they
     must not fade on their own opacity, or the card background vanishes before
     the links do (the leave-desync). Opacity constant, no own transition — they
     ride the panel's visibility toggle. */
  .ne-header--sb-mega-panel .ne-header__megamenu::before,
  .ne-header--sb-mega-panel .ne-header__megamenu::after {
    opacity: 1 !important;
    transition: none !important;
  }
  /* Plain dropdowns (nav items whose panel has NO submenu/products, so they stay
     a .ne-header__dropdown instead of being promoted to a mega) still ran the base
     fade → the leave-lag persisted for them. Same instant, glass-safe show/hide:
     kill only the dropdown's OWN transition (not inner link/product hovers). */
  .ne-header--sb-mega-panel .ne-header__dropdown:not(.ne-header__dropdown--submenu),
  .ne-header--sb-mega-panel .ne-header__dropdown:not(.ne-header__dropdown--submenu)::before,
  .ne-header--sb-mega-panel .ne-header__dropdown:not(.ne-header__dropdown--submenu)::after {
    transition: none !important;
  }
  /* Stack: link columns on top, products row underneath. Only min-WIDTH:0 here —
     min-height:0 on the grid items (links/aside rows) would let their rows
     collapse and overlap; the scroll container's min-height:0 lives on .inner. */
  .ne-header--sb-mega-panel .ne-header__megamenu-links {
    min-width: 0;
  }
  .ne-header--sb-mega-panel .ne-header__megamenu--has-aside .ne-header__megamenu-inner {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto;
  }
  .ne-header--sb-mega-panel .ne-header__megamenu-aside {
    min-width: 0;
  }
  .ne-header--sb-mega-panel .ne-header__megamenu-aside--products {
    grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
  }
  .ne-header--sb-mega-panel .ne-header__megamenu--has-aside .ne-header__megamenu-aside {
    padding-top: var(--ne-space-4, 16px);
    border-top: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.12));
  }
  /* Columns submenu style in the sidebar panel: a group's children sit in a
     column BESIDE its heading (deepest level unfolds sideways), instead of
     stacked underneath. Each group becomes a 2-track grid: [heading | children].
     Top-level leaf links keep flowing normally. */
  .ne-header--sb-mega-panel.ne-header--mm-sub-columns .ne-header__megamenu-links {
    columns: unset;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-4);
  }
  .ne-header--sb-mega-panel.ne-header--mm-sub-columns .ne-header__megamenu-group {
    display: grid;
    grid-template-columns: minmax(96px, max-content) minmax(0, 1fr);
    column-gap: var(--ne-space-5);
    align-items: start;
    margin-bottom: 0;
  }
  /* Heading becomes the left track — drop the underline (it read as a divider
     when stacked); the column gap separates it from the children now. */
  .ne-header--sb-mega-panel.ne-header--mm-sub-columns .ne-header__megamenu-group-heading {
    border-bottom: 0;
    padding-bottom: 0;
    align-self: start;
  }
  /* Flyout submenus: the detail pane fills the links container's HEIGHT (it's
     absolute top:0/bottom:0). Give that container a tall min-height so the
     revealed submenu has room and doesn't need its own scroll — the panel itself
     is already frame-clamped, so nothing exceeds the screen. */
  .ne-header--sb-mega-panel.ne-header--mm-sub-flyout .ne-header__megamenu-links {
    min-height: min(58vh, 440px);
  }
  /* Hover-bridge INSIDE the flyout: a transparent ::after on the HEADING (not on
     the detail list — that has overflow:auto and would CLIP a bridge) extends
     right across the gap to the detail pane. It only exists while the group is
     hovered, so it never blocks sibling rows; being a group descendant, hovering
     it keeps the group :hover alive while the cursor crosses to the detail. */
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout > .ne-header__megamenu-group-heading {
    position: relative;
  }
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout:hover > .ne-header__megamenu-group-heading::after,
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout:focus-within > .ne-header__megamenu-group-heading::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: 100%;
    width: calc(var(--ne-space-6) + 16px);
  }

  /* Menu-links layout — CSS multi-column so each column flows independently.
     Grid would bind all groups in a row to the tallest one, so expanding an
     accordion in column B would push groups in columns A and C down too.
     With `columns:`, each column is its own text-flow: accordion expansion
     lengthens only its own column; other columns are unaffected. */
  .ne-header__megamenu-links {
    columns: var(--ne-mm-link-cols, 3);
    column-gap: var(--ne-space-5);
    /* `display: block` (default) — columns requires block-level flow. */
  }

  /* Responsive wrap (opt-in PER-MEGA via the megamenu_link block toggle →
     .ne-header__megamenu--wrap on the panel). Swaps the fixed multi-column flow
     for an auto-fit grid: each group keeps a readable min width and surplus
     columns wrap onto a new row instead of squeezing their text. Scoped OFF for
     the sidebar/drawer mega variants and the flyout submenu model (header-level
     classes), which drive their own link layout. */
  .ne-header:not(.ne-header--sb-mega-panel):not(.ne-header--sb-mega-drawer):not(.ne-header--mm-sub-flyout) .ne-header__megamenu--wrap .ne-header__megamenu-links {
    columns: auto;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: var(--ne-space-4) var(--ne-space-5);
    align-items: start;
  }

  /* Hide the "View all" links PER-MEGA when the block toggle is off. The
     cloned source anchor keeps its class only until transformSubmenus renames
     it (accordion → --accordion-link--view-all, columns/flyout → --link--view-all),
     so all three variants are covered. */
  .ne-header__megamenu--no-view-all .ne-header__dropdown-link--view-all,
  .ne-header__megamenu--no-view-all .ne-header__megamenu-link--view-all,
  .ne-header__megamenu--no-view-all .ne-header__megamenu-accordion-link--view-all {
    display: none;
  }

  .ne-header__megamenu-group {
    display: block;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
    min-width: 0;
    margin-bottom: var(--ne-space-4);
  }
  .ne-header__megamenu-group:last-child { margin-bottom: 0; }

  /* Group heading = the child-link title, clickable (goes to that child's URL) */
  .ne-header__megamenu-group-heading {
    display: block;
    font-size: var(--ne-font-size-sm);
    font-weight: 700;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    text-decoration: none;
    padding-bottom: var(--ne-space-2);
    margin-bottom: var(--ne-space-1);
    border-bottom: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    line-height: 1.3;
    transition: color var(--ne-transition-fast);
  }
  .ne-header__megamenu-group-heading:hover {
    color: var(--ne-scheme-accent);
  }

  .ne-header__megamenu-list {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
  }

  /* Flyout submenu mode (megamenu_submenu_style = flyout): master-detail INSIDE
     the panel. Group headings + flat links stack in a narrow left column;
     hovering a group reveals its children in the pane to the RIGHT — a column
     within the mega, NOT a floating overlay. ('columns' shows every group's
     list in flow; 'accordion' uses the <details> markup instead of a group.) */
  .ne-header--mm-sub-flyout .ne-header__megamenu-links {
    columns: unset;
    position: relative;
    min-height: 180px;
  }
  /* Left column = headings + leaf rows, capped to a readable width. */
  .ne-header--mm-sub-flyout .ne-header__megamenu-links > .ne-header__megamenu-group--flyout,
  .ne-header--mm-sub-flyout .ne-header__megamenu-links > .ne-header__dropdown-item {
    max-width: 260px;
  }
  /* Static so the detail list positions against the links container, not the group. */
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout {
    position: static;
  }
  .ne-header__megamenu-group--flyout > .ne-header__megamenu-group-heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ne-space-2);
    cursor: pointer;
    border-bottom: 0;
    border-radius: var(--ne-radius-md);
    padding: var(--ne-space-2);
  }
  .ne-header__megamenu-group--flyout:hover > .ne-header__megamenu-group-heading,
  .ne-header__megamenu-group--flyout:focus-within > .ne-header__megamenu-group-heading {
    background: var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.06));
    color: var(--ne-scheme-text);
  }
  .ne-header__megamenu-group--flyout > .ne-header__megamenu-group-heading svg {
    flex: 0 0 auto;
  }
  /* Detail pane: fills the area to the RIGHT of the left column, inside the panel. */
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout > .ne-header__megamenu-list {
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(260px + var(--ne-space-6));
    right: 0;
    padding-left: var(--ne-space-5);
    border-left: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.08));
    overflow-x: hidden;
    overflow-y: auto;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--ne-transition-fast), visibility 0s linear var(--ne-transition-fast);
  }
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout:hover > .ne-header__megamenu-list,
  .ne-header--mm-sub-flyout .ne-header__megamenu-group--flyout:focus-within > .ne-header__megamenu-list {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition-delay: 0s;
  }

  .ne-header__megamenu-link {
    display: block;
    padding: var(--ne-space-2);
    border-radius: var(--ne-radius-md);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
    text-decoration: none;
    transition: all var(--ne-transition-fast);
    line-height: 1.4;
    position: relative;
  }
  .ne-header__megamenu-link:hover {
    color: var(--ne-scheme-text);
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.06));
    transform: translateX(2px);
  }

  /* Sub-link visual variants */
  .ne-header__megamenu-links--pill .ne-header__megamenu-link {
    display: inline-block;
    padding: var(--ne-space-1) var(--ne-space-3);
    border-radius: var(--ne-radius-full);
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-header__megamenu-links--pill .ne-header__megamenu-link:hover {
    transform: none;
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.12));
    border-color: var(--ne-scheme-accent);
  }
  .ne-header__megamenu-links--pill .ne-header__megamenu-list {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--ne-space-1);
  }

  .ne-header__megamenu-links--underline .ne-header__megamenu-link {
    background: transparent;
  }
  .ne-header__megamenu-links--underline .ne-header__megamenu-link::after {
    content: '';
    position: absolute;
    left: var(--ne-space-2);
    right: var(--ne-space-2);
    bottom: var(--ne-space-1);
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--ne-transition-fast);
  }
  .ne-header__megamenu-links--underline .ne-header__megamenu-link:hover {
    background: transparent;
    transform: none;
    color: var(--ne-scheme-accent);
  }
  .ne-header__megamenu-links--underline .ne-header__megamenu-link:hover::after {
    transform: scaleX(1);
  }

  /* Inline <details> accordion — replaces cascading sub-submenus inside a
     megamenu column (same UX as the drawer's nested accordions). */
  .ne-header__megamenu-accordion {
    min-width: 0;
  }
  .ne-header__megamenu-accordion > summary {
    list-style: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ne-space-2);
    padding: var(--ne-space-2);
    border-radius: var(--ne-radius-md);
    font-size: var(--ne-font-size-sm);
    font-weight: 700;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    transition: color var(--ne-transition-fast), background var(--ne-transition-fast);
  }
  .ne-header__megamenu-accordion > summary::-webkit-details-marker { display: none; }
  .ne-header__megamenu-accordion > summary:hover {
    color: var(--ne-scheme-accent);
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.06));
  }
  .ne-header__megamenu-accordion-chevron {
    flex: 0 0 auto;
    transition: transform var(--ne-transition-fast);
  }
  .ne-header__megamenu-accordion[open] > summary .ne-header__megamenu-accordion-chevron {
    transform: rotate(180deg);
  }
  .ne-header__megamenu-accordion-content {
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding: var(--ne-space-1) 0 var(--ne-space-2) var(--ne-space-3);
    margin-left: var(--ne-space-1);
    border-left: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-header__megamenu-accordion-link {
    display: block;
    padding: var(--ne-space-1) var(--ne-space-2);
    border-radius: var(--ne-radius-sm);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
    text-decoration: none;
    line-height: 1.4;
    transition: color var(--ne-transition-fast), background var(--ne-transition-fast), transform var(--ne-transition-fast);
  }
  .ne-header__megamenu-accordion-link:hover {
    color: var(--ne-scheme-text);
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.06));
    transform: translateX(2px);
  }
  .ne-header__megamenu-accordion-link--view-all {
    color: var(--ne-scheme-accent);
    font-weight: 600;
  }

  /* Synthesized megamenu (no megamenu_link block, upgraded from plain dropdown).
     Plain dropdowns that get promoted in "global mega mode" put the original
     .ne-header__dropdown-item rows directly into the CSS-columns links slot.
     Give those rows the same visual language as the accordion-link inside a
     mega (subtle padded row with hover tint), so the look is consistent
     across a panel that mixes accordionized and non-accordion items. */
  .ne-header__megamenu--synthetic .ne-header__megamenu-links > .ne-header__dropdown-item {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
    margin-bottom: 2px;
  }
  .ne-header__megamenu--synthetic .ne-header__megamenu-links > .ne-header__dropdown-item > .ne-header__dropdown-link {
    display: block;
    padding: var(--ne-space-2);
    border-radius: var(--ne-radius-md);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
    text-decoration: none;
  }
  .ne-header__megamenu--synthetic .ne-header__megamenu-links > .ne-header__dropdown-item > .ne-header__dropdown-link:hover {
    color: var(--ne-scheme-text);
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.06));
  }

  /* More-as-megamenu: the overflow panel matches the full content-width of
     other megamenus so More visually reads as "another megamenu slot" rather
     than a narrow dropdown. Cloned .ne-header__nav-item--more-child rows keep
     their existing stacking layout inside the mega-links slot.
     Width + position inherit from the base .ne-header__megamenu rules. */
  .ne-header__megamenu--more .ne-header__megamenu-links > .ne-header__nav-item--more-child {
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    page-break-inside: avoid;
  }
  /* The cloned More-child carries a nav-LINK (pill button) whose label is laid
     out with justify-content:space-between → the text drifted to the middle of
     the wide mega row (user 2026-07-02). Inside a mega, read it as a left-aligned
     column heading like every other mega row. */
  .ne-header__megamenu--more .ne-header__nav-item--more-child > .ne-header__nav-link {
    justify-content: flex-start;
    text-align: left;
  }

  /* ==========================================================================
     HEADER - BUTTON MENU (dropdown_style = buttons)
     --------------------------------------------------------------------------
     Variant of the classic dropdown that cascades right as columns of
     independent buttons. Buttons carry the same `.ne-header__nav-link--btn`
     classes as the top-level nav so shape / 3d depth / glass / glow / shadow
     header effects apply 1:1 without re-declaration.
     Grid geometry:
       - Connector: L-curve on the trigger itself, from trigger-bottom-center
         down+left to the column's trunk top.
       - Trunk: vertical line at the column's left edge (x = 0 of buttons).
       - Branches: horizontal line at each button's top (between siblings).
       - Bottom cap: extra branch under the LAST button so the grid closes.
     Z-index: the open nav-item is lifted above the header surface so the
     connector + trunk render over the header bg, not behind it.
     ========================================================================== */

  .ne-header__btn-menu {
    position: absolute;
    top: calc(100% + var(--ne-header-dropdown-gap, 10px));
    left: 0;
    z-index: 1000;
    isolation: isolate;
    display: flex;
    flex-direction: row;
    align-items: flex-start;
    gap: var(--ne-btn-menu-col-gap, 22px);
    padding: 0;
    background: transparent;
    border: none;
    box-shadow: none;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--ne-transition-base), visibility 0s var(--ne-transition-base);
    --ne-btn-menu-dur: 200ms;
    --ne-btn-menu-line-dur: 180ms;
    --ne-btn-menu-col-gap: 22px;
    --ne-btn-menu-row-gap: 6px;
    color: var(--ne-scheme-text);
  }

  /* Lift nav-item above header bg while open so connector + trunk render in
     front of .ne-header__bg / glass / shadow layers. */
  .ne-header--btn-menu .ne-header__nav-item--has-dropdown {
    position: relative;
  }
  .ne-header--btn-menu .ne-header__nav-item--has-dropdown:hover,
  .ne-header--btn-menu .ne-header__nav-item--has-dropdown:focus-within,
  .ne-header--btn-menu .ne-header__nav-item--has-dropdown.is-open {
    z-index: 1001;
  }

  /* Invisible hover-bridge so the cursor can cross from trigger into menu. */
  .ne-header__btn-menu::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: calc(-1 * (var(--ne-header-dropdown-gap, 10px) + 4px));
    height: calc(var(--ne-header-dropdown-gap, 10px) + 4px);
    background: transparent;
    pointer-events: auto;
  }

  /* Open state — asymmetric opacity transition (Rule 236) snaps opacity to 1
     instantly on open. Individual buttons inside have backdrop-filter (when
     nav_btn_glass on), and an opacity fade on the wrapper would force a
     backdrop-recompute per frame → "bloom-in-late" effect where blur appears
     delayed during the open animation. Snap-on-open lets backdrop render at
     full strength frame-1; fade-on-close (defined in base rule above) gives
     the visual outro without the bloom artifact. Visibility flips with 0s
     delay on both directions so the element is interactive immediately. */
  .ne-header__nav-item:hover > .ne-header__btn-menu,
  .ne-header__nav-item:focus-within > .ne-header__btn-menu,
  .ne-header__nav-item.is-open > .ne-header__btn-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 0s linear 0s, visibility 0s linear 0s;
  }

  /* Flip to the left when viewport-overflow is detected (toggled by JS). */
  .ne-header__btn-menu.ne-header__btn-menu--flip-left {
    left: auto;
    right: 0;
  }

  /* Root panel opens UPWARD when the header sits low (open-up), mirroring the
     panel dropdown — the btn-menu had no open-up so it clipped the viewport
     bottom (user 2026-06-23). Bridge flips to the lower edge too. */
  .ne-header--open-up .ne-header__btn-menu {
    top: auto;
    bottom: calc(100% + var(--ne-header-dropdown-gap, 10px));
  }
  .ne-header--open-up .ne-header__btn-menu::after {
    top: auto;
    bottom: calc(-1 * (var(--ne-header-dropdown-gap, 10px) + 4px));
  }

  /* Long button labels truncate with an ellipsis (parity with panel dropdown). */
  .ne-header__btn-menu__label {
    display: block;
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .ne-header__btn-menu__col {
    position: relative;
    list-style: none;
    margin: 0;
    padding: 0;
    display: inline-flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--ne-btn-menu-row-gap, 6px);
    min-width: max-content;
  }

  /* Sub-columns open rightward next to their parent item. */
  .ne-header__btn-menu__col--sub {
    position: absolute;
    top: 0;
    left: calc(100% + var(--ne-btn-menu-col-gap, 22px));
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--ne-transition-base), visibility 0s var(--ne-transition-base);
  }

  .ne-header__btn-menu__item:hover > .ne-header__btn-menu__col--sub,
  .ne-header__btn-menu__item:focus-within > .ne-header__btn-menu__col--sub {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity var(--ne-transition-base), visibility 0s 0s;
  }

  .ne-header__btn-menu--flip-left .ne-header__btn-menu__col--sub {
    left: auto;
    right: calc(100% + var(--ne-btn-menu-col-gap, 22px));
  }

  /* Sub-column vertical viewport flip (JS toggles --up / --cap) — a bottom item's
     sub-column ran off the screen bottom (user 2026-06-23). --up anchors the
     bottom (opens upward); --cap limits height + scrolls when it fits neither. */
  .ne-header__btn-menu__col--sub.ne-header__btn-menu__col--up {
    top: auto;
    bottom: 0;
  }
  .ne-header__btn-menu__col--sub.ne-header__btn-menu__col--cap {
    max-height: var(--ne-bm-col-maxh, 70vh);
    overflow-y: auto;
    overflow-x: hidden;
  }

  .ne-header__btn-menu__item {
    position: relative;
    margin: 0;
    padding: 0;
    list-style: none;
  }

  /* Base button. Stagger uses ONLY opacity (no transform) so it can coexist
     with the 3D-depth rule's translateY(-1px) !important without fighting.
     When show_bg=true the snippet adds .ne-header__nav-link--btn + the shape
     class so 3d / glass / glow / shadow header effects apply verbatim. */
  .ne-header__btn-menu__btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    /* Center button-menu items horizontally — distinct from regular
       dropdown which keeps left-aligned text. The chevron (when present
       for has-submenu items) still anchors via the parent flex but the
       label sits centered. User request 2026-05-23. */
    justify-content: center;
    text-align: center;
    gap: var(--ne-space-2);
    width: 100%;
    min-width: 100%;
    font-family: var(--ne-header-nav--family, inherit);
    font-weight: var(--ne-header-nav--weight, 500);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text);
    text-decoration: none;
    white-space: nowrap;
    opacity: 0;
    transition:
      opacity var(--ne-btn-menu-dur, 200ms) ease,
      color var(--ne-transition-fast),
      background-color var(--ne-transition-fast);
    transition-delay: calc(var(--i, 0) * var(--ne-btn-menu-speed, 50ms));
  }

  /* Plain-label mode (show_bg=false). Transparent shell, no shape.
     Inherits centered justify from base rule above. */
  .ne-header__btn-menu__btn:not(.ne-header__btn-menu__btn--bg) {
    background: transparent;
    border: 0;
    padding: 6px 0;
    min-width: 0;
  }
  .ne-header__btn-menu__btn:not(.ne-header__btn-menu__btn--bg):hover,
  .ne-header__btn-menu__btn:not(.ne-header__btn-menu__btn--bg):focus-visible {
    color: var(--ne-scheme-accent);
  }

  /* Button-bg mode — fallback paint ONLY when no shape class is present.
     When nav_button_style != 'none', the snippet adds .ne-header__nav-link--btn
     + shape class, which already paints everything (bg / border / radius /
     padding / 3d / glass / glow / shadow). The :not() gate keeps our fallback
     from double-painting a border on top of top-nav rules. */
  .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn) {
    background-color: var(--ne-scheme-btn-bg, transparent);
    color: var(--ne-scheme-btn-text, var(--ne-scheme-text));
    border: 0;
    border-radius: var(--ne-radius-md, 6px);
    padding: 0 var(--ne-space-3);
    min-height: var(--ne-header-btn-height, 36px);
  }
  .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn):hover,
  .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn):focus-visible {
    background-color: var(--ne-scheme-btn-bg-hover, var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.14)));
    color: var(--ne-scheme-btn-text-hover, var(--ne-scheme-accent));
  }

  /* Hover-bridge — invisible pseudo covering the gap zone between the trigger
     and the first button row so the cursor can traverse without losing :hover
     on the nav-item. Pseudo is an abs-pos child of the dropdown (which itself
     is abs-pos inside .ne-header__nav-item), so hovering it counts as hovering
     the ancestor nav-item. */
  .ne-header__btn-menu::before {
    content: '';
    position: absolute;
    top: calc(-1 * var(--ne-header-dropdown-gap, 10px));
    left: 0;
    right: 0;
    height: var(--ne-header-dropdown-gap, 10px);
    z-index: -1;
  }
  /* Sidebar variant — bridge sits on the leading edge instead of the top. */
  .ne-header--left .ne-header__btn-menu::before,
  .ne-header--right .ne-header__btn-menu::before {
    top: 0;
    bottom: 0;
    height: auto;
    width: var(--ne-space-3, 12px);
  }
  .ne-header--left .ne-header__btn-menu::before {
    left: calc(-1 * var(--ne-space-3, 12px));
    right: auto;
  }
  .ne-header--right .ne-header__btn-menu::before {
    right: calc(-1 * var(--ne-space-3, 12px));
    left: auto;
  }

  /* Submenu bridge — extends hit-area across the column-gap so cursor keeps
     :hover on the parent item while moving into the sub-column. Flip variant
     mirrors the bridge to the opposite side. */
  .ne-header__btn-menu__col--sub::before {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(-1 * var(--ne-btn-menu-col-gap, 22px));
    width: var(--ne-btn-menu-col-gap, 22px);
    z-index: -1;
  }
  .ne-header__btn-menu--flip-left .ne-header__btn-menu__col--sub::before,
  .ne-header__btn-menu__item--flip-left > .ne-header__btn-menu__col--sub::before {
    left: auto;
    right: calc(-1 * var(--ne-btn-menu-col-gap, 22px));
  }

  /* Glass propagation — add blur to dropdown buttons so they match the
     header's glass look. Split by shape:
     - Shape-based (pill / rounded / square): KEEP the top-nav's
       --ne-header-btn-bg (user's configured color + opacity). Only the
       backdrop-filter is added so the button stays recognizable.
     - No-shape fallback (nav_button_style='none'): full glass makeover
       (translucent --ne-glass-bg + border + blur). */
  .ne-header.ne-glass .ne-header__btn-menu__btn--bg {
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.2);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.2);
  }
  .ne-header.ne-glass .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn) {
    background-color: var(--ne-glass-bg, rgba(255, 255, 255, 0.08));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-header.ne-glass .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn):hover,
  .ne-header.ne-glass .ne-header__btn-menu__btn--bg:not(.ne-header__nav-link--btn):focus-visible {
    background-color: var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.16));
    border-color: var(--ne-glass-border-hover, var(--ne-glass-border, rgba(255, 255, 255, 0)));
  }

  .ne-header__btn-menu__chevron {
    flex: 0 0 auto;
    opacity: 0.7;
  }

  /* Custom-scheme propagation. Keep --ne-header-btn-bg (user's menu setting)
     for the button fill so visual intent stays consistent; override text +
     border + hover-accent via the scheme tokens. */
  .ne-header__btn-menu[class*="color-scheme-"] {
    background: transparent;
    color: var(--ne-scheme-text);
  }
  .ne-header__btn-menu[class*="color-scheme-"] .ne-header__btn-menu__btn {
    color: var(--ne-scheme-text);
  }
  .ne-header__btn-menu[class*="color-scheme-"] .ne-header__btn-menu__btn--bg {
    border-color: var(--ne-scheme-border, transparent);
  }
  .ne-header__btn-menu[class*="color-scheme-"] .ne-header__btn-menu__btn:hover,
  .ne-header__btn-menu[class*="color-scheme-"] .ne-header__btn-menu__btn:focus-visible {
    color: var(--ne-scheme-accent);
  }

  /* Stagger reveal: each button fades in via a keyframe ANIMATION (not a
     transition) when its hosting column opens. Animation beats transition
     here because the wrapper snaps visibility:hidden→visible (anti-bloom),
     which starves a transition of its baseline; a keyframe fires regardless.
     Reveal is opacity-only so it coexists with the nav_btn_3d transform.
     animation-delay = --i × speed → top-to-bottom cascade across ALL animated
     modes (fade/slide/scale). The trailing opacity:1 is the resting value the
     none-mode override (animation:none) falls back to. */
  .ne-header__nav-item:hover > .ne-header__btn-menu .ne-header__btn-menu__col > .ne-header__btn-menu__item > .ne-header__btn-menu__btn,
  .ne-header__nav-item:focus-within > .ne-header__btn-menu .ne-header__btn-menu__col > .ne-header__btn-menu__item > .ne-header__btn-menu__btn,
  .ne-header__nav-item.is-open > .ne-header__btn-menu .ne-header__btn-menu__col > .ne-header__btn-menu__item > .ne-header__btn-menu__btn {
    opacity: 1;
    animation: ne-btn-menu-item-in var(--ne-btn-menu-item-dur, 300ms) ease-out both;
    animation-delay: calc(var(--i, 0) * var(--ne-btn-menu-speed, 50ms));
  }
  .ne-header__btn-menu__item:hover > .ne-header__btn-menu__col--sub > .ne-header__btn-menu__item > .ne-header__btn-menu__btn,
  .ne-header__btn-menu__item:focus-within > .ne-header__btn-menu__col--sub > .ne-header__btn-menu__item > .ne-header__btn-menu__btn {
    opacity: 1;
    animation: ne-btn-menu-item-in var(--ne-btn-menu-item-dur, 300ms) ease-out both;
    animation-delay: calc(var(--i, 0) * var(--ne-btn-menu-speed, 50ms));
  }
  @keyframes ne-btn-menu-item-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  

  /* Per-level sub-column flip — JS adds this when a specific sub-column's
     right edge overflows the viewport. */
  .ne-header__btn-menu__col--sub.ne-header__btn-menu__col--flip-left {
    left: auto;
    right: calc(100% + var(--ne-btn-menu-col-gap, 22px));
  }

  /* Sidebar-header variants (.ne-header--left / .ne-header--right) — the
     menu opens beside the trigger, vertically centered so it aligns with
     icon-nav items. */
  .ne-header--left .ne-header__nav-item > .ne-header__btn-menu {
    top: 50%;
    left: calc(100% + var(--ne-space-3));
    transform: translateY(-50%);
  }
  .ne-header--right .ne-header__nav-item > .ne-header__btn-menu {
    top: 50%;
    left: auto;
    right: calc(100% + var(--ne-space-3));
    transform: translateY(-50%);
  }
  .ne-header--right .ne-header__nav-item > .ne-header__btn-menu .ne-header__btn-menu__col--sub {
    left: auto;
    right: calc(100% + var(--ne-btn-menu-col-gap, 22px));
  }



  .ne-header__megamenu-aside {
    min-width: 240px;
  }

  /* Products grid: columns = --ne-mm-aside-cols (1..4), aside uses intrinsic width */
  .ne-header__megamenu-aside--products {
    display: grid;
    grid-template-columns: repeat(var(--ne-mm-aside-cols, 2), minmax(140px, 180px));
    gap: var(--ne-space-3);
  }

  .ne-header__megamenu-product {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
    text-decoration: none;
    color: inherit;
    transition: transform var(--ne-transition-fast);
    overflow: hidden;
  }
  .ne-header__megamenu-product:hover {
    transform: translateY(-2px);
  }
  .ne-header__megamenu-product-img {
    width: 100%;
    /* Per-block override (megamenu_link → Image aspect ratio); square default. */
    aspect-ratio: var(--ne-mm-product-aspect, 1 / 1);
    object-fit: cover;
    /* Per-block override (megamenu_link → Image corner radius); falls back to
       the theme's medium radius when the block hasn't published the var. */
    border-radius: var(--ne-mm-product-radius, var(--ne-radius-md));
    display: block;
    transition: transform var(--ne-transition-slow, 0.4s);
  }
  .ne-header__megamenu-product:hover .ne-header__megamenu-product-img {
    transform: scale(1.04);
  }
  .ne-header__megamenu-product-body {
    display: flex;
    flex-direction: column;
    gap: 2px;
  }
  .ne-header__megamenu-product-title {
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text, var(--ne-scheme-text));
    line-height: 1.3;
  }
  .ne-header__megamenu-product-price {
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-text-muted);
    font-weight: 600;
  }
  .ne-header__megamenu-product-price s {
    opacity: 0.6;
    margin-right: var(--ne-space-1);
    color: var(--ne-scheme-text-muted);
  }

  /* Aside promo variant: single promo tile, fixed width so layout stays stable */
  .ne-header__megamenu-aside--promo {
    width: 280px;
    max-width: 100%;
  }

  /* Promo tile: image + body stacked. Image uses aspect-ratio to stay consistent across breakpoints. */
  .ne-header__megamenu-promo {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    border-radius: var(--ne-radius-lg);
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
    text-decoration: none;
    transition: transform var(--ne-transition-base), box-shadow var(--ne-transition-base);
    min-height: 200px;
  }

  .ne-header__megamenu-promo:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 24px rgba(0,0,0,0.3), 0 0 0 1px var(--ne-scheme-accent-glow, rgba(99,102,241,0.2));
  }

  .ne-header__megamenu-promo-img {
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    display: block;
    transition: transform var(--ne-transition-slow, 0.4s);
  }
  /* Per-block aspect-ratio modifiers (schema: promo_aspect_ratio) */
  .ne-header__megamenu-promo--ratio-square .ne-header__megamenu-promo-img { aspect-ratio: 1 / 1; }
  .ne-header__megamenu-promo--ratio-portrait .ne-header__megamenu-promo-img { aspect-ratio: 4 / 5; }
  .ne-header__megamenu-promo--ratio-tall .ne-header__megamenu-promo-img { aspect-ratio: 2 / 3; }
  .ne-header__megamenu-promo--ratio-landscape .ne-header__megamenu-promo-img { aspect-ratio: 4 / 3; }
  .ne-header__megamenu-promo--ratio-wide .ne-header__megamenu-promo-img { aspect-ratio: 16 / 9; }
  .ne-header__megamenu-promo--ratio-adapt .ne-header__megamenu-promo-img { aspect-ratio: auto; }

  .ne-header__megamenu-promo:hover .ne-header__megamenu-promo-img {
    transform: scale(1.04);
  }

  .ne-header__megamenu-promo-body {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1);
    padding: var(--ne-space-3);
  }

  .ne-header__megamenu-promo-eyebrow {
    font-size: var(--ne-font-size-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ne-scheme-accent);
  }

  .ne-header__megamenu-promo-heading {
    font-size: var(--ne-font-size-lg);
    font-weight: 700;
    color: var(--ne-scheme-heading, var(--ne-scheme-text));
    line-height: 1.2;
  }

  .ne-header__megamenu-promo-sub {
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
    line-height: 1.4;
  }

  .ne-header__megamenu-promo-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-1);
    margin-top: var(--ne-space-1);
    font-size: var(--ne-font-size-sm);
    font-weight: 600;
    color: var(--ne-scheme-accent);
    transition: gap var(--ne-transition-fast);
  }

  .ne-header__megamenu-promo:hover .ne-header__megamenu-promo-btn { gap: var(--ne-space-2); }

  /* ==========================================================================
     HEADER - ACTIONS
     ========================================================================== */

  .ne-header__actions {
    display: flex;
    align-items: center;
    /* Same gap token as nav so the menu setting drives both — merchant
       configures one value to control spacing across all header buttons. */
    gap: var(--ne-header-nav-gap, var(--ne-space-2));
  }

  /* Specificity 0,2,0 beats a:not(.ne-btn) (0,1,1) — without this, <a class="ne-header__action">
     inherits accent from global a rule while <button class="ne-header__action"> stays text-muted,
     causing visible color mismatch between search/cart and account/nav-icon.
     The look (size / bg / border / radius / shadow) is now driven by the
     :root token system; this rule only handles layout + accent fallback. */
  .ne-header .ne-header__action {
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    cursor: pointer;
    font-family: inherit;
  }

  .ne-header__action svg { width: 20px; height: 20px; }

  /* position:relative needed for the count badge to anchor to its
     own action button (cart count, wishlist count). Without this,
     the absolute span anchors to the next positioned ancestor and
     drifts onto neighbouring icons. */
  .ne-header__cart,
  .ne-header__wishlist { position: relative; }

  .ne-header__cart-count {
    position: absolute;
    /* Anchor to the ICON's top-right corner (icon = 20px, centred in the
       action-size + 2·padding box), nudged 4px out — so the badge tracks the
       icon when the action has horizontal padding or sits in a sidebar, instead
       of clinging to the link's padding edge (user 2026-06-21). */
    top: calc((var(--ne-action-size, 36px) - 20px) / 2 - 11px);
    right: calc((var(--ne-action-size, 36px) - 20px) / 2 + var(--ne-action-padding-x, 0px) - 11px);
    min-width: 16px; height: 16px;
    padding: 0 4px;
    background: var(--ne-scheme-accent, var(--ne-scheme-accent));
    color: var(--ne-scheme-btn-text, var(--ne-scheme-btn-text));
    font-size: 10px; font-weight: 700;
    line-height: 16px; text-align: center;
    border-radius: var(--ne-radius-full);
    transition: transform var(--ne-transition-spring);
  }

  /* Glass optic on the badge — only when the header buttons are glass, so
     the count bubble matches the surrounding action buttons (user
     2026-05-24). Gradient highlight + inset bevel layer over the accent
     fill, mirroring the nav-btn-glass recipe. */
  .ne-header--nav-btn-glass .ne-header__cart-count {
    background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.32) 0%, rgba(255, 255, 255, 0.06) 100%);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, 0.45),
      inset 0 -1px 0 rgba(0, 0, 0, 0.18);
    backdrop-filter: blur(var(--ne-glass-blur, 8px)) saturate(1.4);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 8px)) saturate(1.4);
  }

  .ne-header__cart-count--hidden { display: none; }

  /* Dark/light toggle in header: override global critical.css toggle styles.
     Now inherits all --ne-action-* vars from .ne-header via .ne-header__action class. */
  .ne-dark-light-toggle--header {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .ne-header--left .ne-header__actions,
  .ne-header--right .ne-header__actions {
    flex-direction: column;
    margin-top: auto;
  }

  /* ==========================================================================
     HEADER - LOCALE SELECTOR (country / language)
     ========================================================================== */

  .ne-header__locale {
    position: relative;
    display: inline-flex;
    align-items: center;
  }

  .ne-header__locale-toggle {
    width: auto !important;
    padding: 0 var(--ne-space-2);
    gap: var(--ne-space-1);
    font-size: var(--ne-font-size-xs);
    font-weight: 600;
    letter-spacing: 0.02em;
  }

  .ne-header__locale-flag {
    font-size: 1.1em;
    line-height: 1;
  }

  .ne-header__locale-label {
    white-space: nowrap;
  }

  .ne-header__locale-chevron {
    transition: transform var(--ne-transition-fast);
  }

  .ne-header__locale.is-open .ne-header__locale-chevron {
    transform: rotate(180deg);
  }

  /* Top header: the locale button grows to fit flag + label + chevron in a row (the
     bar has horizontal room, unlike the sidebar). Beats the fixed .ne-header__action
     width via the extra .ne-header class. */
  .ne-header.ne-header--top .ne-header__locale,
  .ne-header.ne-header--top .ne-header__locale-toggle {
    width: auto !important;
    max-width: none !important;
    min-width: 0;
  }

  /* Sidebar locale button: a left/right bar can't widen the pill the way a top
     header does (there the button just grows). Space is fixed by the bar width, so
     fit the content to it instead of letting the label overflow.
     • no flag  → shrink the font + allow the label ("US · EN") to wrap.
     • w/ flag  → flag on its OWN line above, label pushed below (stacked). */
  /* Let the locale button use the FULL bar width (not the icon-button width) so the
     one-line "DE · EN" has room; the top header widens the pill instead. */
  .ne-header--left .ne-header__locale,
  .ne-header--right .ne-header__locale {
    width: 100%;
    justify-content: center;
  }
  .ne-header--left .ne-header__locale-toggle,
  .ne-header--right .ne-header__locale-toggle {
    /* Pin to the SAME width as the icon action buttons so the locale pill lines up
       with them; the flag (emoji, renders wide + OS-dependent) and label stack inside
       and are clipped to that width rather than pushing the pill wider. */
    width: calc(var(--ne-action-size, 40px) + (var(--ne-action-padding-x, 0px) * 2)) !important;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: center;
    justify-content: center;
    row-gap: 3px;              /* separate the flag from the label → text sits lower */
    padding: 0 1px;
    line-height: 1;
    min-width: 0;
    overflow: hidden;
    text-align: center;
    font-size: clamp(7.5px, calc(var(--ne-header-sidebar-width, 88px) * 0.17), var(--ne-font-size-xs));
  }
  .ne-header--left .ne-header__locale-label,
  .ne-header--right .ne-header__locale-label {
    white-space: nowrap;         /* country · language stay on ONE line under the flag */
    word-spacing: -0.28em;       /* pull "DE · EN" tight */
    letter-spacing: -0.01em;
    min-width: 0;
    max-width: 100%;
  }
  /* Flag emoji renders wide (and clips vary by OS) — scale it down so it fits the
     icon-width pill; overflow:hidden on the toggle trims any residual. */
  .ne-header--left .ne-header__locale-flag,
  .ne-header--right .ne-header__locale-flag {
    font-size: 1.6em;         /* larger flag above the label */
    line-height: 1;
    max-width: 100%;
  }
  /* Drop the chevron in the narrow bar — it costs a whole wrapped row (vertical
     overflow) and the panel state is already signalled by is-open. */
  .ne-header--left .ne-header__locale-chevron,
  .ne-header--right .ne-header__locale-chevron {
    display: none;
  }

  .ne-header__locale-panel {
    position: absolute;
    top: calc(100% + var(--ne-header-dropdown-gap, 10px));
    right: 0;
    min-width: 240px;
    padding: var(--ne-space-3);
    background: var(--ne-scheme-card-bg, var(--ne-scheme-bg));
    border: 1px solid var(--ne-scheme-border, rgba(255,255,255,0.1));
    border-radius: var(--ne-radius-md);
    box-shadow: 0 12px 32px rgba(0,0,0,0.2);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity var(--ne-transition-base), transform var(--ne-transition-base), visibility 0s var(--ne-transition-base, 0.25s);
    z-index: calc(var(--ne-z-header, 40) + 1);
  }
  /* MERGE ONLY — the panel welds to the header exactly like the nav dropdowns:
     transparent element + ::after bg layer (Rule 51: backdrop on the pseudo), no own
     border/shadow (driven by the header dropdown classes / _pmg_sel glow). Non-merge
     keeps the standalone card look above. */
  .ne-header--menu-merge .ne-header__locale-panel {
    background: transparent;
    border: 0 solid var(--ne-glass-border, rgba(255,255,255,0));
    box-shadow: none;
  }
  .ne-header--menu-merge .ne-header__locale-panel::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background-color: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    pointer-events: none;
  }

  .ne-header__locale.is-open .ne-header__locale-panel {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
    transition: opacity var(--ne-transition-base), transform var(--ne-transition-base), visibility 0s 0s;
  }

  .ne-header--left .ne-header__locale-panel,
  .ne-header--right .ne-header__locale-panel {
    top: 0;
    right: auto;
    left: calc(100% + var(--ne-header-dropdown-gap, 10px));
  }
  /* Open direction follows the actions zone (mirrors the nav dropdown flip): the
     locale button sits with the action icons, so when they're at the BOTTOM of the
     bar the panel grows UPWARD (anchored by its bottom edge) instead of running off
     the visible frame; when they're at the TOP it grows downward (default). Right
     sidebar mirrors on the left side (handled by the panel's own left/right base). */
  .ne-header--sb-actions-bottom.ne-header--left .ne-header__locale-panel,
  .ne-header--sb-actions-bottom.ne-header--right .ne-header__locale-panel {
    top: auto;
    bottom: 0;
  }
  .ne-header--sb-actions-top.ne-header--left .ne-header__locale-panel,
  .ne-header--sb-actions-top.ne-header--right .ne-header__locale-panel {
    top: 0;
    bottom: auto;
  }

  .ne-header__locale-form {
    margin: 0;
  }

  .ne-header__locale-field {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1);
  }

  .ne-header__locale-legend {
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }

  .ne-header__locale-select {
    width: 100%;
    padding: var(--ne-space-2) var(--ne-space-3);
    background: var(--ne-scheme-input-bg, transparent);
    color: var(--ne-scheme-input-text, var(--ne-scheme-text));
    border: 1px solid var(--ne-scheme-input-border, var(--ne-scheme-border));
    border-radius: var(--ne-radius-sm);
    font: inherit;
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
  }

  .ne-header__locale-select:focus {
    outline: none;
    border-color: var(--ne-scheme-accent);
  }

  /* ==========================================================================
     HEADER - MOBILE
     ========================================================================== */

  .ne-header__mobile-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: var(--ne-action-size, 40px); height: var(--ne-action-size, 40px);
    border-radius: var(--ne-action-radius, var(--ne-radius-md));
    background: var(--ne-action-bg, transparent);
    border: var(--ne-action-border-width, 0px) solid var(--ne-action-border-color, transparent);
    color: var(--ne-scheme-link, var(--ne-scheme-accent, #6366f1));
    transition: all var(--ne-transition-fast);
  }

  .ne-header__mobile-toggle:hover {
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.1));
  }

  /* ==========================================================================
     MOBILE MENU DIALOG
     ========================================================================== */

  .ne-mobile-menu {
    position: fixed;
    inset: 0;
    width: 100%; max-width: 320px;
    height: 100%; max-height: 100%;
    margin: 0; padding: var(--ne-space-6);
    border: none;
    border-right: 1px solid var(--ne-glass-border);
    background: var(--ne-scheme-bg-elevated);
    color: var(--ne-scheme-text);
    z-index: var(--ne-z-modal);
    flex-direction: column;
    transform: translateX(-100%);
    /* Closed = display:none so the off-screen drawer box never widens the
       document on mobile. A fixed element pushed off-screen with translateX
       still contributes to scrollable overflow → right-side pan / "leicht
       gezoomt" on Android (Regel 410/414; headless masks it). The cart-drawer
       already collapses via [hidden] — mirror that here. The slide animation is
       preserved with @starting-style + allow-discrete (display/overlay stay
       through the transition) on evergreen browsers; older ones just show/hide
       instantly — no width bug either way. */
    display: none;
    transition:
      transform var(--ne-dropdown-speed, 280ms),
      overlay var(--ne-dropdown-speed, 280ms) allow-discrete,
      display var(--ne-dropdown-speed, 280ms) allow-discrete;
  }
  .ne-mobile-menu[open] {
    display: flex;
    transform: translateX(0);
  }
  @starting-style {
    .ne-mobile-menu[open] { transform: translateX(-100%); }
  }

  /* Drawer glass — matches header's glass treatment. Same caveat as the
     header: backdrop-filter on the dialog element would trap descendants'
     own backdrop-filter, so the blur goes on ::before. */
  .ne-mobile-menu.ne-glass {
    background: transparent;
  }
  .ne-mobile-menu.ne-glass::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--ne-glass-bg, rgba(255,255,255,0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(1.4);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(1.4);
    z-index: -1;
    pointer-events: none;
  }
  .ne-mobile-menu.ne-glass.ne-glass--tinted::before {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255,255,255,0.08)));
  }

  /* Drawer effect mirroring — shadow/glow only apply while the drawer is open
     (dialog[open]). Dialog with display:none when closed still leaks its
     box-shadow at the viewport edge because the translateX hides the panel
     but not its outward shadow. Gating on [open] fixes that. */
  .ne-mobile-menu--shadow[open] {
    box-shadow: 0 8px 40px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.3);
  }
  .ne-mobile-menu--glow[open] {
    box-shadow:
      0 0 calc(32px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      0 0 calc(80px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.2));
  }

  /* Drawer button-shape inheritance — the close button, search button and
     footer links consume the same --ne-action-* vars as the header's action
     buttons, so they inherit the shape/color chosen for the header. */
  .ne-mobile-menu--btn-pill {
    --ne-action-radius: var(--ne-radius-full);
    --ne-action-bg: var(--ne-glass-bg, rgba(255,255,255,0.08));
    --ne-action-border-width: 1px;
    --ne-action-border-color: var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-mobile-menu--btn-rounded {
    --ne-action-radius: var(--ne-btn-radius, var(--ne-radius-md));
    --ne-action-bg: var(--ne-glass-bg, rgba(255,255,255,0.08));
    --ne-action-border-width: 1px;
    --ne-action-border-color: var(--ne-glass-border, rgba(255, 255, 255, 0));
  }
  .ne-mobile-menu--btn-square {
    --ne-action-radius: 0;
    --ne-action-bg: var(--ne-glass-bg, rgba(255,255,255,0.08));
    --ne-action-border-width: 1px;
    --ne-action-border-color: var(--ne-glass-border, rgba(255, 255, 255, 0));
  }

  /* DEPRECATED — drawer action items (close X, account, wishlist, theme
     toggle) now share the SAME :root token system as header items. The
     universal `.ne-header__action, .ne-mobile-menu *` rule at the top of
     this stylesheet drives bg / border / radius / size / shadow.

     btn-effect cascade reaches the drawer because tokens live on :root
     (via body:has(.ne-header.X)) — drawer doesn't need its own rule. */

  /* Localization select in drawer header — styled as a visually-quiet picker. */
  .ne-mobile-menu__localization {
    flex: 1;
    margin-right: var(--ne-space-2);
  }
  .ne-mobile-menu__locale-form {
    position: relative;
    display: flex;
    align-items: center;
    /* Fill the locale column so both forms (country + language) span the same
       width — without this the form/label shrink to their content and the
       long "New Zealand (EUR €)" select ends up far wider than "English". */
    width: 100%;
  }
  .ne-mobile-menu__locale-select {
    appearance: none;
    -webkit-appearance: none;
    width: 100%;
    padding: var(--ne-space-2) var(--ne-space-6) var(--ne-space-2) var(--ne-space-3);
    font-family: inherit;
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text);
    background: var(--ne-glass-bg, rgba(255,255,255,0.06));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    border-radius: var(--ne-radius-md);
    cursor: pointer;
  }
  .ne-mobile-menu__locale-chevron {
    position: absolute;
    right: var(--ne-space-2);
    top: 50%;
    transform: translateY(-50%);
    color: var(--ne-scheme-text-muted);
    pointer-events: none;
  }

  /* Standalone locale selector block — rendered inside the drawer header
     (top — inline next to close) or footer (bottom — under account row).
     Stacks country + language selects vertically with subtle labels. */
  .ne-mobile-menu__locale {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
  }
  /* Top position: inline with close button — grow to fill, give close room. */
  .ne-mobile-menu__header .ne-mobile-menu__locale {
    flex: 1;
    margin-right: var(--ne-space-2);
    gap: var(--ne-space-2);
  }
  /* Bottom position: separated from account row with its own divider. */
  .ne-mobile-menu__footer .ne-mobile-menu__locale {
    padding-top: var(--ne-space-3);
    margin-top: var(--ne-space-2);
    border-top: 1px solid var(--ne-scheme-border);
  }
  .ne-mobile-menu__locale-field {
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1, 4px);
    /* Label must fill the form so the 100%-wide select below is measured against
       the full column, not the (shorter) legend/option text — keeps both selects
       equal length. */
    width: 100%;
  }
  .ne-mobile-menu__locale-legend {
    font-size: var(--ne-font-size-xs, 0.75rem);
    color: var(--ne-scheme-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.05em;
  }
  /* In the header-inline position, hide the legends so the selectors sit
     compact next to the close button. */
  .ne-mobile-menu__header .ne-mobile-menu__locale-legend { display: none; }
  /* The locale block stacks two selects → the header row grows tall. Pin the close
     button to the top of that row (instead of vertically centred against both
     selects) so it sits up beside the first field — cleaner composition. Nudge it
     right so the X lines up under the burger toggle (the drawer's right padding
     sits a few px inside the burger's action edge). */
  .ne-mobile-menu__header .ne-mobile-menu__close {
    align-self: flex-start;
    transform: translateX(6px);
  }

  .ne-mobile-menu__header-spacer { flex: 1; }
  .ne-visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }

  .ne-mobile-menu[open] { transform: translateX(0); }

  .ne-mobile-menu::backdrop {
    background: rgba(0,0,0,0.5);
    backdrop-filter: blur(4px);
  }

  /* ==========================================================================
     BURGER DRAWER ↔ FRAME WELD  (section.settings.burger_connect_frame)
     The drawer fuses with the decorative frame. On open (JS adds body classes +
     sets --ne-drawer-edge = the drawer's inner-edge inset in px):
       • the frame's edge on the drawer's side slides inward to the drawer's
         inner edge — its rounded corners travel in with it (the "radii wandern
         nach innen");
       • the drawer sits from the header's bottom edge (--ne-header-height) down
         to the frame's bottom inset, flush to the viewport on its outer side,
         with a frame-radius outer corner + a concave fillet welding its inner
         edge to the frame's top/bottom walls (same radial-gradient tile trick as
         the header FRAME WELD in header-styles.liquid);
       • the header row is never covered: the drawer + its ::backdrop both start
         at --ne-header-height (backdrop top set in menu.liquid), so header +
         drawer + frame read as one continuous surface.
     Values are var-driven (this snippet is a static stylesheet — no Liquid);
     dynamic dim/blur live in menu.liquid's generated style. Solid drawer bg so the fillet reads
     (glass/backdrop-filter can't be reproduced inside a gradient — same caveat
     as the header weld).
     ========================================================================== */
  /* Base weld box. Selector .ne-mobile-menu.ne-mobile-menu--frame-weld (0,2,0)
     deliberately beats the base .ne-mobile-menu (inset:0 / height:100% / border)
     — a single-class (0,1,0) tie let inset:0 win, so the drawer covered the header.
     --ne-drawer-top = the header's REAL bottom edge (px), measured live by the
     drawer JS (--ne-header-height is not reliably present on :root here — the frame
     vars live on .color-scheme-X). No contour: the frame has none either, the weld
     reads via the shifted frame edge + the concave fillet. */
  .ne-mobile-menu.ne-mobile-menu--frame-weld {
    --ne-dw-r: var(--ne-frame-radius, 16px);
    --ne-dw-b: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-bottom, 0px));
    top: var(--ne-drawer-top, var(--ne-header-height, 70px));
    bottom: var(--ne-dw-b);
    height: auto;
    max-height: none;
    background: var(--ne-scheme-bg);
    border: none;
    /* visible so the fixed header-catch (pulled above the drawer with a negative
       top) isn't clipped — the drawer is its containing block via the transform. */
    overflow: visible;
    /* Drop `transform` from the transition: the weld drawer's translateX is a
       function of the interpolating --ne-drawer-edge (it moves every frame on its
       own), so an additional transform transition would double-animate / lag it.
       Keep overlay+display allow-discrete so the dialog stays rendered through the
       exit slide before display:none. */
    transition:
      overlay var(--ne-dropdown-speed, 280ms) allow-discrete,
      display var(--ne-dropdown-speed, 280ms) allow-discrete;
  }
  /* No inner divider lines in weld mode — one continuous surface like the frame
     (the lines between the menu points + the drawer's own contour drop away). */
  .ne-mobile-menu--frame-weld .ne-mobile-menu__link { border-bottom: none; }
  /* The X is autofocused on open (so focus never lands on the invisible catcher) —
     suppress the default focus ring for pointer/touch opens; keyboard users still
     get the :focus-visible ring. */
  .ne-mobile-menu__close:focus:not(:focus-visible) { outline: none; }
  /* Transparent catcher over the free header row — a child of the (modal) dialog,
     so it is NOT inert: a click on the header/burger dismisses the drawer. The open
     drawer's transform makes IT the containing block for this fixed child, so `top`
     is measured from the drawer's top edge — pull it up by --ne-drawer-top to land
     on the viewport header (y:0..header-bottom). It spans the drawer's width, which
     covers the burger (burger sits on the same side as the drawer). The drawer must
     be overflow:visible in weld mode (below) or this would be clipped away. */
  .ne-mobile-menu__header-catch {
    position: fixed;
    top: calc(-1 * var(--ne-drawer-top, var(--ne-header-height, 70px)));
    left: 0;
    right: 0;
    height: var(--ne-drawer-top, var(--ne-header-height, 70px));
    margin: 0;
    padding: 0;
    border: 0;
    background: transparent;
    cursor: pointer;
    outline: none;
    z-index: 1;
  }
  /* The inner (content-facing) edge welds to the frame's top/bottom walls with
     CONCAVE fillets — exactly like the header-weld (header-styles.liquid ::after,
     two radial-gradient tiles). NOT convex border-radius: the corner must curve the
     same way the frame's own inner (mulde) corners do, so the drawer reads as part
     of the frame band. The drawer box itself stays square; the ::after paints a
     drawer-coloured quarter-fillet just outside the inner edge at top + bottom. */
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-left {
    left: 0;
    right: auto;
    border-radius: 0;
  }
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-right {
    right: 0;
    left: auto;
    border-radius: 0;
  }
  /* RIGHT drawer → inner edge is the LEFT edge; fillet strip sits just left of it. */
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-right::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(-1 * var(--ne-dw-r));
    width: var(--ne-dw-r);
    pointer-events: none;
    background:
      radial-gradient(circle at bottom left, transparent var(--ne-dw-r), var(--ne-scheme-bg) calc(var(--ne-dw-r) + 0.5px)) top    / var(--ne-dw-r) var(--ne-dw-r) no-repeat,
      radial-gradient(circle at top left,    transparent var(--ne-dw-r), var(--ne-scheme-bg) calc(var(--ne-dw-r) + 0.5px)) bottom / var(--ne-dw-r) var(--ne-dw-r) no-repeat;
  }
  /* LEFT drawer → inner edge is the RIGHT edge; fillet strip sits just right of it. */
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-left::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    right: calc(-1 * var(--ne-dw-r));
    width: var(--ne-dw-r);
    pointer-events: none;
    background:
      radial-gradient(circle at bottom right, transparent var(--ne-dw-r), var(--ne-scheme-bg) calc(var(--ne-dw-r) + 0.5px)) top    / var(--ne-dw-r) var(--ne-dw-r) no-repeat,
      radial-gradient(circle at top right,    transparent var(--ne-dw-r), var(--ne-scheme-bg) calc(var(--ne-dw-r) + 0.5px)) bottom / var(--ne-dw-r) var(--ne-dw-r) no-repeat;
  }

  /* ------------------------------------------------------------------------
     Lockstep slide: ONE animated variable drives BOTH the drawer transform AND
     the frame edge, so they can never desync (which is what produced the teal
     "panel" beside the sliding drawer). --ne-drawer-edge is REGISTERED via
     @property as a <length> so it INTERPOLATES smoothly — a plain custom prop
     does not, and worse, a property that merely *references* a plain custom prop
     (frame `right: max(.., var(--edge))`) does NOT re-fire its own transition on
     a var change, so the frame used to jump to its open position instantly while
     the drawer still slid in over the full duration → big exposed fill.
     Now the frame `right`/`left` and the drawer `translateX` are both plain
     functions of the interpolating --ne-drawer-edge, recomputed every frame. */
  @property --ne-drawer-edge {
    syntax: "<length>";
    inherits: true;
    initial-value: 0px;
  }
  /* The variable animates on :root (JS sets it there); descendants inherit the
     interpolating value. Harmless when idle — only animates on open/close. */
  /* --ne-drawer-speed (theme setting drawer_slide_speed), NOT --ne-dropdown-speed:
     the nav speed is tuned for small dropdowns and is often ~50ms, which turns a
     480px drawer/panel into a 3-frame jump. Falls back to the nav speed for any
     theme that predates the setting. */
  :root {
    transition: --ne-drawer-edge var(--ne-drawer-speed, var(--ne-dropdown-speed, 280ms)) cubic-bezier(0.4, 0, 0.2, 1);
  }

  /* Weld drawer transform is edge-driven (overrides the base [open] translateX).
     edge = --ne-drawer-w (measured width) → translateX(0) fully open;
     edge = 0 → translateX(±width) fully off-screen. [open] in the selector wins
     the cascade over .ne-mobile-menu--from-*[open] { transform: translateX(0) }. */
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-right[open] {
    transform: translateX(calc(var(--ne-drawer-w, 100vw) - var(--ne-drawer-edge, 0px)));
  }
  .ne-mobile-menu--frame-weld.ne-mobile-menu--from-left[open] {
    transform: translateX(calc(-1 * (var(--ne-drawer-w, 100vw) - var(--ne-drawer-edge, 0px))));
  }

  /* Frame edge follows the drawer inward — max() keeps the frame at its resting
     inset until the drawer's inner edge overtakes it. No `transition` here: the
     motion comes from the interpolating --ne-drawer-edge (recomputed per frame),
     so the frame edge stays glued to the drawer's inner edge the whole slide. */
  body.ne-drawer-open--left .ne-layer-frame--line,
  body.ne-drawer-open--left .ne-layer-frame--area {
    left: max(calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px)), var(--ne-drawer-edge, 0px));
  }
  body.ne-drawer-open--right .ne-layer-frame--line,
  body.ne-drawer-open--right .ne-layer-frame--area {
    right: max(calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px)), var(--ne-drawer-edge, 0px));
  }
  /* No --top / --bottom counterparts on purpose: the only vertical weld is the
     quick-view's top/bottom panel, and that one keeps the floating card's width. A
     retreating frame edge only reads as a weld when the panel spans the whole wall. */

  /* PUSH mode — content slides inward with the frame instead of being overlaid.
     Opt-in (burger_frame_push); transform on the content layer, so avoid with
     position:fixed page content. Also edge-driven → no separate transition. */
  body.ne-drawer-open--push.ne-drawer-open--left .ne-layer-content {
    transform: translateX(var(--ne-drawer-edge, 0px));
  }
  body.ne-drawer-open--push.ne-drawer-open--right .ne-layer-content {
    transform: translateX(calc(-1 * var(--ne-drawer-edge, 0px)));
  }

  .ne-mobile-menu__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: var(--ne-space-6);
    padding-bottom: var(--ne-space-4);
    border-bottom: 1px solid var(--ne-scheme-border);
  }

  /* DEPRECATED — close X button now reads sizing / chrome from the :root
     token system (it has both .ne-mobile-menu__close AND .ne-header__action
     classes; the universal action rule covers it). Keep only display props. */
  .ne-mobile-menu__close {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .ne-mobile-menu__nav {
    flex: 1;
    overflow-y: auto;
  }

  .ne-mobile-menu__link {
    display: block;
    padding: var(--ne-space-3) 0;
    font-size: var(--ne-font-size-base);
    color: var(--ne-scheme-text);
    text-decoration: none;
    border-bottom: 1px solid var(--ne-scheme-border);
    transition: color var(--ne-transition-fast);
  }

  .ne-mobile-menu__link:hover { color: var(--ne-scheme-accent); }

  .ne-mobile-menu__link--sub {
    padding-left: var(--ne-space-4);
    font-size: var(--ne-font-size-sm);
    color: var(--ne-scheme-text-muted);
  }

  .ne-mobile-menu__link--grandchild {
    padding-left: var(--ne-space-8);
    font-size: var(--ne-font-size-xs);
  }

  .ne-mobile-menu__link--view-all {
    color: var(--ne-scheme-accent);
    font-weight: 500;
    font-size: var(--ne-font-size-xs);
  }

  /* Accordion wrapper does NOT draw its own border — the summary inside
     already inherits .ne-mobile-menu__link's border-bottom. Two borders
     (wrapper + summary) would stack and show as double lines after each
     parent row (visible when collapsed — two lines below summary). */
  .ne-mobile-menu__accordion { border-bottom: none; }
  .ne-mobile-menu__accordion--nested { border-bottom: none; }

  .ne-mobile-menu__link--parent {
    display: flex;
    align-items: center;
    justify-content: space-between;
    cursor: pointer;
    list-style: none;
  }

  /* Icon-before-label rows inside the drawer — icon inherits accent via
     currentColor (mask-image pattern, same as custom social icons). */
  .ne-mobile-menu__link--with-icon {
    display: flex;
    align-items: center;
    gap: 18px;
  }
  .ne-mobile-menu__link-label {
    display: inline-flex;
    align-items: center;
    gap: 18px;
    line-height: 1;
  }
  .ne-mobile-menu__link-label-text {
    display: inline-flex;
    align-items: center;
    gap: 18px;
    line-height: 1;
  }
  .ne-mobile-menu__link-icon {
    flex: 0 0 auto;
    width: 22px;
    height: 22px;
    display: inline-flex;
    align-self: center;
    background-color: currentColor;
    -webkit-mask-image: var(--ne-mobile-icon-url);
            mask-image: var(--ne-mobile-icon-url);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
    opacity: 0.9;
  }

  .ne-mobile-menu__link--parent::-webkit-details-marker { display: none; }

  .ne-mobile-menu__chevron {
    transition: transform var(--ne-transition-fast);
    flex-shrink: 0;
  }

  details[open] > .ne-mobile-menu__link--parent > .ne-mobile-menu__chevron {
    transform: rotate(180deg);
  }

  .ne-mobile-menu__sub { padding-bottom: var(--ne-space-2); }
  .ne-mobile-menu__sub--nested { padding-left: var(--ne-space-2); }

  /* ==========================================================================
     BURGER DRAWER — product carousel (mirrors megamenu aside)
     Rendered in a single bottom slot above the drawer footer (Horizon-style),
     not paired per-accordion.
     ========================================================================== */
  .ne-mobile-menu__carousels:empty { display: none; }
  .ne-mobile-menu__carousels {
    padding-top: var(--ne-space-4);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
    flex: 0 0 auto;
  }
  .ne-mobile-menu__carousel {
    /* Negative horizontal margin cancels drawer padding so the track can
       bleed to the drawer edge; vertical margin is handled by the wrapper gap. */
    margin-left: calc(-1 * var(--ne-space-4, 1rem));
    margin-right: calc(-1 * var(--ne-space-4, 1rem));
  }
  .ne-mobile-menu__carousel-track {
    display: flex;
    gap: var(--ne-space-3);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-left: var(--ne-space-4);
    padding: var(--ne-space-1) var(--ne-space-4);
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .ne-mobile-menu__carousel-track::-webkit-scrollbar { display: none; }
  .ne-mobile-menu__carousel-item {
    flex: 0 0 auto;
    width: 160px;
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1);
    text-decoration: none;
    color: var(--ne-scheme-text);
  }
  .ne-mobile-menu__carousel-img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--ne-radius-md, 8px);
    display: block;
    background: var(--ne-glass-bg, rgba(255,255,255,0.04));
  }
  .ne-mobile-menu__carousel-title {
    font-size: var(--ne-font-size-sm);
    font-weight: 500;
    line-height: 1.3;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
  .ne-mobile-menu__carousel-price {
    font-size: var(--ne-font-size-xs);
    color: var(--ne-scheme-accent, currentColor);
  }
  .ne-mobile-menu__carousel-price s {
    color: var(--ne-scheme-text-muted);
    margin-right: var(--ne-space-1);
  }

  /* Drawer promo fallback (when items_beside_menu == 0) */
  .ne-mobile-menu__promo {
    display: block;
    margin: var(--ne-space-3) 0;
    position: relative;
    overflow: hidden;
    border-radius: var(--ne-radius-lg, 12px);
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    text-decoration: none;
    color: var(--ne-scheme-text);
  }
  .ne-mobile-menu__promo-img {
    width: 100%;
    aspect-ratio: 4 / 5;
    object-fit: cover;
    display: block;
  }
  .ne-mobile-menu__promo--ratio-square .ne-mobile-menu__promo-img { aspect-ratio: 1 / 1; }
  .ne-mobile-menu__promo--ratio-portrait .ne-mobile-menu__promo-img { aspect-ratio: 4 / 5; }
  .ne-mobile-menu__promo--ratio-tall .ne-mobile-menu__promo-img { aspect-ratio: 2 / 3; }
  .ne-mobile-menu__promo--ratio-landscape .ne-mobile-menu__promo-img { aspect-ratio: 4 / 3; }
  .ne-mobile-menu__promo--ratio-wide .ne-mobile-menu__promo-img { aspect-ratio: 16 / 9; }
  .ne-mobile-menu__promo--ratio-adapt .ne-mobile-menu__promo-img { aspect-ratio: auto; }
  .ne-mobile-menu__promo-body {
    position: absolute;
    inset: auto 0 0 0;
    padding: var(--ne-space-3);
    background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-1);
    color: #fff;
  }
  .ne-mobile-menu__promo-eyebrow {
    font-size: var(--ne-font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.85;
  }
  .ne-mobile-menu__promo-heading {
    font-size: var(--ne-font-size-base);
    font-weight: 700;
    line-height: 1.2;
  }
  .ne-mobile-menu__promo-sub {
    font-size: var(--ne-font-size-sm);
    opacity: 0.85;
  }
  .ne-mobile-menu__promo-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--ne-space-1);
    font-size: var(--ne-font-size-sm);
    font-weight: 600;
    margin-top: var(--ne-space-1);
  }

  .ne-mobile-menu__search { margin-bottom: var(--ne-space-4); }

  .ne-mobile-menu__search-btn {
    display: flex;
    align-items: center;
    gap: var(--ne-space-2);
    width: 100%;
    padding: var(--ne-space-3);
    background: var(--ne-scheme-bg-surface, rgba(255,255,255,0.04));
    border: 1px solid var(--ne-scheme-border);
    border-radius: var(--ne-radius-md);
    color: var(--ne-scheme-text-muted);
    font-size: var(--ne-font-size-sm);
    cursor: pointer;
    transition: all var(--ne-transition-fast);
  }

  .ne-mobile-menu__search-btn:hover {
    border-color: var(--ne-scheme-accent);
    color: var(--ne-scheme-text);
  }

  .ne-mobile-menu__footer {
    padding-top: var(--ne-space-4);
    border-top: 1px solid var(--ne-scheme-border);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-2);
    flex: 0 0 auto;
  }

  /* Account + theme-toggle share a row: account left, toggle right-aligned. */
  .ne-mobile-menu__footer-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--ne-space-3);
  }
  .ne-mobile-menu__footer-row .ne-mobile-menu__footer-link { padding: 0; }
  .ne-mobile-menu__footer-row .ne-mobile-menu__theme-toggle { margin-left: auto; }

  .ne-mobile-menu__footer-link {
    display: flex;
    align-items: center;
    gap: var(--ne-space-2);
    padding: var(--ne-space-2) 0;
    color: var(--ne-scheme-text-muted);
    text-decoration: none;
    font-size: var(--ne-font-size-sm);
    transition: color var(--ne-transition-fast);
    background: none; border: none;
    cursor: pointer; font-family: inherit;
  }

  .ne-mobile-menu__footer-link:hover { color: var(--ne-scheme-text); }

  /* Default: account + wishlist render as labelled links (icon + text).
     position:relative so the wishlist count badge anchors to the link
     itself (analog .ne-header__wishlist in the desktop header). */
  .ne-mobile-menu__footer-link { position: relative; }

  /* Compact mode: 3 slots are present (account + wishlist + theme-toggle),
     row gets cramped with full labels. Hide the text, render icon-only
     with a circular hover target. aria-label keeps screen-reader access. */
  /* Drawer compact-mode items (account / wishlist / theme-toggle) match
     DEPRECATED — drawer compact items now share the universal token rule
     at the top of this stylesheet. Bg / border / radius / size all inherit
     from :root tokens which body:has() reshapes per button-style. */
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link {
    aspect-ratio: 1;
    justify-content: center;
  }
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link-label {
    display: none;
  }

  /* Icon wrapper — the badge anchors to the HEART itself (not the whole
     icon+label link), so in the default labelled mode it sits on the heart's
     corner instead of overlapping the "Wishlist" text. */
  .ne-mobile-menu__footer-link-icon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
  }

  /* Wishlist count badge — absolutely anchored top-right of the heart icon
     (analog the cart-count in the desktop header). hyprism-features.js
     toggles `ne-header__cart-count--hidden` based on count==0. */
  .ne-mobile-menu__wishlist-count {
    position: absolute;
    top: -5px;
    right: -7px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 16px;
    height: 16px;
    padding: 0 4px;
    border-radius: 9999px;
    background: var(--ne-scheme-accent, var(--ne-color-sale, #ef4444));
    color: var(--ne-scheme-btn-text, #fff);
    font-size: 10px;
    font-weight: 700;
    line-height: 1;
  }
  .ne-mobile-menu__wishlist-count.ne-header__cart-count--hidden { display: none; }

  /* Search sits at the very top (render order already handles this), drawer
     footer auto-grows to the bottom with a configurable safety padding so
     app widgets at the bottom of the viewport don't cover it. */
  .ne-mobile-menu__footer {
    padding-bottom: calc(var(--ne-burger-pad-bottom, 16px) + env(safe-area-inset-bottom, 0px));
  }
  /* Left drawer variant: slides in from the left (unchanged default), right
     variant flips direction. */
  .ne-mobile-menu--from-right {
    left: auto;
    right: 0;
    border-right: none;
    border-left: 1px solid var(--ne-glass-border);
    transform: translateX(100%);
  }
  .ne-mobile-menu--from-right[open] { transform: translateX(0); }
  /* Enter-from for the right drawer must start off the RIGHT edge. Later in
     source than the base @starting-style rule → wins the (0,2,0) tie. */
  @starting-style {
    .ne-mobile-menu--from-right[open] { transform: translateX(100%); }
  }

  /* Burger toggle position — grid-aware on top/floating headers.
     Double-class specificity (0,3,0) beats any .ne-header--logo-* rule (0,2,0)
     or descendant-combinator rule so the setting always wins. */
  .ne-header.ne-header--burger-left .ne-header__mobile-toggle {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }
  .ne-header.ne-header--burger-right .ne-header__mobile-toggle {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }

  /* Sidebar headers: translate burger-left/right into flex order.
     burger-left = top of sidebar (order: -1), burger-right = bottom (order: 99).
     The base sidebar toggle rule sets order: 3 — these overrides lift/sink it.
     NO margin-top: auto — the logo is the single bottom-anchor (see logo rules).
     Multiple auto-margins split free space equally, breaking logo positioning. */
  .ne-header.ne-header--left.ne-header--burger-left .ne-header__mobile-toggle,
  .ne-header.ne-header--right.ne-header--burger-left .ne-header__mobile-toggle {
    order: -1;
    margin: 0;
  }
  .ne-header.ne-header--left.ne-header--burger-right .ne-header__mobile-toggle,
  .ne-header.ne-header--right.ne-header--burger-right .ne-header__mobile-toggle {
    order: 99;
    margin: 0;
  }

  /* When nav_mode == burger: hide inline nav at ALL breakpoints.
     Actions bar is kept in the DOM but pass-through (display: contents) above
     so the cart child participates directly in the header grid while its
     siblings (search/account/theme-toggle) are hidden individually. */
  .ne-header--burger-mode .ne-header__nav { display: none !important; }

  /* Sidebar + burger-mode: logo vertical position via flex `order` that sits
     OUTSIDE the burger/cart range (-1..99). Cart anchors opposite burger at
     order -1 or 99, so logo-top must be below -1 (order: -100) to sit above
     cart, and logo-bottom must be above 99 (order: 100) to sit below cart.
     Auto-margins on the logo push free space toward the opposite side so the
     logo hugs its target edge regardless of where cart/burger ended up.
     Order map:
       logo-top    → order -100 (hardest top)  → margin-bottom: auto
       logo-center → order    1 (between cart + burger) → top+bottom auto
       logo-bottom → order  100 (hardest bottom) → margin-top: auto
     Sidebar container keeps justify-content: flex-start (default). */
  .ne-header--left.ne-header--burger-mode.ne-header--logo-left .ne-header__logo,
  .ne-header--right.ne-header--burger-mode.ne-header--logo-left .ne-header__logo {
    order: -100;
    margin-top: 0;
    margin-bottom: auto;
  }
  .ne-header--left.ne-header--burger-mode.ne-header--logo-center .ne-header__logo,
  .ne-header--right.ne-header--burger-mode.ne-header--logo-center .ne-header__logo {
    order: 1;
    margin-top: auto;
    margin-bottom: auto;
  }
  .ne-header--left.ne-header--burger-mode.ne-header--logo-right .ne-header__logo,
  .ne-header--right.ne-header--burger-mode.ne-header--logo-right .ne-header__logo {
    order: 100;
    margin-top: auto;
    margin-bottom: 0;
  }

  @media (max-width: 749px) {
    /* Mobile: actions bar passes-through (display: contents) so cart participates
       directly in the header grid. All non-cart actions are hidden — drawer has
       equivalents. Same mental model as .ne-header--burger-mode above. */
    .ne-header__actions { display: contents; }
    .ne-header__actions > *:not(.ne-header__cart) { display: none !important; }
    /* Reduce header horizontal padding on mobile so the bar reaches closer
     to the screen edges (user feedback — default 20px felt too inset). */
    .ne-header--top,
    .ne-header--floating,
    .ne-header--sticky-smart {
      padding-left: var(--ne-space-2, 8px);
      padding-right: var(--ne-space-2, 8px);
    }
    /* Mobile layout — force a stable 3-column rhythm regardless of desktop
       logo_position:
         col 1: cart or burger (opposite the burger toggle)
         col 2: logo (centered)
         col 3: burger or cart
       Desktop .ne-header--logo-left/center/right rules otherwise land the
       logo in col 1 or 3, which overlaps the standalone cart on mobile.
       Specificity 0,2,0 ties the desktop rules — source order (media block
       after) wins. Scoped to top/floating so sidebar's row-fallback keeps
       its own row ordering. */
    .ne-header--top .ne-header__logo,
    .ne-header--floating .ne-header__logo,
    .ne-header--sticky-smart .ne-header__logo {
      grid-column: 2;
      grid-row: 1;
      justify-self: center;
    }
    /* Position cart opposite the burger toggle on mobile. Specificity 0,3,0
       matches .ne-header .ne-header__action and wins via source order (later
       in media query). */
    .ne-header.ne-header--burger-right .ne-header__cart {
      grid-column: 1;
      grid-row: 1;
      justify-self: start;
    }
    .ne-header.ne-header--burger-left .ne-header__cart {
      grid-column: 3;
      grid-row: 1;
      justify-self: end;
    }
    /* Burger toggle sits opposite the cart (mirror of cart rules above). */
    .ne-header.ne-header--burger-right .ne-header__mobile-toggle {
      grid-column: 3;
      grid-row: 1;
      justify-self: end;
    }
    .ne-header.ne-header--burger-left .ne-header__mobile-toggle {
      grid-column: 1;
      grid-row: 1;
      justify-self: start;
    }
  }

  /* Desktop override: when not in burger mode, the original ne-hide-desktop
     rule hides the toggle. In burger mode the class is omitted, so the
     toggle stays visible. This is handled by the class emission in Liquid. */

  /* Icon-pairing preview base position is set in the block below
     (.ne-icon-pairing-preview rule). Header-position-aware overrides live
     next to the ne-header class selectors via body:has() so they can win. */

  /* ==========================================================================
     HEADER - ICON NAV MODE
     ========================================================================== */

  .ne-header__nav--icon-mode {
    gap: var(--ne-header-nav-gap, var(--ne-space-1));
  }

  .ne-header__nav-icon-item {
    position: relative;
  }

  /* Icon image rendered as mask-image so it adopts currentColor (dark/light safe) */
  .ne-header__nav-icon-img {
    width: 20px;
    height: 20px;
    background-color: currentColor;
    mask-image: var(--ne-nav-icon-url);
    -webkit-mask-image: var(--ne-nav-icon-url);
    mask-size: contain;
    -webkit-mask-size: contain;
    mask-repeat: no-repeat;
    -webkit-mask-repeat: no-repeat;
    mask-position: center;
    -webkit-mask-position: center;
    display: block;
    flex-shrink: 0;
  }

  /* Inline tooltip label is kept for a11y only — visually hidden.
     The visible tooltip is #ne-header-icon-tooltip which follows the cursor. */
  .ne-header__nav-icon-label {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0,0,0,0); white-space: nowrap; border: 0;
  }

  /* Floating cursor-following tooltip — escapes all stacking contexts via body-mount.
     Positioned via CSS vars set by JS (--ne-tt-x, --ne-tt-y) + anchor translate. */
  .ne-header__icon-tooltip {
    position: fixed;
    top: 0; left: 0;
    transform:
      translate(var(--ne-tt-x, -9999px), var(--ne-tt-y, -9999px))
      translate(var(--ne-tt-anchor-x, -50%), 16px);
    padding: var(--ne-space-1) var(--ne-space-2);
    border-radius: var(--ne-radius-md);
    background: var(--ne-glass-bg-heavy, rgba(15,15,20,0.92));
    backdrop-filter: blur(12px) saturate(1.4);
    -webkit-backdrop-filter: blur(12px) saturate(1.4);
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    color: var(--ne-scheme-text);
    font-size: var(--ne-font-size-xs);
    font-family: var(--ne-header-nav--family, var(--font-body--family));
    font-weight: var(--ne-header-nav--weight, 500);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--ne-transition-fast), visibility var(--ne-transition-fast);
    box-shadow: var(--ne-shadow-md);
    z-index: 9999;
  }

  .ne-header__icon-tooltip.is-visible {
    opacity: 1;
    visibility: visible;
  }

  /* ==========================================================================
     ICON-PAIRING PREVIEW — Customizer-only (request.design_mode) overlay
     Shows the live mapping between top-level menu links and icon-nav blocks.
     Never rendered on live storefront (Liquid conditional on request.design_mode).
     ========================================================================== */
  .ne-icon-pairing-preview {
    position: fixed;
    top: calc(var(--ne-header-height, 70px) + var(--ne-space-4, 16px));
    left: var(--ne-space-3, 12px);
    z-index: 99999;
    width: 300px;
    max-height: 70vh;
    overflow: auto;
    padding: 12px 14px;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
    font-size: 12px;
    line-height: 1.4;
    color: #fff;
    background: rgba(15, 17, 24, 0.88);
    backdrop-filter: blur(12px) saturate(1.4);
    -webkit-backdrop-filter: blur(12px) saturate(1.4);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 10px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45);
    pointer-events: auto;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 0.18s ease, transform 0.18s ease;
    visibility: hidden;
  }
  .ne-icon-pairing-preview.is-active {
    opacity: 1;
    transform: translateY(0);
    visibility: visible;
  }
  .ne-icon-pairing-preview__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 8px;
    margin-bottom: 10px;
    padding-bottom: 8px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  }
  .ne-icon-pairing-preview__title {
    font-weight: 600;
    letter-spacing: 0.02em;
  }
  .ne-icon-pairing-preview__toggle {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.18);
    color: inherit;
    border-radius: 4px;
    padding: 2px 8px;
    font-size: 11px;
    cursor: pointer;
  }
  .ne-icon-pairing-preview__summary {
    font-size: 11px;
    color: rgba(255, 255, 255, 0.65);
    margin-bottom: 8px;
  }
  .ne-icon-pairing-preview__list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .ne-icon-pairing-preview__item {
    display: grid;
    grid-template-columns: 22px 1fr auto auto;
    gap: 6px;
    align-items: center;
    padding: 4px 6px;
    border-radius: 6px;
    background: rgba(255, 255, 255, 0.04);
  }
  .ne-icon-pairing-preview__item--unused {
    background: rgba(239, 68, 68, 0.06);
    border: 1px dashed rgba(239, 68, 68, 0.25);
  }
  .ne-icon-pairing-preview__subhead {
    margin: 10px 0 6px;
    padding-top: 8px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: rgba(255, 255, 255, 0.55);
    border-top: 1px solid rgba(255, 255, 255, 0.08);
  }
  .ne-icon-pairing-preview__idx {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: rgba(255, 255, 255, 0.55);
    text-align: right;
  }
  .ne-icon-pairing-preview__label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .ne-icon-pairing-preview__status {
    font-size: 10px;
    padding: 2px 6px;
    border-radius: 999px;
    font-weight: 600;
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
  .ne-icon-pairing-preview__status--ok {
    background: rgba(34, 197, 94, 0.18);
    color: rgb(134, 239, 172);
  }
  .ne-icon-pairing-preview__status--warn {
    background: rgba(234, 179, 8, 0.18);
    color: rgb(253, 224, 71);
  }
  .ne-icon-pairing-preview__status--miss {
    background: rgba(239, 68, 68, 0.18);
    color: rgb(252, 165, 165);
  }
  .ne-icon-pairing-preview__status--pinned {
    background: rgba(168, 85, 247, 0.18);
    color: rgb(216, 180, 254);
  }
  .ne-icon-pairing-preview__status--slot {
    background: rgba(59, 130, 246, 0.16);
    color: rgb(147, 197, 253);
  }
  .ne-icon-pairing-preview__item--sync {
    grid-template-columns: 140px 1fr;
  }
  .ne-icon-pairing-preview__item--sync .ne-icon-pairing-preview__label {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10.5px;
    color: rgba(255, 255, 255, 0.7);
  }
  .ne-icon-pairing-preview__classline {
    margin: 4px 0 2px;
    padding: 6px 8px;
    font-size: 10.5px;
    line-height: 1.5;
    color: rgba(255, 255, 255, 0.75);
    background: rgba(255, 255, 255, 0.04);
    border-radius: 6px;
    word-break: break-all;
  }
  .ne-icon-pairing-preview__classline code {
    display: inline-block;
    margin: 1px 3px 1px 0;
    padding: 1px 5px;
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: rgb(147, 197, 253);
    background: rgba(59, 130, 246, 0.14);
    border-radius: 4px;
  }
  .ne-icon-pairing-preview__classline em {
    color: rgba(255, 255, 255, 0.4);
    font-style: italic;
  }
  .ne-icon-pairing-preview__label code {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 10px;
    color: rgba(255, 255, 255, 0.7);
  }
  .ne-icon-pairing-preview.is-collapsed .ne-icon-pairing-preview__list,
  .ne-icon-pairing-preview.is-collapsed .ne-icon-pairing-preview__summary,
  .ne-icon-pairing-preview.is-collapsed .ne-icon-pairing-preview__classline,
  .ne-icon-pairing-preview.is-collapsed .ne-icon-pairing-preview__subhead {
    display: none;
  }

  /* Header-position-aware placement: the base rule assumes a top header and
     places the preview below it on the left. Sidebar headers occupy the left
     or right edge, so the preview moves past the header's inner edge + gap.
     Floating header adds extra top offset to clear the frame space. */
  body:has(.ne-header--left) .ne-icon-pairing-preview {
    top: var(--ne-space-3, 12px);
    left: calc(var(--ne-header-height, 70px) + var(--ne-space-3, 12px));
  }
  body:has(.ne-header--right) .ne-icon-pairing-preview {
    top: var(--ne-space-3, 12px);
    left: var(--ne-space-3, 12px);
    right: calc(var(--ne-header-height, 70px) + var(--ne-space-3, 12px));
  }
  body:has(.ne-header--floating) .ne-icon-pairing-preview {
    top: calc(var(--ne-header-height, 70px) + var(--ne-space-3, 12px) * 2);
  }

  /* Hide pairing preview when icons can't influence the rendered nav:
     (a) mobile breakpoint — nav collapses into the burger drawer
     (b) burger-mode on desktop — nav is hidden entirely, icon blocks are inert */
  body:has(.ne-header--burger-mode) .ne-icon-pairing-preview {
    display: none !important;
  }
  @media (max-width: 749px) {
    .ne-icon-pairing-preview {
      display: none !important;
    }
  }

  /* Sidebar: icon nav stacks vertically (inherits flex-direction: column from nav) */
  .ne-header--left .ne-header__nav--icon-mode,
  .ne-header--right .ne-header__nav--icon-mode {
    align-items: center;
  }

  /* Top/floating + icon-nav + logo-center: nav is in grid column 1 via
     logo-center rules — justify-self: start makes icons hug the left edge,
     which doesn't read as "centered". Switch to justify-self: center so the
     icon row sits visually centered inside its column. Actions (col 3) stay
     end-aligned so the three-section rhythm is preserved. */
  .ne-header--logo-center .ne-header__nav--icon-mode {
    justify-self: center;
  }
  .ne-header--logo-left .ne-header__nav--icon-mode {
    justify-self: center;
  }
  .ne-header--logo-right .ne-header__nav--icon-mode {
    justify-self: center;
  }

  /* ==========================================================================
     RESPONSIVE
     ========================================================================== */

  @media (max-width: 749px) {
    /* ⚠️ SIDEBAR→TOP-BAR COLLAPSE — SPEZIFITÄT MUSS die Desktop-Regeln SCHLAGEN.
       Die Desktop-Sidebar-Geometrie lebt in header-styles.liquid (non-weld `.ne-header--left`
       (0,1,0); weld `.ne-header--left.ne-header.ne-header--frame-weld` (0,3,0)+!important).
       Seit dem 256-KB-Split (header-styles → header-styles-2) sind Desktop- und Mobil-Regeln in
       ZWEI getrennten stylesheet-Blöcken; deren Concat-Reihenfolge ist NICHT garantiert
       Render-Reihenfolge → bei gleicher Spezifität+Wichtigkeit gewann plötzlich Desktop → der
       Sidebar kollabierte auf Mobil nicht (fixed, top/bottom gepinnt = Voll-Höhe, width:auto =
       schmale Spalte links). Fix: Mobil-Selektoren um EINE Klasse höher (non-weld → `.ne-header`
       davor = (0,2,0); weld → `.ne-header--has-frame` = (0,4,0)) → gewinnen unabhängig von der
       Block-Reihenfolge. (has-frame ist bei Weld immer gesetzt: Weld ⟹ enable_frame, header.liquid:737.) */
    .ne-header.ne-header--left,
    .ne-header.ne-header--right {
      position: relative;
      width: 100%;
      height: var(--ne-header-height);
      flex-direction: row;
      padding: 0 var(--ne-header-padding-x, 20px);
      border-radius: 0;
    }

    .ne-header--left .ne-header__actions,
    .ne-header--right .ne-header__actions {
      flex-direction: row;
      margin-top: 0;
    }

    .ne-header--logo-center .ne-header__logo {
      position: static;
      transform: none;
    }

    /* Frame-weld sidebar → welded TOP bar on mobile (matches the top-header look, user
       "zur Vereinfachung"). Without this the generic .ne-header--frame-weld forces
       position:fixed!important and the left/right frame-weld rules stretch top→bottom
       (full-height) → the header covered the whole page. Re-map to the TOP frame-weld
       geometry: flush top, sides inset by the frame margin, content height, row. The
       0,3,0 selector ties the desktop frame-weld rules and wins by source order. */
    .ne-header--left.ne-header.ne-header--frame-weld.ne-header--has-frame,
    .ne-header--right.ne-header.ne-header--frame-weld.ne-header--has-frame {
      top: 0 !important;   /* start at the very top edge, exactly like the TOP-header on mobile (NOT inset by fw-t — that added the frame margin above the header so it didn't start at the top, and made the sidebar header look different from the top one). The mouth below (frame-area top push) reveals the radii, not a top inset. */
      left: var(--ne-fw-l) !important;
      right: var(--ne-fw-rt) !important;
      bottom: auto !important;
      width: auto !important;
      height: auto !important;             /* natural ROW height (was pinned to --ne-header-height, which rendered as the full sidebar/setting → too tall). Floor it at --ne-header-height like the TOP-header (min-height, header-styles.liquid:423/434) so it matches exactly: pure logo height alone was a few px SHORTER than the top bar. */
      min-height: var(--ne-header-height) !important;
      display: grid !important;            /* SAME 3-col grid as the top-header mobile layout → identical cart/logo/burger positions (flex positioned them differently). Reset the sidebar's desktop vertical spine grid. The cart (4962) + toggle (4973) grid-placement rules aren't top-scoped, so they already land here; only the logo column + padding needed matching. */
      grid-template-columns: auto minmax(0, 1fr) auto !important;
      grid-template-rows: auto !important;
      align-items: center !important;
      padding: 0 var(--ne-space-2, 8px) !important;   /* match the TOP-header mobile padding (header-styles.liquid:4939) */
      border-radius: var(--ne-fw-r) var(--ne-fw-r) 0 0 !important;
    }
    /* Swap the vertical side-fillet for the top bar's bottom-corner fillets (weld into
       the frame's SIDE margins, exactly like .ne-header--top.ne-header--frame-weld::after). */
    .ne-header--left.ne-header.ne-header--frame-weld.ne-header--has-frame::after,
    .ne-header--right.ne-header.ne-header--frame-weld.ne-header--has-frame::after {
      top: auto !important;
      left: 0 !important;
      right: 0 !important;
      bottom: calc(-1 * var(--ne-fw-r)) !important;
      width: auto !important;
      height: var(--ne-fw-r) !important;
      background:
        radial-gradient(circle at bottom right, transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) left  / var(--ne-fw-r) var(--ne-fw-r) no-repeat,
        radial-gradient(circle at bottom left,  transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) right / var(--ne-fw-r) var(--ne-fw-r) no-repeat !important;
    }

    /* Logo → centre column, in-flow (drives the header height like the top header — an absolute
       logo doesn't contribute height, collapsing the bar to icon height). Mirrors the top-header
       logo rule (header-styles.liquid:4952). Cart (col 1) + burger (col 3) are placed by the
       non-top-scoped grid rules 4962/4973. */
    .ne-header--left.ne-header.ne-header--frame-weld.ne-header--has-frame .ne-header__logo,
    .ne-header--right.ne-header.ne-header--frame-weld.ne-header--has-frame .ne-header__logo {
      position: static !important;
      transform: none !important;
      margin: 0 !important;
      grid-column: 2 !important;
      grid-row: 1 !important;
      justify-self: center !important;
    }

    /* Content offset on mobile: the section's inline body{padding-left:sidebar-width} (not
       mobile-guarded) pushed content sideways for a header that is now a TOP bar. Re-map to
       a TOP layout — side padding = frame margin, top padding = bar height + fillet radius
       (fixed frame-weld bar reserves no space). :has() so it tracks position + frame-weld. */
    body:has(.ne-header--left.ne-header--frame-weld),
    body:has(.ne-header--right.ne-header--frame-weld) {
      padding-left: 0 !important;   /* frame side-clearance is handled by --page-margin (sections); avoid double-inset */
      padding-right: 0 !important;
      padding-top: var(--ne-header-height, 70px) !important;   /* content reaches the mouth top edge (header bottom). The frame-radius is NOT added — that pushed the content down by the radius and left a strip of frame background between the mouth and a full-bleed hero image = exactly the frame radius (user 2026-07-21, "Streifen = Eckenradius"). Full-bleed first sections round their top corners below to match the mouth. */
    }
    /* First full-bleed section reaches the mouth top edge → round its top corners to
       match the frame mouth so the image is clipped to the fillet (no square poke).
       Full-width only; text sections' inline padding already clears the corners. */
    body:has(.ne-header--left.ne-header--frame-weld) .ne-layer-content > .shopify-section:first-child > .ne-section--full-width,
    body:has(.ne-header--right.ne-header--frame-weld) .ne-layer-content > .shopify-section:first-child > .ne-section--full-width {
      border-top-left-radius: var(--ne-frame-radius, 16px) !important;
      border-top-right-radius: var(--ne-frame-radius, 16px) !important;
      overflow: hidden;
    }
    /* Plain (non-weld) sidebar falls back to an in-flow relative top bar (reserves its own
       space) → just drop the sideways sidebar-width offset. */
    body:has(.ne-header--left:not(.ne-header--frame-weld)),
    body:has(.ne-header--right:not(.ne-header--frame-weld)) {
      padding-left: 0 !important;
      padding-right: 0 !important;
    }

    /* Sidebar frame-MERGE mouth on mobile: the desktop rule insets the frame's inner edge
       by the full sidebar width (a SIDE mouth) → the left frame ended up far wider than the
       right. On mobile the header is a welded TOP bar, so restore symmetric frame margins
       (no side mouth). */
    body:has(.ne-header--left) .ne-layer-frame.ne-layer-frame--area,
    body:has(.ne-header--right) .ne-layer-frame.ne-layer-frame--area {
      left: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px)) !important;
      right: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px)) !important;
    }

    /* Frame-MOUTH on mobile: the header is now a TOP bar, so the mouth must open at the
       TOP (mirror the desktop top-mouth, sections/header.liquid:536) — NOT the sidebar's
       SIDE mouth (line 525, which fires because the header is technically --left). Push
       the frame area's top down to the header's bottom edge → the content well opens
       BELOW the header and the header's bottom fillets read as the mouth. Without this
       the frame's top sat level with the header ("Oberkante nicht richtig"). */
    body:has(.ne-header--left.ne-header--frame-mouth) .ne-layer-frame.ne-layer-frame--area,
    body:has(.ne-header--right.ne-header--frame-mouth) .ne-layer-frame.ne-layer-frame--area {
      top: max(var(--ne-fw-t, 8px), calc(var(--ne-header-height, 70px) - var(--ne-mouth-shift, 0px))) !important;   /* mouth opens at header-bottom (header now at top:0 → bottom = header-height); mirror the desktop top-mouth formula (sections/header.liquid:536), incl. scroll-shift */
    }
    /* In mouth mode the frame mulde now forms the mouth from its OWN rounded top corners
       (same element/radius as its bottom) — so drop the header's ::after weld-fillets
       (mirror sections/header.liquid:538). Keeping both stacked two different radii at the
       top of the mouth vs the bottom ("anderer Radius oben als unten"). */
    .ne-header--left.ne-header.ne-header--frame-weld.ne-header--frame-mouth::after,
    .ne-header--right.ne-header.ne-header--frame-weld.ne-header--frame-mouth::after {
      display: none !important;
    }
  }

  /* ===== Native sticky header (top sticky modes) =========================
     A top-anchored header follows an above-header section then pins, NATIVELY
     via position: sticky (compositor-driven → no jank), replacing the old JS
     top-follow (position: fixed + per-frame style.top → 1-frame lag = jump).
       1. display: contents collapses the short .shopify-section wrapper so the
          header's containing block becomes the tall <body> → sticky has room to
          travel the whole page (a sticky element only sticks within its parent).
       2. top: 0 !important also overrides the JS inline style.top, so the
          legacy follow in sections/header.liquid is inert (redundant — safe to
          remove later); native sticky does the follow-then-pin.
       3. spacer hidden — sticky is in-flow and reserves its own space.
     Scoped to top sticky modes only (floating/sidebar keep their fixed rules). */
  .shopify-section:has(.ne-header--sticky:not(.ne-header--floating):not(.ne-header--frame-weld):not(.ne-header--overlay-top):not(.ne-header--transparent):not(.ne-header--initial-hidden)),
  .shopify-section:has(.ne-header--sticky-smart:not(.ne-header--floating):not(.ne-header--frame-weld):not(.ne-header--overlay-top):not(.ne-header--transparent):not(.ne-header--initial-hidden)) {
    display: contents;
  }
  /* The fixed-overlay top states (overlay / transparent-on-top / hidden-until-scroll)
     render NO spacer and REQUIRE position: fixed so the first section starts at the
     viewport top underneath the header. Exclude them here or this !important sticky
     rule forces them in-flow → the header reserves its height → the first section is
     pushed down and never overlapped. Only bites when a fixed-overlay mode is combined
     with a sticky mode (the reported overlay + "always visible" case). */
  .ne-header--sticky:not(.ne-header--floating):not(.ne-header--frame-weld):not(.ne-header--overlay-top):not(.ne-header--transparent):not(.ne-header--initial-hidden),
  .ne-header--sticky-smart:not(.ne-header--floating):not(.ne-header--frame-weld):not(.ne-header--overlay-top):not(.ne-header--transparent):not(.ne-header--initial-hidden) {
    position: sticky !important;
    top: 0 !important;
  }
  /* pin-bottom: ride the viewport BOTTOM (overlapping the full-height section
     above), ramp up as it scrolls, then pin at the TOP — all native via a
     top+bottom sticky box. top:0 alone lets the header scroll off the top
     while the tall hero is on screen. */
  .ne-header--pin-bottom:not(.ne-header--floating):not(.ne-header--frame-weld) {
    bottom: 0 !important;
  }
  .ne-header--sticky:not(.ne-header--floating):not(.ne-header--frame-weld) ~ .ne-header-spacer,
  .ne-header--sticky-smart:not(.ne-header--floating):not(.ne-header--frame-weld) ~ .ne-header-spacer {
    display: none !important;
  }
  /* ===== End native sticky header ===================================== */

  /* 3D glass logo (header variant) — wrapper sizes to the logo; canvas over the flat fallback.
     Compound selector beats the block's `.ne-glogo { height: var(--ne-gl-h, 420px) }`
     (same specificity → source-order would otherwise make the header logo 420px tall). */
  .ne-glogo.ne-glogo--header { position: relative; display: inline-block; width: var(--ne-header-logo-width, 120px); height: auto; line-height: 0; overflow: clip; overflow-clip-margin: 200px; }
  .ne-glogo--header .ne-glogo__fallback { position: static; width: 100%; height: auto; display: block; opacity: 1; transition: opacity .5s ease; }
  /* Canvas extends far beyond the logo slot so the rotating 3D motif can overflow
     the header without being clipped at the canvas edge (header overflow is visible). */
  .ne-glogo--header .ne-glogo__canvas { position: absolute; inset: -150%; width: 400%; height: 400%; max-width: none; display: block; opacity: 0; transition: opacity .6s ease; pointer-events: none; z-index: 1; }
  /* Mobile: the 200px clip-margin lets the oversized 400% canvas leak past the viewport
     (its box reaches ~415px on a 390px screen) and widens the document → whole-page side-pan.
     Clip the canvas hard to the logo box on mobile — the 3D model stays fully visible
     (it is fit to the element, not the canvas); only the off-screen rotation bulge is lost. */
  @media (max-width: 749px) {
    .ne-glogo.ne-glogo--header { overflow-clip-margin: 0px; }
  }
/* END_SNIPPET:header-styles-2 */

/* START_SNIPPET:header-styles (INDEX:160) */
/* ==========================================================================
     HEADER - ACTION BUTTON TOKENS
     Single source of truth for the visual styling of every "action-like"
     element across the header and the drawer. Six element classes resolve
     to the same look:
       .ne-header__action            (header cart / search / account / wishlist / drawer X)
       .ne-header__mobile-toggle     (burger toggle)
       .ne-dark-light-toggle--header (theme toggle in header)
       .ne-dark-light-toggle--floating (theme toggle in drawer)
       .ne-mobile-menu__footer-link  (drawer account / wishlist)
       .ne-mobile-menu__close        (drawer close X)

     Tokens are defined at :root and modifier classes on .ne-header are
     detected via `body:has(.ne-header.X)` to override tokens for the whole
     page. Pill mode uses scope-level token overrides on the pill container
     so descendants are stripped to clean while the container itself keeps
     the outer token values (defines its own bg / border / shadow directly).
     ========================================================================== */
  :root {
    /* Sizing */
    --ne-action-size: var(--ne-header-btn-height, 36px);
    /* Look — defaults match "none" button-style: clean transparent square */
    --ne-action-bg: transparent;
    --ne-action-border-color: transparent;
    --ne-action-border-width: 0px;
    --ne-action-radius: 0;
    /* Effect — defaults to a transparent zero-shadow that's a valid value
       in a comma-list (so 3D / glass effects can compose with it without
       invalidating the box-shadow declaration). */
    --ne-action-shadow: 0 0 0 transparent;
    --ne-action-shadow-hover: var(--ne-action-shadow);
    /* Hover background token. DEFAULT = transparent → no hover "box" in plain
       text/icon mode (Button style = None, Hover style = None). It is raised to
       a real colour only inside a shape context (rule directly below). This is
       what makes "None / None" hover clean (colour / underline / bold only). */
    --ne-action-bg-hover: transparent;
  }

  /* Shape context → action-icons get a hover background, resolved from the
     SAME source nav-links use (custom-scheme override → scheme button-bg-hover
     → 0.18 glass last resort) so the two never diverge. Active when a
     persistent button style is applied to actions, OR a hover-style is set.
     Pill grouping sets its own value further down. */
  .ne-header--actions-shape,
  .ne-header--hover-style-rounded,
  .ne-header--hover-style-pill,
  .ne-header--hover-style-square {
    --ne-action-bg-hover: var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, rgba(255, 255, 255, 0.18)));
  }
  /* No --ne-action-color indirection — the icon color rule below
     reads --ne-scheme-link directly. Indirection through
     --ne-action-color was unreliable in some customizer cascade
     contexts (icons could keep an old token value while the scheme
     itself updated). Direct usage means the cascade is simply: icon
     reads --ne-scheme-link from its nearest scheme ancestor
     (.ne-header.color-scheme-X or body.color-scheme-X). */

  /* Style + Effect modifier tokens are now published from menu.liquid's
     <style> block via Liquid (more reliable than body:has() in the
     Customizer iframe). Defaults at :root above stay as the fallback when
     no menu block is present. */

  /* Pill mode override — descendants of pill containers strip the SHADOW
     and BORDER (so we don't get double-glow / double-outline against the
     pill container's chrome). Background stays — the merchant's button bg
     color shows on items even when wrapped in a pill, as requested.

     Wrapped in @media (min-width: 750px) so this only fires on desktop.
     On mobile (<750px) the @media block elsewhere makes .ne-header__actions
     display:contents which means the pill container doesn't render anyway,
     and the strip would incorrectly kill the cart's glow (since the
     burger-mode class isn't added unless nav_mode is explicitly 'burger').
     Cart icon in mobile burger layout inherits :root tokens → glows. */
  @media (min-width: 750px) {
    .ne-header--pill-nav .ne-header__nav,
    .ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions {
      --ne-action-border-width: 0px;
      /* Use `0 0 0 transparent` (not `none`) so the glass-on-glass rule's
         box-shadow composition (`inset highlights, var(--ne-action-shadow)`)
         stays valid. `none` invalidates the whole multi-value list per CSS
         spec — drops the inset highlights too. Same lesson as Round 17. */
      --ne-action-shadow: 0 0 0 transparent;
      --ne-action-shadow-hover: 0 0 0 transparent;
      /* Pill-mode: same decoupling reasoning as base --ne-action-bg-hover
         above. Slightly stronger 0.22 alpha for pill-mode where pills sit
         on the glass pill container backdrop (which already adds a faint
         tint) so the hover differential reads clearly. */
      --ne-action-bg-hover: rgba(255, 255, 255, 0.22);
    }
  }

  /* Universal action-button rule — every action-like element reads the
     same tokens. This replaces the ~30 duplicated CSS rules that styled
     each element class separately. !important on bg / border / shadow
     ensures the token system always wins against legacy element-specific
     rules that might still set those properties indirectly. */
  .ne-header__action,
  .ne-header__mobile-toggle,
  .ne-dark-light-toggle--header,
  .ne-mobile-menu .ne-header__action,
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link,
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating,
  .ne-mobile-menu__close {
    width: calc(var(--ne-action-size, 36px) + (var(--ne-action-padding-x, 0px) * 2)) !important;
    height: var(--ne-action-size) !important;
    background: var(--ne-action-bg) !important;
    border: var(--ne-action-border-width) solid var(--ne-action-border-color) !important;
    border-radius: var(--ne-action-radius) !important;
    /* Direct read of --ne-scheme-link with fallback chain — anchor
       icons (search/account/wishlist) and `<button>` cart end up on
       the same colour. !important needed to beat critical.css
       `a:not(.ne-btn)` (specificity 0,1,1) on the anchors, otherwise
       they'd inherit --ne-scheme-link from the global rule while the
       cart `<button>` would use the .ne-header__action rule —
       producing inconsistent two-tone icon rows. With direct usage,
       both branches resolve identically. */
    /* Color fallback chain mirrors the nav-button chain so action-icons
       and nav-buttons read the same source-of-truth when no custom-scheme
       is active:
         1. --ne-hbtn-plain-text  (custom-scheme override)
         2. --ne-scheme-btn-text  (scheme's Button-Label — same as nav)
         3. --ne-scheme-link      (legacy fallback — pre-unification)
         4. --ne-scheme-accent    (final hex fallback)
       Previously this skipped (2) and went straight to (3), which gave
       nav vs action a visible color difference when the scheme defined
       button_label ≠ link. */
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-link, var(--ne-scheme-accent, #6366f1)))) !important;
    box-shadow: var(--ne-action-shadow) !important;
    transition: background 150ms ease, border-color 150ms ease, box-shadow 200ms ease, color 150ms ease;
    box-sizing: border-box;
    /* Anchor the count badge to the icon itself (top-right corner) so it tracks
       the cart/wishlist icon when actions are spread or stacked (sidebar) —
       previously it anchored to the header and drifted off the icon. */
    position: relative;
  }
  .ne-header__action:hover,
  .ne-header__mobile-toggle:hover,
  .ne-dark-light-toggle--header:hover,
  .ne-mobile-menu .ne-header__action:hover,
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link:hover,
  .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating:hover,
  .ne-mobile-menu__close:hover {
    /* Hover background = the single --ne-action-bg-hover token. It is
       `transparent` by default (no hover box in plain text/icon mode) and is
       set to the real hover colour ONLY under a shape context (persistent
       button style on actions, hover-style, or pill grouping — see the rule
       just below the :root block). This keeps "no shape" hover clean and makes
       nav-links + action-icons resolve from the SAME source when a shape IS
       active (no more green-vs-indigo mismatch). */
    background: var(--ne-action-bg-hover, transparent) !important;
    box-shadow: var(--ne-action-shadow-hover) !important;
    /* Hover-color chain mirrors plain-color chain — same priority order
       so the visible icon color stays consistent between hover states. */
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-btn-text-hover, var(--ne-scheme-btn-text, var(--ne-scheme-link, currentColor)))) !important;
  }

  /* Pill container chrome — uses the OUTER token values (defined on :root
     by modifier classes), NOT the descendant override. Since the override
     sits on the same selector that styles the container, var() resolves
     to the descendant value. We work around this by reading variables in
     calc()/raw values that aren't part of the override set. */
  .ne-header--pill-nav .ne-header__nav,
  .ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.16));
    border-radius: calc(var(--ne-action-radius, 9999px) + 4px);
    padding: 4px;
    align-items: center;
    box-sizing: content-box;
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }
  /* Pill container effect — class-based rule (no :has() needed since the
     pill modifier IS on .ne-header itself). Reads --ne-pill-shadow which
     is published by menu.liquid (a separate variable from --ne-action-shadow
     so the descendant strip doesn't kill the container's effect). */
  .ne-header--pill-nav.ne-header--btn-effect .ne-header__nav,
  .ne-header--pill-actions:not(.ne-header--burger-mode).ne-header--btn-effect .ne-header__actions {
    box-shadow: var(--ne-pill-shadow, none);
  }

  /* Burger-mode cart glow rescue — when pill-actions is on AND burger-mode
     activates, the actions container gets `display: contents` and the
     pill-mode shadow strip rule (with :not(burger)) doesn't fire. Cart
     should inherit --ne-action-shadow from :root (set by menu.liquid).
     This explicit rule re-applies the token shadow with the highest specificity
     to defend against any rule that might be leaking the strip into burger. */
  .ne-header.ne-header--burger-mode.ne-header--btn-effect .ne-header__action,
  .ne-header.ne-header--burger-mode.ne-header--btn-effect .ne-header__cart,
  .ne-header.ne-header--burger-mode.ne-header--btn-effect .ne-header__mobile-toggle {
    box-shadow: var(--ne-action-shadow, none) !important;
  }

  /* ==========================================================================
     HEADER - BASE
     ========================================================================== */

  .ne-header {
    display: grid;
    /* Default grid: logo/actions take their natural width (auto), nav fills the
       remaining middle as a 1fr track that can shrink to 0. With this model the
       overflow-nav JS detects tight space EARLY (items overflow nav.clientWidth
       as soon as natural width > remaining mid-space), so the "More" collapse
       kicks in well before the viewport reaches burger breakpoint.
       The previous `1fr minmax(0, auto) 1fr` let nav claim max-content while 1fr
       tracks padded the sides — items never overflowed until viewport was so
       narrow that burger-mode had already taken over, so More never triggered.
       Logo-center overrides this with its own template below. */
    grid-template-columns: auto minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--ne-space-2);
    z-index: var(--ne-z-header);
    padding: var(--ne-header-padding-y, 0) var(--ne-header-padding-x, 20px);
    transition: background var(--ne-transition-base), box-shadow var(--ne-transition-base), transform var(--ne-transition-base), opacity var(--ne-transition-base);
    position: relative;
    background: transparent;
  }

  /* Theme-block wrapper transparency — Shopify auto-wraps every theme block
     in <div class="shopify-block">. Without display:contents that wrapper
     becomes the grid/flex child in .ne-header, breaking the layout (<nav>
     and the burger <dialog> are nested one level deeper than the CSS expects).
     display: contents makes the wrapper invisible for layout — its children
     (nav, dialog, marker, style, script) participate in the header grid /
     sidebar flex directly. */
  .ne-header > .shopify-block { display: contents; }

  /* Background via ::before for opacity control without affecting child opacity */
  .ne-header::before {
    content: '';
    position: absolute;
    inset: 0;
    background-color: var(--ne-scheme-bg);
    opacity: var(--ne-header-bg-opacity, 0.8);
    z-index: -1;
    border-radius: inherit;
    transition: opacity var(--ne-transition-base), background var(--ne-transition-base);
  }

  /* Soft edges — fade the header background into the page instead of a hard
     cut. On the ::before bg/glass layer ONLY (Rule 49). Top + bottom fade
     independently with their own sizes. The mask reads --ne-sea-top /
     --ne-sea-bottom: by default the full set sizes (static fallback, no JS);
     when "auto-fade at docked edge" is on, JS ramps each edge's size
     continuously toward 0 as that edge nears the screen edge, so the swap is
     smooth (tied to scroll position). min(size, 50%) stops the two fades from
     crossing, so equal sizes always meet exactly at the centre — the darkest
     point stays centred. */
  .ne-header--soft-edge { --ne-sea-bottom: var(--ne-header-soft-edge-bottom-size, 24px); }
  .ne-header--soft-edge-top { --ne-sea-top: var(--ne-header-soft-edge-top-size, 24px); }
  .ne-header--soft-edge::before,
  .ne-header--soft-edge-top::before {
    -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 min(var(--ne-sea-top, 0px), 50%), #000 calc(100% - min(var(--ne-sea-bottom, 0px), 50%)), transparent 100%);
    mask-image: linear-gradient(to bottom, transparent 0, #000 min(var(--ne-sea-top, 0px), 50%), #000 calc(100% - min(var(--ne-sea-bottom, 0px), 50%)), transparent 100%);
  }

  /* Glass header: blur + bg on ::before, NEVER on the header itself.
     backdrop-filter on the header element would establish a new backdrop-root,
     so descendants (dropdown/megamenu) could only sample the header's interior
     — their own backdrop-filter would have no effect. Override critical.css here. */
  .ne-header.ne-glass {
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  .ne-header.ne-glass::before {
    background: var(--ne-glass-bg, rgba(255,255,255,0.08));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    opacity: var(--ne-header-bg-opacity, 1);
  }

  /* Backdrop-root conflict (Rule 49) — when .ne-glass + .ne-header--nav-btn-
     glass are both on, the header's own backdrop-filter (above) traps the
     descendant nav-button + dropdown backdrop-filters. Result: buttons +
     dropdown appear transparent (no blur visible) because they can only
     sample inside the header's own backdrop-root (which contains nothing).
     Fix: when nav-btn-glass is ON, suppress header's own backdrop-filter on
     ::before — header still has semi-transparent bg for visible chrome,
     but descendants get to do their own blur sampling page-bg. User can
     opt-out by turning nav-btn-glass OFF if they want only the header bar
     to be the glass panel. Same logic for body-hidden + nav-btn-glass. */
  .ne-header.ne-glass.ne-header--nav-btn-glass::before,
  .ne-header.ne-header--body-hidden.ne-header--nav-btn-glass::before {
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }

  /* Tinted header glass: ::before uses scheme accent color instead of neutral white */
  .ne-header.ne-glass.ne-glass--tinted::before {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255,255,255,0.08)));
  }

  /* The global .ne-glass--tinted[class*="glass"] rule (critical.css) paints the
     ELEMENT background-color. Its :hover variant (0,3,0) beats
     .ne-header.ne-glass (0,2,0), so on hover a tint band appeared ON the header
     element — even though the header's glass/tint must live on ::before only
     (Rule 49: the element stays transparent so descendant dropdowns can blur).
     Most visible on a floating header with low bg-opacity (the element is
     otherwise see-through; no tint → nothing on hover, tint → band on hover).
     Force the element transparent in every state; the tint stays on ::before,
     gated by --ne-header-bg-opacity (user 2026-05-25). */
  .ne-header.ne-glass.ne-glass--tinted,
  .ne-header.ne-glass.ne-glass--tinted:hover,
  .ne-header.ne-glass.ne-glass--tinted:focus-within {
    background: transparent !important;
  }

  /* Logo, Nav, Actions — grid-column set via ne-header--logo-* modifier classes only */
  .ne-header__logo {
    display: flex;
    align-items: center;
    gap: var(--ne-space-3);
  }

  .ne-header__actions {
    justify-self: end;
  }

  /* Mobile toggle: right zone (column 3) */
  .ne-header__mobile-toggle {
    grid-column: 3;
    justify-self: end;
  }

  /* Burger-mode + mobile: the cart-icon that lives inside .ne-header__actions
     is the single cart entry-point (previous design shipped a second standalone
     button which was CSS-gated but produced overhead + specificity bugs).
     To keep the layout clean, .ne-header__actions uses `display: contents` so
     its children participate directly in the header grid (for top/floating) or
     flex-column (for sidebar). All non-cart action children are hidden — the
     drawer has equivalents for search/account/theme-toggle. */
  .ne-header--burger-mode .ne-header__actions {
    display: contents;
  }
  .ne-header--burger-mode .ne-header__actions > *:not(.ne-header__cart) {
    display: none !important;
  }
  /* User opt-out: section-level show_cart_in_burger_mode = false.
     Scoped to burger-mode so the cart stays visible in other nav modes
     (the class is emitted from a section setting that doesn't know the
     current nav mode — the menu block's sync script adds burger-mode). */
  .ne-header--burger-mode.ne-header--burger-no-cart .ne-header__cart { display: none !important; }

  /* Burger-mode toggle visibility — the base .ne-hide-desktop hides the
     toggle on desktop; this override restores it whenever the menu block
     signals burger-mode via the sync script. */
  .ne-header--burger-mode .ne-header__mobile-toggle.ne-hide-desktop { display: flex !important; }

  /* Burger-mode on top/floating: the user's logo_position is ignored —
     the logo auto-centers so burger toggle and cart balance on the sides.
     (Previously Liquid forced logo_position = 'center'; we can't read
     nav_mode in Liquid anymore, so CSS handles it via the sync-added class.
     Triple-class specificity (0,3,0) beats the logo-left/right rules above.) */
  .ne-header.ne-header--burger-mode:not(.ne-header--left):not(.ne-header--right) .ne-header__logo {
    grid-column: 2;
    grid-row: 1;
    justify-self: center;
  }
  /* Burger toggle sits opposite the cart on top/floating burger-mode. */
  .ne-header--burger-mode.ne-header--burger-left .ne-header__mobile-toggle {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }
  .ne-header--burger-mode.ne-header--burger-right .ne-header__mobile-toggle {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }

  /* Cart positioning in burger-mode (top/floating) — opposite the burger toggle.
     Specificity 0,3,0 beats logo-* rules and matches .ne-header .ne-header__action. */
  .ne-header--burger-mode.ne-header--burger-right .ne-header__cart {
    grid-column: 1;
    grid-row: 1;
    justify-self: start;
  }
  .ne-header--burger-mode.ne-header--burger-left .ne-header__cart {
    grid-column: 3;
    grid-row: 1;
    justify-self: end;
  }
  /* Sidebar burger-mode: flex order instead of grid column. Position cart
     opposite the burger toggle — if toggle at top (order -1) → cart at bottom,
     if toggle at bottom (order 99) → cart at top.
     NO auto-margins on cart — the logo is the single bottom-anchor. Mixing
     auto-margins across multiple items splits free space and breaks logo-
     top/center/bottom positioning. */
  .ne-header--left.ne-header--burger-mode .ne-header__cart,
  .ne-header--right.ne-header--burger-mode .ne-header__cart {
    grid-column: unset;
    grid-row: unset;
    justify-self: unset;
    margin: 0;
  }
  .ne-header--left.ne-header--burger-mode.ne-header--burger-left .ne-header__cart,
  .ne-header--right.ne-header--burger-mode.ne-header--burger-left .ne-header__cart {
    order: 99;
  }
  .ne-header--left.ne-header--burger-mode.ne-header--burger-right .ne-header__cart,
  .ne-header--right.ne-header--burger-mode.ne-header--burger-right .ne-header__cart {
    order: -1;
  }

  /* Spacer to prevent content from being hidden behind sticky/floating header */
  .ne-header-spacer {
    display: block;
    flex-shrink: 0;
  }

  /* ==========================================================================
     HEADER - POSITIONS
     ========================================================================== */

  /* Top header: escape the .shopify-section grid (grid-column: 2 = content-width)
     so the bar spans the full viewport edge-to-edge. Constrained mode
     (--constrained) overrides below with max-width + margin: auto. */
  .ne-header--top {
    position: relative;
    min-height: var(--ne-header-height);
    width: 100%;
    grid-column: 1 / -1;
    border-radius: var(--ne-header-radius);
  }

  .ne-header--floating {
    position: fixed;
    top: var(--ne-space-3, 12px);
    left: var(--ne-header-side-offset, var(--ne-space-3));
    right: var(--ne-header-side-offset, var(--ne-space-3));
    min-height: var(--ne-header-height);
    border-radius: var(--ne-header-radius);
  }

  .ne-header--left {
    display: flex;
    position: fixed;
    top: 0; left: 0; bottom: 0;
    width: var(--ne-header-sidebar-width, var(--ne-header-height));
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    padding: max(var(--ne-header-padding-y, 0px), var(--ne-space-4)) var(--ne-space-2);
    border-radius: 0 var(--ne-header-radius) var(--ne-header-radius) 0;
  }

  .ne-header--right {
    display: flex;
    position: fixed;
    top: 0; right: 0; bottom: 0;
    width: var(--ne-header-sidebar-width, var(--ne-header-height));
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    padding: max(var(--ne-header-padding-y, 0px), var(--ne-space-4)) var(--ne-space-2);
    border-radius: var(--ne-header-radius) 0 0 var(--ne-header-radius);
  }

  /* Rotated sidebar nav (opt-in): the nav-link text reads vertically along the
     sidebar. writing-mode rotates the inline flow, so the link sizes to the
     rotated text (height auto). */
  .ne-header--sb-spine .ne-header__nav {
    gap: var(--ne-header-nav-gap, var(--ne-space-4, 16px));
  }
  .ne-header--sb-spine .ne-header__nav-link {
    writing-mode: vertical-rl;
    text-orientation: mixed;
    height: auto;
    width: fit-content;
    min-width: var(--ne-action-size, 36px);
    /* Centre each (possibly button-backed) link in the sidebar column — auto
       inline-margins centre a flex child regardless of its width. */
    margin-inline: auto;
    justify-content: center;
  }
  /* Left sidebar: read bottom-to-top (sideways-lr) so the baseline faces the
     content side — mirror of the right sidebar's top-to-bottom (vertical-rl). */
  .ne-header--left.ne-header--sb-spine .ne-header__nav-link {
    writing-mode: sideways-lr;
  }
  /* Centre the rotated links horizontally in the (wider) sidebar instead of
     hugging the start/end edge. */
  .ne-header--left.ne-header--sb-spine .ne-header__nav-item,
  .ne-header--right.ne-header--sb-spine .ne-header__nav-item {
    justify-content: center;
  }
  /* Spine button pills: writing-mode rotates the pill 90°, so the horizontal
     "Höhe"/"Padding" axes drive the wrong visual dimension. Remap so the
     settings stay intuitive on a vertical pill:
       • nav_btn_height  → horizontal THICKNESS (width across the spine)
       • nav_btn_padding_x → vertical LENGTH (padding-block down the spine)
     (0,3,0) beats the base spine link rule (0,2,0) and the --btn rule (0,1,0). */
  .ne-header--sb-spine .ne-header__nav-link.ne-header__nav-link--btn {
    height: auto;
    /* THICKNESS via min-width (shrink-proof): width on a flex child is only a
       flex-basis and collapses to content under shrink — min-width is honoured
       by the flex algorithm, and the rotated text is always narrower than the
       setting, so the pill ends up exactly this thick. */
    width: auto;
    min-width: var(--ne-header-btn-height, 36px);
    flex: 0 0 auto;
    padding-left: 0;
    padding-right: 0;
    padding-top: var(--ne-header-btn-padding-x, 14px);
    padding-bottom: var(--ne-header-btn-padding-x, 14px);
  }
  /* Equal sizing ("Angleichen") in spine: equalise the pill LENGTH — every
     pill floors to the tallest one's height (JS measures --ne-nav-btn-length).
     In normal nav the same toggle equalises width instead. */
  .ne-header--sb-spine.ne-header--nav-equal-width .ne-header__nav-link {
    min-height: var(--ne-nav-btn-length, 0);
  }
  /* Nav-links cloned INTO a dropdown / "More" panel (items that have their own
     submenu keep their trigger link) must NOT inherit the spine rotation or the
     pill axis-swap/equal-length sizing — they read horizontally like normal
     dropdown rows. !important defeats the broad spine pill rules above. */
  .ne-header--sb-spine .ne-header__dropdown .ne-header__nav-link,
  .ne-header--sb-spine .ne-header__btn-menu .ne-header__nav-link {
    writing-mode: horizontal-tb !important;
    /* Match the plain dropdown rows so the panel reads uniformly: full-width,
       left-aligned, no pill fill (the spine pills carry a filled --btn bg that
       looked different from the other rows). flex keeps text + submenu chevron
       on one line. */
    display: flex !important;
    align-items: center !important;
    justify-content: flex-start !important;
    gap: var(--ne-space-2, 8px) !important;
    width: 100% !important;
    min-width: 0 !important;
    height: auto !important;
    min-height: 0 !important;
    text-align: left !important;
    padding: var(--ne-space-2, 8px) var(--ne-space-3, 12px) !important;
    background: transparent !important;
    border: 0 !important;
    /* Strip the 3D lift/shadow + glass so submenu rows match the flat rows. */
    box-shadow: none !important;
    transform: none !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    font-size: var(--ne-font-size-sm, 0.875rem) !important;
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text)) !important;
    flex: 0 1 auto !important;
  }
  /* Spine width follows the header-height setting (JS), so scale the logo to
     fit the (often narrower) sidebar instead of letting it overflow. */
  .ne-header--sb-spine .ne-header__logo,
  .ne-header--sb-spine .ne-header__logo img {
    max-width: 100%;
    height: auto;
  }
  /* Rotate the logo 90° (independent toggle) to complete the spine look —
     opposite direction per side so the logo's top faces the content. */
  .ne-header--sb-logo-rotate .ne-header__logo {
    transform: rotate(90deg) !important;
    transform-origin: center;
  }
  .ne-header--left.ne-header--sb-logo-rotate .ne-header__logo {
    transform: rotate(-90deg) !important;
  }

  /* Sticky modes — position: fixed (not sticky) so it works inside the
     body's flex layout (sticky in flex containers has unreliable
     pinning behaviour across browsers). The header-spacer below
     reserves layout space so content doesn't jump under the header. */
  .ne-header--sticky {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
  }

  .ne-header--sticky.ne-header--floating {
    position: fixed;
    top: var(--ne-space-3, 12px);
    left: var(--ne-header-side-offset, var(--ne-space-3));
    right: var(--ne-header-side-offset, var(--ne-space-3));
  }

  /* Smart sticky: hide on scroll down, show on scroll up */
  .ne-header--sticky-smart {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    transition: transform var(--ne-transition-base), opacity var(--ne-transition-base);
  }

  /* Floating smart sticky: preserve floating offset */
  .ne-header--sticky-smart.ne-header--floating {
    top: var(--ne-space-3, 12px);
    left: var(--ne-header-side-offset, var(--ne-space-3));
    right: var(--ne-header-side-offset, var(--ne-space-3));
  }

  .ne-header--sticky-smart.ne-header--hidden {
    transform: translateY(-200%);
    opacity: 0;
  }

  /* ==========================================================================
     FRAME WELD (settings.enable_frame + header_connect_frame) — top + sidebar.
     The header fuses to the decorative frame: it sits flush ON the frame's edge
     (aligned to the frame inset, OUTER corners = frame radius) and welds to the
     frame's walls with concave fillets (::after, two radial-gradient tiles).
     The frame width is NOT added to the offset — the header sits flush at the
     frame's outer edge so its content stays centred. Top: fillets bottom-left/
     right → side walls; slides up on smart-sticky hide leaving only the frame.
     Sidebar (left/right): fillets on the inner edge → top+bottom walls; static.
     Best with a solid header bg — the fillet is --ne-scheme-bg at the header bg
     opacity; glass (backdrop-filter) can't be reproduced, so it reads as a tint.
     ========================================================================== */
  .ne-header.ne-header--frame-weld {
    --ne-fw-r: var(--ne-frame-radius, 16px);
    --ne-fw-t: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px));
    --ne-fw-l: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px));
    --ne-fw-rt: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px));
    --ne-fw-b: calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-bottom, 0px));
    position: fixed !important;
  }

  /* ---- TOP: flush at the very top (covers the frame's top margin) but INSET on the sides
       by the frame margin, so the frame's side margin shows beside it (matching the content
       below) and the top corners are rounded (frame radius). Content stays centred. ---- */
  .ne-header--top.ne-header.ne-header--frame-weld {
    top: 0 !important;
    left: var(--ne-fw-l) !important;
    right: var(--ne-fw-rt) !important;
    bottom: auto !important;          /* release pin-bottom */
    width: auto !important;           /* left+right define width (beats --top width:100%) */
    height: auto !important;          /* collapse to content height → content stays centred */
    border-radius: var(--ne-fw-r) var(--ne-fw-r) 0 0 !important;
  }
  .ne-header--top.ne-header.ne-header--frame-weld::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: calc(-1 * var(--ne-fw-r));
    height: var(--ne-fw-r);
    z-index: -1;
    pointer-events: none;
    opacity: var(--ne-header-bg-opacity, 0.8);
    /* fillets at the header's own bottom corners → weld into the frame's side margins */
    background:
      radial-gradient(circle at bottom right, transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) left  / var(--ne-fw-r) var(--ne-fw-r) no-repeat,
      radial-gradient(circle at bottom left,  transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) right / var(--ne-fw-r) var(--ne-fw-r) no-repeat;
  }
  /* top-header content clearance (header starts at 0, covers to header-height; fillet adds r) */
  .ne-header--top.ne-header--frame-weld ~ .ne-header-spacer {
    min-height: calc(var(--ne-header-height) + var(--ne-frame-radius, 16px)) !important;
  }

  /* ---- LEFT sidebar: flush on the outer (left) edge — covers the left margin — but INSET
       on the ends (top+bottom) by the frame margin; outer (left) corners rounded; inner
       (right) fillets weld into the frame's top/bottom margins. ---- */
  .ne-header--left.ne-header.ne-header--frame-weld {
    top: var(--ne-fw-t) !important;
    bottom: var(--ne-fw-b) !important;
    left: 0 !important;
    right: auto !important;
    height: auto !important;          /* top+bottom define height */
    border-radius: var(--ne-fw-r) 0 0 var(--ne-fw-r) !important;   /* outer (left) corners */
  }
  .ne-header--left.ne-header.ne-header--frame-weld::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    right: calc(-1 * var(--ne-fw-r));
    width: var(--ne-fw-r);
    z-index: -1;
    pointer-events: none;
    opacity: var(--ne-header-bg-opacity, 0.8);
    background:
      radial-gradient(circle at bottom right, transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) top    / var(--ne-fw-r) var(--ne-fw-r) no-repeat,
      radial-gradient(circle at top right,    transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) bottom / var(--ne-fw-r) var(--ne-fw-r) no-repeat;
  }

  /* ---- RIGHT sidebar: mirror ---- */
  .ne-header--right.ne-header.ne-header--frame-weld {
    top: var(--ne-fw-t) !important;
    bottom: var(--ne-fw-b) !important;
    right: 0 !important;
    left: auto !important;
    height: auto !important;
    border-radius: 0 var(--ne-fw-r) var(--ne-fw-r) 0 !important;   /* outer (right) corners */
  }
  .ne-header--right.ne-header.ne-header--frame-weld::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    left: calc(-1 * var(--ne-fw-r));
    width: var(--ne-fw-r);
    z-index: -1;
    pointer-events: none;
    opacity: var(--ne-header-bg-opacity, 0.8);
    background:
      radial-gradient(circle at bottom left, transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) top    / var(--ne-fw-r) var(--ne-fw-r) no-repeat,
      radial-gradient(circle at top left,    transparent var(--ne-fw-r), var(--ne-scheme-bg) calc(var(--ne-fw-r) + 0.5px)) bottom / var(--ne-fw-r) var(--ne-fw-r) no-repeat;
  }

  /* ==========================================================================
     HEADER - PAGE-WIDTH CONSTRAINT
     When settings.header_apply_page_width is on, the HEADER BAR stays
     full-width (background spans the whole viewport) while the inner
     CONTENT (logo / nav / actions) is centred into the page-width track.
     Achieved by extending --ne-header-side-offset (Liquid-computed as
     max(min_margin, (100vw - page-width) / 2)) into the bar's
     horizontal padding. Earlier behaviour clipped the whole bar with
     max-width + margin-inline:auto, which left empty side strips
     outside the bar (user-reported regression 2026-05-28).
     ========================================================================== */

  /* position: relative / sticky-non-smart — padding-inline pushes content
     inward; bar bg fills the viewport. */
  .ne-header--top.ne-header--constrained,
  .ne-header--sticky.ne-header--constrained:not(.ne-header--floating):not(.ne-header--sticky-smart) {
    padding-inline: var(--ne-header-side-offset, var(--ne-header-padding-x, 20px)) !important;
  }

  /* position: fixed (sticky-smart, non-floating) — same principle: bar
     stays full-width (base sticky-smart rule sets left:0 right:0),
     content padded inward via side-offset. */
  .ne-header--sticky-smart.ne-header--constrained:not(.ne-header--floating) {
    padding-inline: var(--ne-header-side-offset, var(--ne-header-padding-x, 20px)) !important;
  }

  /* Hide animation for sticky-smart constrained: base .ne-header--hidden
     rule already does translateY(-200%) + opacity 0 — no constrained
     compose needed any more (we dropped the translateX centring). */

  /* Floating + constrained: --ne-header-side-offset is set directly by Liquid (header_styles)
     to max(page_margin, (100vw - page-width) / 2). The base floating rule reads this var,
     so no separate CSS rule is needed here — the bar centering is driven entirely by the var. */

  /* Transparent top header (constrained) — same full-bleed bar with
     inner side-offset padding. */
  .ne-header--top.ne-header--transparent.ne-header--constrained {
    left: 0;
    right: 0;
    padding-inline: var(--ne-header-side-offset, var(--ne-header-padding-x, 20px)) !important;
  }

  /* Transparent-on-top header: transparent at scrollY=0, gains bg on scroll.
     backdrop-filter also removed while transparent — even invisible, it would
     trap dropdown's blur. Once scrolled, the normal bg + glass rules apply. */
  .ne-header--initial-transparent:not(.ne-header--scrolled),
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-glass {
    background: transparent !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
    border: none !important;
    box-shadow: none !important;
  }

  /* Transparent-on-top header: SOFT FADE between transparent (top) and opaque
     (scrolled). The bg lives on ::before (opacity), so fading its opacity +
     backdrop-filter + the header box-shadow over --ne-transition-base cross-fades
     cleanly (user 2026-07-11). transform/opacity stay on base for the smart-hide
     slide. History: the fade was once 0s'd to kill a near-top colour/shadow flash
     that fired when .ne-header--scrolled toggled at y≈0 (2026-05-26); the scroll
     threshold now sits ~half a viewport below the fold (see _solidAt floor in
     header.liquid) so the toggle happens away from the top and no longer flashes. */
  .ne-header--initial-transparent {
    transition:
      background var(--ne-transition-base),
      box-shadow var(--ne-transition-base),
      transform var(--ne-transition-base),
      opacity var(--ne-transition-base) !important;
  }
  .ne-header--initial-transparent::before {
    transition:
      opacity var(--ne-transition-base),
      background var(--ne-transition-base),
      backdrop-filter var(--ne-transition-base),
      -webkit-backdrop-filter var(--ne-transition-base) !important;
  }

  .ne-header--initial-transparent:not(.ne-header--scrolled)::before,
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-glass::before {
    opacity: 0 !important;
    background: transparent !important;
  }

  /* Glass-tint guard: the universal .ne-glass--tinted[class*="glass"] rule
     (critical.css) paints an accent background with !important — AND it has a
     :hover variant (.ne-glass--tinted[class*="glass"]:hover, 0,3,0) that
     repaints the bg on hover. In header states meant to be invisible
     (transparent-on-top at scrollY=0, smart-hide, hidden-until-scroll) the base
     bg was suppressed, but the :hover variant tied our base suppression on
     specificity and won on source order → the header re-appeared on hover (only
     with tint on — user 2026-05-24). We must therefore also suppress the
     :hover/:focus-within states with an extra class (:hover = 0,4,0) so we
     outrank the global :hover rule. */
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-glass--tinted,
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-glass--tinted:hover,
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-glass--tinted:focus-within,
  .ne-header--sticky-smart.ne-header--hidden.ne-glass--tinted,
  .ne-header--sticky-smart.ne-header--hidden.ne-glass--tinted:hover,
  .ne-header--initial-hidden:not(.ne-header--scrolled).ne-glass--tinted,
  .ne-header--initial-hidden:not(.ne-header--scrolled).ne-glass--tinted:hover {
    background-color: transparent !important;
    border-color: transparent !important;
  }
  /* Off-screen states only — transparent-on-top stays interactive (its nav
     links overlay the hero and must remain clickable). */
  .ne-header--sticky-smart.ne-header--hidden {
    pointer-events: none;
  }

  /* Hidden-until-scroll: translate header off-screen at scrollY=0, reveal on scroll.
     position: fixed so it can animate in without reserving layout space. */
  .ne-header--initial-hidden {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    transition: transform var(--ne-transition-base), opacity var(--ne-transition-base);
  }
  .ne-header--initial-hidden:not(.ne-header--scrolled) {
    transform: translateY(-100%);
    opacity: 0;
    pointer-events: none;
  }

  /* Transparent top header: edge-to-edge full-bleed (user wants no top/side gaps). */
  .ne-header--top.ne-header--transparent {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    border-radius: 0;
  }

  /* Overlay top header: like transparent-on-top but KEEPS its background/glass
     (no transparency toggle) so the header — and its soft-edge gradient — runs
     over the first section instead of pushing it down. No spacer is rendered
     (header.liquid), so the first section starts at the viewport top. */
  .ne-header--overlay-top {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    border-radius: 0;
  }

  /* DEPRECATED — the .ne-header { --ne-action-* defaults } and the
     .ne-header--actions-shape.ne-header--nav-btn-X overrides have been
     replaced by the :root token system + body:has() modifier cascade at
     the top of this stylesheet. Tokens now flow to the entire page
     (including the drawer that lives outside .ne-header) and there's no
     need for the actions-shape opt-in — modifiers always apply.

     The transparent-mode hover override below stays because it's a
     specific styling decision, not a token. */
  .ne-header--transparent .ne-header__action:hover {
    background: var(--ne-glass-bg-hover, rgba(255,255,255,0.1));
  }

  /* ==========================================================================
     HEADER - EFFECTS
     ========================================================================== */

  .ne-header--shadow {
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4), 0 1px 4px rgba(0, 0, 0, 0.2);
  }

  .ne-header--glow {
    box-shadow:
      0 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      0 0 calc(60px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.2));
  }

  /* Transparent-on-top: suppress the bar shadow/glow ONLY while the bar is
     transparent (at the top of the page). Once scrolled, the bar gains its
     background, so the shadow/glow should return like a normal opaque header.
     Gated on :not(.ne-header--scrolled) — previously this suppressed the bar
     effect permanently, so it never came back after scroll (user 2026-05-26). */
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-header--shadow,
  .ne-header--initial-transparent:not(.ne-header--scrolled).ne-header--glow {
    box-shadow: none !important;
  }

  /* `effect_scope = buttons_only` — opt-out of the bar effect while keeping
     the buttons' effect intact. Mirrors transparent-mode behaviour but is
     orthogonal to it (works on opaque headers too). */
  .ne-header--bar-effect-off.ne-header--shadow,
  .ne-header--bar-effect-off.ne-header--glow {
    box-shadow: none !important;
  }

  /* `hide_header_body` — strips the bar's bg / border / glass / box-shadow
     so the pill containers (and / or logo) become the visible chrome on
     their own. Useful for transparent-style headers that only want pills
     floating above the page content. */
  .ne-header--body-hidden,
  .ne-header--body-hidden.ne-glass,
  .ne-header--body-hidden.ne-header--shadow,
  .ne-header--body-hidden.ne-header--glow {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
  /* …but NOT when a merge is active: the menu-merge dropdown/mega panel
     lives at z-index:-2 and tucks BEHIND the header's opaque ::before mask (z-index:-1).
     hide_header_body would strip that mask → the panel becomes visible OVER the header
     (the global-settings-path bug: global hide_header_body=true vs section=false). The
     :not() guards keep the mask opaque exactly in the merge case. */
  .ne-header--body-hidden:not(.ne-header--menu-merge):not(.ne-header--frame-merge)::before,
  .ne-header--body-hidden:not(.ne-header--menu-merge):not(.ne-header--frame-merge)::after {
    background: transparent !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    box-shadow: none !important;
    border: 0 !important;
  }

  /* Optional: apply the same glow / shadow to the logo image so it visually
     pairs with the bar. drop-shadow filter is used (instead of box-shadow)
     so the glow follows the logo silhouette, not its bounding box.
     Default glow color = scheme.text — auto-matches the logo color via
     dark/light pair (white text for dark mode → white glow for white
     logo; black text for light mode → dark glow for black logo). User
     report 2026-05-23: glow stayed white even under black logo.
     `.ne-header--logo-glow-accent` modifier (settings → logo_glow_accent
     toggle) switches to scheme.accent for a coloured halo. */
  .ne-header--logo-effect.ne-header--glow .ne-header__logo img {
    --ne-logo-glow-color: var(--ne-scheme-text, rgba(255, 255, 255, 0.85));
    filter: drop-shadow(0 0 calc(8px * var(--ne-header-glow-intensity, 0.5)) var(--ne-logo-glow-color))
            drop-shadow(0 0 calc(20px * var(--ne-header-glow-intensity, 0.5)) var(--ne-logo-glow-color));
  }
  .ne-header--logo-glow-accent.ne-header--logo-effect.ne-header--glow .ne-header__logo img {
    --ne-logo-glow-color: var(--ne-scheme-accent, rgba(99, 102, 241, 0.6));
  }
  .ne-header--logo-effect.ne-header--shadow .ne-header__logo img {
    filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.4));
  }

  /* Instead: shadow/glow applies only to each nav button/action */
  .ne-header--transparent.ne-header--shadow .ne-header__nav-link--btn,
  .ne-header--transparent.ne-header--shadow .ne-header__action {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  }

  .ne-header--transparent.ne-header--glow .ne-header__nav-link--btn,
  .ne-header--transparent.ne-header--glow .ne-header__action {
    box-shadow:
      0 0 calc(16px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.5));
  }

  [data-ne-mode="alternate"] .ne-header--shadow {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
  }

  /* DEPRECATED — replaced by :root token system + body:has() modifier
     cascade at the top of this stylesheet. Tokens flow to ALL action-like
     elements (header buttons, drawer items, theme toggles) automatically.

     Nav-links (text mode) still rely on the rules below — they're not
     part of the action-button token system because they have different
     padding / typography needs. */
  .ne-header--btn-effect.ne-header--shadow .ne-header__nav-link,
  .ne-header--btn-effect.ne-header--shadow .ne-header__nav-link--btn {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  }
  .ne-header--btn-effect.ne-header--glow .ne-header__nav-link,
  .ne-header--btn-effect.ne-header--glow .ne-header__nav-link--btn {
    box-shadow:
      0 0 calc(14px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      0 0 calc(28px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent-glow, rgba(99,102,241,0.3));
    transition: box-shadow 0.25s ease;
  }
  .ne-header--btn-effect.ne-header--glow .ne-header__nav-link:hover,
  .ne-header--btn-effect.ne-header--glow .ne-header__nav-link--btn:hover {
    box-shadow:
      0 0 calc(28px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.8)),
      0 0 calc(56px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent-glow, rgba(99,102,241,0.5));
  }

  /* ==========================================================================
     HEADER - LOGO POSITIONS
     ========================================================================== */

  /* Logo left (default): logo col1, nav col2 (centered auto), actions col3
     grid-row: 1 on all items prevents auto-placement cursor bug */
  .ne-header--logo-left .ne-header__logo { grid-column: 1; grid-row: 1; justify-self: start; }
  .ne-header--logo-left .ne-header__nav  { grid-column: 2; grid-row: 1; }
  .ne-header--logo-left .ne-header__actions { grid-column: 3; grid-row: 1; justify-self: end; }

  /* Logo center: nav col1, logo col2 (auto), actions col3
     grid-row: 1 on all items forces same row — without it the auto-placement
     cursor advances past col1 after placing logo at col2, pushing nav to row 2 */
  .ne-header--logo-center {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  }
  .ne-header--logo-center .ne-header__logo {
    grid-column: 2; grid-row: 1;
    justify-self: center;
    position: static;
    transform: none;
  }
  /* Nav fills col 1 (minmax(0, 1fr)) so applyOverflowNav() reads
     nav.clientWidth === track width and can detect overflow. With justify-self:
     start the nav shrinks to content and overflow is never detected.
     NO overflow: hidden — it would clip the absolute-positioned .ne-header__dropdown
     panels. applyOverflowNav hides overflowing items via display:none, so brief
     visual overflow during measure is imperceptible. */
  .ne-header--logo-center .ne-header__nav  {
    grid-column: 1; grid-row: 1;
    justify-self: stretch;
    min-width: 0;
    justify-content: flex-start;
  }
  .ne-header--logo-center .ne-header__actions { grid-column: 3; grid-row: 1; justify-self: end; }

  /* Logo right: actions col1 (left), nav col2 (centered auto), logo col3 (right)
     grid-row: 1 on all items — same auto-placement cursor fix as logo-center */
  .ne-header--logo-right .ne-header__logo { grid-column: 3; grid-row: 1; justify-self: end; }
  .ne-header--logo-right .ne-header__nav  { grid-column: 2; grid-row: 1; }
  .ne-header--logo-right .ne-header__actions { grid-column: 1; grid-row: 1; justify-self: start; }

  /* Nav viewport-centered (opt-in): symmetric 1fr auto 1fr grid forces the
     middle (nav) column to viewport center regardless of logo / actions
     content widths. Default header layout uses `auto 1fr auto` where the
     middle nav column shifts whenever logo and actions are different widths.
     Logo-center mode already uses 1fr auto 1fr by design — this modifier
     extends the same symmetry to logo-left and logo-right. */
  .ne-header--nav-vp-centered.ne-header--logo-left:not(.ne-header--left):not(.ne-header--right),
  .ne-header--nav-vp-centered.ne-header--logo-right:not(.ne-header--left):not(.ne-header--right) {
    grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  }
  .ne-header--nav-vp-centered.ne-header--logo-left:not(.ne-header--left):not(.ne-header--right) .ne-header__nav,
  .ne-header--nav-vp-centered.ne-header--logo-right:not(.ne-header--left):not(.ne-header--right) .ne-header__nav {
    justify-self: center;
  }

  /* === Below-logo arrangement (nav_arrangement: below-logo) =============
     Nav drops to a second row spanning the full width, centered, while
     row 1 keeps the logo + actions. Top/floating headers only — sidebar
     headers stack vertically by nature (Phase 4). Works with any logo
     position. The header grid grows to two rows (min-height, so no clip);
     the nav row stretches to the full track so applyOverflowNav reads the
     full container width (the `below` branch in menu.liquid). */
  .ne-header--nav-arrange-below:not(.ne-header--left):not(.ne-header--right) {
    /* Row 1 = the full configured header height (logo/actions centred in it,
       like a normal header); row 2 = the nav, which ADDS to the height instead
       of squeezing the logo out the top. */
    grid-template-rows: var(--ne-header-height) auto;
    row-gap: var(--ne-space-1, 4px);
  }
  .ne-header--nav-arrange-below:not(.ne-header--left):not(.ne-header--right) .ne-header__nav {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-self: stretch;
    justify-content: center;
    min-width: 0;
  }
  /* Below-nav alignment (left / centre / right). Centre is the default above. */
  .ne-header--nav-arrange-below.ne-header--below-align-left:not(.ne-header--left):not(.ne-header--right) .ne-header__nav { justify-content: flex-start; }
  .ne-header--nav-arrange-below.ne-header--below-align-right:not(.ne-header--left):not(.ne-header--right) .ne-header__nav { justify-content: flex-end; }

  /* Nav alignment Left / Right (opt-in): the nav lives in the middle grid
     track and centers its items by default (justify-content: center). These
     modifiers push the items to the start (towards the logo) or the end
     (towards the actions) of that track instead. Works for any logo
     position. Sidebar (left/right header) uses its own vertical layout and
     ignores these (nav_alignment is hidden there via visible_if). */
  .ne-header--nav-align-left .ne-header__nav { justify-content: flex-start; }
  .ne-header--nav-align-right .ne-header__nav { justify-content: flex-end; }
  /* Edge-padding to push nav-points away from the section edge when the
     nav is anchored left or right. Only applies for those alignments —
     auto / viewport (centered modes) ignore the var. Setting lives in
     header section schema (nav_edge_padding, visible_if alignment is
     left or right). */
  .ne-header--nav-align-left .ne-header__nav,
  .ne-header--nav-align-right .ne-header__nav {
    padding-inline: var(--ne-header-nav-edge-padding, 0);
  }

  /* Pill grouping (opt-in): wraps either nav-items, action-icons, or both
     into a single shared pill container with glass bg + rounded outline.
     Individual items lose their per-item bg/border so the whole group
     reads as one chip. Burger-mode keeps `.ne-header__actions` on
     display:contents (cart only) so pill-actions naturally degrades there.

     Sizing is auto-derived from the menu block's `button height` setting
     (--ne-action-size). Pill = action-size + 8px padding so the items
     keep their natural square aspect ratio inside the container. The
     pill's border-radius mirrors --ne-action-radius (button shape) so a
     "square" nav style yields a square-ish pill, "pill" yields a fully
     rounded pill, etc.

     IMPORTANT: backdrop-filter goes on ::before, not on the pill itself.
     A direct backdrop-filter on the container would create a backdrop-root
     and capture descendant blur (Rule 49) — dropdowns inside the nav-pill
     would render fully transparent because their own backdrop-filter would
     sample the pill's backdrop instead of the page behind it. */
  .ne-header--pill-nav .ne-header__nav,
  .ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions {
    position: relative;
    border: 1px solid var(--ne-glass-border, rgba(255,255,255,0.16));
    border-radius: calc(var(--ne-action-radius, 999px) + 4px);
    /* Padding-only sizing — explicit height was causing 1px overflow at
       the bottom (border + padding + items = 46px against a 44px height
       cap, items shifted slightly down via align-items center). Letting
       content drive the size gives pixel-perfect symmetry. */
    padding: 4px;
    align-items: center;
    box-sizing: content-box;
  }
  .ne-header--pill-nav .ne-header__nav::before,
  .ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    /* Default surface = solid scheme bg (matches the bar when glass is OFF).
       Glass override below applies when the header has .ne-glass. */
    background: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    z-index: -1;
    pointer-events: none;
  }
  /* Glass header → pills inherit the glass surface (translucent + blur).
     Without this the pills would always paint solid scheme bg even when the
     bar is glass — looks inconsistent.

     Body-hidden mode: bar's own glass is suppressed but pills should keep
     their glass-mode styling so the user can still see "the header" when the
     bar is invisible. We DON'T require .ne-glass on the bar in that mode. */
  .ne-header.ne-glass.ne-header--pill-nav .ne-header__nav::before,
  .ne-header.ne-glass.ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions::before,
  .ne-header.ne-header--body-hidden.ne-header--pill-nav .ne-header__nav::before,
  .ne-header.ne-header--body-hidden.ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions::before {
    background: var(--ne-glass-bg, rgba(255,255,255,0.08));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
  }

  /* Pill-nav backdrop-root trap (Rule 49) — when nav-item dropdown is open,
     the pill ::before's backdrop-filter creates a backdrop-root that traps
     the dropdown's own backdrop-filter. Dropdown then samples only the
     pill-bg area instead of the page-bg → dropdown appears non-blurry
     against pill (or weakly-blurred). Fix: when ANY nav-item is hover-open
     or focus-within or has .is-open, drop the pill's backdrop-filter (only
     the filter, KEEP the semi-transparent bg so pill still has visible
     chrome). Same for pill-actions when an actions-mounted dropdown opens
     (rare, but defensive). Trade-off: pill loses crisp blur on hover, but
     dropdown gains crisp blur (user-perceived as expected behavior). */
  .ne-header.ne-glass.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown:hover) .ne-header__nav::before,
  .ne-header.ne-glass.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown:focus-within) .ne-header__nav::before,
  .ne-header.ne-glass.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown.is-open) .ne-header__nav::before,
  .ne-header.ne-header--body-hidden.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown:hover) .ne-header__nav::before,
  .ne-header.ne-header--body-hidden.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown:focus-within) .ne-header__nav::before,
  .ne-header.ne-header--body-hidden.ne-header--pill-nav:has(.ne-header__nav-item--has-dropdown.is-open) .ne-header__nav::before {
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
  }
  /* Body-hidden + glass OFF → pills paint solid scheme bg (no blur) so the
     scheme's color comes through cleanly — matches user expectation that the
     pills are "the header" when the bar is hidden. */
  .ne-header.ne-header--body-hidden:not(.ne-glass).ne-header--pill-nav .ne-header__nav::before,
  .ne-header.ne-header--body-hidden:not(.ne-glass).ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions::before {
    background: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  /* Pill is content-width (not full-column) and vertically centered in
     the header — without this override the nav inherits align-self:stretch
     + justify-self:stretch from .ne-header__nav and the pill spans the
     entire grid track instead of just hugging the items. */
  .ne-header--pill-nav .ne-header__nav {
    align-self: center;
    justify-self: center;
    width: max-content;
    gap: 4px;
  }
  .ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions {
    align-self: center;
    gap: 4px;
  }

  /* Nav-links in pill mode — nav-links are NOT part of the action-button
     token system (they have their own padding / typography rules). Strip
     border-color + box-shadow so the pill is the only outline-chrome, but
     KEEP the user's button background so it shows on items inside the pill
     (matches action-icon behaviour, Image #96). */
  .ne-header--pill-nav .ne-header__nav-link,
  .ne-header--pill-nav .ne-header__nav-link--btn {
    align-self: center;
    border-color: transparent !important;
    box-shadow: none !important;
  }
  .ne-header--pill-nav .ne-header__nav-link:hover,
  .ne-header--pill-nav .ne-header__nav-link--btn:hover {
    background: var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.14)) !important;
    border-radius: var(--ne-action-radius, 999px) !important;
  }

  /* btn-effect + pill = effect on the pill CONTAINER, not individual items.
     Without this, the per-item box-shadow gets stripped by the pill rules
     above and the user sees no glow/shadow at all when btn-effect is on. */
  .ne-header--pill-nav.ne-header--btn-effect.ne-header--glow .ne-header__nav,
  .ne-header--pill-actions:not(.ne-header--burger-mode).ne-header--btn-effect.ne-header--glow .ne-header__actions {
    box-shadow:
      0 0 calc(14px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99,102,241,0.5)),
      0 0 calc(28px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent-glow, rgba(99,102,241,0.3));
  }
  .ne-header--pill-nav.ne-header--btn-effect.ne-header--shadow .ne-header__nav,
  .ne-header--pill-actions:not(.ne-header--burger-mode).ne-header--btn-effect.ne-header--shadow .ne-header__actions {
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.4);
  }

  /* DEPRECATED — burger-mode cart glow is now handled automatically by the
     token system. The pill rule's `:not(.ne-header--burger-mode)` guard
     means tokens aren't overridden in burger-mode → items inherit the
     :root --ne-action-shadow value that body:has(.ne-header.ne-header--btn-effect.X)
     publishes. No specificity wars needed. */

  /* Sidebar (left/right) header overrides: flip the pill layout to vertical.
     Pill width hugs content (centered in sidebar column), height auto-grows
     to fit the stacked nav-links / action-icons. Padding-block is action-
     size / 2 + 4px so the first / last items sit safely INSIDE the curved
     top / bottom regardless of nav button-style — earlier we used the radius
     directly, which exploded to 503.5px when the buttons were "pill" style
     (radius 999px) and pushed everything off the column (Image #44). */
  .ne-header--left.ne-header--pill-nav .ne-header__nav,
  .ne-header--right.ne-header--pill-nav .ne-header__nav,
  .ne-header--left.ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions,
  .ne-header--right.ne-header--pill-actions:not(.ne-header--burger-mode) .ne-header__actions {
    width: max-content;
    height: auto;
    margin-inline: auto;
    flex-direction: column;
    padding: calc(var(--ne-action-size, 36px) / 2 + 4px) 4px;
    flex: 0 0 auto;
  }
  /* Sidebar nav still needs flex: 1 1 auto to claim middle space between
     logo and actions — but wrapping it in a fixed-width pill above kills
     that. Restore the centering via vertical margin auto on the pill. */
  .ne-header--left.ne-header--pill-nav .ne-header__nav,
  .ne-header--right.ne-header--pill-nav .ne-header__nav {
    margin-block: auto;
  }

  /* Sidebar (left/right) headers are flex-column. Translate logo-position into
     flex-order so top/center/bottom vertical placement works the same way the
     horizontal placement does on top/floating headers. The actions row stays
     at the opposite end via order + margin-top:auto on __actions. */
  .ne-header--left,
  .ne-header--right {
    justify-content: flex-start;
  }
  .ne-header--left.ne-header--logo-left  .ne-header__logo,
  .ne-header--right.ne-header--logo-left .ne-header__logo { order: 0; margin-bottom: var(--ne-space-3); }
  .ne-header--left.ne-header--logo-left  .ne-header__nav,
  .ne-header--right.ne-header--logo-left .ne-header__nav  { order: 1; }
  .ne-header--left.ne-header--logo-left  .ne-header__actions,
  .ne-header--right.ne-header--logo-left .ne-header__actions { order: 2; margin-top: auto; }

  /* Sidebar + logo-center: the three rows (nav, logo, actions) need to sit
     at top / middle / bottom without overlapping. Using justify-content on
     the sidebar parent spreads them evenly and avoids dueling auto-margins. */
  .ne-header--left.ne-header--logo-center,
  .ne-header--right.ne-header--logo-center {
    justify-content: space-between;
  }
  .ne-header--left.ne-header--logo-center  .ne-header__logo,
  .ne-header--right.ne-header--logo-center .ne-header__logo { order: 1; margin: 0; flex: 0 0 auto; }
  .ne-header--left.ne-header--logo-center  .ne-header__nav,
  .ne-header--right.ne-header--logo-center .ne-header__nav  { order: 0; flex: 0 0 auto; }
  .ne-header--left.ne-header--logo-center  .ne-header__actions,
  .ne-header--right.ne-header--logo-center .ne-header__actions { order: 2; margin: 0; }

  .ne-header--left.ne-header--logo-right  .ne-header__logo,
  .ne-header--right.ne-header--logo-right .ne-header__logo { order: 2; margin-top: auto; }
  .ne-header--left.ne-header--logo-right  .ne-header__nav,
  .ne-header--right.ne-header--logo-right .ne-header__nav  { order: 1; }
  .ne-header--left.ne-header--logo-right  .ne-header__actions,
  .ne-header--right.ne-header--logo-right .ne-header__actions { order: 0; }

  /* Sidebar burger toggle: ignore grid rules, use flex order slot at the end */
  .ne-header--left .ne-header__mobile-toggle,
  .ne-header--right .ne-header__mobile-toggle {
    grid-column: unset;
    grid-row: unset;
    justify-self: unset;
    order: 3;
    margin-top: var(--ne-space-3);
  }

  /* ==========================================================================
     HEADER - LOGO
     ========================================================================== */

  .ne-header__logo a {
    display: flex;
    align-items: center;
    text-decoration: none;
    color: var(--ne-scheme-text, var(--ne-scheme-text));
  }

  .ne-header__logo-img {
    width: var(--ne-header-logo-width, 120px);
    max-width: 100%;
    height: auto;
    display: block;
    flex-shrink: 0;
  }

  /* Inverse logo: hidden by default — only CSS-swap when [data-ne-logo-swap] is set.
     Without the attribute, no hiding occurs, so fallback always shows the single logo. */
  .ne-header__logo-img--inverse {
    display: none;
  }
  [data-ne-mode="alternate"] [data-ne-logo-swap] .ne-header__logo-img--standard {
    display: none;
  }
  [data-ne-mode="alternate"] [data-ne-logo-swap] .ne-header__logo-img--inverse {
    display: block;
  }

  .ne-header__shop-name {
    font-size: var(--ne-font-size-xl);
    font-weight: 700;
    letter-spacing: -0.02em;
  }

  /* ==========================================================================
     HEADER - NAVIGATION
     ========================================================================== */

  /* Search icon "next to logo" variant: sits in the logo's grid column,
     justified toward the nav so it reads as a sibling of the logo. Works
     cleanly with logo-left; for logo-center/right the icon hugs the column
     edge closest to the nav. */
  /* Search-next-to-logo: the icon is rendered INSIDE .ne-header__logo as a flex
     sibling of the <a>. The parent's gap + align-items handle spacing. No grid
     positioning needed — the search inherits the logo's grid cell. For
     logo-right, the search should sit to the LEFT of the logo, so we flip it
     via flex order. */
  .ne-header__search-logoside {
    flex: 0 0 auto;
  }
  .ne-header--logo-right .ne-header__search-logoside {
    order: -1;
  }

  .ne-header__nav {
    display: flex;
    align-items: stretch;
    align-self: stretch;
    /* Nav fills the middle 1fr track; center items inside so the layout keeps
       its visually balanced look (items not bunched at the logo edge). */
    justify-content: center;
    gap: var(--ne-header-nav-gap, var(--ne-space-1));
    /* min-width: 0 allows the grid track (minmax(0, auto)) to shrink below
       min-content when logo/actions demand space. Overflow stays visible on
       both axes: dropdowns are absolute-positioned descendants of nav-items
       and MUST extend outside nav bounds (dropdown can be 240px+ wide, nav
       items can be 40px — horizontal clip would cut the dropdown).
       Items that don't fit are removed from layout via display:none by
       applyOverflowNav() using offsetWidth measurement — no visual clip
       needed because hidden items take zero space. */
    min-width: 0;
    overflow: visible;
    flex-wrap: nowrap;
  }

  /* Nav-items stretch to the full header height so `top: 100%` on their dropdown
     ALWAYS equals the actual header bottom, even when the logo forces the header
     taller than --ne-header-height. The nav-link inside stays visually centered
     via `display: flex; align-items: center`. Fixes dropdown overlap with header
     when logo overflows the configured height var. */
  .ne-header__nav-item {
    position: relative;
    align-self: stretch;
    display: flex;
    align-items: center;
  }

  /* Overflow "More" dropdown: each entry is a clone of a full nav-item so that
     megamenu / submenu items keep their panels. Inside the More dropdown (a
     vertical stack), render each clone as a block-width row with a standard
     dropdown-link look. Megamenu panels stay viewport-fixed; nested dropdowns
     cascade to the right like regular submenus. */
  .ne-header__nav-item--more-child {
    display: block;
    width: 100%;
    align-self: auto;
  }
  .ne-header__nav-item--more-child > .ne-header__nav-link {
    display: flex;
    width: 100%;
    padding: var(--ne-space-2) var(--ne-space-3);
    border-radius: var(--ne-radius-sm);
    justify-content: space-between;
    color: var(--ne-scheme-text);
  }
  .ne-header__nav-item--more-child > .ne-header__nav-link:hover {
    background: var(--ne-scheme-accent-glow, rgba(255,255,255,0.05));
    color: var(--ne-scheme-accent);
  }
  /* Cascading submenus from a More child open to the right, not below.
     transform: none resets the base dropdown's translateX(-50%) centring, which
     would otherwise pull the left:100% flyout back over the More panel. */
  .ne-header__nav-item--more-child > .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    top: 0;
    left: 100%;
    margin-top: 0;
    margin-left: var(--ne-space-2, 8px);
    transform: none !important;
  }
  /* Viewport-aware More-child flyout (JS toggles these on hover via
     initMoreFlyoutFlip). Default = right + down; flip left when the right edge
     would overflow, anchor the bottom (open up) when the list would clip the
     viewport bottom, and cap height + scroll when it fits neither way. (0,4,0)
     beats the default (0,3,0) above; no !important needed (transform stays). */
  .ne-header__nav-item--more-child.is-flyout-left > .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    left: auto;
    right: 100%;
    margin-left: 0;
    /* right:100% lands the flyout's right edge at the item's left edge, which is
       inset into the More panel by its padding (--ne-space-3) → the flyout
       overlapped the panel bg (user 2026-06-23). Clear the panel padding + the
       normal gap so it sits fully outside the panel. */
    margin-right: calc(var(--ne-space-3, 12px) + var(--ne-space-2, 8px));
  }
  .ne-header__nav-item--more-child.is-flyout-up > .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    top: auto;
    bottom: 0;
  }
  .ne-header__nav-item--more-child.is-flyout-cap > .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    max-height: var(--ne-more-flyout-maxh, 70vh);
    overflow-y: auto;
    overflow-x: hidden;
  }
  /* Horizontal hover-bridge for the side flyout — the base ::before bridges the
     TOP gap (for dropdowns that open below), but this flyout opens to the SIDE
     across an 8px margin gap, leaving a dead zone the cursor falls through
     before reaching it (user: "bridge zu knapp"). Span the side gap instead.
     `.ne-header` prefix → (0,4,1) so it wins over the open-up ::before override
     (0,2,1), which would otherwise re-anchor this bridge to the top/bottom. */
  .ne-header .ne-header__nav-item--more-child > .ne-header__dropdown:not(.ne-header__dropdown--mega)::before {
    top: 0;
    bottom: 0;
    left: auto;
    right: 100%;
    width: calc(var(--ne-space-2, 8px) + 8px);
    height: auto;
  }
  .ne-header .ne-header__nav-item--more-child.is-flyout-left > .ne-header__dropdown:not(.ne-header__dropdown--mega)::before {
    right: auto;
    left: 100%;
  }

  /* Bridge the gap between nav-link and dropdown so hover is not lost.
     Extends below nav-item to cover the dropdown's margin-top gap.
     No z-index — purely pointer-event catchment, no visual layer needed. */
  .ne-header__nav-item--has-dropdown::after {
    content: '';
    position: absolute;
    top: 100%;
    left: -8px;
    right: -8px;
    height: calc(var(--ne-space-1) + 8px);
  }

  .ne-header__nav-link {
    display: flex;
    align-items: center;
    gap: var(--ne-space-1);
    /* Match action-icon box dimensions so nav-links and action-icons look
       identical when no button-style is selected (both plain rectangles). */
    height: var(--ne-action-size, 36px);
    padding: 0 var(--ne-space-3);
    border-radius: 0;
    font-size: var(--ne-header-nav--size, var(--ne-font-size-sm));
    font-family: var(--ne-header-nav--family, var(--font-body--family));
    font-weight: var(--ne-header-nav--weight, 500);
    color: var(--ne-scheme-text-muted, var(--ne-scheme-text-muted));
    transition: all var(--ne-transition-fast);
    text-decoration: none;
    white-space: nowrap;
  }

  .ne-header__nav-link:hover {
    color: var(--ne-scheme-text, var(--ne-scheme-text));
    /* No hover background here — a hover "box" only appears in a shape context
       (persistent button style, or Hover style). The shape rules below add the
       background; "None / None" hover stays clean (colour / underline / bold). */
  }

  /* Nav TEXT-links read the SAME --ne-hbtn-* colour vars as action-icons so
     the "Colour align" toggle + custom button scheme + hover colour role
     reach plain text-links too. Previously only buttons + icons read them →
     nav vs actions could diverge, and a custom button scheme had NO visible
     effect in text-link mode (Bug A + B). Specificity (0,2,0)/(0,3,0) beats
     critical.css `a:not(.ne-btn)` (0,1,1) and the rules above. Off-state
     fallback = --ne-scheme-link / --ne-scheme-text == the pre-change rendered
     colours, so presets with neither toggle on stay pixel-identical. Placed
     BEFORE the --btn-* shape rules → styled buttons keep their own fallback. */
  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link {
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-link, var(--ne-scheme-accent)));
  }
  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link:hover {
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-text));
  }

  /* === HOVER TREATMENT — only active when Button style = "None" ===
     menu.liquid's sync script adds .ne-header--hover-style-{rounded|pill|square}
     / --hover-underline / --hover-bold ONLY when nav_button_style == 'none'
     (mirrors the settings' visible_if). A button-shaped background appears on
     hover even though the resting state is plain text/icon. The hover bg uses
     the same chain as styled-button hover so it reads like a real button.
     Applies to nav-links AND action-icons for a consistent hover look. */
  .ne-header--hover-style-rounded .ne-header__nav-link:hover,
  .ne-header--hover-style-pill .ne-header__nav-link:hover,
  .ne-header--hover-style-square .ne-header__nav-link:hover {
    background: var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-action-bg-hover)));
  }
  .ne-header--hover-style-rounded .ne-header__nav-link:hover { border-radius: var(--ne-btn-radius, var(--ne-radius-md, 8px)); }
  .ne-header--hover-style-pill .ne-header__nav-link:hover { border-radius: var(--ne-radius-full, 9999px); }
  .ne-header--hover-style-square .ne-header__nav-link:hover { border-radius: 0; }

  /* Action-icon radius is set !important from --ne-action-radius, so the
     hover override needs !important too. Background already flips on hover
     via the universal action:hover rule. */
  .ne-header--hover-style-rounded .ne-header__action:hover { border-radius: var(--ne-btn-radius, var(--ne-radius-md, 8px)) !important; }
  .ne-header--hover-style-pill .ne-header__action:hover { border-radius: var(--ne-radius-full, 9999px) !important; }
  .ne-header--hover-style-square .ne-header__action:hover { border-radius: 0 !important; }

  /* Faux-bold on hover (nav-links). -webkit-text-stroke fakes the weight
     WITHOUT reflowing the nav row — a real font-weight:700 would widen the
     label and shift its siblings. Underline is handled by the menu block's
     nav_underline_effect (.ne-header--ul-*). */
  .ne-header--hover-bold .ne-header__nav-link:hover {
    -webkit-text-stroke: 0.4px currentColor;
  }

  /* Action-icon bold equivalent — icons have no text, so map bold to a
     thicker SVG stroke (all header icons are stroke-2 outlines). Underline
     is handled by the menu block's nav_underline_effect (.ne-header--ul-*). */
  .ne-header--hover-bold .ne-header__action:hover svg {
    stroke-width: 2.6;
    transition: stroke-width 150ms ease;
  }

  /* Nav-link BUTTONS hover: use scheme's button-bg/text-hover so the
     header button reads like a regular .ne-btn--primary on hover —
     consistent with the rest of the theme. Sources mirror Color
     Schemes → Button background hover + Button label hover. Compound
     selector ensures specificity (0,2,0):hover = (0,3,0) beats
     `a:not(.ne-btn)` (0,1,1) AND the previous nav-link:hover rule. */
  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link--btn:hover {
    color: var(--ne-hbtn-hover-text, var(--ne-scheme-btn-text-hover, var(--ne-scheme-btn-text, #fff))) !important;
    /* Symmetric with action-icon hover chain — --ne-scheme-btn-bg-hover is
       always a sensible color (explicit setting OR color_lighten(button, 10%)
       fallback from css-variables.liquid), so no need for CSS-side color-mix. */
    background: var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent))) !important;
    border-color: var(--ne-hbtn-hover-border, var(--ne-scheme-btn-border-hover, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-accent)))) !important;
  }

  .ne-header__nav-chevron {
    transition: transform var(--ne-transition-fast);
    opacity: 0.5;
  }

  .ne-header__nav-item:hover .ne-header__nav-chevron {
    transform: rotate(180deg);
    opacity: 1;
  }

  /* Button style for transparent header nav items */
  .ne-header__nav-link--btn {
    border: 1px solid transparent;
    height: var(--ne-header-btn-height, 36px);
    padding-left: var(--ne-header-btn-padding-x, 14px);
    padding-right: var(--ne-header-btn-padding-x, 14px);
    display: inline-flex;
    align-items: center;
  }

  /* Nav-link buttons: use scheme.button_label for plain text (consistent
     with the rest of theme's button text). Ancestor list via :is()
     covers everywhere these classes appear:
       • `.ne-header` — top-level nav-link buttons
       • `.ne-header__btn-menu` — dropdown button-menu items
       • `.ne-mobile-menu` — burger drawer items (body-mounted, outside header)
     Specificity (0,1,0) from :is() + (0,1,0) from class = (0,2,0),
     beats critical.css `a:not(.ne-btn)` (0,1,1). User report 2026-05-23:
     dropdown + mobile buttons lost styling — they don't share the
     .ne-header ancestor in the DOM. */
  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link--btn-rounded {
    border-radius: var(--ne-btn-radius, var(--ne-radius-md));
    border-color: var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255, 255, 255, 0)));
    background: var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08))));
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-text)));
  }

  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link--btn-pill {
    border-radius: var(--ne-radius-full);
    border-color: var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255, 255, 255, 0)));
    background: var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08))));
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-text)));
  }

  :is(.ne-header, .ne-header__btn-menu, .ne-mobile-menu) .ne-header__nav-link--btn-square {
    border-radius: 0;
    border-color: var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255, 255, 255, 0)));
    background: var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08))));
    color: var(--ne-hbtn-plain-text, var(--ne-scheme-btn-text, var(--ne-scheme-text)));
  }

  /* ==========================================================================
     NAV BUTTON — 3D DEPTH EFFECT
     ========================================================================== */

  /* 3D depth effect — unmistakable "raised pill" look. Hard bottom shadow + accent
     glow + translateY establishes depth; hover lifts higher, active presses down.
     The 3D shadows are COMPOSED with --ne-action-shadow (the user's chosen
     glow / shadow effect) so 3D + glow co-exist instead of one killing the
     other (see: user-reported "glow disappears with 3D"). When no btn-effect
     is active, --ne-action-shadow = 0 0 0 transparent (invisible).

     Drawer elements (`.ne-mobile-menu .X`, `.ne-mobile-menu__footer-link`,
     drawer theme-toggle) join via `html:has(...)` so 3D applies consistently
     across header AND drawer when the merchant picked nav-btn-3d. */
  /* 3D plain — mirrors the button-block fx-lift + fx-glow combo
     (blocks/button.liquid:260 + :248). Pre-lifted baseline so hover
     can pop further: translateY(-4px) + 0 0 14px accent-glow@0.5.
     --ne-action-shadow composed in so user's header shadow/glow effect
     coexists with the 3D treatment. */
  .ne-header.ne-header--nav-btn-3d .ne-header__nav-link--btn,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__action,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__mobile-toggle,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-dark-light-toggle--header,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu .ne-header__action,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating {
    box-shadow:
      0 0 14px var(--ne-scheme-accent-glow, rgba(120,120,255,0.5)),
      var(--ne-action-shadow) !important;
    /* Plain position = 0 (same as 3D-off). The earlier translateY(-4px)
       pre-lift shifted ALL buttons up at rest, not just on hover (user
       2026-05-24). 3D depth now comes from the shadow + the click press. */
    transform: translateY(0) !important;
    transition: box-shadow 0.15s ease, transform 0.12s ease !important;
  }

  .ne-header.ne-header--nav-btn-3d .ne-header__nav-link--btn:hover,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__action:hover,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__mobile-toggle:hover,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-dark-light-toggle--header:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu .ne-header__action:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating:hover {
    /* Glow intensifies on hover, but the button KEEPS its plain
       translateY(-4px) position — the extra -7px lift shifted the buttons
       up into the row above on hover (user 2026-05-24). Differentiation is
       now carried by the brighter glow alone, no positional jump. */
    box-shadow:
      0 0 22px var(--ne-scheme-accent-glow, rgba(120,120,255,0.7)),
      var(--ne-action-shadow-hover) !important;
    /* Lift UP on hover (like the button-block fx-lift) — plain stays at 0,
       hover rises, click presses down (user 2026-05-24). */
    transform: translateY(-4px) !important;
  }

  /* Active state — press-down feedback: the button dips into the surface on
     click (translateY +2px below the 0 baseline) + the shadow tightens.
     This is the click animation the merchant liked (user 2026-05-24). */
  .ne-header.ne-header--nav-btn-3d .ne-header__nav-link--btn:active,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__action:active,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-header__mobile-toggle:active,
  .ne-header--actions-shape.ne-header--nav-btn-3d .ne-dark-light-toggle--header:active,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu .ne-header__action:active,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link:active,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-3d) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating:active {
    box-shadow: var(--ne-action-shadow) !important;
    transform: translateY(2px) !important;
  }

  /* ==========================================================================
     NAV BUTTON — GLASS EFFECT
     ========================================================================== */

  /* Glass effect on buttons: keep the user's nav-btn-bg token as base (same
     as nav-link buttons) so nav + actions read as one set under glass. The
     backdrop-filter rule below adds the blur on top, identical to nav.
     Previously --ne-action-bg pointed at --ne-glass-bg, which is a different
     theme token and made action pills visibly lighter/darker than nav. */
  .ne-header--actions-shape.ne-header--nav-btn-glass {
    --ne-action-bg: var(--ne-scheme-btn-bg, var(--ne-glass-bg, rgba(255,255,255,0.08)));
    --ne-action-border-color: var(--ne-glass-border, rgba(255, 255, 255, 0));
    --ne-action-border-width: 1px;
  }

  /* Glass-button look — unified rule: ALWAYS shows the inner-gradient
     highlight + border + inset shadows (visible look regardless of whether
     header glassmorphism is on). Plus backdrop-filter as an additional
     layer that adds real blur when the underlying compositing pipeline
     permits (no-op when an ancestor has backdrop-filter active per Rule
     49, no-op when header bg is fully opaque — but no visual harm in
     either case). Previously the gradient fallback only kicked in with
     header.glass and the plain backdrop-filter alone left buttons looking
     identical to the header bg (= invisible effect) when no header glass.
     Drawer items via html:has() so glass styling reaches all element
     types in the body-mounted drawer. */
  .ne-header.ne-header--nav-btn-glass .ne-header__nav-link--btn,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__action,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__mobile-toggle,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-dark-light-toggle--header,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu .ne-header__action,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, rgba(255,255,255,0.06))) !important;
    /* Aligned to the normal-button glass recipe (critical.css glass-buttons):
       overlay blend + 0.02 gradient bottom + --ne-glass-border so header nav
       buttons read identically to .ne-btn glass (user 2026-05-24). */
    background-blend-mode: overlay;
    border-color: var(--ne-hbtn-plain-border, var(--ne-glass-border, rgba(255,255,255,0.12))) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.4);
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 12px)) saturate(1.4);
    /* Compose inset glass highlights with the user's --ne-action-shadow so
       glow / shadow co-exists with the glass treatment. */
    box-shadow:
      inset 0 1px 0 rgba(255,255,255,0.35),
      inset 0 -1px 0 rgba(0,0,0,0.12),
      var(--ne-action-shadow) !important;
  }

  /* When header itself has backdrop-filter (.ne-glass), descendant
     backdrop-filter is neutered (Rule 49). Suppress to avoid wasted GPU
     work — the gradient + border + inset highlights from the rule above
     already carry the "glass" look. */
  .ne-header.ne-glass.ne-header--nav-btn-glass .ne-header__nav-link--btn,
  .ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__action,
  .ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__mobile-toggle,
  .ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass .ne-dark-light-toggle--header,
  html:has(.ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu .ne-header__action,
  html:has(.ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link,
  html:has(.ne-header.ne-glass.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Glass-button HOVER — keeps the same glass character as plain (inset
     highlights are inherited from the plain rule's box-shadow, which is
     NOT overridden here; gradient overlay matches plain's 0.18→0.04 alpha
     so the glass identity carries through). Only the underlying bg color
     changes to scheme.button_bg_hover for the visible plain → hover
     differential. Previously the gradient was bumped to 0.38→0.14 alpha
     which bleached out light-scheme button_bg_hover colors. */
  .ne-header.ne-header--nav-btn-glass .ne-header__nav-link--btn:hover,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__action:hover,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-header__mobile-toggle:hover,
  .ne-header--actions-shape.ne-header--nav-btn-glass .ne-dark-light-toggle--header:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu .ne-header__action:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__footer-link:hover,
  html:has(.ne-header.ne-header--actions-shape.ne-header--nav-btn-glass) .ne-mobile-menu__footer-row--compact .ne-mobile-menu__theme-toggle .ne-dark-light-toggle--floating:hover {
    background:
      linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-btn-bg, var(--ne-action-bg-hover)))) !important;
    /* Match the plain glass recipe (overlay blend + glass-border). */
    background-blend-mode: overlay;
    border-color: var(--ne-hbtn-hover-border, var(--ne-scheme-btn-border-hover, var(--ne-scheme-btn-border, var(--ne-glass-border, rgba(255,255,255,0.12))))) !important;
  }

  /* Spine pills (vertical): the 180deg gloss stretches over the pill's LONG
     axis (~3× a normal button height), diluting the capsule highlight until
     the pill reads flatter than e.g. the add-to-cart button (user QA
     2026-07-23). Run gradient + bevel across the SHORT axis instead (light
     from the left, standard top-left convention) so the gloss falloff
     distance matches a horizontal button. Dropdown-/More-panel clones read
     horizontally and are reset to transparent — the :not() guards keep them
     out (and lift specificity above the base+hover glass rules, hence the
     dedicated :hover twin below for the hover bg-color swap). */
  .ne-header--sb-spine.ne-header--nav-btn-glass .ne-header__nav-link--btn:not(.ne-header__dropdown .ne-header__nav-link):not(.ne-header__btn-menu .ne-header__nav-link) {
    background:
      linear-gradient(90deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-plain-bg, var(--ne-scheme-btn-bg, rgba(255,255,255,0.06))) !important;
    box-shadow:
      inset 1px 0 0 rgba(255,255,255,0.35),
      inset -1px 0 0 rgba(0,0,0,0.12),
      var(--ne-action-shadow) !important;
  }
  .ne-header--sb-spine.ne-header--nav-btn-glass .ne-header__nav-link--btn:hover:not(.ne-header__dropdown .ne-header__nav-link):not(.ne-header__btn-menu .ne-header__nav-link) {
    background:
      linear-gradient(90deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.02) 100%),
      var(--ne-hbtn-hover-bg, var(--ne-scheme-btn-bg-hover, var(--ne-scheme-btn-bg, var(--ne-action-bg-hover)))) !important;
  }

  /* Sidebar: override grid, use column flex instead */
  .ne-header--left,
  .ne-header--right {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
  }

  .ne-header--left .ne-header__logo,
  .ne-header--right .ne-header__logo,
  .ne-header--left .ne-header__nav,
  .ne-header--right .ne-header__nav,
  .ne-header--left .ne-header__actions,
  .ne-header--right .ne-header__actions {
    grid-column: unset;
    justify-self: unset;
    width: 100%;
  }

  /* Sidebar-mode logo:
     - Size is driven entirely by settings.logo_width (inherited from base
       .ne-header__logo-img { width: var(--ne-header-logo-width, 120px) }).
     - height: auto preserves the natural aspect ratio.
     - NO max-height cap: the sidebar is full-viewport-height, so there's
       no height constraint (unlike top mode where header-height bounds the
       logo). Capping height here would break aspect ratio and produce
       horizontally-stretched logos at wide logo_width settings.
     - max-width: none overrides the base { max-width: 100% } which would
       otherwise clamp the logo to the (narrow) sidebar width and stall the
       publishSidebarWidth() growth loop. */
  .ne-header--left .ne-header__logo img,
  .ne-header--right .ne-header__logo img {
    height: auto;
    max-width: none;
  }

  /* Center the logo link and its img inside the sidebar column */
  .ne-header--left .ne-header__logo,
  .ne-header--right .ne-header__logo,
  .ne-header--left .ne-header__logo a,
  .ne-header--right .ne-header__logo a {
    display: flex;
    justify-content: center;
    align-items: center;
  }

  .ne-header--left .ne-header__nav,
  .ne-header--right .ne-header__nav {
    flex: 1;
    flex-direction: column;
    justify-content: center;
    align-items: stretch;
    align-self: stretch;
    padding: var(--ne-space-3) 0;
  }

  /* ============================================================
     Sidebar layout — 11-row CSS Grid with explicit zones. Each
     zone (top / middle / bottom) hosts up to 3 sub-rows so logo,
     nav and actions never collide. Actions takes the LEFTOVER
     zone (computed in Liquid: see sb_actions_zone). Replaces the
     fragile flex-order + margin-auto chains.

       row 1   = top-zone slot 1 (logo if top)
       row 2   = top-zone slot 2 (nav if top)
       row 3   = top-zone slot 3 (actions if top)
       row 4   = 1fr filler
       row 5   = middle-zone slot 1 (logo if center)
       row 6   = middle-zone slot 2 (nav if center)
       row 7   = middle-zone slot 3 (actions if center)
       row 8   = 1fr filler
       row 9   = bottom-zone slot 1 (logo if bottom)
       row 10  = bottom-zone slot 2 (nav if bottom)
       row 11  = bottom-zone slot 3 (actions if bottom)

     Grid wins over the earlier `display: flex` rules because
     setting display:grid on the same element supersedes them. */
  .ne-header--left,
  .ne-header--right {
    display: grid;
    grid-template-columns: 1fr;
    /* Centre the content column in the sidebar. The column can be wider than the
       padding-box (the logo/nav define a min-content wider than the 1fr share),
       so without this it packs left (flex-start) and everything sits a few px
       right of the true centre. `center` distributes the overflow symmetrically
       → logo, pills, More and action icons all share the header's centre line. */
    justify-content: center;
    justify-items: center;
    grid-template-rows:
      auto auto auto
      1fr
      auto auto auto
      1fr
      auto auto auto;
  }
  /* Logo position */
  .ne-header--left.ne-header--logo-left .ne-header__logo,
  .ne-header--right.ne-header--logo-left .ne-header__logo {
    grid-row: 1;
    order: unset;
    margin: 0;
  }
  .ne-header--left.ne-header--logo-center .ne-header__logo,
  .ne-header--right.ne-header--logo-center .ne-header__logo {
    grid-row: 5;
    order: unset;
    margin: 0;
  }
  .ne-header--left.ne-header--logo-right .ne-header__logo,
  .ne-header--right.ne-header--logo-right .ne-header__logo {
    grid-row: 9;
    order: unset;
    margin: 0;
  }
  /* Nav position */
  .ne-header--left.ne-header--sb-nav-top .ne-header__nav,
  .ne-header--right.ne-header--sb-nav-top .ne-header__nav {
    grid-row: 2;
    order: unset;
    flex: 0 0 auto;
    margin: 0;
  }
  .ne-header--left.ne-header--sb-nav-center .ne-header__nav,
  .ne-header--right.ne-header--sb-nav-center .ne-header__nav {
    grid-row: 6;
    order: unset;
    flex: 0 0 auto;
    margin: 0;
  }
  .ne-header--left.ne-header--sb-nav-bottom .ne-header__nav,
  .ne-header--right.ne-header--sb-nav-bottom .ne-header__nav {
    grid-row: 10;
    order: unset;
    flex: 0 0 auto;
    margin: 0;
  }
  /* Actions position — Liquid picks the LEFTOVER zone (logo + nav
     + actions occupy 3 distinct zones whenever possible; same-zone
     pairs push actions to the opposite end). Keep flex-direction:
     column from the existing sidebar rule (icons stacked vertically)
     and stretch (full column width) so the inner column-flex centers
     icons horizontally. */
  .ne-header--left.ne-header--sb-actions-top .ne-header__actions,
  .ne-header--right.ne-header--sb-actions-top .ne-header__actions {
    grid-row: 3;
    order: unset;
    margin-top: 0;
  }
  .ne-header--left.ne-header--sb-actions-center .ne-header__actions,
  .ne-header--right.ne-header--sb-actions-center .ne-header__actions {
    grid-row: 7;
    order: unset;
    margin-top: 0;
  }
  .ne-header--left.ne-header--sb-actions-bottom .ne-header__actions,
  .ne-header--right.ne-header--sb-actions-bottom .ne-header__actions {
    grid-row: 11;
    order: unset;
    margin-top: 0;
  }
  /* Special case: logo-bottom + nav-bottom — user wants logo BELOW
     nav (logo as the very last visible item before actions). Swap
     their default rows in this exact combo. */
  .ne-header--left.ne-header--logo-right.ne-header--sb-nav-bottom .ne-header__nav,
  .ne-header--right.ne-header--logo-right.ne-header--sb-nav-bottom .ne-header__nav {
    grid-row: 9;
  }
  .ne-header--left.ne-header--logo-right.ne-header--sb-nav-bottom .ne-header__logo,
  .ne-header--right.ne-header--logo-right.ne-header--sb-nav-bottom .ne-header__logo {
    grid-row: 10;
  }

  /* Stretch nav-items to the full sidebar content width so dropdown's 100%
     anchor always lands at the header's inner edge — regardless of pill/button
     padding, nav-button style, or header width. The pill itself stays at its
     intrinsic width via justify-content inside the stretched item.
     Result: dropdown offset from header's outer edge is always exactly
     --ne-header-padding-x + --ne-header-dropdown-gap → constant visual gap. */
  .ne-header--left .ne-header__nav-item,
  .ne-header--right .ne-header__nav-item {
    align-self: stretch;
    display: flex;
    align-items: center;
  }
  .ne-header--left .ne-header__nav-item { justify-content: flex-start; }
  .ne-header--right .ne-header__nav-item { justify-content: flex-end; }
  .ne-header--left .ne-header__nav--icon-mode .ne-header__nav-item,
  .ne-header--right .ne-header__nav--icon-mode .ne-header__nav-item {
    justify-content: center;
  }

  /* ==========================================================================
     HEADER - DROPDOWN / MEGA MENU
     ========================================================================== */

  /* Dropdown inherits header visual language: glass bg, header radius, scheme-reactive border.
     Base uses translucent bg + blur so the glassmorphism effect is always visible.
     When header has .ne-glass or is transparent, bg becomes even more translucent. */
  .ne-header__dropdown {
    position: absolute;
    /* Anchored to the nav LINK's bottom (50% of the full-height nav-item + half
       the link height) rather than the header edge, so the dropdown sits right
       under the point. The gap can go negative to overlap up into the header. */
    top: calc(50% + var(--ne-action-size, 36px) / 2 + var(--ne-header-dropdown-gap, 10px));
    left: 50%;
    transform: translateX(-50%);
    min-width: 240px;
    padding: var(--ne-space-3);
    border-radius: var(--ne-dropdown-radius, var(--ne-header-radius, var(--ne-radius-lg)));
    border: 0 solid var(--ne-glass-border, rgba(255, 255, 255, 0));
    /* Background + blur are applied on ::after pseudo, NOT the element itself.
       `backdrop-filter` on the dropdown would form a new backdrop-root, so any
       descendant (e.g. submenu) could only sample the dropdown's interior with
       its own backdrop-filter — the blur would look weak or identical to bg.
       Moving it to ::after confines the backdrop-root to the pseudo; the submenu
       (DOM child of dropdown, not of ::after) escapes that root and samples the
       body correctly. See CLAUDE.md rule 51. */
    background-color: transparent;
    /* No default box-shadow — effect is driven solely by header_effect (none/shadow/glow).
       Default shadow here leaked into "none" mode, making the dropdown always look shadowed
       regardless of the header's actual effect setting. */
    box-shadow: none;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    z-index: var(--ne-z-overlay);
    margin-top: 0;
  }
  /* Side-flyout panels (sidebar + top-merge): drop the panel's HORIZONTAL padding
     so the rows (width:100%) span the full panel width → their hover hitbox reaches
     the panel's border edge, killing the dead panel-padding zone the cursor fell
     through on the way to a side flyout (user 2026-07-08: hitbox not felt at the
     edge → oversized bridge + open/close jitter). This beats the sb-spine
     `width:100% !important` on the rows because it removes the padding instead of
     fighting the row width. The rows keep their OWN inline padding for text inset;
     the panel's ::after bg still fills the full border-box (inset:0). Vertical
     padding stays. Megamenus manage their own layout → excluded. */
  .ne-header--left .ne-header__dropdown:not(.ne-header__dropdown--mega),
  .ne-header--right .ne-header__dropdown:not(.ne-header__dropdown--mega),
  .ne-header--menu-merge .ne-header__dropdown:not(.ne-header__dropdown--mega) {
    padding-inline: 0;
  }

  /* Glass + bg pseudo — sits behind dropdown content (z:-1 within the dropdown's
     stacking context) so text/links render above it. Children default to z:auto
     = 0 > -1, so no need to lift them explicitly.
     Base bg = solid scheme background so "Glass OFF" is truly opaque.
     The glass + transparent-header overrides below use --ne-glass-bg for a
     translucent fill; the custom-scheme override wins over those via higher
     specificity (color-scheme-N is emitted on the dropdown when
     dropdown_color_scheme_mode == 'custom'). */
  .ne-header__dropdown::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    border-radius: inherit;
    background-color: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    pointer-events: none;
  }
  /* Invisible hover-bridge so the cursor can cross the gap from nav-item
     to dropdown without losing the hover state. Spans the full gap + a bit. */
  .ne-header__dropdown::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: calc(-1 * max(var(--ne-header-dropdown-gap, 10px) + 4px, 0px));
    height: max(var(--ne-header-dropdown-gap, 10px) + 4px, 0px);
    background: transparent;
    pointer-events: auto;
  }

  /* === Open-up flip ====================================================
     When the header sits low in the viewport (JS adds .ne-header--open-up —
     e.g. a section placed ABOVE it in the header group pushes it down until
     it scrolls up to pin, so there's no room below), regular dropdowns open
     UPWARD and the hover-bridge moves to the dropdown's lower edge. Flips
     back to open-down as the header rises to the top. (fade/scale are
     direction-agnostic; the optional slide just settles from above.) */
  .ne-header--open-up .ne-header__dropdown {
    top: auto;
    bottom: calc(50% + var(--ne-action-size, 36px) / 2 + var(--ne-header-dropdown-gap, 10px));
  }
  .ne-header--open-up .ne-header__dropdown::before {
    top: auto;
    bottom: calc(-1 * max(var(--ne-header-dropdown-gap, 10px) + 4px, 0px));
  }
  /* Radial dropdown direction is driven per-point by data-radial-vflip (see
     menu.liquid) so the open-up flip above doesn't apply — a point opens UP only
     when it's actually low in the viewport, never covering its own point. */

  /* === Dropdown soft edge ==============================================
     Fades the dropdown / megamenu background into the page at the far edge
     (bottom when open-down, top when the dropdown is flipped open-up) — the
     same idea as the header soft edge. Masks the ::after bg/glass layer ONLY
     (the links are children of the dropdown element, not of ::after, so the
     mask traps nothing — Rule 49). */
  .ne-header--dropdown-soft-edge .ne-header__dropdown::after,
  .ne-header--dropdown-soft-edge .ne-header__megamenu::after {
    /* Each edge fades by its own amount (--ne-dse-top/right/bottom/left).
       mask-composite intersects the horizontal + vertical gradients so the
       four edges fade independently. Edges set to 0 stay solid, so the rounded
       corners there remain visible — the fade then follows the radius. */
    -webkit-mask-image:
      linear-gradient(to right, transparent 0, #000 var(--ne-dse-left, 0px), #000 calc(100% - var(--ne-dse-right, 0px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--ne-dse-top, 0px), #000 calc(100% - var(--ne-dse-bottom, 0px)), transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
      linear-gradient(to right, transparent 0, #000 var(--ne-dse-left, 0px), #000 calc(100% - var(--ne-dse-right, 0px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--ne-dse-top, 0px), #000 calc(100% - var(--ne-dse-bottom, 0px)), transparent 100%);
    mask-composite: intersect;
  }
  /* Flip top/bottom fade when the dropdown opens upward (header low / pinned to
     the bottom), so the far-edge fade stays on the outer side. Opt-in. */
  .ne-header--dropdown-soft-edge.ne-header--dropdown-se-flip.ne-header--open-up .ne-header__dropdown::after {
    -webkit-mask-image:
      linear-gradient(to right, transparent 0, #000 var(--ne-dse-left, 0px), #000 calc(100% - var(--ne-dse-right, 0px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--ne-dse-bottom, 0px), #000 calc(100% - var(--ne-dse-top, 0px)), transparent 100%);
    mask-image:
      linear-gradient(to right, transparent 0, #000 var(--ne-dse-left, 0px), #000 calc(100% - var(--ne-dse-right, 0px)), transparent 100%),
      linear-gradient(to bottom, transparent 0, #000 var(--ne-dse-bottom, 0px), #000 calc(100% - var(--ne-dse-top, 0px)), transparent 100%);
  }

  /* === Custom dropdown optics (inherit-optics-from-header OFF) ==========
     Decouples the dropdown / megamenu glass + transparency from the header.
     When the menu block's "inherit optics from header" toggle is off, the
     dropdown's own settings win (via !important) over the header-glass rules:
       • background transparency via opacity on the ::after bg layer
       • optional own glassmorphism with its own blur amount. */
  .ne-header--dropdown-custom-optics .ne-header__dropdown::after,
  .ne-header--dropdown-custom-optics .ne-header__megamenu::after {
    opacity: var(--ne-dropdown-bg-opacity, 1);
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
  }
  .ne-header--dropdown-custom-optics.ne-header--dropdown-glass .ne-header__dropdown::after,
  .ne-header--dropdown-custom-optics.ne-header--dropdown-glass .ne-header__megamenu::after {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.08)) !important;
    backdrop-filter: blur(var(--ne-dropdown-glass-blur, 16px)) saturate(1.4) !important;
    -webkit-backdrop-filter: blur(var(--ne-dropdown-glass-blur, 16px)) saturate(1.4) !important;
  }
  /* Accent-tinted glass bg. */
  .ne-header--dropdown-custom-optics.ne-header--dropdown-glass.ne-header--dropdown-glass-tint .ne-header__dropdown::after,
  .ne-header--dropdown-custom-optics.ne-header--dropdown-glass.ne-header--dropdown-glass-tint .ne-header__megamenu::after {
    background: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.08))) !important;
  }
  /* Custom dropdown effect (shadow / glow) on the dropdown element (reliable).
     Note: this is a box-shadow of the element box, so it does NOT fade with the
     soft edge (drop-shadow of the masked ::after did not render in this
     structure). Larger blur keeps the edge soft. */
  .ne-header--dropdown-effect-shadow .ne-header__dropdown,
  .ne-header--dropdown-effect-shadow .ne-header__megamenu {
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.32) !important;
  }
  .ne-header--dropdown-effect-glow .ne-header__dropdown,
  .ne-header--dropdown-effect-glow .ne-header__megamenu {
    box-shadow: 0 0 26px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.5)) !important;
  }

  /* Glass dropdown ONLY when the header glass effect is explicitly enabled.
     (Until 2026-05-27 this also fired for transparent-on-top headers, which
     forced a frosted dropdown even with glass off — and even after the header
     scrolled into its solid state, because the old selector lacked the
     :not(.ne-header--scrolled) guard. Now: glass-on → glass dropdown;
     transparent-on-top (glass off) → transparent dropdown, see below;
     everything else → solid scheme bg (base rule).)
     Applied on ::after for the same backdrop-root isolation reason as base. */
  .ne-header.ne-glass .ne-header__dropdown::after,
  .ne-header.ne-glass .ne-header__megamenu::after {
    background-color: var(--ne-glass-bg, rgba(255,255,255,0.08));
    backdrop-filter: blur(calc(var(--ne-glass-blur, 18px) * 1.4)) saturate(1.8);
    -webkit-backdrop-filter: blur(calc(var(--ne-glass-blur, 18px) * 1.4)) saturate(1.8);
  }

  /* Transparent-on-top header (glass OFF) → fully transparent dropdown so it
     matches the see-through header floating over the hero. Scoped to
     :not(.ne-header--scrolled) → once the header scrolls into its solid state,
     the dropdown falls back to the solid scheme bg (base rule). :not(.ne-glass)
     yields to the explicit glass path above. Specificity (0,4,1) beats base
     (0,1,1). Readability over the hero depends on the image — by design. */
  .ne-header--initial-transparent:not(.ne-header--scrolled):not(.ne-glass) .ne-header__dropdown::after,
  .ne-header--initial-transparent:not(.ne-header--scrolled):not(.ne-glass) .ne-header__megamenu::after {
    background-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* "none" mode: instant — no transitions at all. Snap on open, snap on
     close. Useful for merchants who want the dropdown to behave like a
     classic menu with zero motion. */
  .ne-header--dropdown-none .ne-header__dropdown,
  .ne-header--dropdown-none .ne-header__megamenu {
    transition: none;
  }

  /* === Glass FADE — wrapper-snap + content-fade (Rules 218 + 236) ===
     Backdrop-filter recompute during opacity 0→1 produces "bloom-in"
     where saturation appears late. Trick: keep wrapper opacity binary
     (snap 0/1, no fade), animate only the children's opacity. Wrapper
     appears INSTANTLY at full glass strength → no bloom. Children
     cross-fade gives the visual "fade-in" character.

     Closed wrapper uses asymmetric opacity transition: snaps to 0 on
     close after the children fade out (delay = duration). Visibility
     follows the same delay so the wrapper stays in the DOM during
     close-fade. */
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__megamenu {
    transition:
      opacity 0s linear var(--ne-transition-base),
      visibility 0s linear var(--ne-transition-base);
  }
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transition:
      opacity 0s linear 0s,
      visibility 0s linear 0s;
  }
  /* Content children: invisible by default, fade in over duration when parent open */
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__megamenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__megamenu > * {
    opacity: 0;
    transition: opacity var(--ne-transition-base);
  }
  /* Children translate-in for visible fade motion. Mirror of the btn-menu
     children-only transform pattern: bump opacity duration + add translateY
     on DROPDOWN ITEMS (not wrapper), so the fade is clearly perceptible
     without forcing the wrapper to opacity-fade (which would bloom-in the
     glass backdrop). Applies regardless of glass/transparent gating since
     the children-only transform doesn't risk backdrop recompute. */
  .ne-header--dropdown-fade .ne-header__dropdown > *,
  .ne-header--dropdown-fade .ne-header__megamenu > * {
    /* opacity:0 base is UNCONDITIONAL (not gated to glass/transparent) so
       the panel children actually fade in plain mode too. Child opacity is
       bloom-safe (Rule 294) and plain headers carry no backdrop-filter, so
       there is zero bloom risk. Previously the opacity fade only existed in
       the .ne-glass / .ne-header--transparent rules above, leaving plain
       headers with the barely-perceptible translateY(6px) alone (user
       report 2026-05-24: plain panel fade not visible). */
    opacity: 0;
    transform: translateY(6px);
    transition:
      opacity 320ms ease-out,
      transform 320ms cubic-bezier(0.2, 0.7, 0.3, 1) !important;
  }
  .ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown > *,
  .ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown > *,
  .ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown > *,
  .ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu > *,
  .ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu > *,
  .ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu > * {
    opacity: 1;
    transform: translateY(0);
  }

  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu > *,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__dropdown > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__dropdown > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__dropdown > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:hover > .ne-header__megamenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item:focus-within > .ne-header__megamenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__nav-item.is-open > .ne-header__megamenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu > *,
  .ne-header--transparent.ne-header--dropdown-fade .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu > * {
    opacity: 1;
  }

  /* === Glass SLIDE / SCALE — asymmetric opacity-snap + transform animation ===
     OPEN:  opacity snaps 0→1 in 0s → backdrop full strength frame 1.
            Transform animates over duration so motion is preserved.
     CLOSE: opacity fades 1→0 over duration (graceful exit).
     Includes both top-level dropdowns (nav-item triggers) AND submenus
     (dropdown-item--has-submenu triggers) so submenu animations also
     snap visibility instantly on open instead of waiting for the
     closed-state delayed visibility transition. */
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__megamenu {
    transition:
      opacity var(--ne-transition-base),
      visibility 0s linear var(--ne-transition-base),
      transform var(--ne-transition-base);
  }
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__megamenu {
    transition:
      opacity var(--ne-transition-base),
      visibility 0s linear var(--ne-transition-base),
      transform var(--ne-transition-spring);
  }
  /* Slide open — top dropdown + megamenu + submenu all get snap-opacity */
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-slide .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transition:
      opacity 0s linear 0s,
      visibility 0s linear 0s,
      transform var(--ne-transition-base);
  }
  /* Scale open — same pattern with spring transition */
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__dropdown,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__megamenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  :is(.ne-header.ne-glass, .ne-header--dropdown-glass).ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__megamenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
  .ne-header--transparent.ne-header--dropdown-scale .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
    transition:
      opacity 0s linear 0s,
      visibility 0s linear 0s,
      transform var(--ne-transition-spring);
  }

  /* Custom dropdown scheme WINS over glass — user-selected scheme applied solid.
     Glass rule is `.ne-header.ne-glass .ne-header__dropdown::after` (0,3,1).
     We prefix `.ne-header` to reach (0,3,1) tied + source order wins, and
     add a dedicated `.ne-header.ne-glass` variant at (0,4,1) for belt-and-braces. */
  .ne-header .ne-header__dropdown[class*="color-scheme-"]::after,
  .ne-header .ne-header__megamenu[class*="color-scheme-"]::after,
  .ne-header.ne-glass .ne-header__dropdown[class*="color-scheme-"]::after,
  .ne-header.ne-glass .ne-header__megamenu[class*="color-scheme-"]::after,
  .ne-header--transparent .ne-header__dropdown[class*="color-scheme-"]::after,
  .ne-header--transparent .ne-header__megamenu[class*="color-scheme-"]::after {
    background-color: var(--ne-scheme-bg, rgba(20, 20, 30, 1));
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }

  /* Sidebar headers: dropdown appears beside the nav item, vertically centered.
     Correct sidebar gap formula is `space-2 + dropdown-gap`:
       - nav-item stretches to full sidebar inner width (see .ne-header--left
         .ne-header__nav-item rule), so its right edge lies at `space-2` from
         the sidebar's outer edge (`padding: ... var(--ne-space-2)` on sidebar).
       - adding `dropdown-gap` beyond that puts the dropdown `dropdown-gap` px
         outside the sidebar — mirroring the visible gap in the top header
         (where dropdown sits `dropdown-gap` below the header's bottom edge).
     Do NOT use --ne-header-edge-to-item here: that's a VERTICAL measure
     (item-bottom → header-bottom in the top header) and doesn't describe the
     horizontal item-right → sidebar-right distance (item is stretched). */
  /* Sidebar variants: megamenu reverts to position:absolute so top/left
     resolve relative to the nav-item (beside the trigger, vertically
     centered). Base uses fixed for top-header viewport centering. */
  .ne-header--left .ne-header__megamenu,
  .ne-header--right .ne-header__megamenu {
    position: absolute;
  }
  .ne-header--left .ne-header__dropdown,
  .ne-header--left .ne-header__megamenu {
    top: 50%;
    left: calc(100% + var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px));
    transform: translateY(-50%);
  }
  /* Sidebar variant: bridge spans space-2 + gap on the left edge */
  .ne-header--left .ne-header__dropdown::before,
  .ne-header--left .ne-header__megamenu::before {
    top: 0;
    bottom: 0;
    right: 100%;
    left: auto;
    height: auto;
    width: calc(var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px) + 4px);
  }
  .ne-header--left.ne-header--dropdown-slide .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-slide .ne-header__megamenu {
    transform: translateX(-18px) translateY(-50%);
  }
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--left.ne-header--dropdown-slide .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateX(0) translateY(-50%);
  }
  /* Sidebar-left + scale: preserve translateY(-50%) anchor; scale toward the attached edge */
  .ne-header--left.ne-header--dropdown-scale .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-scale .ne-header__megamenu {
    transform: translateY(-50%) scale(0.85);
    transform-origin: left center;
  }
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__dropdown,
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item:hover > .ne-header__megamenu,
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item:focus-within > .ne-header__megamenu,
  .ne-header--left.ne-header--dropdown-scale .ne-header__nav-item.is-open > .ne-header__megamenu {
    transform: translateY(-50%) scale(1);
  }

  .ne-header--right .ne-header__dropdown,
  .ne-header--right .ne-header__megamenu {
    top: 50%;
    left: auto;
    right: calc(100% + var(--ne-space-2, 8px) + var(--ne-header-dropdown-gap, 10px));
    transform: translateY(-50%);
  }

  /* Sidebar viewport-flip: when a nav-item is in the lower half of the viewport
     (JS adds .ne-header__nav-item--flip-v), anchor the dropdown by its bottom
     edge so it opens UPWARDS instead of clipping the viewport. When near the
     top (.--flip-top), anchor by top edge and open DOWNWARDS. */
  .ne-header--left .ne-header__nav-item--flip-top > .ne-header__dropdown,
  .ne-header--left .ne-header__nav-item--flip-top > .ne-header__megamenu,
  .ne-header--right .ne-header__nav-item--flip-top > .ne-header__dropdown,
  .ne-header--right .ne-header__nav-item--flip-top > .ne-header__megamenu {
    top: 0;
    bottom: auto;
    transform: none;
  }
/* END_SNIPPET:header-styles */

/* START_SNIPPET:image (INDEX:161) */
.image {
    display: block;
    position: relative;
    overflow: hidden;
    width: 100%;
    height: auto;
  }

  .image > img {
    width: 100%;
    height: auto;
  }
/* END_SNIPPET:image */

/* START_SNIPPET:kinetic-fx (INDEX:162) */
.ne-kt.ne-section { overflow: visible; }
  .ne-kt__wrap { width: 100%; }

  .ne-kt__headline {
    margin: 0;
    font-family: var(--ne-kt-family, inherit);
    font-weight: var(--ne-kt-weight, 700);
    font-style: var(--ne-kt-style, normal);
    font-size: var(--ne-kt-size, clamp(32px, 8vw, 88px));
    line-height: var(--ne-kt-lh, 1.05);
    letter-spacing: -0.01em;
    text-align: var(--ne-kt-align, left);
    text-wrap: balance;
  }
  .ne-kt--upper .ne-kt__headline { text-transform: uppercase; }

  /* Vertical headline — set by blocks/kinetic-heading when adv_rotate is exactly
     ±90°. Real writing-mode instead of transform:rotate() so the line wraps by the
     available HEIGHT (a rotated horizontal box wraps by width, then rotates). The
     box shrinks to the narrow column (max-content) so the advanced %-offset stops
     drifting outward as the window widens. */
  .ne-kt--vert { width: fit-content; max-width: 100%; }
  .ne-kt--vert .ne-kt__wrap { width: fit-content; }
  .ne-kt--vert .ne-kt__headline {
    text-orientation: mixed;
    max-height: 88vh;     /* wrap to a new column before overflowing the viewport height */
    text-wrap: initial;   /* 'balance' is meaningless for one vertical column */
  }
  .ne-kt--vert-cw .ne-kt__headline { writing-mode: vertical-rl; }  /* CW tilt, read top→bottom (matches rotate(90deg)) */
  .ne-kt--vert-ccw .ne-kt__headline { writing-mode: sideways-lr; } /* CCW tilt, read bottom→top (matches rotate(-90deg)) */

  .ne-kt__word {
    display: inline-block;
    /* Base state = fully visible. Reveal animations live on this OUTER span;
       hover effects live on the INNER .ne-kt__w so their transforms never fight
       the reveal. The hidden initial state is applied only under .ne-kt--anim
       (added by JS when motion is allowed), so no-JS / reduced-motion render a
       clean, static headline. */
  }
  .ne-kt__w { display: inline-block; }

  .ne-kt__sub {
    margin-top: clamp(16px, 2vw, 28px);
    max-width: 46rem;
    color: var(--ne-scheme-text-muted, currentColor);
    font-size: clamp(15px, 1.4vw, 18px);
    line-height: 1.6;
  }
  .ne-kt--center .ne-kt__sub,
  .ne-kt[style*="center"] .ne-kt__sub { margin-inline: auto; }
  .ne-kt__btn { margin-top: clamp(20px, 2.5vw, 32px); }
  .ne-kt__empty {
    color: var(--ne-scheme-text-muted, currentColor);
    font-style: italic;
  }

  /* ── Hover micro-interactions (desktop pointer) ───────────── */
  @media (hover: hover) and (prefers-reduced-motion: no-preference) {
    /* No letter-spacing transition: animating/transitioning letter-spacing changes
       a word's width → reflows the line (worse with text-wrap: balance) → jitter.
       The Tracking reveal uses the headline animation; the Tracking hover uses a
       reflow-free transform (scaleX) instead. */
    .ne-kt__w {
      transition: color 0.25s ease, transform 0.25s ease,
        text-shadow 0.25s ease, -webkit-text-stroke 0.2s ease;
    }
    .ne-kt--hover-accent .ne-kt__word:hover .ne-kt__w { color: var(--ne-scheme-accent, #6366f1); }
    .ne-kt--hover-glow .ne-kt__word:hover .ne-kt__w {
      color: var(--ne-scheme-accent, #6366f1);
      text-shadow: 0 0 18px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.6));
    }
    .ne-kt--hover-weight .ne-kt__word:hover .ne-kt__w {
      font-weight: 900;
      -webkit-text-stroke: 0.022em currentColor;
    }
    .ne-kt--hover-lift .ne-kt__word:hover .ne-kt__w { transform: translateY(-10px) scale(1.06); }
    .ne-kt--hover-tracking .ne-kt__word:hover .ne-kt__w { transform: scaleX(1.12); }
    .ne-kt--hover-skew .ne-kt__word:hover .ne-kt__w { transform: skewX(-14deg); }
    .ne-kt--hover-underline .ne-kt__w { position: relative; }
    .ne-kt--hover-underline .ne-kt__w::after {
      content: "";
      position: absolute;
      left: 0;
      right: 0;
      bottom: -0.08em;
      height: 2px;
      background: var(--ne-scheme-accent, #6366f1);
      transform: scaleX(0);
      transform-origin: left;
      transition: transform 0.28s ease;
    }
    .ne-kt--hover-underline .ne-kt__word:hover .ne-kt__w::after { transform: scaleX(1); }
  }

  /* Section-level effects */
  .ne-kt.ne-section--shadow {
    box-shadow: var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35));
  }
  .ne-kt.ne-section--glow {
    box-shadow: var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }
  .ne-kt.ne-section--shadow.ne-section--glow {
    box-shadow:
      var(--ne-section-shadow-effect, 0 12px 40px rgba(0, 0, 0, 0.35)),
      var(--ne-section-glow-shadow, 0 0 40px var(--ne-scheme-accent-glow, rgba(99, 102, 241, 0.3)));
  }

  /* ── Per-word reveal, triggered by JS on scroll-into-view ───── */
  .ne-kt--anim.ne-kt--rise .ne-kt__word,
  .ne-kt--anim.ne-kt--fade .ne-kt__word { opacity: 0; }
  .ne-kt--anim.ne-kt--rise .ne-kt__word { transform: translateY(0.5em); filter: blur(5px); }
  .ne-kt--anim.ne-kt--tracking .ne-kt__headline { opacity: 0; }

  .ne-kt--anim.ne-kt--in.ne-kt--rise .ne-kt__word {
    animation: ne-kt-rise 0.6s cubic-bezier(0.2, 0.7, 0.2, 1) both;
    animation-delay: calc(var(--i, 0) * var(--ne-kt-step, 6) * 14ms);
  }
  .ne-kt--anim.ne-kt--in.ne-kt--fade .ne-kt__word {
    animation: ne-kt-fade 0.5s ease both;
    animation-delay: calc(var(--i, 0) * var(--ne-kt-step, 6) * 14ms);
  }
  .ne-kt--anim.ne-kt--in.ne-kt--tracking .ne-kt__headline {
    animation: ne-kt-track 0.7s ease both;
  }

  @keyframes ne-kt-rise {
    from { opacity: 0; transform: translateY(0.5em); filter: blur(5px); }
    to   { opacity: 1; transform: none; filter: blur(0); }
  }
  @keyframes ne-kt-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  @keyframes ne-kt-track {
    from { letter-spacing: -0.06em; opacity: 0.35; }
    to   { letter-spacing: 0.03em; opacity: 1; }
  }
/* END_SNIPPET:kinetic-fx */

/* START_SNIPPET:menu-styles (INDEX:163) */
/* ============================================================
   Nav redesign — arrangements + interactions (Phase 0+ home)
   Lives in the menu BLOCK (not header-styles.liquid) to stay
   clear of that file's 256KB ceiling. Stylesheet tags bundle
   into compiled_assets/styles.css identically (Rule 311).
   All rules below are gated by .ne-header--nav-arrange-* /
   --nav-* modifier classes so defaults change nothing.
   ============================================================ */

/* --- Focus dimming -------------------------------------------------
   Hovering/focusing one nav item fades the others, concentrating
   attention on the active point. Pure CSS, works in any arrangement. */
.ne-header--nav-dim .ne-header__nav-item {
  transition: opacity var(--ne-transition-fast, 0.18s) ease;
}
.ne-header--nav-dim .ne-header__nav:hover > .ne-header__nav-item:not(:hover),
.ne-header--nav-dim .ne-header__nav:focus-within > .ne-header__nav-item:not(:focus-within) {
  opacity: var(--ne-nav-dim-opacity, 0.4);
}
/* MERGE nav-dim fix — the dim above puts opacity on the nav-ITEM, but in merge the panel
   (megamenu/dropdown) is a CHILD of it. On a nav→nav SWITCH the outgoing (now-unhovered) item
   dims, and a child can't be more opaque than its dimmed parent → the still-closing panel goes
   semi-transparent (the weld brackets, a nav-item ::before, technically dim too but are tiny).
   A plain leave has no hovered item → no dim → the panel closes opaque (why leaving is clean).
   Fix: in merge DON'T dim the item — keep it opaque and dim only its LABEL children (link,
   chevron, …), leaving the panel AND brackets fully opaque so a switch-close matches a plain
   leave. The label-dim effect is unchanged. Scoped to .ne-header--menu-merge (+0,1,0 over the
   base rule → wins) so non-merge headers keep the original item-level dim. (user 2026-07-07) */
.ne-header--nav-dim.ne-header--menu-merge .ne-header__nav-item > *:not(.ne-header__megamenu):not(.ne-header__dropdown) {
  transition: opacity var(--ne-transition-fast, 0.18s) ease;
}
.ne-header--nav-dim.ne-header--menu-merge .ne-header__nav:hover > .ne-header__nav-item:not(:hover),
.ne-header--nav-dim.ne-header--menu-merge .ne-header__nav:focus-within > .ne-header__nav-item:not(:focus-within) {
  opacity: 1;
}
.ne-header--nav-dim.ne-header--menu-merge .ne-header__nav:hover > .ne-header__nav-item:not(:hover) > *:not(.ne-header__megamenu):not(.ne-header__dropdown),
.ne-header--nav-dim.ne-header--menu-merge .ne-header__nav:focus-within > .ne-header__nav-item:not(:focus-within) > *:not(.ne-header__megamenu):not(.ne-header__dropdown) {
  opacity: var(--ne-nav-dim-opacity, 0.4);
}

/* --- Underline effects (nav_underline_effect: static | sweep | fade) ---
   ONE ::after bar under the nav-link text AND under each action icon, so
   links and icons animate identically. (The old version used a static
   text-decoration underline on links — at a different position than the
   animated bar — and reached only the icons.) Minimal style only
   (nav_button_style == none). transform-origin is overridden per arc side
   in the radial arrangement (Phase 3). */

/* Geometry — nav bar under the text span (matches the old underline
   position + width); action bar under the icon glyph (proven anchor). */
.ne-header--ul-static .ne-header__nav-link > span,
.ne-header--ul-sweep .ne-header__nav-link > span,
.ne-header--ul-fade .ne-header__nav-link > span {
  position: relative;
}
.ne-header--ul-static .ne-header__nav-link > span::after,
.ne-header--ul-sweep .ne-header__nav-link > span::after,
.ne-header--ul-fade .ne-header__nav-link > span::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -3px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  pointer-events: none;
}
/* Below + edge underline: drop the underline to the nav-link's bottom (the nav
   row sits at the header's bottom edge), tab-style, instead of just under the
   text. The calc moves it from the text baseline down to the link's edge. */
.ne-header--nav-arrange-below.ne-header--below-underline-edge .ne-header__nav-link > span::after {
  bottom: calc((1.4em - var(--ne-action-size, 36px)) / 2);
}
/* Equal-width nav buttons: every link takes the widest one's width (JS measures
   --ne-nav-btn-width), with the label centred for a uniform row. */
.ne-header--nav-equal-width .ne-header__nav-link {
  width: var(--ne-nav-btn-width, auto);
  justify-content: center;
}
/* …but a cloned nav-item INSIDE the More panel (has-dropdown clone) must FILL the panel
   row like the plain dropdown-links beside it — equal-width's --ne-nav-btn-width (130px)
   otherwise left its visible pill narrower than its full-width hitbox (user 2026-07-21).
   (0,3,0) beats the equal-width rule (0,2,0); space-between keeps the chevron on the edge. */
.ne-header--nav-equal-width .ne-header__nav-item--more-child > .ne-header__nav-link {
  width: 100%;
  justify-content: space-between;
}
/* On items WITH a dropdown the chevron sits to the right of the word; extend
   the bar under it (gap + 12px chevron) so the underline is centred on the
   word+chevron — matching the centred dropdown below it. */
.ne-header--ul-static .ne-header__nav-item--has-dropdown .ne-header__nav-link > span::after,
.ne-header--ul-sweep .ne-header__nav-item--has-dropdown .ne-header__nav-link > span::after,
.ne-header--ul-fade .ne-header__nav-item--has-dropdown .ne-header__nav-link > span::after {
  right: calc(-1 * (var(--ne-space-1, 4px) + 12px));
}
.ne-header--ul-static .ne-header__action,
.ne-header--ul-sweep .ne-header__action,
.ne-header--ul-fade .ne-header__action {
  position: relative;
}
.ne-header--ul-static .ne-header__action::after,
.ne-header--ul-sweep .ne-header__action::after,
.ne-header--ul-fade .ne-header__action::after {
  content: "";
  position: absolute;
  left: 16%;
  right: 16%;
  top: calc(50% + 14px);
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  pointer-events: none;
}

/* static — instant on/off */
.ne-header--ul-static .ne-header__nav-link > span::after,
.ne-header--ul-static .ne-header__action::after {
  opacity: 0;
}
.ne-header--ul-static .ne-header__nav-item:hover > .ne-header__nav-link > span::after,
.ne-header--ul-static .ne-header__nav-item:focus-within > .ne-header__nav-link > span::after,
.ne-header--ul-static .ne-header__action:hover::after,
.ne-header--ul-static .ne-header__action:focus-visible::after {
  opacity: 1;
}

/* fade — opacity transition */
.ne-header--ul-fade .ne-header__nav-link > span::after,
.ne-header--ul-fade .ne-header__action::after {
  opacity: 0;
  transition: opacity var(--ne-nav-sweep-dur, 0.28s) ease;
}
.ne-header--ul-fade .ne-header__nav-item:hover > .ne-header__nav-link > span::after,
.ne-header--ul-fade .ne-header__nav-item:focus-within > .ne-header__nav-link > span::after,
.ne-header--ul-fade .ne-header__action:hover::after,
.ne-header--ul-fade .ne-header__action:focus-visible::after {
  opacity: 1;
}

/* sweep — horizontal grow */
.ne-header--ul-sweep .ne-header__nav-link > span::after,
.ne-header--ul-sweep .ne-header__action::after {
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--ne-nav-sweep-dur, 0.28s) cubic-bezier(0.22, 1, 0.36, 1);
}
.ne-header--ul-sweep .ne-header__nav-item:hover > .ne-header__nav-link > span::after,
.ne-header--ul-sweep .ne-header__nav-item:focus-within > .ne-header__nav-link > span::after,
.ne-header--ul-sweep .ne-header__action:hover::after,
.ne-header--ul-sweep .ne-header__action:focus-visible::after {
  transform: scaleX(1);
}

/* --- Sidebar spine: rotate the nav-link underline 90° ---------------------
   The spine rotates the nav-link TEXT (writing-mode), so the physical `bottom`
   underline renders as a short horizontal bar at the foot of the vertical text
   instead of running alongside it. Redraw it as a VERTICAL bar down the block-end
   side of the text: right sidebar (vertical-rl) → block-end faces LEFT; left
   sidebar (sideways-lr) → faces RIGHT. Action icons are NOT rotated → their bar
   (::after on .ne-header__action) is left untouched. */
.ne-header--sb-spine .ne-header__nav-link > span::after {
  top: 0;
  bottom: -3px;      /* small tail past the text end (mirrors the horizontal -3px) */
  height: auto;
  width: 2px;
  left: auto;
  right: auto;
}
.ne-header--right.ne-header--sb-spine .ne-header__nav-link > span::after { left: -3px; }
.ne-header--left.ne-header--sb-spine .ne-header__nav-link > span::after { right: -3px; }
/* has-dropdown: the chevron sits along the block flow (past the word in the spine),
   so extend the bar's LENGTH (bottom) rather than its width; keep the correct side. */
.ne-header--right.ne-header--sb-spine .ne-header__nav-item--has-dropdown .ne-header__nav-link > span::after {
  left: -3px; right: auto;
  bottom: calc(-1 * (var(--ne-space-1, 4px) + 12px));
}
.ne-header--left.ne-header--sb-spine .ne-header__nav-item--has-dropdown .ne-header__nav-link > span::after {
  right: -3px; left: auto;
  /* sideways-lr reads bottom→top, so the chevron sits ABOVE the word (block-start). Extend the bar
     UPWARD (top) to cover word+chevron; the old `bottom` extension ran the wrong way → the bar
     overshot BELOW the word and never reached the chevron (user 2026-07-08). Keep the -3px bottom tail. */
  top: calc(-1 * (var(--ne-space-1, 4px) + 12px));
  bottom: -3px;
}
/* Panel content (More-child clones) lives INSIDE a panel with horizontal-tb text (writing-mode is
   reset there), yet the generic `.sb-spine .nav-link > span::after` rules above still matched their
   nav-link and drew the spine's VERTICAL side-bar beside the word (user: "Unterstrich sitzt seitlich
   am Punkt, nicht darunter", 2026-07-08). Restore the base HORIZONTAL underline for nested nav-links.
   0,4,0 (+ later source) beats the spine has-dropdown rule (0,4,0). Only More-child clones carry a
   nav-link inside a panel; plain dropdown links use .ne-header__dropdown-link → untouched. */
.ne-header--sb-spine :is(.ne-header__megamenu, .ne-header__dropdown) .ne-header__nav-item .ne-header__nav-link > span::after {
  top: auto;
  bottom: -3px;
  left: 0;
  right: 0;
  width: auto;
  height: 2px;
}
/* Sidebar-spine chevrons: the nav panels open to the SIDE (left→right, right→left) and the nav TEXT
   is rotated 90° (writing-mode), but the chevron is an inline SVG (replaced element) that writing-mode
   does NOT rotate — so it kept pointing DOWN. Rotate it to point TOWARD the opening panel; flip 180°
   on open (user 2026-07-08). Scoped to top-level items (More-child chevrons live in a horizontal panel
   and are handled by the normal flip). */
.ne-header--left.ne-header--sb-spine .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__nav-link .ne-header__nav-chevron {
  transform: rotate(-90deg);  /* points RIGHT → toward the panel */
}
.ne-header--right.ne-header--sb-spine .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__nav-link .ne-header__nav-chevron {
  transform: rotate(90deg);   /* points LEFT → toward the panel */
}
.ne-header--left.ne-header--sb-spine .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__nav-link .ne-header__nav-chevron {
  transform: rotate(90deg);   /* flip 180° from base on open */
}
.ne-header--right.ne-header--sb-spine .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__nav-link .ne-header__nav-chevron {
  transform: rotate(-90deg);
}
/* Sweep grows along the text length (scaleY), from the inline-start end:
   vertical-rl starts at the TOP, sideways-lr (left) reads bottom→top → grow from bottom. */
.ne-header--sb-spine.ne-header--ul-sweep .ne-header__nav-link > span::after {
  transform: scaleY(0);
}
.ne-header--right.ne-header--sb-spine.ne-header--ul-sweep .ne-header__nav-link > span::after { transform-origin: center top; }
.ne-header--left.ne-header--sb-spine.ne-header--ul-sweep .ne-header__nav-link > span::after { transform-origin: center bottom; }
.ne-header--sb-spine.ne-header--ul-sweep .ne-header__nav-item:hover > .ne-header__nav-link > span::after,
.ne-header--sb-spine.ne-header--ul-sweep .ne-header__nav-item:focus-within > .ne-header__nav-link > span::after {
  transform: scaleY(1);
}

/* --- Dropdown connector trace (nav_dropdown_trace) -----------------------
   ::before draws the dropdown-wide top edge (the underline, bound to the panel
   top); ::after runs down the alignment edge AFTER the sweep, forming an L.
   Sweep direction + vertical edge follow data-radial-side (set in radial). */
.ne-header__dropdown-trace { display: none; }
.ne-header--nav-dd-trace .ne-header__dropdown-trace {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 1;
  /* Corner radius = the panel's own radius (same fallback chain) so the L-join
     rounds to match the dropdown corner instead of overhanging it. When the
     panel radius is 0 the corner collapses to a sharp 90° (the original look).
     The rounded corner is drawn as a BORDER on ::after (GPU-anti-aliased, no
     gradient stair-stepping) and revealed with the vertical line — see below. */
  --ne-ddc-r: var(--ne-dropdown-radius, var(--ne-header-radius, var(--ne-radius-lg, 0px)));
}
/* The connector trace is a NARROW-dropdown element (links the nav item to the
   panel edge). When a plain dropdown is promoted to a full-width megamenu
   (synthesizeMegamenu moves ALL dropdown children, incl. this span, into the
   links slot), the trace would draw a stray L across the mega panel. Megamenus
   have their own hover-bridge (::before), so suppress the trace inside them.
   (0,3,0) beats the (0,2,0) display:block above. */
.ne-header--nav-dd-trace .ne-header__megamenu .ne-header__dropdown-trace {
  display: none;
}
.ne-header--nav-dd-trace .ne-header__dropdown-trace::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--ne-ddc-r);
  right: var(--ne-ddc-r);
  /* border-top (not a filled background bar) so the horizontal renders with the
     SAME anti-aliasing / visual weight as the bordered corner + vertical on
     ::after — otherwise the solid bar looks thicker than the AA'd border. */
  height: 0;
  border-top: 2px solid currentColor;
  transform: scaleX(0);
  transform-origin: center;
  /* Closed state carries the delay → on CLOSE the horizontal line retracts LAST
     (mirror of open, where it draws FIRST). Open state overrides to no delay. */
  transition: transform var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1) var(--ne-dropdown-speed, 280ms);
}
.ne-header--nav-dd-trace .ne-header__dropdown-trace::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: var(--ne-ddc-r);
  left: 0;
  width: calc(var(--ne-ddc-r) + 2px);
  /* Rounded corner + vertical drawn as ONE bordered L: border-left = the
     vertical spine, border-top = the short cap that meets the horizontal
     ::before, border-top-left-radius = the rounded corner (browser-smoothed,
     so no pixelation). Revealed top→bottom via clip-path so the corner draws
     in FIRST (right after the horizontal sweep), then the spine extends down —
     it's part of the stroke sequence, not a static overlay that lingers. */
  border: 0 solid currentColor;
  border-left-width: 2px;
  border-top-width: 2px;
  border-top-left-radius: var(--ne-ddc-r);
  clip-path: inset(0 0 100% 0);
  /* Closed state has NO delay → on CLOSE the corner+vertical retract FIRST. Open
     state overrides to add the delay (draws after the horizontal sweep). */
  transition: clip-path var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1);
}
/* Sweep direction + which edge the vertical runs down, per arc side. The corner
   follows the vertical: left-side items run the spine down the RIGHT edge, so
   flip the border to the top-RIGHT corner. Default + right-side keep top-left. */
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="left"] .ne-header__dropdown-trace::before { transform-origin: left center; }
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="left"] .ne-header__dropdown-trace::after {
  left: auto;
  right: 0;
  border-left-width: 0;
  border-right-width: 2px;
  border-top-left-radius: 0;
  border-top-right-radius: var(--ne-ddc-r);
}
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="right"] .ne-header__dropdown-trace::before { transform-origin: right center; }
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="right"] .ne-header__dropdown-trace::after { left: 0; right: auto; }
/* Centre point: the sweep grows symmetrically from the middle and the vertical
   line is cancelled — the fade is applied to both sides instead (coherent). */
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="center"] .ne-header__dropdown-trace::before { transform-origin: center; }
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="center"] .ne-header__dropdown-trace::after { display: none; }
/* Draw on open (hover / focus / is-open). CHILD combinator (> trace) is
   essential: a DESCENDANT combinator here matched the nested submenu's trace
   span too, so opening the PARENT dropdown drew the submenu's line to scaleX(1)
   prematurely → when the submenu later opened its line was already full and
   "appeared at once" instead of drawing (user 2026-06-24). With `>` this only
   draws the first-level trace (direct child of the dropdown); the submenu /
   more-child traces draw solely via their own open rules below. */
.ne-header--nav-dd-trace .ne-header__nav-item:hover > .ne-header__dropdown > .ne-header__dropdown-trace::before,
.ne-header--nav-dd-trace .ne-header__nav-item:focus-within > .ne-header__dropdown > .ne-header__dropdown-trace::before,
.ne-header--nav-dd-trace .ne-header__nav-item.is-open > .ne-header__dropdown > .ne-header__dropdown-trace::before {
  transform: scaleX(1);
  transition: transform var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1);
}
.ne-header--nav-dd-trace .ne-header__nav-item:hover > .ne-header__dropdown > .ne-header__dropdown-trace::after,
.ne-header--nav-dd-trace .ne-header__nav-item:focus-within > .ne-header__dropdown > .ne-header__dropdown-trace::after,
.ne-header--nav-dd-trace .ne-header__nav-item.is-open > .ne-header__dropdown > .ne-header__dropdown-trace::after {
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1) var(--ne-dropdown-speed, 280ms);
}
/* Submenu trace — same draw-on-open as the top-level panel, scoped to the
   submenu trigger (dropdown-item--has-submenu). The base trace styles
   (display/inset/::before/::after at the top of this block) already match the
   submenu's trace span via descendant selectors; only the open-state draw was
   missing, so submenus showed the panel but no connector line (user 2026-06-23).
   Works for nested submenus inside "More" too (the more-child clone carries the
   span). */
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::before,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::before,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::before {
  transform: scaleX(1);
  transition: transform var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1);
}
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::after,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::after,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu > .ne-header__dropdown-trace::after {
  clip-path: inset(0 0 0 0);
  transition: clip-path var(--ne-dropdown-speed, 280ms) cubic-bezier(0.22, 1, 0.36, 1) var(--ne-dropdown-speed, 280ms);
}
/* Trace-mode submenu coordination — mirror the top-level panel reveal so the
   submenu's BOX + LINE animate in sequence instead of the whole element
   opacity-fading as a block ("panel appears at once" while items staggered,
   user 2026-06-24). The container opens INSTANTLY (so the connector line draws
   inside it + holds still — the line is the motion, so suppress slide/scale),
   the box (::after) reveals AFTER the horizontal sweep, in sync with the
   vertical line + the already-staggered items (2431+). Scoped to nav-dd-trace +
   --submenu only → the non-trace submenu slide/scale/fade modes are untouched.
   These selectors never match top-level panels (submenu's parent is a
   dropdown-item, not a nav-item), so no conflict with the top-level block. */
.ne-header--nav-dd-trace .ne-header__dropdown--submenu {
  opacity: 1 !important;
  transform: none !important;
  visibility: hidden;
  transition: visibility 0s linear calc(var(--ne-dropdown-speed, 280ms) * 2);
}
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu {
  opacity: 1 !important;
  transform: none !important;
  visibility: visible;
  transition: visibility 0s 0s;
}
/* Box (::after) stays hidden for one duration — until the horizontal sweep
   completes — then fades in over the next, alongside the vertical line + items.
   visibility:hidden is essential (opacity:0 alone leaves a glass box faintly
   rendered). Mirror of the top-level ::after reveal (2355-2369). */
/* `.ne-header` prefix → (0,3,1) so the closed box BEATS the always-on
   custom-optics rule `.ne-header--dropdown-custom-optics .ne-header__dropdown
   ::after { opacity: var(--ne-dropdown-bg-opacity) }` (0,2,1, header-styles
   1979) — which the submenu IS and which otherwise kept the box at opacity 1
   when closed → it appeared instantly instead of revealing after the sweep
   (user 2026-06-24). Top-level wins this already via its (0,3,1) reveal rule. */
.ne-header.ne-header--nav-dd-trace .ne-header__dropdown--submenu::after {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu::after,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu::after,
.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu::after {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms),
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
/* Glass box: backdrop-filter can't fade with opacity — snap off promptly on
   close (mirror of 2374-2381). */
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__dropdown--submenu::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease, visibility 0s linear 0s;
}
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu::after,
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu::after,
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms), visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
/* Custom-optics: box fades to the configured background opacity (mirror 2386). */
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:hover > .ne-header__dropdown--submenu::after,
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu:focus-within > .ne-header__dropdown--submenu::after,
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__dropdown-item--has-submenu.is-open > .ne-header__dropdown--submenu::after {
  opacity: var(--ne-dropdown-bg-opacity, 1);
}

/* Trace-mode More-child flyout coordination. Overflowed nav items inside the
   "More" panel open their OWN dropdown to the side; these were excluded from
   the top-level coordination (:not(--more-child)) and aren't --submenu, so the
   container block-faded (opacity 0→1, masking the line) + the box (::after)
   stayed opacity 1 → line + panel "just appeared" while only the items
   staggered (user 2026-06-24, "More submenu erscheint wie zuvor"). Give them
   the same coordination as the regular submenu: container instant (opacity 1,
   visibility-based hide), box reveals after the horizontal sweep. The line
   already draws via the generic nav-item trace rule + the items already stagger
   (the more-child IS a nav-item). Mirrors the submenu block above. */
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child > .ne-header__dropdown {
  opacity: 1 !important;
  visibility: hidden;
  transition: visibility 0s linear calc(var(--ne-dropdown-speed, 280ms) * 2);
}
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child:hover > .ne-header__dropdown,
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child:focus-within > .ne-header__dropdown,
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child.is-open > .ne-header__dropdown {
  opacity: 1 !important;
  visibility: visible;
  transition: visibility 0s 0s;
}
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child > .ne-header__dropdown::after {
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child:hover > .ne-header__dropdown::after,
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child:focus-within > .ne-header__dropdown::after,
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item--more-child.is-open > .ne-header__dropdown::after {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms),
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item--more-child > .ne-header__dropdown::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease, visibility 0s linear 0s;
}
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item--more-child:hover > .ne-header__dropdown::after,
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item--more-child:focus-within > .ne-header__dropdown::after,
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item--more-child.is-open > .ne-header__dropdown::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms), visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__nav-item--more-child:hover > .ne-header__dropdown::after,
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__nav-item--more-child:focus-within > .ne-header__dropdown::after,
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__nav-item--more-child.is-open > .ne-header__dropdown::after {
  opacity: var(--ne-dropdown-bg-opacity, 1);
}
/* The panel-wide top edge replaces the short text underline on dropdown items. */
.ne-header--nav-dd-trace .ne-header__nav-item--has-dropdown > .ne-header__nav-link > span::after {
  opacity: 0;
}
/* Coordinate the panel reveal with the line: the container opens INSTANTLY (so
   the connector line can draw inside it), then the box background fades in AFTER
   the horizontal sweep — in sync with the vertical line + the staggered items.
   The container holds still (the line is the motion), so slide/scale is
   suppressed while the trace is on. */
/* All trace coordination is scoped to TOP-LEVEL nav-item dropdowns (incl. the
   More panel, which is a --more item) and explicitly NOT --more-child items —
   the overflowed items inside the More panel keep their normal flyout dropdowns.
   Submenus (dropdown-item > dropdown) are never nav-item children, so they're
   naturally excluded too. */
.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown { opacity: 1 !important; }
/* Container stays visible until the whole close mirror finishes (2× duration —
   box/items/vertical retract first, horizontal line last) before hiding, so the
   trailing horizontal retract isn't cut off. Higher specificity beats the fade
   rule's visibility transition. Open shows instantly (rule below). */
.ne-header.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown {
  visibility: hidden;
  transition: visibility 0s linear calc(var(--ne-dropdown-speed, 280ms) * 2);
}
.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__dropdown {
  visibility: visible;
  transition: visibility 0s 0s;
}
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="left"] > .ne-header__dropdown,
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="right"] > .ne-header__dropdown { transform: none !important; }
.ne-header--nav-dd-trace .ne-header__nav-item[data-radial-side="center"] > .ne-header__dropdown,
.ne-header--nav-dd-trace:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item:not([data-radial-side]):not(.ne-header__nav-item--more-child) > .ne-header__dropdown { transform: translateX(-50%) !important; }
/* Sidebar (non-radial): dropdown sits BESIDE the nav item (positioned via
   left/right + vertical-centre). The trace's translateX centring is top-header
   only — keep translateY(-50%) here so the panel doesn't shoot off-screen. */
.ne-header--nav-dd-trace.ne-header--left .ne-header__nav-item:not([data-radial-side]):not(.ne-header__nav-item--more-child) > .ne-header__dropdown,
.ne-header--nav-dd-trace.ne-header--right .ne-header__nav-item:not([data-radial-side]):not(.ne-header__nav-item--more-child) > .ne-header__dropdown { transform: translateY(-50%) !important; }
/* Sidebar RADIAL (items carry data-radial-side): same intent — keep translateY(-50%)
   so the panel stays centred BESIDE the point. The generic trace rule at :491 sets
   transform:none for [data-radial-side], which would drop the panel onto the point
   and overlap it (user 2026-07-11). Higher specificity (adds .ne-header--left/right). */
.ne-header--nav-dd-trace.ne-header--left  .ne-header__nav-item[data-radial-side] > .ne-header__dropdown,
.ne-header--nav-dd-trace.ne-header--right .ne-header__nav-item[data-radial-side] > .ne-header__dropdown { transform: translateY(-50%) !important; }
/* The box (::after) stays hidden for one duration — until the horizontal sweep
   completes — then fades in over the next, alongside the vertical line + items.
   visibility:hidden is essential: opacity:0 alone leaves the glass backdrop-filter
   faintly rendered, so the box looked like it appeared on hover. */
.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown::after {
  opacity: 0;
  visibility: hidden;
  /* Closed state: NO opacity delay → on CLOSE the box fades out FIRST (with the
     items + vertical line). visibility hides after the fade. */
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__dropdown::after {
  opacity: 1;
  visibility: visible;
  /* Open state: delayed → box appears AFTER the horizontal sweep. */
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms),
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
/* Glass box: backdrop-filter can't fade with opacity (it stays rendered until
   visibility flips, then snaps). Animating blur(0) flickers, so instead snap the
   glass box off PROMPTLY on close (visibility 0s) — it disappears WITH the items
   instead of lingering last; opens delayed like the rest. */
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              visibility 0s linear 0s;
}
.ne-header.ne-glass.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__dropdown::after {
  transition: opacity var(--ne-dropdown-speed, 280ms) ease var(--ne-dropdown-speed, 280ms),
              visibility 0s linear var(--ne-dropdown-speed, 280ms);
}
/* Custom dropdown optics: the box fades to the configured background opacity
   (transparency), not a forced 1 — so the trace respects the transparency
   setting (the line stays, the background goes transparent). Scoped to
   custom-optics so the inherit-from-header path stays fully opaque. */
.ne-header--dropdown-custom-optics.ne-header--nav-dd-trace .ne-header__nav-item:not(.ne-header__nav-item--more-child):is(:hover, :focus-within, .is-open) > .ne-header__dropdown::after {
  opacity: var(--ne-dropdown-bg-opacity, 1);
}
/* Sub-items reveal top-to-bottom. With the trace they start after the
   horizontal sweep (--ne-ddstart = one panel-duration) so they follow the
   descending line; with the panel fade animation alone they start immediately
   (--ne-ddstart = 0), matching the button-dropdown's staggered fade. */
.ne-header--nav-dd-trace { --ne-ddstart: var(--ne-dropdown-speed, 280ms); }
.ne-header--dropdown-fade:not(.ne-header--nav-dd-trace) { --ne-ddstart: 0s; }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              transform var(--ne-dropdown-speed, 280ms) ease;
}
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item {
  opacity: 1;
  transform: translateY(0);
}
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(2) { transition-delay: var(--ne-ddstart, 0s); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(3) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.17); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(4) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.33); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(5) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.5); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(6) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.67); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(7) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.83); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(8) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 1); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__dropdown-item:nth-child(n+9) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 1.17); }

/* Synthetic-megamenu rows live in .ne-header__megamenu-links (NOT a
   .ne-header__dropdown), so the open-reveal above never reaches them — they
   stayed at opacity:0 forever and the mega looked EMPTY (All Products / Contact,
   user 2026-07-02). The megamenu panel runs its OWN open/close fade, so just
   reveal the inner rows when the owning nav-item is open. */
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item:is(:hover, :focus-within, .is-open) > .ne-header__megamenu :is(.ne-header__dropdown-item, .ne-header__nav-item--more-child) {
  opacity: 1;
  transform: none;
}

/* More-panel rows are nav-items (not .ne-header__dropdown-item), so the reveal
   above doesn't cover them — they'd ride the container's 2× hide and linger
   after the box/line on close. Fade them WITH the box: in delayed, out prompt. */
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item--more > .ne-header__dropdown > .ne-header__nav-item--more-child {
  opacity: 0;
  transition: opacity var(--ne-dropdown-speed, 280ms) ease;
}
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__nav-item--more:is(:hover, :focus-within, .is-open) > .ne-header__dropdown > .ne-header__nav-item--more-child {
  opacity: 1;
  transition-delay: var(--ne-ddstart, 0s);
}

/* Submenu sub-items reveal top-to-bottom too (parity with the parent panel) —
   previously the submenu appeared "as a whole" (user 2026-06-23). The submenu's
   direct children are the trace span + the anchors, so target anchors via
   nth-of-type (the span is ignored). The stagger follows the submenu's own trace
   line; view-all (1st anchor) leads, the rest cascade. */
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown--submenu > .ne-header__dropdown-link {
  opacity: 0;
  transform: translateY(-6px);
  transition: opacity var(--ne-dropdown-speed, 280ms) ease,
              transform var(--ne-dropdown-speed, 280ms) ease;
}
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link {
  opacity: 1;
  transform: translateY(0);
}
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(2) { transition-delay: var(--ne-ddstart, 0s); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(3) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.17); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(4) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.33); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(5) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.5); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(6) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.67); }
:is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item--has-submenu:is(:hover, :focus-within, .is-open) > .ne-header__dropdown--submenu > .ne-header__dropdown-link:nth-of-type(n+7) { transition-delay: calc(var(--ne-ddstart, 0s) + var(--ne-dropdown-speed, 280ms) * 0.83); }

/* --- Hidden until hover --------------------------------------------
   Nav items hidden at rest, revealed when the header is hovered, has
   keyboard focus, or the cursor nears the viewport top (JS adds
   .ne-header--nav-revealed). Items use visibility:hidden (NOT the
   container) so they keep layout for the overflow-measurement JS and
   so the affordance hint on the container stays visible. Touch falls
   back to the burger drawer (unaffected). */
/* Scroll-idle reveal (nav_scroll_reveal): fade the nav points out WHILE the page
   is scrolling, bring them back when it settles (JS toggles .ne-header--nav-scrolling
   on scroll, drops it after an idle delay). A motion parallel to the hover-reveal
   (user 2026-07-11). Fades the whole nav container so it works for every
   arrangement, incl. the absolutely-positioned radial points. */
.ne-header--nav-scroll-reveal .ne-header__nav {
  transition: opacity 0.4s ease,
              transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              filter 0.4s ease;
}
/* All types fade + go inert while scrolling; the animation type adds a transform
   or filter on top (user 2026-07-11). */
.ne-header--nav-scroll-reveal.ne-header--nav-scrolling .ne-header__nav {
  opacity: 0;
  pointer-events: none;
}
/* scale — shrink in place toward the points' centroid (radial publishes it; other
   layouts fall back to centre) so the nav doesn't drift when the logo is off-centre. */
.ne-header--nav-sanim-scale.ne-header--nav-scrolling .ne-header__nav {
  transform: scale(0.85);
  transform-origin: var(--ne-nav-scale-origin, center);
}
/* blur — soften out */
.ne-header--nav-sanim-blur.ne-header--nav-scrolling .ne-header__nav {
  filter: blur(8px);
}
/* collapse — shrink toward the logo centre and tuck behind it. The origin is
   published by applyRadialNav (radial); other layouts fall back to centre. */
.ne-header--nav-sanim-collapse.ne-header--nav-scrolling .ne-header__nav {
  transform: scale(0.12);
  transform-origin: var(--ne-nav-collapse-origin, center);
}
@media (prefers-reduced-motion: reduce) {
  .ne-header--nav-scroll-reveal.ne-header--nav-scrolling .ne-header__nav {
    opacity: 1; pointer-events: auto; transform: none; filter: none;
  }
}
.ne-header--nav-hidden .ne-header__nav {
  position: relative;
}
/* Suppress the nav-link button chrome at rest (when a button style is on) so
   word / dots hints never show empty pills — including the glass frosted blur,
   which carries no !important and would otherwise stay visible. Returns on
   reveal. */
.ne-header--nav-hidden:not(:hover):not(:focus-within):not(.ne-header--nav-revealed) .ne-header__nav-link {
  background: transparent !important;
  border-color: transparent !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
/* bar + none: items fully hidden. visibility:hidden (not the container)
   keeps layout for the overflow-measurement JS and keeps the container's
   hint visible. word-reveal keeps items in place (handled below). */
.ne-header--nav-hidden:not(.ne-header--nav-hint-word) .ne-header__nav-item {
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.25s ease, visibility 0s linear 0.25s;
}
.ne-header--nav-hidden:not(.ne-header--nav-hint-word):hover .ne-header__nav-item,
.ne-header--nav-hidden:not(.ne-header--nav-hint-word):focus-within .ne-header__nav-item,
.ne-header--nav-hidden:not(.ne-header--nav-hint-word).ne-header--nav-revealed .ne-header__nav-item {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.25s ease, visibility 0s linear 0s;
}

/* "bar" hint — a faint short bar centred where the nav sits, signalling
   "hover to reveal". Fades out once the nav is shown. */
.ne-header--nav-hidden.ne-header--nav-hint-bar .ne-header__nav::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 26px;
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.45;
  transform: translate(-50%, -50%);
  transition: opacity 0.2s ease;
  pointer-events: none;
}
.ne-header--nav-hidden.ne-header--nav-hint-bar:hover .ne-header__nav::before,
.ne-header--nav-hidden.ne-header--nav-hint-bar:focus-within .ne-header__nav::before,
.ne-header--nav-hidden.ne-header--nav-hint-bar.ne-header--nav-revealed .ne-header__nav::before {
  opacity: 0;
}

/* "word" hint — a horizontal placeholder line at the vertical CENTRE of each
   item; on reveal the words grow out of the line (squished → full height) and
   the line fades. Reads as the words being compressed onto the line at rest. */
.ne-header--nav-hidden.ne-header--nav-hint-word .ne-header__nav-link {
  position: relative;
}
.ne-header--nav-hidden.ne-header--nav-hint-word .ne-header__nav-link > span,
.ne-header--nav-hidden.ne-header--nav-hint-word .ne-header__nav-chevron {
  opacity: 0;
  transform: scaleY(0.06);
  transform-origin: center;
  transition: opacity 0.28s ease, transform 0.32s cubic-bezier(0.22, 1, 0.36, 1);
}
.ne-header--nav-hidden.ne-header--nav-hint-word .ne-header__nav-link::before {
  content: "";
  position: absolute;
  left: var(--ne-space-3, 12px);
  right: var(--ne-space-3, 12px);
  top: 50%;
  transform: translateY(-50%);
  height: 2px;
  border-radius: 2px;
  background: currentColor;
  opacity: 0.5;
  transition: opacity 0.28s ease;
  pointer-events: none;
}
.ne-header--nav-hidden.ne-header--nav-hint-word:hover .ne-header__nav-link > span,
.ne-header--nav-hidden.ne-header--nav-hint-word:hover .ne-header__nav-chevron,
.ne-header--nav-hidden.ne-header--nav-hint-word:focus-within .ne-header__nav-link > span,
.ne-header--nav-hidden.ne-header--nav-hint-word:focus-within .ne-header__nav-chevron,
.ne-header--nav-hidden.ne-header--nav-hint-word.ne-header--nav-revealed .ne-header__nav-link > span,
.ne-header--nav-hidden.ne-header--nav-hint-word.ne-header--nav-revealed .ne-header__nav-chevron {
  opacity: 1;
  transform: scaleY(1);
}
.ne-header--nav-hidden.ne-header--nav-hint-word:hover .ne-header__nav-link::before,
.ne-header--nav-hidden.ne-header--nav-hint-word:focus-within .ne-header__nav-link::before,
.ne-header--nav-hidden.ne-header--nav-hint-word.ne-header--nav-revealed .ne-header__nav-link::before {
  opacity: 0;
}

/* === Radial arrangement (nav_arrangement: radial) =====================
   Nav items sit on an elliptical arc around the logo. JS positions each
   item via inline left/top = logo centre + arc offset; items stay
   horizontal and readable. Top/floating headers only (sidebar = Phase 4).
   The header keeps its normal height — the arc OVERFLOWS below it (stands
   over the page), so the header bar isn't pulled taller. */
.ne-header--nav-arrange-radial:not(.ne-header--left):not(.ne-header--right) {
  overflow: visible !important;
}
/* Sidebar radial: let the arc bulge out of the sidebar into the content. */
.ne-header--nav-arrange-radial.ne-header--left,
.ne-header--nav-arrange-radial.ne-header--right {
  overflow: visible;
}
/* Force the radial nav to fill the WHOLE sidebar (overriding the sb-nav grid
   placement), so the arc geometry depends only on the logo position — the
   nav-position setting no longer shifts the nav box / the arc. */
.ne-header--nav-arrange-radial.ne-header--left .ne-header__nav,
.ne-header--nav-arrange-radial.ne-header--right .ne-header__nav {
  position: absolute !important;
  inset: 0 !important;
  grid-row: auto !important;
  grid-column: auto !important;
}
.ne-header--nav-arrange-radial .ne-header__nav {
  position: absolute;
  inset: 0;
  pointer-events: none;
  overflow: visible;
  z-index: 5;
  display: block;
}
.ne-header--nav-arrange-radial .ne-header__nav-item {
  position: absolute;
  transform: translate(-50%, -50%);
  pointer-events: auto;
  transition: left 0.35s cubic-bezier(0.22, 1, 0.36, 1),
              top 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
/* The global media reset (svg { height:auto; max-width:100% }) collapses the inline
   has-dropdown chevron to 0×0 inside the rotated, shrink-to-fit radial pill (classic
   SVG-in-flex collapse — the absolute item has no definite width to resolve the SVG's
   auto height against). Pin its intrinsic size back so the caret stays visible in the
   radial arrangement (user 2026-07-12, Bild 24 sidebar). */
.ne-header--nav-arrange-radial .ne-header__nav-chevron {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
}
/* Sunburst orientation (radial_orient: sunburst): rotate the pill ALONG its ray
   to the logo, "wie die Strahlen einer Sonne" (user 2026-07-11). Only the pill
   rotates — the item FRAME stays unrotated so the dropdown still opens beside/
   horizontal per Rule 458. --ne-radial-rot is set per-item in applyRadialNav();
   back-facing rays are pre-flipped 180° in JS so the label never reads upside-down. */
.ne-header--radial-sunburst.ne-header--nav-arrange-radial .ne-header__nav-item > .ne-header__nav-link {
  /* !important + the extra .ne-header--nav-arrange-radial class beat the
     button-style transforms (nav_btn_3d ships transform: translateY(0/-4px/2px)
     !important at equal specificity) so the pill ALSO rotates in button optik,
     not just the plain-text nav (user 2026-07-11). */
  /* --ne-radial-shift lets the top-centred fan edge-anchor the pill ON THE LINK (the
     item stays centred on its point so the dropdown drops straight down, not dragged
     sideways — user 2026-07-12, D). Side layouts leave it 0% and anchor via the item. */
  transform: translateX(var(--ne-radial-shift, 0%)) rotate(var(--ne-radial-rot, 0deg)) !important;
  /* Pivot about the pill's INNER edge (toward the logo) so that edge stays welded to
     the radius as the pill tilts — "randbündig zum Radius" (user 2026-07-12). Set per
     item in applyRadialNav; falls back to centre. */
  transform-origin: var(--ne-radial-origin, center);
  transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
/* The buttons dropdown style forces has-dropdown items to position:relative
   (tied specificity with the rule above → source order would win and knock
   those points out of the arc). Keep them absolute in the radial layout. */
.ne-header--nav-arrange-radial.ne-header--btn-menu .ne-header__nav-item--has-dropdown {
  position: absolute;
}
/* ============================================================
   Radial dropdown / btn-menu OPENING (user 2026-07-12, full spec).
   Every panel emanates from the pill's OUTER TIP (the ray end away from the logo),
   CENTRED on it, opening in data-radial-open's direction — so it reads as "growing
   out of" the label and never sits over the logo/other points:
     up    → bottom-centre of the panel on the tip, grows UP
     down  → top-centre on the tip, grows DOWN
     left  → right edge on the tip, vertically centred, grows LEFT
     right → left edge on the tip, vertically centred, grows RIGHT
   --ne-rr-oy / -obot / -ox / -oright are px offsets of that tip from the item box,
   measured per point in applyRadialNav (the pill rotates about its inner edge, so its
   tip can't be resolved in CSS). Applies to BOTH the plain dropdown and the btn-menu;
   the dropdown-gap nudges the panel off the tip in the open direction. transform owns
   the centring, so the radial slide/scale transform animations are dropped (the panel
   still fades via its base opacity/visibility transition). */
/* Vertical anchor on the tip: up → panel BOTTOM edge on the tip (grows up); down /
   left / right → panel TOP edge on the tip (grows down, or sideways from the top). */
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-open="up"] > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  top: auto; bottom: var(--ne-rr-obot, 50%);
  margin: 0 0 var(--ne-header-dropdown-gap, 10px) 0;
}
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-open="down"] > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  top: var(--ne-rr-oy, 50%); bottom: auto;
  margin: var(--ne-header-dropdown-gap, 10px) 0 0 0;
}
/* Horizontal alignment of the up/down panels — EDGE-aligned to the tip (not centred):
   left → LEFT edge on the tip, right → RIGHT edge, centre (top-header middle) → centred. */
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-halign="left"]:is([data-radial-open="up"], [data-radial-open="down"]) > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  left: var(--ne-rr-ox, 0px); right: auto; transform: none;
}
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-halign="right"]:is([data-radial-open="up"], [data-radial-open="down"]) > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  right: var(--ne-rr-oright, 0px); left: auto; transform: none;
}
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-halign="center"]:is([data-radial-open="up"], [data-radial-open="down"]) > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  left: var(--ne-rr-ox, 50%); right: auto; transform: translateX(-50%);
}
/* Sideways opens (top-header centre fan-out): near edge on the tip, TOP edge aligned
   to the tip (not vertically centred — user 2026-07-12). */
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-open="right"] > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  top: var(--ne-rr-oy, 50%); bottom: auto;
  left: var(--ne-rr-ox, 0px); right: auto;
  margin: 0 0 0 var(--ne-header-dropdown-gap, 10px);
  transform: none; transform-origin: left top;
}
.ne-header--nav-arrange-radial .ne-header__nav-item[data-radial-open="left"] > :is(.ne-header__dropdown, .ne-header__btn-menu) {
  top: var(--ne-rr-oy, 50%); bottom: auto;
  right: var(--ne-rr-oright, 0px); left: auto;
  margin: 0 var(--ne-header-dropdown-gap, 10px) 0 0;
  transform: none; transform-origin: right top;
}
/* Radial items are absolutely positioned siblings on one stacking level, so
   items later in the DOM paint over an open dropdown. Lift the active item
   (hover / focus / open) above the others so its panel sits on top. */
.ne-header--nav-arrange-radial .ne-header__nav-item:hover,
.ne-header--nav-arrange-radial .ne-header__nav-item:focus-within,
.ne-header--nav-arrange-radial .ne-header__nav-item.is-open {
  z-index: 20;
}

/* (Radial dropdown + btn-menu positioning is unified above, driven by
   data-radial-open — see "Radial dropdown / btn-menu OPENING DIRECTION".
   data-radial-side below still drives the slide/scale animation + the soft-edge
   / text-align couplings.) */
/* (The radial slide/scale TRANSFORM animations were removed: the tip-centred opening
   above owns `transform` (translateX/Y(-50%)), so a competing scale()/translateY()
   would knock the panel off its tip. Radial dropdowns now fade via the base opacity/
   visibility transition. data-radial-side still drives the couplings below.) */

/* Couple the soft-edge fade to the alignment: the fade lands on the OUTER edge
   (away from the logo) so the alignment edge — where the connector line runs —
   stays solid. --ne-dse-h carries the horizontal fade amount (max of L/R). */
.ne-header--dropdown-se-couple .ne-header__nav-item[data-radial-side="left"] > .ne-header__dropdown {
  --ne-dse-left: var(--ne-dse-h, 0px);
  --ne-dse-right: 0px;
}
.ne-header--dropdown-se-couple .ne-header__nav-item[data-radial-side="right"] > .ne-header__dropdown {
  --ne-dse-right: var(--ne-dse-h, 0px);
  --ne-dse-left: 0px;
}
/* Centre point: symmetric — fade BOTH sides (the vertical line is cancelled). */
.ne-header--dropdown-se-couple .ne-header__nav-item[data-radial-side="center"] > .ne-header__dropdown {
  --ne-dse-left: var(--ne-dse-h, 0px);
  --ne-dse-right: var(--ne-dse-h, 0px);
}

/* Couple sub-item text alignment to the panel alignment (radial only). */
.ne-header--dropdown-text-couple .ne-header__nav-item[data-radial-side="left"] .ne-header__dropdown-link { text-align: right; }
.ne-header--dropdown-text-couple .ne-header__nav-item[data-radial-side="right"] .ne-header__dropdown-link { text-align: left; }
.ne-header--dropdown-text-couple .ne-header__nav-item[data-radial-side="center"] .ne-header__dropdown-link { text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .ne-header--ul-sweep .ne-header__nav-link > span::after,
  .ne-header--ul-sweep .ne-header__action::after,
  .ne-header--ul-fade .ne-header__nav-link > span::after,
  .ne-header--ul-fade .ne-header__action::after,
  .ne-header--nav-dd-trace .ne-header__dropdown-trace::before,
  .ne-header--nav-dd-trace .ne-header__dropdown-trace::after,
  .ne-header--nav-dd-trace .ne-header__dropdown-item,
  .ne-header--dropdown-fade .ne-header__dropdown-item,
  .ne-header--nav-dim .ne-header__nav-item,
  .ne-header--nav-hidden .ne-header__nav-item,
  .ne-header--nav-hidden .ne-header__nav::before,
  .ne-header--nav-hidden .ne-header__nav-link > span,
  .ne-header--nav-hidden .ne-header__nav-chevron,
  .ne-header--nav-hidden .ne-header__nav-link::before {
    transition-duration: 0.01ms;
  }
  :is(.ne-header--nav-dd-trace, .ne-header--dropdown-fade) .ne-header__dropdown-item {
    transition-delay: 0s !important;
  }
}

/* ============================================================
   Menu-merge + Drawer variant (moved here from header-styles.liquid
   to stay under its 256KB ceiling — Rule 311; bundles into styles.css
   identically). All rules gated by .ne-header--menu-merge /
   --sb-mega-drawer modifier classes → defaults unchanged.
   ============================================================ */
/* DRAWER VARIANT (T5.3) — sb-mega-drawer inherits all panel geometry, adds a
   slide-IN from the sidebar edge (transform beats panel's transform:none via
   the extra class; close stays instant). Glass slide re-samples bg (Rule 231). */
.ne-header--sb-mega-drawer .ne-header__nav-item > .ne-header__megamenu {
  transform: translateX(-100%) !important;
  /* Literal timing — NOT var(--ne-transition-base): that token already carries a
     cubic-bezier, so appending one produced two timing functions → invalid → the
     whole transition was dropped and the panel's `transition:none` won → JUMP. */
  transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1) !important;
}
.ne-header--sb-mega-drawer.ne-header--right .ne-header__nav-item > .ne-header__megamenu {
  transform: translateX(100%) !important;
}
.ne-header--sb-mega-drawer .ne-header__nav-item:hover > .ne-header__megamenu,
.ne-header--sb-mega-drawer .ne-header__nav-item:focus-within > .ne-header__megamenu,
.ne-header--sb-mega-drawer .ne-header__nav-item.is-open > .ne-header__megamenu {
  transform: translateX(0) !important;
}

/* MENU-MERGE (menu_merge_header → .ne-header--menu-merge, forces dropdown-slide).
   Menu = seamless extension of the header: adopts bg opacity, 0-gap connect,
   outer corners rounded, mega inset, z-index below header. Top + sidebar.
   Radius = header radius, or frame radius when the frame is on. */
.ne-header--menu-merge { --ne-merge-r: var(--ne-header-radius, 0px); }
.ne-header--menu-merge.ne-header--has-frame {
  --ne-merge-r: var(--ne-frame-radius, var(--ne-header-radius, 0px));
}

/* Adopt the header's background opacity so both read as one surface. */
.ne-header--menu-merge .ne-header__dropdown::after,
.ne-header--menu-merge .ne-header__megamenu::after,
.ne-header--menu-merge .ne-header__locale-panel::after {
  opacity: var(--ne-header-bg-opacity, 0.9);
}

/* The concave weld fillets (below) render OUTSIDE the panel box, but the sidebar
   panel clips (overflow:hidden — Firefox scroll-fix). In MERGE we drop the clip so
   the tiles show; the inner block keeps its own overflow-y:auto so content still
   scrolls. ⚠️ Firefox: this re-exposes the tall-panel content-escape the clip
   guarded against — user opted into this trade-off for the weld look (Option B). */
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  /* overflow:clip (NOT visible) — the weld fillets (::before) still show because
     overflow-clip-margin lets painting extend the merge-radius beyond the box, but
     TALL content (e.g. a long single-column collection list) is contained again by
     the panel's max-height instead of spilling out below the capped bg (the "bg is
     only a strip, items hang underneath" bug). overflow:visible had defeated the
     max-height clip entirely. The inner block keeps its own overflow-y:auto scroll. */
  overflow: clip !important;
  overflow-clip-margin: var(--ne-merge-r, 14px) !important;
}

/* ── TOP header: menu drops flush from the header's bottom edge ────────── */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  /* Only the outer (bottom) corners round; the top butts against the header. Excludes More-child
     flyouts: they are DETACHED side-flyouts (bindSbMoreFlyout), not header-welded panels — they get
     full-corner radius + their own positioning below. (user 2026-07-08) */
  border-radius: 0 0 var(--ne-merge-r) var(--ne-merge-r);
  /* Below the header bar so the header bg/shadow covers the joint, never the
     menu — keeps the seam clean (no shadow line bleeding through). */
  /* UNDER the header's opaque ::before (the bg, z-index:-1) so the transform-slide
     tucks the panel BEHIND the header/sidebar (bg + nav labels) instead of over it —
     the panel is a DOM descendant of .ne-header, so a positive z-index painted it on
     TOP of the header content. Still renders above the PAGE because the whole
     .ne-header stacking context sits at --ne-z-header (40). */
  z-index: -2;
}
/* Dropdown is position:absolute (relative to the nav-item) → top:100% drops it
   just under the trigger. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown {
  top: 100% !important;
}
/* Megamenu is position:FIXED → top:100% would resolve against the VIEWPORT (panel
   lands at the viewport's bottom edge, off-screen — read as "megamenu doesn't open").
   Anchor it to the header's actual bottom edge instead (published by JS as
   --ne-header-bottom) so it butts flush under the header with no gap. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  top: var(--ne-header-bottom, calc(var(--ne-header-height, 60px) + 10px)) !important;
}

/* Merge slide — TOP header: the sidebar TRANSFORM slide rotated 90°. The panel slides
   DOWN from behind the opaque header via translateY (NOT clip-path — Firefox drops the
   background of a clip-path-animating element, see the sidebar block's note). The
   header (opaque, higher z-index) masks the panel's off-screen half so it reads as
   pushed out from under the header. The horizontal translateX(-50%) centring is folded
   into the same transform (translate X + Y). Non-glass top headers otherwise had NO
   slide (dropdown-slide is glass-gated) — this gives merge its own, matching the
   sidebar. Closed = shifted a full height up behind the header; OPEN = 0. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  clip-path: none !important;
  transform: translate(-50%, -110%) !important;
  /* Pin the panel ALWAYS opaque — EXACTLY what the working sidebar does (header-styles.liquid:3189).
     Without this the panel inherits the base megamenu opacity (0 closed / 1 open, header-styles.liquid
     2933/2545) which is NOT in the transition list → it snaps to 0 the instant a close starts and the
     300ms translateY runs behind an already-blank box ("verschwindet schlagartig"). With opacity held
     at 1, show/hide is gated purely by `visibility` (delayed 300ms on close below), so the
     translate(-50%,-110%) ↔ translate(-50%,0) slide is painted the whole way, both directions. */
  opacity: 1 !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):hover > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):hover > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__megamenu {
  transform: translate(-50%, 0) !important;
  visibility: visible !important;
  /* --ne-mm-open-delay (JS, 0 by default) delays THIS panel's open on a nav→nav switch until
     the outgoing fast-closing panel is gone — so the new panel doesn't slide out over the old
     one lingering half-transparent across the nav. Mirrors the sidebar (menu.liquid:3446). */
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms), visibility 0s linear var(--ne-mm-open-delay, 0ms), box-shadow 220ms ease !important;
}
/* Content-fade on CLOSE — the panel's BG is z-index:-2 so it tucks under the header, but its
   position:fixed FLOW CONTENT (text/images) paints OVER the header during the up-slide (a
   stacking quirk: z-index/contain/isolation all fail to hold it back). With the panel now pinned
   opaque (so the bg slides VISIBLY under the header), fading only the CONTENT out in 100ms keeps
   the visible bg-retract while stopping anything text-shaped from crossing the header/nav. Open is
   INSTANT (transition opacity 0s) so the content is solid the moment it slides in. This is the
   fade/clip trade-off: content fades rather than sliding under; a header-bottom clip would let the
   content slide under too, but needs a structural change — deferred unless wanted. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu .ne-header__megamenu-inner {
  opacity: 0 !important;
  /* INSTANT on close (0s, not a fade): the content crosses the header in the first few ms of the
     up-slide, so any fade duration leaves it half-visible over the header/logo. Hiding it
     immediately kills the leak; the panel BG (z-index:-2, masked by the header) keeps sliding
     visibly under the header. Trade-off: content pops rather than sliding under — the full "content
     slides under too" needs the header-bottom clip (structural). */
  transition: opacity 0s !important;
}
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:hover > .ne-header__megamenu .ne-header__megamenu-inner,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:focus-within > .ne-header__megamenu .ne-header__megamenu-inner,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item.is-open > .ne-header__megamenu .ne-header__megamenu-inner {
  opacity: 1 !important;
  transition: opacity 0s !important;
}
/* Mega: inset ≥2× radius per side so the rounded silhouette has breathing
   room and never touches the viewport/frame edge. */
.ne-header--top.ne-header--menu-merge .ne-header__megamenu {
  width: calc(min(
    calc(var(--page-width, 90rem) - var(--ne-section-margin, var(--page-margin, 20px)) * 2),
    calc(100vw - var(--ne-section-margin, var(--page-margin, 20px)) * 2)
  ) - var(--ne-merge-r) * 4) !important;
}
/* With the frame on, the inset must reference the FRAME's inner edge (not the
   page-width) so the menu's rounded bottom corners clear the frame's rounded
   corners. Per side = frame clearance (offset + padding) + 2× the corner radius,
   so the two radii never overlap. Centred via the base left:50% / translateX(-50%),
   so the reduced width stays symmetric. More specific (0,4,0) than both the merge
   width rule and the has-frame width rule → wins. */
.ne-header--top.ne-header--menu-merge.ne-header--has-frame .ne-header__megamenu {
  width: calc(100vw
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px) + var(--ne-merge-r) * 2)
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px) + var(--ne-merge-r) * 2)) !important;
}
/* Panel width mirrored into a var (SAME expressions as the two width rules above) purely so the
   weld-bracket can position off the panel's OWN centred edge: bracket left/right = 50% − panel-w/2.
   The width rules themselves are left untouched (their specificity matters). The panel is centred
   (left:50% / translateX(-50%)); the old bracket GUESSED its edge from frame-offset+padding, which
   only matched with symmetric padding → it drifted off the panel otherwise. 50% resolves against
   the same containing block the panel uses, so this holds in always-visible AND frame-mouth. */
.ne-header--top.ne-header--menu-merge {
  --ne-merge-panel-w: calc(min(
    calc(var(--page-width, 90rem) - var(--ne-section-margin, var(--page-margin, 20px)) * 2),
    calc(100vw - var(--ne-section-margin, var(--page-margin, 20px)) * 2)
  ) - var(--ne-merge-r) * 4);
}
.ne-header--top.ne-header--menu-merge.ne-header--has-frame {
  --ne-merge-panel-w: calc(100vw
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-left, 0px) + var(--ne-merge-r) * 2)
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-right, 0px) + var(--ne-merge-r) * 2));
}

/* Narrow viewports — every megamenu (Collections, All Products, More, …). The
   product aside (3 × 180px ≈ 564px) squeezes the flexible link column into a ~240px
   strip, so the links wrap ugly / the More submenu columns overlap. Below this width
   REDUCE the aside — fewer + smaller cards — instead of removing it, so the link
   column (1fr) keeps a readable width but products stay. (User: Produkte reduzieren,
   nicht komplett raus; gilt für alle Megamenüs, nicht nur More.) */
@media (max-width: 1100px) {
  .ne-header__megamenu-aside--products {
    grid-template-columns: repeat(2, minmax(0, 150px)) !important;
  }
  .ne-header__megamenu-aside--products > *:nth-child(n+3) {
    display: none !important;
  }
}

/* ── SIDEBAR header (left/right): menu emerges flush from the sidebar edge ─
   Inner corners (touching the sidebar) stay square; only the outer corners
   round, so the menu reads as a piece pushed out of the sidebar bar. */
.ne-header--left.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown,
.ne-header--left.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  left: var(--ne-header-sidebar-width, 88px) !important;
  border-radius: 0 var(--ne-merge-r) var(--ne-merge-r) 0 !important;
  /* UNDER the header's opaque ::before (the bg, z-index:-1) so the transform-slide
     tucks the panel BEHIND the header/sidebar (bg + nav labels) instead of over it —
     the panel is a DOM descendant of .ne-header, so a positive z-index painted it on
     TOP of the header content. Still renders above the PAGE because the whole
     .ne-header stacking context sits at --ne-z-header (40). */
  z-index: -2;
}
.ne-header--right.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  right: var(--ne-header-sidebar-width, 88px) !important;
  left: auto !important;
  border-radius: var(--ne-merge-r) 0 0 var(--ne-merge-r) !important;
  /* UNDER the header's opaque ::before (the bg, z-index:-1) so the transform-slide
     tucks the panel BEHIND the header/sidebar (bg + nav labels) instead of over it —
     the panel is a DOM descendant of .ne-header, so a positive z-index painted it on
     TOP of the header content. Still renders above the PAGE because the whole
     .ne-header stacking context sits at --ne-z-header (40). */
  z-index: -2;
}

/* Merge slide — SIDEBAR: the panel slides out from BEHIND the opaque sidebar via a
   TRANSFORM (translateX), NOT clip-path. A clip-path wipe looked perfect in Chromium,
   but Firefox has a compositing bug: while clip-path animates, FF stops painting the
   element's BACKGROUND layers (panel bg, ::after, even a child's bg) — only text and
   images stay opaque — so the closing/switching panel showed the page THROUGH it
   (user's "halbtransparentes verschwindendes Panel", 2026-07-06; reproduced in FF 1509
   headless via translateX-vs-clip A/B, Chromium never showed it). translateX is
   GPU-composited and paints fully opaque in BOTH engines. The sidebar (opaque, z-index
   above the panel) masks the panel's off-screen half, so it still reads as "pushed out
   from under the sidebar". Closed = shifted a full width behind the sidebar; OPEN
   (0,6,0 > base 0,5,0) = 0. clip-path forced to none so no stray dropdown-slice clip
   re-triggers the FF bug. The dropdown folds its translateY(-50%) centring into the
   same transform (translate X + Y). */
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  clip-path: none !important;
  transform: translateX(-110%) !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown {
  clip-path: none !important;
  /* UNIFIED with the megamenu: fixed + JS --ne-dd-top (item-centred) so panel + weld bracket share
     the SAME fixed coordinate system (no absolute-vs-fixed divergence). Centring is now the top
     anchor, so the slide is translateX-only (no -50%). Fallback = frame-top + 2× radius. */
  position: fixed !important;
  top: var(--ne-dd-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) !important;
  /* Neutralise any `bottom` (e.g. the open-up rule sets bottom:calc(...) + top:auto). The merge top
     wins via !important, but a leftover `bottom` would set BOTH edges → the panel is stretched to the
     gap between them = a thin strip, viewport-HEIGHT dependent (the "Panel schrumpft zum Balken" bug;
     the merge megamenu already resets bottom:auto, dropdowns didn't). height must stay = content. */
  bottom: auto !important;
  transform: translateX(-110%) !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  clip-path: none !important;
  transform: translateX(110%) !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown {
  clip-path: none !important;
  position: fixed !important;
  top: var(--ne-dd-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) !important;
  bottom: auto !important;  /* see left rule — neutralise open-up's bottom so the panel isn't squished to a strip */
  transform: translateX(110%) !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
/* OPEN — slide fully out (transform → 0). Same target for left + right; dropdown keeps
   its vertical centre. */
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__megamenu,
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__megamenu,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__megamenu,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__megamenu {
  transform: translateX(0) !important;
  /* Open delay is JS-driven via --ne-mm-open-delay (default 0 = instant). Sidebar
     panels sit at different heights (JS anchors each to its item's centre), so a
     direct nav→nav switch would overlap old + new. On a SWITCH the JS sets the delay
     AND force-closes the previous panel at this same mouseenter (.ne-mm-closing) so it
     is fully gone before this one opens; on a FRESH hover the delay stays 0 so there is
     no lag. Visibility rides the same delay so no sliver shows during the wait. */
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms), visibility 0s linear var(--ne-mm-open-delay, 0ms) !important;
}
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__dropdown,
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__dropdown,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__dropdown,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__dropdown {
  transform: translateX(0) !important;
  /* Open delay JS-driven — see the megamenu open rule above. */
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms), visibility 0s linear var(--ne-mm-open-delay, 0ms) !important;
}
/* The panel force-hide (header-styles ~3202/3223: transition:none !important, 0,6,0)
   ALSO kills the retract. Re-enable the transform+visibility transition for the closed
   MERGE panel — one class more specific (0,7,0) so it wins — so the panel slides BACK
   behind the sidebar on close instead of snapping. Descendants keep their transition:none
   (no item lingering). Covers dropdown + megamenu. */
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):not(:focus-within):not(.is-open) > .ne-header__megamenu,
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):not(:focus-within):not(.is-open) > .ne-header__dropdown {
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
/* The plain dropdown hides via opacity:0 in the base (header-styles ~1993), but the merge close
   re-enables only transform+visibility (above) — NOT opacity → opacity SNAPPED to 0 instantly, so
   the panel vanished before its slide even ran (and before the weld bracket finished retracting =
   "brackets zogen sich alleine zurück"). Force opacity constant so the merge dropdown rides the
   transform+visibility slide EXACTLY like the megamenu — full-opacity through the 300ms slide,
   hidden only at the end via visibility. (user 2026-07-07) */
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__dropdown {
  opacity: 1 !important;
}
/* THE megamenu-vs-dropdown asymmetry behind the "first hover→hover overlap". The megamenu forces
   its visibility INSTANTLY (header-styles 3207 visible / 3216 hidden, !important) — its comment says
   the base dropdown-fade effect otherwise HELD visibility for its fade duration + per-item stagger,
   so the panel lingered. The dropdown never got that force, so on a SWITCH the outgoing dropdown's
   visibility was held by the fade and it stayed on screen (in place, opacity:1) OVER the incoming =
   the overlap — worst on the FIRST switch, before the fade's state settled. Mirror the megamenu:
   force visibility instant (closed rides the re-enabled `visibility 0s linear 300ms` slide, then
   gone; open rides the open-delay). Merge-scoped → non-merge dropdowns untouched. (user 2026-07-07) */
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):focus-within > .ne-header__dropdown,
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).is-open > .ne-header__dropdown {
  visibility: visible !important;
}
.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child):not(:focus-within):not(.is-open) > .ne-header__dropdown:not(.ne-header__dropdown--submenu) {
  visibility: hidden !important;
}
/* Force-close on a nav→nav SWITCH. The JS adds .ne-mm-closing to the OUTGOING item at the
   INCOMING item's mouseenter, so the outgoing close STARTS at the same t=0 as the incoming's
   300ms open-delay → the switch is overlap-free no matter how the mouseenter/mouseleave events
   fall (the overlapping hover-pads can fire enter BEFORE leave, which used to leave the outgoing
   on screen a few ms past the delay = the residual overlap). pointer-events:none drops the item's
   :hover so the panel reverts to its NORMAL closed slide — every base closed rule applies
   (transform, visibility, shadow/glow gating, bracket shrink), no per-position override needed.
   The class is removed after CLOSE ms. Covers TOP + sidebar. (user 2026-07-07) */
/* pointer-events:none only on the TOP header (drops :hover so the outgoing runs its closed slide).
   NOT on the sidebar: there .ne-mm-closing is persistent, and pointer-events:none would block the
   mouseenter that re-opens the item — the direct-force rule below closes it without needing :hover
   dropped. */
.ne-header--menu-merge:not(.ne-header--left):not(.ne-header--right) .ne-header__nav-item.ne-mm-closing {
  pointer-events: none;
}
/* On a SWITCH the outgoing must be GONE before the incoming's open-delay (CLOSE=250ms) ends, else
   the two panels co-exist = the "slight overlap" (Frame 50: the outgoing dropdown sat FULLY EXTENDED
   at its position while the incoming opened above it — different heights, both visible).
   Two things were wrong:
   (1) A `:not(:hover)` close rule can't win here — `pointer-events:none` drops the outgoing's :hover
       only on the NEXT hit-test, so if the cursor RESTS on the incoming the outgoing stays stale-
       :hover and its OPEN rule (translateX 0) keeps it extended. So pin the closed transform DIRECTLY
       on .ne-mm-closing (no :hover dependency), same specificity as the open rule (0,6,0) but LATER
       in source → wins the tie even while stale-hovered.
   (2) It must be FAST (180ms < 250ms) so it's behind the sidebar with a clean ~70ms gap before the
       incoming appears. visibility:hidden rides the same 180ms so nothing lingers.
   Split left/right for the slide direction. Only an active switch; a normal mouse-away close keeps
   the smooth 300ms slide. (user 2026-07-07) */
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).ne-mm-closing > .ne-header__megamenu,
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).ne-mm-closing > .ne-header__dropdown {
  transform: translateX(-110%) !important;
  visibility: hidden !important;
  transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 180ms !important;
}
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).ne-mm-closing > .ne-header__megamenu,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child).ne-mm-closing > .ne-header__dropdown {
  transform: translateX(110%) !important;
  visibility: hidden !important;
  transition: transform 180ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 180ms !important;
}
/* Sidebar merge — 2× corner-radius clearance from the frame's TOP + BOTTOM edges so
   the panel's rounded corners never overlap the frame's rounded corners (the base
   sb-mega-panel top sits only 2px inside the frame → the two 14px radii collided).
   top moves down 2× radius; max-height shrinks by 2× radius on BOTH ends so a tall
   panel also keeps the bottom clearance on short viewports. Mirrors the top-header
   2× radius inset (A2). --ne-merge-r inherits from .ne-header (= frame radius). */
.ne-header--left.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu,
.ne-header--right.ne-header--menu-merge.ne-header--sb-mega-panel .ne-header__nav-item:not(.ne-header__nav-item--more-child) > .ne-header__megamenu {
  /* JS (positionMergePanel) publishes --ne-mm-top on the nav-item = the item-centred,
     band-clamped position; the calc() is the pre-JS fallback (frame-top + 2× radius).
     Item-scoped var survives the panel being rebuilt on open (inline top on the panel
     itself was lost → panel snapped back to the fallback). */
  top: var(--ne-mm-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) !important;
  max-height: calc(100vh
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)
    - (var(--ne-frame-offset, 8px) + var(--ne-frame-padding-bottom, 0px) + var(--ne-merge-r) * 2)) !important;
}

/* Shadow on the merged menu (M5), directional AWAY from the seam so nothing bleeds
   through the joint. Applied whenever merge is on — the menu lifts off the content
   even when the header effect is 'none' (user request). Glow replaces it when the
   header uses glow. !important wins over the base dropdown box-shadow:none. */
/* TOP: gate the shadow to the OPEN state. The box-shadow is on the PANEL, so it rides
   the slide — left in the base (closed) state it sweeps UP over the header as the panel
   retracts (same paint escape as the content leak). Closed = no shadow; open = the
   directional shadow. The box-shadow transition lives ONLY on the open transform rule
   (see 'box-shadow' in the :hover transition) → it fades IN on open but changes
   INSTANTLY on close, so the shadow is gone the moment the panel starts retracting. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  box-shadow: 0 0 0 0 transparent !important;
}
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:hover > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:focus-within > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item.is-open > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:hover > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:focus-within > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item.is-open > .ne-header__megamenu {
  box-shadow: 0 20px 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--left.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown,
.ne-header--left.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  box-shadow: 20px 0 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--right.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item > .ne-header__megamenu {
  box-shadow: -20px 0 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__dropdown,
.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__megamenu {
  box-shadow:
    0 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    0 0 calc(60px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99, 102, 241, 0.2)) !important;
}
/* SIDEBAR glow — DIRECTIONAL (spills AWAY from the sidebar seam) so the halo is
   clipped at the header edge instead of bleeding back over the sidebar. A symmetric
   glow on the z:-2 tucked panel is only masked as far as the header bg is opaque
   (--ne-header-bg-opacity); a translucent merged header let the halo leak onto the
   sidebar (user 2026-07-09). Offsetting the glow toward the content side with a
   negative spread keeps it entirely off the header. Mirrors the directional shadow
   above; scaled by the same glow slider. (0,4,2) beats the symmetric rule (0,3,2). */
.ne-header--left.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__dropdown,
.ne-header--left.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__megamenu {
  box-shadow:
    calc(14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(30px * var(--ne-header-glow-intensity, 0.5)) -6px var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    calc(26px * var(--ne-header-glow-intensity, 0.5)) 0 calc(64px * var(--ne-header-glow-intensity, 0.5)) -12px var(--ne-scheme-accent, rgba(99, 102, 241, 0.25)) !important;
}
.ne-header--right.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__dropdown,
.ne-header--right.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__megamenu {
  box-shadow:
    calc(-14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(30px * var(--ne-header-glow-intensity, 0.5)) -6px var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    calc(-26px * var(--ne-header-glow-intensity, 0.5)) 0 calc(64px * var(--ne-header-glow-intensity, 0.5)) -12px var(--ne-scheme-accent, rgba(99, 102, 241, 0.25)) !important;
}
/* TOP glow — same open-gating as the directional shadow so the glow halo doesn't sweep
   the header on close. Overrides the position-agnostic glow rule above (more classes). */
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item > .ne-header__megamenu {
  box-shadow: 0 0 0 0 transparent !important;
}

/* ── LOCALE PANEL: adopt the merged nav-dropdown optic ──────────────────────
   The locale dropdown reads as the same welded surface as the nav dropdowns:
   merge radius + directional shadow, replaced by the directional accent glow
   when the merged header uses glow. Open-state gated like the nav items. */
/* Welded corners: 0 on the seam side (toward the bar), --ne-merge-r away from it —
   matches the nav dropdown (left bar → left corners square, right rounded). */
/* Full merge MECHANIC (mirrors the nav dropdowns): z-index:-2 tuck behind the
   header bg, welded 0-gap connect at the bar edge, and a transform-slide out from
   under the header — opacity held at 1 so show/hide is purely visibility-gated (the
   slide paints the whole way, both engines). Sidebar = fixed + slide on X (anchored
   to the button's y via --ne-loc-dd-top, JS); top = absolute + slide on Y. */
.ne-header--menu-merge .ne-header__locale-panel {
  z-index: -2 !important;
  opacity: 1 !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 300ms !important;
}
.ne-header--left.ne-header--menu-merge .ne-header__locale-panel {
  position: fixed !important;
  top: var(--ne-loc-dd-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) !important;
  bottom: auto !important;
  left: var(--ne-header-sidebar-width, 88px) !important;
  right: auto !important;
  border-radius: 0 var(--ne-merge-r) var(--ne-merge-r) 0 !important;
  transform: translateX(-110%) !important;
}
.ne-header--right.ne-header--menu-merge .ne-header__locale-panel {
  position: fixed !important;
  top: var(--ne-loc-dd-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) !important;
  bottom: auto !important;
  right: var(--ne-header-sidebar-width, 88px) !important;
  left: auto !important;
  border-radius: var(--ne-merge-r) 0 0 var(--ne-merge-r) !important;
  transform: translateX(110%) !important;
}
.ne-header--top.ne-header--menu-merge .ne-header__locale-panel {
  /* Anchor to the REAL header bottom (JS-published, like the nav megamenu) so the panel
     butts flush under the bar instead of top:100% of the shorter action button (which
     sat above the header bottom → covered). position:fixed so header-bottom resolves
     against the viewport; right-aligned to the button (--ne-loc-dd-right, JS). */
  position: fixed !important;
  top: var(--ne-header-bottom, calc(var(--ne-header-height, 60px) + 10px)) !important;
  bottom: auto !important;
  right: var(--ne-loc-dd-right, 16px) !important;
  left: auto !important;
  border-radius: 0 0 var(--ne-merge-r) var(--ne-merge-r) !important;
  transform: translateY(-110%) !important;
}
/* OPEN → slide to rest (translate 0 covers both the X sidebar + Y top slide).
   visibility drives show/hide; the 0ms delay on open lets it appear immediately. */
.ne-header--left.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel,
.ne-header--right.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel,
.ne-header--top.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel {
  transform: translate(0, 0) !important;
  visibility: visible !important;
  transition: transform 300ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 0ms, box-shadow 220ms ease !important;
}
.ne-header--left.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow: 20px 0 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--right.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow: -20px 0 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--top.ne-header--menu-merge .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow: 0 20px 28px -12px rgba(0, 0, 0, 0.42) !important;
}
.ne-header--left.ne-header--menu-merge.ne-header--glow .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow:
    calc(14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(30px * var(--ne-header-glow-intensity, 0.5)) -6px var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    calc(26px * var(--ne-header-glow-intensity, 0.5)) 0 calc(64px * var(--ne-header-glow-intensity, 0.5)) -12px var(--ne-scheme-accent, rgba(99, 102, 241, 0.25)) !important;
}
.ne-header--right.ne-header--menu-merge.ne-header--glow .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow:
    calc(-14px * var(--ne-header-glow-intensity, 0.5)) 0 calc(30px * var(--ne-header-glow-intensity, 0.5)) -6px var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    calc(-26px * var(--ne-header-glow-intensity, 0.5)) 0 calc(64px * var(--ne-header-glow-intensity, 0.5)) -12px var(--ne-scheme-accent, rgba(99, 102, 241, 0.25)) !important;
}
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__locale.is-open .ne-header__locale-panel {
  box-shadow:
    0 0 calc(24px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    0 0 calc(60px * var(--ne-header-glow-intensity, 0.5)) var(--ne-scheme-accent, rgba(99, 102, 241, 0.2)) !important;
}
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item:hover > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item:focus-within > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item.is-open > .ne-header__dropdown,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item:hover > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item:focus-within > .ne-header__megamenu,
.ne-header--top.ne-header--menu-merge.ne-header--glow .ne-header__nav-item.is-open > .ne-header__megamenu {
  /* DIRECTIONAL down (off-seam) — the top-header seam is the panel's TOP edge, so a
     symmetric halo bloomed UP over the header and cut a bright line across the merge
     ("Kante über dem Header", user 2026-07-09 img #40, only with header_effect=glow =
     the GLOBAL header config). Offset the glow down + negative spread → it stays off
     the header, matching the sidebar directional rules above. */
  box-shadow:
    0 calc(14px * var(--ne-header-glow-intensity, 0.5)) calc(30px * var(--ne-header-glow-intensity, 0.5)) -6px var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)),
    0 calc(26px * var(--ne-header-glow-intensity, 0.5)) calc(64px * var(--ne-header-glow-intensity, 0.5)) -12px var(--ne-scheme-accent, rgba(99, 102, 241, 0.25)) !important;
}

/* A "More"-child flyout in a sidebar is a DETACHED side-flyout: JS (bindSbMoreFlyout) pins it FIXED
   just past the More panel's outer edge, bottom-aligned with the panel's end (via --ne-mc-*). Full
   radius on ALL corners — the sidebar base squares the seam-side corners to weld a panel to the
   sidebar, which is wrong here (this panel floats across a gap). Covers dropdown + (synthetic)
   megamenu children. Scoped to left/right (no sb-mega requirement — the More panel is a plain
   dropdown here). (user 2026-07-08) */
.ne-header--left .ne-header__nav-item--more-child > :is(.ne-header__dropdown:not(.ne-header__dropdown--submenu), .ne-header__megamenu),
.ne-header--right .ne-header__nav-item--more-child > :is(.ne-header__dropdown:not(.ne-header__dropdown--submenu), .ne-header__megamenu),
.ne-header--left .ne-header__dropdown-item--has-submenu > .ne-header__dropdown--submenu,
.ne-header--right .ne-header__dropdown-item--has-submenu > .ne-header__dropdown--submenu {
  position: absolute !important;   /* CB = the position:relative parent item; JS sets left/top */
  right: auto !important;
  bottom: auto !important;
  margin: 0 !important;
  border-radius: var(--ne-dropdown-radius, var(--ne-header-radius, var(--ne-radius-lg))) !important;
}
/* Hover-bridge across the gap between the (full-width) item and the detached side-flyout. JS sets
   --ne-mc-bridge to the measured gap so the cursor never drops the hover crossing it. Full flyout
   height (top:0/bottom:0) so it catches the cursor at the item's row. */
.ne-header--left .ne-header__nav-item--more-child > :is(.ne-header__dropdown:not(.ne-header__dropdown--submenu), .ne-header__megamenu)::before,
.ne-header--left .ne-header__dropdown-item--has-submenu > .ne-header__dropdown--submenu::before {
  content: '' !important;
  position: absolute !important;
  top: -10px !important; bottom: -10px !important; height: auto !important;
  right: 100% !important; left: auto !important;
  width: var(--ne-mc-bridge, 24px) !important;
}
.ne-header--right .ne-header__nav-item--more-child > :is(.ne-header__dropdown:not(.ne-header__dropdown--submenu), .ne-header__megamenu)::before,
.ne-header--right .ne-header__dropdown-item--has-submenu > .ne-header__dropdown--submenu::before {
  content: '' !important;
  position: absolute !important;
  top: -10px !important; bottom: -10px !important; height: auto !important;
  left: 100% !important; right: auto !important;
  width: var(--ne-mc-bridge, 24px) !important;
}

/* TOP header merge — More-child + cascading submenu flyouts are DETACHED side-flyouts, IDENTICAL to
   the sidebar: JS (bindSbMoreFlyout) pins them beside the parent panel on the item's row, all-corner
   radius, 8px gap. The top-merge panel rules (top-squared radius + slide-from-behind-the-header +
   z-index:-2) are excluded above via :not(--more-child); submenus never matched them. So here we only
   anchor: position:absolute off the item (JS sets left/top), reset the base seam offsets, and kill the
   base dropdown centring/slide `transform` — THAT residual transform was the extra open animation the
   sidebar sub-panel didn't have (user 2026-07-08). */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item--more-child > :is(.ne-header__dropdown:not(.ne-header__dropdown--submenu), .ne-header__megamenu),
.ne-header--top.ne-header--menu-merge .ne-header__dropdown-item--has-submenu > .ne-header__dropdown--submenu {
  position: absolute !important;   /* CB = the position:relative parent item; JS sets left/top */
  right: auto !important;
  bottom: auto !important;
  margin: 0 !important;
  transform: none !important;      /* drop the base translateX(-50%) centring + any slide/scale effect */
  border-radius: var(--ne-dropdown-radius, var(--ne-header-radius, var(--ne-radius-lg))) !important;
}
/* Hover-bridge on the side JS ACTUALLY placed the flyout (dynamic flip publishes .ne-mc-placed-*).
   !important beats the base more-child bridge (header-styles.liquid:1439, right:100%, non-important),
   which would otherwise pin the bridge to the wrong edge when the flyout flips left. */
.ne-header--top.ne-header--menu-merge .ne-mc-placed-right::before {
  content: '' !important;
  position: absolute !important;
  top: -10px !important; bottom: -10px !important; height: auto !important;
  right: 100% !important; left: auto !important;
  width: var(--ne-mc-bridge, 24px) !important;
}
.ne-header--top.ne-header--menu-merge .ne-mc-placed-left::before {
  content: '' !important;
  position: absolute !important;
  top: -10px !important; bottom: -10px !important; height: auto !important;
  left: 100% !important; right: auto !important;
  width: var(--ne-mc-bridge, 24px) !important;
}

/* ── Inline click-accordion — the DEEPEST submenu inside "More" (JS adds .ne-sub-inline, toggles
   .ne-sub-expanded on click). Renders IN-FLOW inside the parent flyout, indented behind a faint guide
   line, collapsed via max-height:0 (JS sets the exact open height for a smooth grow) — so the parent
   panel simply grows downward instead of spawning a colliding 3rd side panel. No panel chrome of its
   own. Wins over the side-flyout rules via the extra .ne-sub-inline class + !important. (user 2026-07-08) */
.ne-header .ne-header__dropdown-item--has-submenu.ne-sub-inline > .ne-header__dropdown--submenu {
  position: static !important;
  inset: auto !important;
  transform: none !important;
  min-width: 0 !important;
  max-width: none !important;
  width: auto !important;
  margin: var(--ne-space-1, 4px) 0 !important;
  padding: 0 0 0 var(--ne-space-3, 12px) !important;   /* subtle indent, no guide line */
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  background: none !important;
  visibility: visible !important;
  opacity: 1 !important;
  pointer-events: auto !important;
  max-height: 0;
  overflow: hidden;
  transition: max-height 260ms cubic-bezier(0.4, 0, 0.2, 1);
}
/* No backdrop bg / hover-bridge pseudo for the inline variant — it is not a floating panel. */
.ne-header .ne-header__dropdown-item--has-submenu.ne-sub-inline > .ne-header__dropdown--submenu::before,
.ne-header .ne-header__dropdown-item--has-submenu.ne-sub-inline > .ne-header__dropdown--submenu::after {
  display: none !important;
}
/* Chevron becomes an accordion caret: DOWN when collapsed, UP when expanded (base points right). */
.ne-header .ne-header__dropdown-item--has-submenu.ne-sub-inline > .ne-header__dropdown-link .ne-header__dropdown-chevron {
  transform: rotate(90deg) !important;
}
.ne-header .ne-header__dropdown-item--has-submenu.ne-sub-inline.ne-sub-expanded > .ne-header__dropdown-link .ne-header__dropdown-chevron {
  transform: rotate(-90deg) !important;
}

/* Concave connection fillets (M3): radial-gradient tiles weld the menu's inner
   corners into the sidebar/header (frame-weld tech). CRITICAL: the tiles must sit
   BESIDE the menu on the CONTENT side of the seam (left sidebar → tiles above/below
   the menu at x ≥ sidebar edge; top header → tiles left/right of the menu at
   y below the header). Placing them OVER the sidebar/header (left:-r / top:-r) hides
   them — the menu is z-index (header-1) < the opaque sidebar/header. Colour = scheme
   bg at header opacity (glass can't be re-blurred). */
/* ── SIDEBAR dropdown weld — DECOUPLED (matches the megamenu + top header) ───────────────────
   The fillet used to ride the panel's own ::before (top/bottom overhang), but pinned to the
   PANEL it was hidden by the panel's stacking/clip → the weld read as MISSING (user 2026-07-07).
   Pinned instead to the SEAM on the NAV-ITEM (position:absolute, item-centred exactly like the
   dropdown: top:50% + translateY(-50%)) so it sits OUTSIDE the panel and grows IN PLACE — concave
   weld present the WHOLE slide, no riding. Spans the dropdown's band via JS-published
   --ne-dd-height. z-index:-1 = above the z:-2 panel, below the opaque sidebar (same as megamenu
   bracket). Scoped left/right + menu-merge → nothing else touched. A nav-item has EITHER a
   megamenu OR a dropdown, so this and the megamenu :has(> .megamenu)::before never collide. */
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown)::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  left: var(--ne-header-sidebar-width, 88px);
  top: calc(var(--ne-dd-top, 0px) - var(--ne-bracket-r));
  /* -1px pulls the BOTTOM fillet UP into the opaque panel. Even after the integer-snap (offsetHeight
     + Math.round top, 2026-07-08) a sub-pixel gap persisted on SOME panels — offsetHeight rounds ±0.5px
     vs the panel's fractional painted height (user-confirmed 2026-07-08). Biasing to a ≤1px OVERLAP is
     the correct fix: an overlap into the same-coloured panel bg is invisible, a gap below it is not.
     The bottom tile is anchored at background-position:bottom, so REDUCING height moves it UP (07-07's
     +2px went the wrong way = wider). The top tile is anchored at `top` (unchanged) → stays flush. */
  height: calc(var(--ne-dd-height, 0px) + var(--ne-bracket-r) * 2 - 1px);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top right,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom right, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown)::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  right: var(--ne-header-sidebar-width, 88px);
  left: auto;
  top: calc(var(--ne-dd-top, 0px) - var(--ne-bracket-r));
  /* -1px pulls the bottom fillet UP into the opaque panel (invisible overlap) so sub-pixel snapping
     never leaves a visible gap. See the left bracket for the full rationale (user-confirmed 2026-07-08). */
  height: calc(var(--ne-dd-height, 0px) + var(--ne-bracket-r) * 2 - 1px);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top left,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom left, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown):focus-within::before,
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown).is-open::before,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown):focus-within::before,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__dropdown).is-open::before {
  --ne-bracket-r: var(--ne-merge-r);
  /* Grows with the panel's open-delay on a switch (base transition has no delay → close immediate). */
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms);
}
/* The top-level dropdown's OWN ::before (the base gap-bridge) is now superseded by the decoupled
   nav-item weld above — hide it in sidebar merge so it can't leave a stray strip at the seam.
   Direct-child selector → submenu flyout bridges (nested deeper) are untouched. */
.ne-header--left.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown::before,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item > .ne-header__dropdown::before {
  display: none !important;
}
/* Items pushed into the "More" overflow menu are CLONES (--more-child) stacked as a vertical
   LIST inside the More panel — they do NOT weld to the sidebar seam, so suppress the seam bracket
   on them (the seam weld is only for TOP-LEVEL sidebar nav-items). Their own in-panel weld, if we
   add one, is a separate bottom-edge treatment. (user 2026-07-07) */
.ne-header--menu-merge .ne-header__nav-item--more-child:has(> .ne-header__dropdown)::before {
  display: none !important;
}

/* ── LOCALE panel weld bracket — same decoupled concave fillet as the nav dropdown.
   Pinned to the seam on the .ne-header__locale container (position:fixed), spans the
   panel band via JS-published --ne-loc-dd-top + --ne-loc-dd-height, grows --ne-bracket-r
   0→merge-r on open so the concave weld is present the whole slide. z-index:-1 (above
   the z:-2 panel, below the opaque sidebar). Sidebar merge only. */
.ne-header--left.ne-header--menu-merge .ne-header__locale::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  left: var(--ne-header-sidebar-width, 88px);
  top: calc(var(--ne-loc-dd-top, 0px) - var(--ne-bracket-r));
  height: calc(var(--ne-loc-dd-height, 0px) + var(--ne-bracket-r) * 2 - 1px);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top right,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom right, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--right.ne-header--menu-merge .ne-header__locale::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  right: var(--ne-header-sidebar-width, 88px);
  left: auto;
  top: calc(var(--ne-loc-dd-top, 0px) - var(--ne-bracket-r));
  height: calc(var(--ne-loc-dd-height, 0px) + var(--ne-bracket-r) * 2 - 1px);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top left,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom left, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--left.ne-header--menu-merge .ne-header__locale.is-open::before,
.ne-header--right.ne-header--menu-merge .ne-header__locale.is-open::before {
  --ne-bracket-r: var(--ne-merge-r);
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms);
}
/* TOP header locale bracket — horizontal concave fillets at the header bottom, on the
   panel's outer corners (edges JS-published as --ne-loc-bracket-left/right), growing
   0→merge-r on open. Mirrors the nav top bracket (1806). */
.ne-header--top.ne-header--menu-merge .ne-header__locale::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  top: var(--ne-header-bottom, calc(var(--ne-header-height, 60px) + 10px));
  left:  calc(var(--ne-loc-bracket-left, 100%) - var(--ne-bracket-r));
  right: calc(var(--ne-loc-bracket-right, 0px) - var(--ne-bracket-r));
  height: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at bottom left,  transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) left  / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom right, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) right / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--top.ne-header--menu-merge .ne-header__locale.is-open::before {
  --ne-bracket-r: var(--ne-merge-r);
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms);
}

/* ── SIDEBAR megamenu weld — DECOUPLED (matches the top header) ──────────────────────────────
   The dropdown fillets above ride their own centred panel (fine — a dropdown barely moves). The
   MEGAMENU fillet instead is pinned to the SEAM (the sidebar's inner edge) on the NAV-ITEM
   (position:fixed) and grows IN PLACE — it does NOT ride the panel's translateX, so the concave
   weld is present for the WHOLE slide (no sharp corner mid-animation, matching the top header). It
   tracks the panel's vertical band via the JS-published --ne-mm-top (panel top) + --ne-mm-height
   (panel height); bracket and panel are both position:fixed off the same left/top base, so they
   stay glued in every mode. z-index:-1 (above the z:-2 panel, below the opaque sidebar) = same as
   the top bracket. Scoped to left/right + menu-merge → nothing else touched. */
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu)::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  left: var(--ne-header-sidebar-width, 88px);
  top: calc(var(--ne-mm-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) - var(--ne-bracket-r));
  height: calc(var(--ne-mm-height, 0px) + var(--ne-bracket-r) * 2);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top right,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom right, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu)::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  right: var(--ne-header-sidebar-width, 88px);
  left: auto;
  top: calc(var(--ne-mm-top, calc(var(--ne-frame-offset, 8px) + var(--ne-frame-padding-top, 0px) + var(--ne-merge-r) * 2)) - var(--ne-bracket-r));
  height: calc(var(--ne-mm-height, 0px) + var(--ne-bracket-r) * 2);
  width: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at top left,    transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) top    / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom left, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) bottom / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu):focus-within::before,
.ne-header--left.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu).is-open::before,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu):focus-within::before,
.ne-header--right.ne-header--menu-merge .ne-header__nav-item:has(> .ne-header__megamenu).is-open::before {
  --ne-bracket-r: var(--ne-merge-r);
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms);
}
/* Dead-zone flicker fix: the nav flex container has a 16px gap between items, so moving
   between two nav points crosses a strip where NO item is hovered → the panel closed then
   reopened = flicker ("hängt am Übergang"). A HOVER-PAD pseudo extends each item's hover
   target 8px into the gap on both sides so the targets meet in the middle — no dead zone.
   position:absolute keeps it out of layout (the item's measured width is unchanged, so the
   responsive "More" collapse is untouched — padding did change the width and broke it).
   z-index:-1 sits it ABOVE the nav container's background (so it's hittable in the gap) but
   BEHIND the label link (so clicks/hover still reach the link, not the pad). */
/* Keep the label link ABOVE the hover-pad (z:2 > pad z:1) so clicks/hover land on the
   link, not the pad — otherwise the pad, painted on top, would swallow nav clicks. Exclude
   the panels so their z-index:-2 (tuck-under-header) is untouched. */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item > :not(.ne-header__megamenu):not(.ne-header__dropdown) {
  position: relative;
  z-index: 2;
}
.ne-header--top.ne-header--menu-merge .ne-header__nav-item::after {
  content: '';
  position: absolute;
  top: 0;
  /* height:100% (not bottom:0) — the nav-item is display:flex and the pseudo is treated
     as a flex item, so top:0/bottom:0 collapsed it to a 12px strip instead of full height. */
  height: 100%;
  /* Gap is 16px; extend 10px per side so adjacent pads OVERLAP by ~4px in the middle
     (8px would meet edge-to-edge and leave a 1px dead spot dead-centre). In the overlap
     the later item wins (topmost), so the hand-off is seamless with no closed frame. */
  left: -10px;
  right: -10px;
  /* z:1 lifts the pad ABOVE the nav container so it's hittable in the gap (z:-1 sat below
     the container → the gap hit the container, not the pad). Label link (z:2) stays above. */
  z-index: 1;
}

/* Growing weld bracket — animatable radius. Registered so it can transition
   (a plain custom prop can't). Drives the concave fillet's size 0 → merge-r. */
@property --ne-bracket-r {
  syntax: '<length>';
  inherits: false;
  initial-value: 0px;
}
/* TOP header — the bracket is DECOUPLED from the sliding panel and pinned to the SEAM
   (header bottom) at the panel's outer corners. It's positioned off the panel's OWN centred
   edge (50% − panel-w/2), NOT a frame-inset guess, so it lands on the panel exactly in every
   mode: the panel is left:50% / translateX(-50%) and 50% here resolves against the SAME
   containing block (viewport when always-visible; the header when frame-mouth's transform makes
   the header the CB) → they track together, no per-mode override. --ne-bracket-r GROWS 0 → merge-r
   on open, and the `- bracket-r` makes the fillet EXPAND OUTWARD from the panel corner as it grows
   (weld forms in place, not riding the slide). Lives on the nav-item (never translates). */
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):has(> .ne-header__megamenu, > .ne-header__dropdown)::before {
  content: '';
  position: fixed;
  --ne-bracket-r: 0px;
  top: var(--ne-header-bottom, calc(var(--ne-header-height, 60px) + 10px));
  /* Narrow dropdowns publish their MEASURED edges (JS initTopMergeBrackets, CB-relative) so the
     fillets land on the real panel corners; full-width megamenus have no var set → fall back to the
     centred `50% − panel-w/2` formula. (user 2026-07-08) */
  left:  calc(var(--ne-dd-bracket-left,  50% - var(--ne-merge-panel-w) / 2) - var(--ne-bracket-r));
  right: calc(var(--ne-dd-bracket-right, 50% - var(--ne-merge-panel-w) / 2) - var(--ne-bracket-r));
  height: var(--ne-bracket-r);
  z-index: -1;
  pointer-events: none;
  opacity: var(--ne-header-bg-opacity, 0.9);
  background:
    radial-gradient(circle at bottom left,  transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) left  / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat,
    radial-gradient(circle at bottom right, transparent var(--ne-bracket-r), var(--ne-scheme-bg) calc(var(--ne-bracket-r) + 0.5px)) right / var(--ne-bracket-r) var(--ne-bracket-r) no-repeat;
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1);
}
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):has(> .ne-header__megamenu, > .ne-header__dropdown):hover::before,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):has(> .ne-header__megamenu, > .ne-header__dropdown):focus-within::before,
.ne-header--top.ne-header--menu-merge .ne-header__nav-item:not(.ne-header__nav-item--more-child):has(> .ne-header__megamenu, > .ne-header__dropdown).is-open::before {
  --ne-bracket-r: var(--ne-merge-r);
  /* Grow the bracket only AFTER the panel's open-delay on a nav→nav switch (mirrors the panel's
     own transform delay, --ne-mm-open-delay) so the bracket no longer appears before the panel.
     CLOSE keeps the base transition (3715, no delay) → the bracket retracts immediately with the
     panel. The transition on the OPEN state is what the browser uses when growing. (user 2026-07-07) */
  transition: --ne-bracket-r 300ms cubic-bezier(0.4, 0, 0.2, 1) var(--ne-mm-open-delay, 0ms);
}
/* (No frame-mouth override needed: the 50% − panel-w/2 position resolves against the same
   containing block the panel does, so it's already correct when the header's transform makes
   it the CB. Verified identical to the old override: header-relative left = merge-r*2 − bracket-r.) */
/* END_SNIPPET:menu-styles */

/* START_SNIPPET:product-card (INDEX:171) */
/* Card participates in parent subgrid (5 rows) when parent declares
     `display: grid` + `grid-template-rows: auto 1fr auto auto auto`.
     Outside a subgrid parent the card still renders correctly as a
     normal grid (each row fills naturally). */
  .ne-product-card {
    position: relative;
    display: grid;
    grid-template-rows: auto 1fr auto auto auto;
    grid-row: span 5;
    row-gap: 0;
    padding: var(--ne-card-padding, 0);
    /* Solid backplate via ::before instead of direct `background` —
       when the carousel's track has `mask-image: linear-gradient`,
       cards positioned in the fade zone end up partially transparent
       (their pixels have alpha < 1, exposing section-bg through the
       card body as a horizontal line that varies with the section
       background's content). The ::before is a child of the card,
       so the mask applied at track-level still affects it — BUT
       since the ::before paints at z-index 0 across the full inset:0
       rect with no subgrid involvement, it eliminates the subpixel
       gap leak between subgrid track boundaries inside the card.
       Direct `background` on the card itself is removed so we don't
       paint twice. */
    background: transparent;
    border-radius: var(--ne-card-radius, var(--ne-radius-md));
    overflow: hidden;
    /* Force a dedicated GPU compositing layer. Normalises the
       anti-aliasing between the card's border-radius (bg paint) and
       the media's clip-path (image clip) because both render in the
       same GPU pass inside this isolated layer — the 1px subpixel
       hairline / asymmetric corner-padding collapses when the
       browser doesn't have to compose across layers.
       `isolation: isolate` starts a fresh stacking context;
       `transform: translateZ(0)` triggers GPU-accelerated
       compositing and is overridden on :hover by translateY(-4px)
       (hover transform replaces base, keeping GPU layer active). */
    isolation: isolate;
    transform: translate3d(0, 0, 0);
    backface-visibility: hidden;
    /* will-change hints the compositor to keep this element's GPU
       layer permanently allocated, eliminating the layer
       create/destroy flicker on hover-transform start (visible as a
       moving 1-px hairline at media→title boundary during scroll +
       hover transitions, varying position between hovers because
       layer allocation timing is non-deterministic). */
    will-change: transform;
    box-shadow:
      var(--ne-card-shadow-inset, 0 0 0 transparent),
      var(--ne-card-shadow-outer, 0 0 0 transparent);
    /* Match the image-zoom transition timing + curve (500ms ease-out-
       expo) so the card-lift and image-zoom finish on the same beat
       — different durations on parent vs child transforms produced
       perceptible micro-stutter at the start of hover (user report
       2026-05-23, persisted after compose-translate3d fix). */
    transition: transform 500ms cubic-bezier(0.16, 1, 0.3, 1), box-shadow var(--ne-transition-base), color var(--ne-transition-fast);
  }
  /* Card scrim (settings.card_scrim_enable): a smooth gradient that sits
     BEHIND the media + text (over the card backplate only), like the
     collection-card overlay — it deepens the card surface toward the
     bottom for legibility without dimming the product photo or the labels
     (user 2026-07-23). The `> ` selector beats `.ne-product-card > *`
     (below) which would otherwise force it to position:relative (making it
     an in-flow grid strip) and z-index:1 (on top of everything).
     Colour + both stop-alphas are precomputed in Liquid and passed as vars;
     color-mix applies the picked colour at each alpha. */
  .ne-product-card > .ne-product-card__scrim {
    position: absolute;
    inset: 0;
    /* No border-radius: the card's overflow-clip already rounds this. */
    pointer-events: none;
    z-index: 0;
    background: linear-gradient(
      to bottom,
      color-mix(in srgb, var(--ne-card-scrim-color, #000) var(--ne-card-scrim-top, 33%), transparent) 0%,
      color-mix(in srgb, var(--ne-card-scrim-color, #000) var(--ne-card-scrim-bot, 90%), transparent) 100%
    );
  }

  /* Subgrid mode: card inherits parent's 5 row lines */
  .ne-pg__grid > .ne-product-card,
  .ne-pg__slide > .ne-product-card,
  [data-product-grid] .ne-product-card,
  [data-search-grid] .ne-product-card,
  /* collection-switcher + related/complementary grids were missing here —
     without subgrid their cards laid out rows independently, so prices
     drifted when products with and without swatches mix (user 2026-07-23) */
  .ne-cs__grid > .ne-product-card,
  .ne-rp-grid > .ne-product-card {
    grid-template-rows: subgrid;
  }

  /* ── Card layouts ─────────────────────────────────────────────────
     Overlay + landscape opt OUT of the 5-row subgrid (they group
     title/price/swatches/atc in a `__body` wrapper). `!important` on
     grid-row/grid-template-rows beats both the subgrid rule and the
     `.ne-pg--rows-N` span rules (neither carries !important). */
  .ne-product-card--layout-overlay,
  .ne-product-card--layout-landscape {
    grid-row: auto !important;
    grid-template-rows: none !important;
  }

  /* Overlay — the whole card is the image; text sits on it over a
     bottom scrim (same idea as the collection cards). */
  .ne-product-card--layout-overlay {
    display: block !important;
    aspect-ratio: var(--ne-card-overlay-aspect, 3 / 4);
    padding: 0 !important;
  }
  .ne-product-card--layout-overlay .ne-product-card__media {
    position: absolute;
    inset: 0;
    aspect-ratio: auto !important;
    height: 100%;
    border-radius: inherit;
    clip-path: inset(0) !important;
  }
  .ne-product-card--layout-overlay .ne-product-card__media img,
  .ne-product-card--layout-overlay .ne-product-card__media picture {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .ne-product-card--layout-overlay .ne-product-card__body {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 3;
    display: flex;
    flex-direction: column;
    gap: var(--ne-card-title-price-gap, 8px);
    padding: 16px var(--ne-card-overlay-pad, 16px) 14px;
    color: #fff;
    pointer-events: none;
  }
  /* Scrim moved OFF this short text-height body (which crammed the gradient fade
     onto one pixel row → the faint hairline) to a SUPERSAMPLED full-media overlay:
     painted 3× and scaled back to 1× so the down-scale bilinearly averages the
     8-bit alpha steps — the same trick that fixed the arc-gallery focus card. The
     card is a permanent GPU layer (isolation + translate3d + will-change, above),
     so its scrim otherwise bands like any 1:1 GPU-textured gradient. (2026-07-20) */
  .ne-product-card--layout-overlay .ne-product-card__media::after {
    content: "";
    position: absolute;
    bottom: 0;
    left: 0;
    width: 300%;
    height: 300%;
    transform: scale(0.3333);
    transform-origin: bottom left;
    z-index: 2;
    pointer-events: none;
    background: linear-gradient(to top,
      rgba(0, 0, 0, var(--ne-card-overlay-scrim, 0.65)) 0%,
      rgba(0, 0, 0, calc(var(--ne-card-overlay-scrim, 0.65) * 0.62)) 10%,
      rgba(0, 0, 0, calc(var(--ne-card-overlay-scrim, 0.65) * 0.36)) 20%,
      rgba(0, 0, 0, calc(var(--ne-card-overlay-scrim, 0.65) * 0.19)) 30%,
      rgba(0, 0, 0, calc(var(--ne-card-overlay-scrim, 0.65) * 0.09)) 40%,
      rgba(0, 0, 0, calc(var(--ne-card-overlay-scrim, 0.65) * 0.03)) 50%,
      transparent 62%);
  }
  .ne-product-card--layout-overlay .ne-product-card__body > * { pointer-events: auto; }
  .ne-product-card--layout-overlay .ne-product-card__title,
  .ne-product-card--layout-overlay .ne-product-card__title-link,
  .ne-product-card--layout-overlay .ne-product-card__vendor,
  .ne-product-card--layout-overlay .ne-product-card__price,
  .ne-product-card--layout-overlay .ne-product-card__price span,
  .ne-product-card--layout-overlay .ne-product-card__price s {
    color: #fff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
  }
  .ne-product-card--layout-overlay .ne-product-card__vendor { opacity: 0.85; }

  /* Landscape — image fills the card height on the left, text column
     on the right (vertically centred). */
  .ne-product-card--layout-landscape {
    display: grid !important;
    grid-template-columns: var(--ne-card-landscape-media, 42%) 1fr;
    align-items: stretch;
    padding: 0 !important;
  }
  .ne-product-card--layout-landscape .ne-product-card__media {
    grid-column: 1;
    aspect-ratio: auto !important;
    height: 100%;
    border-radius: 0;
    clip-path: inset(0) !important;
  }
  .ne-product-card--layout-landscape .ne-product-card__media img,
  .ne-product-card--layout-landscape .ne-product-card__media picture {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }
  .ne-product-card--layout-landscape .ne-product-card__body {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--ne-card-title-price-gap, 8px);
    padding: 16px 18px;
    min-width: 0;
  }

  /* Solid backplate — paints the card's bg across the entire box
     at z-index 0 (below content). Independent of subgrid sizing →
     no subpixel gap exposes the layer behind the card. The ::before
     inherits the card's border-radius via `inherit` and sits inside
     the card's overflow:hidden + clip, so corners stay clean.

     Reads --ne-scheme-card-bg first so the per-scheme "Card background"
     colour-role (Color schemes → card_bg setting) actually drives the
     visible card surface — the bare scheme-bg is the cascade fallback
     for stores that left card_bg blank. */
  .ne-product-card::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--ne-scheme-card-bg, var(--ne-scheme-bg, var(--ne-scheme-bg-surface, #fff)));
    border-radius: inherit;
    z-index: 0;
    pointer-events: none;
  }
  /* Transparent surface mode: kill the backplate so section-bg shows
     through. Hairline border also off (no card edge to outline). */
  .ne-product-card--transparent::before {
    background: transparent;
  }
  .ne-product-card--transparent::after {
    box-shadow: none;
  }
  /* Hairline border — matches the look of recently-viewed-grid solid
     cards. Drawn on a dedicated ::after ring at z-index 2 so it sits
     ABOVE the ::before backplate (which paints scheme-bg at inset: 0
     and would otherwise cover an inset shadow on the card itself).
     Using inset box-shadow instead of `border: 1px solid` keeps the
     line in the same border-radius rect as ::before — no border-box
     vs padding-box mismatch that previously exposed a corner hairline. */
  .ne-product-card:not(.ne-glass):not(.ne-product-card--transparent)::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: inset 0 0 0 1px var(--ne-scheme-border, rgba(127, 127, 127, 0.15));
    z-index: 2;
    pointer-events: none;
  }
  /* Children render above the backplate. Subgrid-positioned children
     stay in their tracks; this only adjusts paint stacking. */
  .ne-product-card > * {
    position: relative;
    z-index: 1;
  }

  /* (Overflow modifiers removed — base card has no overflow:hidden
     anymore; clip-path on the image handles all corner scenarios.) */

  /* Hover transform must include translateZ(0) — replacing the base
     translateZ with a 2D-only translateY collapses the card's GPU
     compositing layer for one frame, which exposes a subpixel
     hairline along the card's border-radius edge (variant of Rule
     133, but on the card-bg edge instead of the media clip-path).
     Keeping translateZ(0) in the hover value preserves the layer. */
  .ne-product-card:hover { transform: translate3d(0, -4px, 0); }

  /* Card glass — Rule 237 pattern: bg + backdrop-filter directly on the
     host (not on ::before), so the card's own hover-transform doesn't
     trap a descendant's backdrop-filter. Theme-Effects → Glassmorphism
     vars (--ne-glass-bg / --ne-glass-blur / --ne-glass-saturate) drive
     all glass surfaces uniformly (sections, blocks AND cards) so the
     visual intensity stays consistent (user request 2026-05-23).
     will-change combines transform AND backdrop-filter — the base
     .ne-product-card rule sets `will-change: transform` for hover-lift
     GPU compositing, and we need backdrop-filter on top so the glass
     layer survives sibling hover-transforms. Replacing with single
     value would drop the transform hint and lose the hover-glow on
     cards (user report 2026-05-23). */
  .ne-product-card.ne-glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.05)) !important;
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    /* Border removed (user 2026-05-23): on dark page-bg the 1px white
       glass-border was visible as a bottom contour line on cards,
       same root cause as the group block fix earlier. The backdrop
       blur + bg-tint already define the card boundary visually. */
    border: 0;
    --ne-card-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.1);
    will-change: transform, backdrop-filter;
  }
  .ne-product-card.ne-glass::before { content: none; }
  .ne-product-card.ne-glass:hover {
    background: var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.10)) !important;
    --ne-card-shadow-inset: inset 0 1px 0 rgba(255, 255, 255, 0.15);
  }
  /* Tint variant — opt-in via `card_glass_tint` toggle in product-grid
     block. Uses background-color LONGHAND (not background shorthand)
     so it doesn't reset background-image — when glass-shine is also
     active, the shine's bg-image gradient stacks on top of the tinted
     color cleanly instead of being overwritten. Specificity (0,3,0)
     + !important beats both the base .ne-product-card.ne-glass rule
     and the universal .ne-glass--tinted[class*="glass"] rule.
     User report 2026-05-23: card_glass_tint had no visible effect
     despite the toggle being enabled — root cause was the bg shorthand
     setting bg-image: none, which combined with shine's gradient could
     produce inconsistent visuals. */
  .ne-product-card.ne-glass.ne-glass--tinted {
    background-color: var(--ne-glass-bg-tinted, var(--ne-glass-bg, rgba(255, 255, 255, 0.05))) !important;
    border-color: var(--ne-glass-border-tinted, var(--ne-glass-border, rgba(255, 255, 255, 0.12))) !important;
  }
  .ne-product-card.ne-glass.ne-glass--tinted:hover {
    background-color: var(--ne-glass-bg-tinted-hover, var(--ne-glass-bg-hover, rgba(255, 255, 255, 0.10))) !important;
  }
  /* Solid-mode tint — tint toggle works on solid surface cards too.
     Overrides the ::before backplate (the visible card surface in
     solid mode) with the accent-tinted color. The card's own bg
     stays transparent so only ::before paints. User 2026-05-23:
     tint setting was previously gated to glass mode only via
     visible_if; user expected it to work for solid surface too. */
  .ne-product-card--tinted-solid::before {
    background: var(--ne-glass-bg-tinted, var(--ne-scheme-accent, rgba(99, 102, 241, 0.4))) !important;
  }
  .ne-product-card--tinted-solid:hover::before {
    background: var(--ne-glass-bg-tinted-hover, var(--ne-scheme-accent-hover, var(--ne-scheme-accent, rgba(99, 102, 241, 0.5)))) !important;
  }

  /* Card chrome modifiers — all plug into --ne-card-shadow-outer.
     Glow uses a hover-only value so it only lights up on hover;
     shadow is visible at rest + slightly stronger on hover. */
  /* Shadow blur values dampened so they fit within the carousel's
     track-padding-inline buffer (= gap, typically 16-20px). Sharper
     blur radii would get clipped at the track border, producing a
     visible hard-edge "contour" instead of a soft fade. */
  .ne-product-card--shadow {
    --ne-card-shadow-outer: 0 3px 10px rgba(0,0,0,0.12);
  }
  .ne-product-card--shadow:hover {
    --ne-card-shadow-outer: 0 5px 14px rgba(0,0,0,0.16);
  }
  .ne-product-card--glow:hover {
    --ne-card-shadow-outer: 0 0 16px var(--ne-scheme-accent-glow, rgba(127,127,127,0.22));
  }
  /* When both shadow + glow are on, hover prefers the glow chip (more
     visually distinctive). Resting state keeps the shadow. */
  .ne-product-card--shadow.ne-product-card--glow:hover {
    --ne-card-shadow-outer:
      0 0 14px var(--ne-scheme-accent-glow, rgba(127,127,127,0.22)),
      0 4px 12px rgba(0,0,0,0.14);
  }

  /* Media row 1 — uses clip-path (not overflow:hidden) so hover
     scale on the image still renders rounded corners. With
     overflow:hidden, the media rect clipped the scaled-up image
     rectangularly on hover (square corners visible at hover state).
     clip-path keeps the rounded shape at any scale. */
  .ne-product-card__media {
    position: relative;
    display: block;
    clip-path: inset(0 round var(--ne-card-radius, var(--ne-radius-md)) var(--ne-card-radius, var(--ne-radius-md)) 0 0);
  }
  /* Full-radius variant: all 4 corners rounded with image_radius. */
  .ne-product-card--img-full-radius .ne-product-card__media {
    clip-path: inset(0 round var(--ne-image-radius, var(--ne-card-radius)));
  }
  /* Padded without explicit image_radius: square media (just clips
     hover-scale overflow, no rounded corners). */
  .ne-product-card--padded:not(.ne-product-card--img-full-radius) .ne-product-card__media {
    clip-path: inset(0);
  }
  /* Transparent surface: no card frame to bound rounded image-top
     corners — kill the default top-radius so image edge is flat
     against the surrounding section. User can still opt-in via
     image_radius (full-radius variant) for floating-card look. */
  .ne-product-card--transparent:not(.ne-product-card--img-full-radius) .ne-product-card__media {
    clip-path: inset(0);
  }

  /* Frame approach removed — the 1px inset fill painted the
     outermost pixel of the image in card-bg colour, which visually
     shifted the perceived image-corner-radius 1px inward relative
     to the card-corner-radius (the curves then looked mismatched).
     We accept the 1px subpixel hairline as a known browser rendering
     artefact; the image clip-path uses the same card_radius value
     as the card's border-radius so geometry is matched, only AA
     between the two rendering passes may leak 1px in some setups. */
  .ne-product-card__media--square { aspect-ratio: 1; }
  .ne-product-card__media--portrait { aspect-ratio: 3/4; }
  .ne-product-card__media--landscape { aspect-ratio: 4/3; }

  .ne-product-card__image {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Explicit center — belts-and-braces for the rare case where an
       inherited object-position from global img styles would pull
       the frame off-centre by a subpixel. */
    object-position: center center;
    transform-origin: center center;
    /* Permanent GPU layer so hover scale3d doesn't trigger a layer
       create/destroy cycle. Without this, the image is part of the
       card's layer at rest, gets promoted to its own layer on
       hover-transform, and the layer-boundary subpixel drift renders
       as a moving hairline at the image→title seam. */
    transform: translate3d(0, 0, 0);
    will-change: transform, opacity;
    backface-visibility: hidden;
    /* Smoother zoom transition — ease-out-expo curve eases the
       acceleration off so the zoom feels analog instead of
       linearly stepped, eliminating the perceived stutter at the
       start of the hover (user report 2026-05-23). Slightly longer
       duration (500ms) also helps the compositor coalesce the
       transform updates per frame. */
    transition: opacity var(--ne-transition-base), transform 500ms cubic-bezier(0.16, 1, 0.3, 1);
  }
  .ne-product-card__image--secondary { position: absolute; inset: 0; opacity: 0; }
  /* Hover zoom + secondary-image swap only on devices with a true hover
     pointer. On touch, :hover sticks while scrolling over the image, which
     zooms it (or swaps to image 2) and blocks the scroll gesture. Gating
     behind @media (hover: hover) keeps these effects mouse/trackpad-only;
     touch devices get a static, scroll-friendly image. */
  @media (hover: hover) {
    .ne-product-card--has-secondary:hover .ne-product-card__image--primary { opacity: 0; }
    /* Hover transforms compose scale3d WITH the base translate3d(0,0,0)
       so the GPU compositor layer stays pinned through the hover transition
       (Rule 162 — replacing the translate kills the layer for one frame,
       visible as a stutter at the start of the zoom). User report 2026-05-23:
       image zoom on hover had slight stutter. */
    .ne-product-card--has-secondary:hover .ne-product-card__image--secondary { opacity: 1; transform: scale3d(1.03, 1.03, 1) translate3d(0, 0, 0); }
    .ne-product-card--hover-zoom:hover .ne-product-card__image--primary { transform: scale3d(1.06, 1.06, 1) translate3d(0, 0, 0); }
  }

  .ne-product-card__placeholder {
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--ne-space-8);
    /* Only the placeholder needs a neutral surface — real images
       cover 100% of the media box and don't need a backplate. */
    background: var(--ne-scheme-bg-surface, rgba(127,127,127,0.08));
  }
  .ne-product-card__placeholder svg { width: 100%; height: auto; opacity: 0.3; }

  /* Badge container — position driven by global theme-settings.
     Visual badge style (.ne-badge--card / --sale / --sold-out) lives in
     critical.css so JS-built grids can reuse it without loading this snippet. */
  .ne-product-card__badges {
    position: absolute;
    top:    var(--ne-badge-top, 8px);
    right:  var(--ne-badge-right, auto);
    bottom: var(--ne-badge-bottom, auto);
    left:   var(--ne-badge-left, 8px);
    display: flex;
    gap: var(--ne-space-1);
    z-index: 2;
  }

  /* Quick view trigger button — positioned over media. Position modifier
     classes (--pos-X) drive top/bottom/left/right + center-on-hover anchor.
     Style modifier classes (--style-X) shape padding / icon+text layout.
     Glass modifier adds frosted backdrop-blur surface. */
  .ne-product-card__quick-view {
    position: absolute;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 8px 12px;
    border: 0;
    color: var(--ne-scheme-text, inherit);
    cursor: pointer;
    opacity: 0;
    transform: translateY(8px);
    transition:
      opacity var(--ne-transition-fast, 0.18s ease),
      transform var(--ne-transition-fast, 0.18s ease),
      background var(--ne-transition-fast, 0.18s ease);
  }
  /* Reveal on card hover, or when the button itself is keyboard-focused.
     NOTE: use the button's own :focus-visible, NOT the card's :focus-within —
     closing the quick-view modal returns focus to this trigger button, and
     :focus-within would then keep the button "stuck" visible on the card after a
     mouse-driven open/close (user 2026-07-24). :focus-visible only matches the
     keyboard heuristic, so restored mouse focus no longer reveals it, while
     keyboard users still get the button on tab. */
  .ne-product-card:hover .ne-product-card__quick-view,
  .ne-product-card__quick-view:focus-visible {
    opacity: 1;
    transform: translateY(0);
  }
  .ne-product-card__quick-view:hover {
    background: var(--ne-scheme-accent, #6366f1);
    color: var(--ne-scheme-accent-fg, #fff);
  }
  .ne-product-card__quick-view-icon { flex: 0 0 auto; }
  .ne-product-card__quick-view-text {
    font-size: 13px;
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
  }

  /* Style modifiers */
  .ne-product-card__quick-view--style-icon {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: 50% !important;
  }
  .ne-product-card__quick-view--style-text,
  .ne-product-card__quick-view--style-icon-text {
    border-radius: 999px !important;
  }

  /* Glass surface (frosted backdrop). Without this modifier the button
     is solid scheme-bg with subtle border. */
  .ne-product-card__quick-view--glass {
    background: var(--ne-glass-bg, rgba(255, 255, 255, 0.18));
    backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 14px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 1px solid var(--ne-glass-border, rgba(255, 255, 255, 0.5));
    color: #fff;
  }
  .ne-product-card__quick-view:not(.ne-product-card__quick-view--glass) {
    background: var(--ne-scheme-bg, #fff);
    border: 1px solid var(--ne-scheme-border, rgba(0, 0, 0, 0.1));
  }

  /* Position modifiers — corner-anchored or center-on-hover */
  .ne-product-card__quick-view--pos-top-left {
    top: var(--ne-space-2, 8px);
    left: var(--ne-space-2, 8px);
  }
  .ne-product-card__quick-view--pos-top-right {
    top: var(--ne-space-2, 8px);
    right: var(--ne-space-2, 8px);
  }
  .ne-product-card__quick-view--pos-bottom-left {
    bottom: var(--ne-space-2, 8px);
    left: var(--ne-space-2, 8px);
  }
  .ne-product-card__quick-view--pos-bottom-right {
    bottom: var(--ne-space-2, 8px);
    right: var(--ne-space-2, 8px);
  }
  /* Center-on-hover: dead-center over the image */
  .ne-product-card__quick-view--pos-center {
    top: 50%;
    left: 50%;
    transform: translate(-50%, calc(-50% + 8px));
  }
  .ne-product-card:hover .ne-product-card__quick-view--pos-center,
  .ne-product-card:focus-within .ne-product-card__quick-view--pos-center {
    transform: translate(-50%, -50%);
  }

  /* ── Mobile quick-view behaviour (≤749px, no hover) ──────────────────────
     The QV button is hover-only by default; on touch it stays invisible but
     still clickable over the image, so a tap silently opens QV instead of the
     product page. These two opt-in toggles make the behaviour explicit. */
  @media (max-width: 749px) {
    /* Disable on mobile: remove the button entirely so a tap falls through to
       the product link (the card media is an anchor to the product URL). */
    .ne-product-card__quick-view--mobile-off {
      display: none !important;
    }
    /* Always show the icon on mobile so the QV affordance is visible
       (there is no hover state on touch to reveal it otherwise). */
    .ne-product-card__quick-view--mobile-show {
      opacity: 1 !important;
      transform: translateY(0) !important;
    }
    .ne-product-card__quick-view--mobile-show.ne-product-card__quick-view--pos-center {
      transform: translate(-50%, -50%) !important;
    }
  }

  /* Wishlist heart — position from global theme settings (Wishlist tab),
     active color from --ne-wishlist-active-color (custom heart color). */
  .ne-product-card__wishlist {
    position: absolute;
    top:    var(--ne-wishlist-top, 8px);
    right:  var(--ne-wishlist-right, 8px);
    bottom: var(--ne-wishlist-bottom, auto);
    left:   var(--ne-wishlist-left, auto);
    z-index: 3;
    background: none;
    border: none;
    cursor: pointer;
    padding: var(--ne-space-1);
    opacity: 0;
    transform: translateY(-4px);
    transition: all var(--ne-transition-fast);
    color: var(--ne-scheme-text);
  }
  /* "Always filled" mode: heart visible at rest, full opacity, accent color. */
  .ne-product-card__wishlist--filled {
    opacity: 1;
    transform: translateY(0);
    color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444));
  }
  .ne-product-card:hover .ne-product-card__wishlist { opacity: 0.7; transform: translateY(0); }
  .ne-product-card__wishlist:hover { opacity: 1 !important; color: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444)); }
  .ne-product-card__wishlist.ne-wishlist-btn--active .ne-product-card__wishlist-icon { fill: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444)); stroke: var(--ne-wishlist-active-color, var(--ne-color-sale, #ef4444)); }
  .ne-product-card__wishlist.ne-wishlist-btn--active { opacity: 1 !important; }

  /* Title row 2 (1fr) — the inner `.ne-product-card__title-clamp`
     alone owns the clipping. Typo vars from the block (font role,
     weight, style) land on the h3 and cascade to the clamp + link. */
  .ne-product-card__title {
    margin: 0;
    padding: var(--ne-space-3) var(--ne-space-3) 0;
    font-family: var(--ne-card-title-font, inherit);
    font-size: var(--ne-font-size-sm);
    font-weight: var(--ne-card-title-weight, 500);
    font-style: var(--ne-card-title-style, normal);
    line-height: 1.3;
    transition: color var(--ne-transition-fast);
  }
  /* Sledgehammer suppression of every text-decoration / outline
     source on the card's title anchor — Shopify base + theme + UA
     all conspire to paint underlines on `<a>` in various states; we
     enforce none across the board. Specificity (0,3,0) + !important
     beats every reasonable upstream rule. */
  .ne-product-card a,
  .ne-product-card a:hover,
  .ne-product-card a:focus,
  .ne-product-card a:focus-visible,
  .ne-product-card a:active,
  .ne-product-card a:visited {
    text-decoration: none !important;
    color: inherit;
  }
  .ne-product-card__title-link { display: block; }
  /* Suppress the global :focus-visible outline on the inner title
     anchor (critical.css paints 2px accent outline at 2px offset —
     drew a line under the title link box that lingered after click).
     The wishlist / quick-view / atc buttons keep their own focus
     rings so keyboard a11y is unchanged. */
  .ne-product-card__title-link:focus,
  .ne-product-card__title-link:focus-visible,
  .ne-product-card__media:focus,
  .ne-product-card__media:focus-visible {
    outline: none !important;
  }
  .ne-product-card__title-clamp {
    display: -webkit-box;
    -webkit-line-clamp: var(--ne-card-title-lines, 2);
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* No padding-bottom here — earlier the clamp had `padding-bottom:
       2 px` to protect descenders (g/y/p) on the last clipped line,
       but that 2 px leaked into the title→price gap and made it
       optically larger than the vendor→title gap at the same slider
       value. Modern browsers fit descenders inside the line-box at
       line-height 1.3, so the clamp doesn't slice them anyway. If
       descender clipping reappears on some font/size combo, restore
       the 2 px here AND offset the price row's padding-top by the
       same amount to keep the inter-row gap uniform. */
  }
  /* no-clamp mode — fall back to natural layout, no truncation. */
  .ne-product-card--title-no-clamp .ne-product-card__title-clamp {
    display: block;
    -webkit-line-clamp: unset;
    overflow: visible;
  }
  /* Hover — neutral opacity dimming instead of accent-color shift.
     Works for every scheme (e.g. violet accent where the base title
     may already sit close to the accent color and an accent swap
     would be invisible). */
  .ne-product-card:hover .ne-product-card__title { opacity: 0.75; }
  .ne-product-card__vendor {
    display: block;
    font-size: var(--ne-font-size-xs);
    /* Match title + price line-height (1.3) so the leading below the
       vendor text matches the leading above the title — keeps all
       inter-row gaps optically equal at the same slider value. */
    line-height: 1.3;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
    /* Vendor sits above the title within the same row; the gap to the
       title follows the merchant's row-gap slider so it stays in
       rhythm with the rest of the card. Falls back to 2 px so legacy
       cards without the slider value keep a tight vendor→title gap. */
    margin-bottom: var(--ne-card-title-price-gap, 2px);
  }

  /* Price row 3 — top padding is the row gap; bottom padding is 0 so
     the next row's top-padding alone defines the inter-row gap. The
     last visible row carries the card's bottom edge-padding via the
     :last-child rule below. Line-height matches the title row (1.3)
     so the leading above the price baseline matches the leading
     below the title — otherwise inherit body line-height (1.65)
     adds extra leading above price that visually reads as a larger
     gap than the row-gap slider value. */
  .ne-product-card__price {
    padding: var(--ne-card-title-price-gap, 8px) var(--ne-space-3) 0;
    display: flex;
    align-items: center;
    gap: var(--ne-space-2);
    font-size: var(--ne-font-size-sm);
    line-height: 1.3;
  }
  /* Sale-price text reads --ne-scheme-sale DIRECTLY (not via the
     --ne-color-sale alias at :root). Same lesson learned from the
     header-icon cascade: an indirection layered at :root resolves
     against scheme-1's sale_price in some cascade contexts even
     when the card is inside .color-scheme-N. Direct read against
     the scheme-bound token works deterministically. */
  .ne-product-card__price-sale { color: var(--ne-scheme-sale, #f87171); font-weight: 600; }
  .ne-product-card__price-compare { font-size: var(--ne-font-size-xs); opacity: 0.7; }
  .ne-product-card__price-sold-out { opacity: 0.7; }
  .ne-product-card__unit-price { display: block; margin-top: 2px; font-size: var(--ne-font-size-xs); color: var(--ne-scheme-fg-muted); font-weight: 400; }

  /* Swatches row 4 (container always reserved when show_swatches).
     Top-padding uses the same --ne-card-title-price-gap token as every
     other row so the merchant's row-gap slider drives spacing
     uniformly (vendor→title→price→swatches→atc). Bottom is 0 — see
     :last-child rule below. */
  .ne-product-card__swatches {
    padding: var(--ne-card-title-price-gap, 8px) var(--ne-space-3) 0;
    display: flex;
    align-items: center;
    gap: var(--ne-space-1);
    min-height: 14px;
  }
  .ne-product-card__swatch {
    width: 14px;
    height: 14px;
    border-radius: var(--ne-radius-full);
    /* Soft inset ring instead of a 1px border — a hard border antialiases
       poorly on a 14px circle and looked pixelated (user 2026-05-24). The
       inset shadow follows the circle smoothly and still defines light
       swatches on a light card. Swatch-glass (when on) overrides this with
       the glass bead via the body[data-ne-swatch-glass] rule. */
    border: 0;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12);
  }
  .ne-product-card__swatch--image {
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
  }
  .ne-product-card__swatch-more { font-size: var(--ne-font-size-xs); opacity: 0.7; }

  /* ATC row 5 — top padding is the row gap; bottom carried by the
     :last-child rule below (ATC is the last row when enabled). */
  .ne-product-card__atc-wrap {
    position: relative;
    padding: var(--ne-card-title-price-gap, 8px) var(--ne-space-3) 0;
  }

  /* Whichever row renders last (price / swatches / atc-wrap depending
     on which features are toggled on) gets the card's bottom
     edge-padding. This decouples the card-edge spacing from the
     inter-row gap, so the merchant's row-gap slider gives a true
     0-px bunched layout instead of leaking 12 px from a fixed
     padding-bottom under every row. */
  .ne-product-card > :last-child {
    padding-bottom: var(--ne-space-3);
  }
  .ne-product-card__atc-btn {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--ne-space-2);
    font-size: var(--ne-font-size-xs);
    padding-block: var(--ne-space-2);
  }
  .ne-product-card__atc-btn[data-loading] { pointer-events: none; opacity: 0.6; }

  /* Popover */
  .ne-quick-add-popover[hidden] { display: none; }
  .ne-quick-add-popover {
    position: absolute;
    bottom: calc(100% + var(--ne-space-2));
    left: 0;
    right: 0;
    background: var(--ne-scheme-bg, transparent);
    /* Subtle hairline — the opaque scheme-border read as a bright line on dark
       schemes (user 2026-05-24). */
    border: 1px solid color-mix(in srgb, var(--ne-scheme-text, #888) 10%, transparent);
    border-radius: var(--ne-radius-md);
    padding: var(--ne-space-3);
    display: flex;
    flex-direction: column;
    gap: var(--ne-space-3);
    z-index: 10;
    box-shadow: 0 8px 24px rgba(0,0,0,0.3);
    animation: ne-popover-in var(--ne-transition-fast) ease both;
  }
  @keyframes ne-popover-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
  }
  .ne-quick-add-popover__label {
    margin-bottom: var(--ne-space-1);
    font-size: var(--ne-font-size-xs);
    text-transform: uppercase;
    letter-spacing: 0.05em;
    opacity: 0.7;
  }
  /* Selected colour name sits inline next to the COLOR label, normal case,
     small gap (user 2026-05-24). */
  .ne-quick-add-popover__color-name {
    margin-left: var(--ne-space-2);
    text-transform: none;
    letter-spacing: 0;
    font-weight: 500;
  }
  .ne-quick-add-popover__swatches { display: flex; flex-wrap: wrap; gap: var(--ne-space-1); }
  .ne-quick-add-popover__swatch {
    padding: var(--ne-space-1) var(--ne-space-3);
    font-size: var(--ne-font-size-xs);
    background: var(--ne-scheme-variant-bg, transparent);
    color: var(--ne-scheme-variant-text, var(--ne-scheme-text));
    /* Thin, subtle outline (matches the colour-swatch 1px hairline) instead of
       a heavy scheme border (user 2026-05-24). */
    border: 1px solid color-mix(in srgb, var(--ne-scheme-text, #888) 14%, transparent);
    /* Pill shape, matching the popover ATC button (--ne-btn-radius), not the
       PDP variant radius (user 2026-05-24). */
    border-radius: var(--ne-btn-radius, 999px);
    cursor: pointer;
    transition: background var(--ne-transition-fast), border-color var(--ne-transition-fast);
  }
  .ne-quick-add-popover__swatch--selected {
    background: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent));
    color: var(--ne-scheme-variant-text-selected, var(--ne-scheme-btn-text));
    border-color: var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent));
  }
  /* Colour options render as dots (mirrors the PDP variant picker). Thin 1px
     selection ring + glass bead when swatch-glass is on (user 2026-05-24). */
  .ne-quick-add-popover__swatches--color { gap: var(--ne-space-2); }
  /* min 24px tap target (WCAG 2.5.8) without enlarging the 22px visual dot. */
  .ne-quick-add-popover__swatch--color { padding: 0; background: transparent; border: 0; min-width: 24px; min-height: 24px; display: inline-flex; align-items: center; justify-content: center; }
  .ne-quick-add-popover__dot {
    display: block;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), var(--ne-swatch-glass-fx, 0 0 0 transparent);
  }
  .ne-quick-add-popover__dot--image { background-size: cover; background-position: center; background-repeat: no-repeat; }
  .ne-quick-add-popover__swatch--color.ne-quick-add-popover__swatch--selected { background: transparent; }
  .ne-quick-add-popover__swatch--color.ne-quick-add-popover__swatch--selected .ne-quick-add-popover__dot {
    box-shadow:
      0 0 0 1px var(--ne-scheme-variant-bg-selected, var(--ne-scheme-accent)),
      var(--ne-swatch-glass-fx, 0 0 0 transparent);
  }
  .ne-quick-add-popover__atc { width: 100%; font-size: var(--ne-font-size-xs); padding-block: var(--ne-space-2); }
  /* Inherit the card's glass optic on the popover panel when the card surface
     is glass. NOTE: the card's own backdrop-filter makes it a backdrop-root,
     so a nested backdrop-filter here is a no-op (Rule 49) — the popover can't
     re-blur the page. We frost it with a translucent scheme-bg layer instead so
     it reads as glass, and drop the border (a transparent rim over the
     backdrop gap showed as a white line) (user 2026-05-24). */
  .ne-product-card.ne-glass .ne-quick-add-popover {
    background: color-mix(in srgb, var(--ne-scheme-bg, #111) 78%, transparent);
    backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    -webkit-backdrop-filter: blur(var(--ne-glass-blur, 16px)) saturate(var(--ne-glass-saturate, 1.4));
    border: 0;
  }
  /* Tinted card → popover inherits the accent tint. Uses --ne-scheme-accent
     directly (always resolves) rather than --ne-glass-bg-tinted, layered over
     the frosted base (user 2026-05-24). */
  .ne-product-card.ne-glass.ne-glass--tinted .ne-quick-add-popover {
    background:
      linear-gradient(0deg,
        color-mix(in srgb, var(--ne-scheme-accent, #6366f1) 48%, transparent),
        color-mix(in srgb, var(--ne-scheme-accent, #6366f1) 48%, transparent)),
      color-mix(in srgb, var(--ne-scheme-bg, #111) 62%, transparent);
  }
  body[data-ne-glass-shine="1"] .ne-product-card.ne-glass .ne-quick-add-popover {
    background-image: var(--ne-glass-shine-bg);
  }
/* END_SNIPPET:product-card */

/* START_SNIPPET:scramble-text (INDEX:183) */
.ne-scramble__dud {
    color: var(--ne-scheme-accent, currentColor);
    opacity: 0.85;
  }
/* END_SNIPPET:scramble-text */

/* START_SNIPPET:view-transitions (INDEX:194) */
/* Opt this document into same-origin cross-document view transitions.
     Motion only — reduced-motion users keep plain, instant navigation. */
  @media (prefers-reduced-motion: no-preference) {
    @view-transition { navigation: auto; }
  }

  /* The PDP media frame is the stable morph target — NOT the raw <img>. The
     frame carries the border-radius + overflow clip + the correct visible box,
     so its snapshot is rounded and correctly positioned. (Naming the bare image
     would capture a square, hover-scaled, offset box.) The clicked card's media
     frame is given the same name in JS just before navigating. */
  .ne-product__media-main {
    view-transition-name: hyprism-vt-hero;
  }

  /* Morph timing: ease the size/position tween of the paired element. */
  ::view-transition-group(hyprism-vt-hero) {
    animation-duration: 0.45s;
    animation-timing-function: cubic-bezier(0.2, 0.7, 0.2, 1);
  }
  ::view-transition-image-pair(hyprism-vt-hero) { isolation: isolate; }

  /* Robust against an un-cached destination image: hold the already-loaded
     card image visible for the whole morph and fade the product-page image in
     on top as it paints. An un-decoded hero never shows as a blank box — worst
     case the high-res image just "sharpens" in over the card thumbnail. */
  ::view-transition-old(hyprism-vt-hero) { animation: 0.45s both ne-vt-hold; }
  ::view-transition-new(hyprism-vt-hero) { animation: 0.45s both ne-vt-fade; }
  @keyframes ne-vt-hold { from { opacity: 1; } to { opacity: 1; } }
  @keyframes ne-vt-fade { from { opacity: 0; } to { opacity: 1; } }

  /* Snap the whole-page root (no cross-fade): cross-fading the root between two
     dark, glass-heavy pages caused a dim "dip" and re-sampled backdrop-filter
     elements (header/frame) into the snapshot — a flicker. Snapping it means the
     new page is there instantly and only the hero frame morphs over it. */
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
/* END_SNIPPET:view-transitions */