/*!
 * velius-blocks.css — served-site behavior styles (D-A2-2 / D-011).
 *
 * The native behavior layer for the served-site block partials. Pairs with
 * /vendor/velius-blocks.js. Vanilla CSS only (no preprocessor, no npm) — the
 * served site links ONLY Tailwind (app.css) + this file + htmx.min.js.
 *
 * Progressive enhancement: `html.js-enabled` is added by the bundle, so
 * JS-only chrome (carousel arrows/dots, filter buttons) is hidden without JS
 * and the content degrades to a scrollable strip / fully-visible grid.
 * Accordions (<details>) + marquees (keyframes) need NO JS at all.
 */

/* A hidden item is always hidden, even inside a flex/grid track. */
[hidden] { display: none !important; }

/* ── Carousel ─────────────────────────────────────────────────────────── */
[data-vb-carousel] { position: relative; }

[data-vb-carousel-track] {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    gap: 1rem;
    padding: 0;
    margin: 0;
    list-style: none;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
[data-vb-carousel-track]::-webkit-scrollbar { display: none; }

.vb-carousel__slide {
    scroll-snap-align: start;
    flex: 0 0 100%;
    margin: 0;
}
@media (min-width: 640px) {
    .vb-carousel__slide { flex-basis: 50%; }
}
@media (min-width: 1024px) {
    .vb-carousel__slide { flex-basis: 33.3333%; }
}

/* VE-006.11 — the hero is a full-bleed BANNER carousel: ONE slide visible per
   viewport at every breakpoint. The shared .vb-carousel__slide responsive
   flex-basis (2-up @640px, 3-up @1024px) above is correct for the multi-item
   testimonial/logo_grid carousels but made hero slides render side-by-side on
   wider screens. This descendant override (specificity 0,2,0 > the 0,1,0 media
   queries above) pins hero slides to 100% basis + removes the track gap so the
   full-bleed slides have no seam. Backward-compatible: every non-hero carousel
   is untouched (no ancestor .vb-hero-carousel). */
.vb-hero-carousel .vb-carousel__slide {
    flex: 0 0 100%;
}
/* VE-006.13 — make the hero drag actually work in the browser. The shared
   [data-vb-carousel-track] rule sets scroll-behavior: smooth (good for
   arrow/dot navigation), but that SMOOTHS every track.scrollLeft write during
   a drag → the drag lags/jitters and feels broken. Override to auto here so
   per-move scrollLeft writes are instant (the slide tracks the pointer 1:1);
   goTo()'s explicit behavior:'smooth' still produces a clean animated
   snap-to-nearest on release (the scrollTo() behavior option overrides CSS).
   touch-action: pan-x lets a horizontal swipe drive the carousel while
   vertical swipes still scroll the page. user-select: none stops text/image
   selection during a mouse drag; cursor grab/grabbing gives a clear affordance. */
.vb-hero-carousel__track {
    gap: 0;
    scroll-behavior: auto;
    touch-action: pan-x;
    -webkit-user-select: none;
    user-select: none;
    cursor: grab;
}
.vb-hero-carousel__track.is-dragging {
    cursor: grabbing;
}

/* JS-only controls — hidden until the bundle is active. */
.vb-carousel__prev,
.vb-carousel__next,
.vb-carousel__dots {
    display: none;
}
html.js-enabled .vb-carousel__prev,
html.js-enabled .vb-carousel__next,
html.js-enabled .vb-carousel__dots {
    display: flex;
}

.vb-carousel__prev,
.vb-carousel__next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border-radius: 9999px;
    border: 1px solid rgba(0, 0, 0, 0.1);
    background: #fff;
    color: #111827;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.15);
}
.vb-carousel__prev { left: 0.25rem; }
.vb-carousel__next { right: 0.25rem; }
.vb-carousel__prev:disabled,
.vb-carousel__next:disabled { opacity: 0.4; cursor: default; }

.vb-carousel__dots {
    justify-content: center;
    gap: 0.5rem;
    margin-top: 0.75rem;
    list-style: none;
    padding: 0;
}
.vb-carousel__dot {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 9999px;
    border: 0;
    background: #d1d5db;
    cursor: pointer;
    padding: 0;
}
.vb-carousel__dot.is-active { background: #111827; }

/* ── Count-up (stats) ──────────────────────────────────────────────────── */
/* Pure JS animation; no static styling needed beyond the partial's classes. */

/* ── Accordion (faq) — native <details>/<summary>, NO JS ──────────────── */
.vb-accordion__summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}
/* A pure-CSS chevron that rotates when the disclosure opens. */
.vb-accordion__summary::-webkit-details-marker { display: none; }
.vb-accordion__summary::after {
    content: '';
    flex: none;
    width: 0.5rem;
    height: 0.5rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    transition: transform 0.2s ease;
    opacity: 0.6;
}
.vb-accordion__item[open] .vb-accordion__summary::after {
    transform: rotate(225deg);
}

/* ── Filter (portfolio) ────────────────────────────────────────────────── */
.vb-filter__controls {
    display: none;
    flex-wrap: wrap;
    gap: 0.5rem;
    list-style: none;
    padding: 0;
    margin: 0;
}
html.js-enabled .vb-filter__controls { display: flex; }

.vb-filter__btn {
    border-radius: 9999px;
    border: 1px solid #e5e7eb;
    background: #f9fafb;
    color: #374151;
    padding: 0.25rem 0.85rem;
    font-size: 0.875rem;
    cursor: pointer;
}
.vb-filter__btn.is-active {
    background: #111827;
    color: #fff;
    border-color: #111827;
}

/* ── Lightbox (gallery) + modal video (hero) — native <dialog> ─────────── */
.vb-dialog {
    border: 0;
    background: transparent;
    padding: 0;
    margin: auto;
    max-width: min(92vw, 1100px);
    max-height: 92vh;
    color: #fff;
}
.vb-dialog::backdrop {
    background: rgba(15, 15, 20, 0.85);
}

.vb-lightbox__frame,
.vb-video__frame {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
}
.vb-lightbox__image {
    max-width: min(92vw, 1100px);
    max-height: 80vh;
    width: auto;
    height: auto;
    border-radius: 0.5rem;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
}
[data-vb-video] {
    max-width: min(92vw, 1100px);
    max-height: 80vh;
    width: 100%;
    border-radius: 0.5rem;
    background: #000;
}
.vb-dialog__close,
.vb-dialog__prev,
.vb-dialog__next {
    position: absolute;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    border-radius: 9999px;
    border: 0;
    background: rgba(255, 255, 255, 0.15);
    color: #fff;
    font-size: 1.5rem;
    line-height: 1;
    cursor: pointer;
}
.vb-dialog__close:hover,
.vb-dialog__prev:hover,
.vb-dialog__next:hover { background: rgba(255, 255, 255, 0.3); }
.vb-dialog__close { top: 0.5rem; right: 0.5rem; }
.vb-dialog__prev { top: 50%; left: 0.5rem; transform: translateY(-50%); }
.vb-dialog__next { top: 50%; right: 0.5rem; transform: translateY(-50%); }
.vb-lightbox__caption {
    text-align: center;
    margin-top: 0.5rem;
    font-size: 0.875rem;
    color: #e5e7eb;
}

/* Without JS, a lightbox trigger is a plain link to the full image — show a
   subtle "opens larger" cursor and keep the thumbnail framed. */
[data-vb-lightbox-trigger] { cursor: zoom-in; }

/* ── Marquee (marquee / logo_grid motion=marquee) — CSS keyframes, NO JS ── */
.vb-marquee {
    overflow: hidden;
    white-space: nowrap;
}
.vb-marquee__track {
    display: inline-flex;
    align-items: center;
    gap: 2.5rem;
    padding-right: 2.5rem; /* mirrors the gap so the loop seam is even */
    will-change: transform;
    animation: vb-marquee var(--vb-marquee-duration, 22s) linear infinite;
}
.vb-marquee--reverse .vb-marquee__track { animation-direction: reverse; }
.vb-marquee:hover .vb-marquee__track { animation-play-state: paused; }

@keyframes vb-marquee {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); } /* track holds the content twice → seamless */
}

.vb-marquee--fast  { --vb-marquee-duration: 12s; }
.vb-marquee--normal { --vb-marquee-duration: 22s; }
.vb-marquee--slow  { --vb-marquee-duration: 38s; }

@media (prefers-reduced-motion: reduce) {
    .vb-marquee__track { animation: none; }
}

/* ── Nav (nav / nav_locked) — D-C-1 ────────────────────────────────────── */
/* Progressive enhancement: the menu is a plain, fully-visible link list by
   default. The mobile hamburger toggle (vb-nav__toggle) is hidden until the
   bundle is active (html.js-enabled); when JS is on, the menu collapses behind
   the toggle on small screens and expands when the nav is .is-open. Submenu
   dropdowns are native <details> (no JS). sticky is pure CSS. */
.vb-nav {
    border-bottom: 1px solid rgba(0, 0, 0, 0.08);
    background: #fff;
    color: #111827;
}
.vb-nav--sticky { position: sticky; top: 0; z-index: 30; }
.vb-nav__inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    max-width: 80rem;
    margin: 0 auto;
    padding: 0 1.5rem;
    min-height: 4rem;
}
.vb-nav__brand {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 700;
    color: inherit;
    text-decoration: none;
    margin-right: auto;
}
.vb-nav__brand-logo { height: 2rem; width: auto; }
.vb-nav__menu {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}
.vb-nav__link {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.5rem 0.75rem;
    color: inherit;
    text-decoration: none;
    font-size: 0.95rem;
    border-radius: 0.375rem;
}
.vb-nav__link:hover { background: rgba(0, 0, 0, 0.05); }

/* Submenu dropdown — native <details>, no JS. */
.vb-nav__submenu > summary {
    list-style: none;
    cursor: pointer;
}
.vb-nav__submenu > summary::-webkit-details-marker { display: none; }
.vb-nav__summary::after {
    content: '';
    display: inline-block;
    width: 0.4rem;
    height: 0.4rem;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    transform: rotate(45deg);
    opacity: 0.6;
    margin-left: 0.25rem;
}
.vb-nav__submenu[open] .vb-nav__summary::after { transform: rotate(225deg); }
.vb-nav__submenu-list {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    min-width: 10rem;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 0.375rem;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
.vb-nav__submenu-item { margin: 0; }
.vb-nav__submenu-link {
    display: block;
    padding: 0.5rem 0.75rem;
    color: inherit;
    text-decoration: none;
    font-size: 0.95rem;
}
.vb-nav__submenu-link:hover { background: rgba(0, 0, 0, 0.05); }

.vb-nav__cta {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 1rem;
    border-radius: 0.375rem;
    background: #111827;
    color: #fff;
    text-decoration: none;
    font-size: 0.95rem;
    font-weight: 600;
}
.vb-nav__cta:hover { background: #1f2937; }

/* Hamburger toggle — JS-only (hidden until the bundle is active). */
.vb-nav__toggle {
    display: none;
    margin-left: auto;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 0.375rem;
    background: #fff;
    color: #111827;
    font-size: 1.25rem;
    line-height: 1;
    cursor: pointer;
}
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Sidebar layout (vertical nav rail) ─────────────────────────────────── */
.vb-nav--sidebar .vb-nav__inner {
    flex-direction: column;
    align-items: stretch;
    max-width: 16rem;
    min-height: auto;
    padding: 1rem;
}
.vb-nav--sidebar .vb-nav__brand { margin-right: 0; margin-bottom: 0.75rem; }
.vb-nav--sidebar .vb-nav__menu { flex-direction: column; align-items: stretch; }
.vb-nav--sidebar .vb-nav__cta { margin-left: 0; margin-top: 0.75rem; }

/* ── Collapsed / mobile state (JS-driven) ───────────────────────────────── */
/* Only meaningful when JS is active: below the small-screen breakpoint the
   menu collapses behind the hamburger; .is-open (toggled by the bundle)
   reveals it. Above the breakpoint the menu is always inline regardless of JS.
   Without JS the menu is fully visible at every width (graceful degradation). */
@media (max-width: 767px) {
    html.js-enabled .vb-nav--horizontal .vb-nav__toggle,
    html.js-enabled .vb-nav--overlay .vb-nav__toggle {
        display: inline-flex;
    }
    html.js-enabled .vb-nav--horizontal .vb-nav__menu,
    html.js-enabled .vb-nav--overlay .vb-nav__menu {
        display: none;
        flex-direction: column;
        align-items: stretch;
        width: 100%;
    }
    html.js-enabled .vb-nav--horizontal.is-open .vb-nav__menu,
    html.js-enabled .vb-nav--overlay.is-open .vb-nav__menu {
        display: flex;
        padding-bottom: 0.5rem;
    }
}

/* ── Overlay layout (full-screen) ───────────────────────────────────────── */
.vb-nav--overlay .vb-nav__menu { display: flex; }
@media (max-width: 767px) {
    html.js-enabled .vb-nav--overlay .vb-nav__menu {
        position: fixed;
        inset: 0;
        background: #fff;
        z-index: 40;
        flex-direction: column;
        align-items: stretch;
        justify-content: center;
        gap: 0.5rem;
        padding: 3rem 1.5rem;
        font-size: 1.25rem;
    }
    html.js-enabled .vb-nav--overlay:not(.is-open) .vb-nav__menu { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    /* Nav has no motion animations; this block documents the consideration. */
}

/* ── Scroll reveal — CSS-first progressive enhancement ─────────────────── */
/* Only animates where animation-timeline is supported; otherwise content is
   fully visible (no JS fallback needed — content is never hidden by default). */
@supports (animation-timeline: view()) {
    [data-vb-reveal] {
        animation: vb-reveal both linear;
        animation-timeline: view();
        animation-range: entry 5% cover 30%;
    }
    @keyframes vb-reveal {
        from { opacity: 0; transform: translateY(1.25rem); }
        to { opacity: 1; transform: none; }
    }
}
@media (prefers-reduced-motion: reduce) {
    [data-vb-reveal] { animation: none !important; }
}

/* ── Typed / rotating text (D-TC-arter-2b) ────────────────────────────────
   A blinking caret (::after) marks the typing cursor. The caret only shows
   when JS is active (html.js-enabled) and the element is mid-rotation; it is
   a pure-CSS flourish (the JS swaps text, the CSS blinks). */
[data-vb-typed]::after {
    content: '';
    display: none;             /* hidden without JS */
}
html.js-enabled [data-vb-typed][data-vb-typed-init]::after {
    content: '|';
    display: inline;
    margin-left: 1px;
    animation: vb-typed-blink 1s step-end infinite;
    opacity: 0.85;
}
@keyframes vb-typed-blink {
    50% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
    html.js-enabled [data-vb-typed][data-vb-typed-init]::after {
        animation: none;
        opacity: 0.85;
    }
}

/* ── Modal popup (D-TC-arter-2b) — native <dialog> content frame ──────────
   The generic dialog surface + close affordance. Template layers (e.g.
   arter.css) theme the panel surface for their aesthetic. */
dialog[data-vb-modal] {
    border: 0;
    background: transparent;
    padding: 0;
    margin: auto;
    max-width: min(92vw, 480px);
    max-height: 92vh;
    color: inherit;
}
dialog[data-vb-modal]::backdrop {
    background: rgba(15, 15, 20, 0.85);
}
.vb-modal__panel {
    position: relative;
    padding: 1.5rem;
    border-radius: 0.5rem;
}
.vb-modal__close {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 9999px;
    border: 0;
    background: rgba(0, 0, 0, 0.1);
    color: inherit;
    font-size: 1.35rem;
    line-height: 1;
    cursor: pointer;
}

/* ── Layout archetypes (D-0e-1) ──────────────────────────────────────────
   The non-chrome region ARRANGEMENT layer. header/footer stay chrome in every
   archetype (rendered by site/page.blade.php); only the <main> interior layout
   differs. vertical-scroll is pure stacking (no CSS needed — it is the byte-
   identical D-B2-1 default). sidebar-dominant arranges sidebar + main
   side-by-side via CSS grid and collapses on narrow viewports. */

.site-archetype-sidebar-dominant {
    display: grid;
    grid-template-columns: minmax(14rem, 18rem) 1fr;
    gap: 2rem;
    align-items: start;
    max-width: 80rem;
    margin: 0 auto;
    padding: 0 1.5rem;
}
.site-archetype-sidebar-dominant__sidebar {
    position: sticky;
    top: 1rem;
}
@media (max-width: 767px) {
    /* Responsive: stack vertically on narrow viewports. */
    .site-archetype-sidebar-dominant {
        grid-template-columns: 1fr;
        gap: 1.5rem;
    }
    .site-archetype-sidebar-dominant__sidebar {
        position: static;
    }
}

/* ── vcard-app-shell archetype (D-TC-arter-1) ────────────────────────────
    arter's fixed 3-column shell: InfoBar (left) | content (center) |
    MenuBar (right). Both side panels are sticky so the profile + nav stay in
    view while the center column scrolls. Collapses to a single stacked column
    on narrow viewports (<1024px — three columns need real width). The
    arter "content slides on panel open" interaction is TC-2; this is the
    responsive 3-column arrangement only. */
.site-archetype-vcard-app-shell {
    display: grid;
    grid-template-columns: minmax(14rem, 18rem) 1fr minmax(14rem, 18rem);
    gap: 2rem;
    align-items: start;
    max-width: 80rem;
    margin: 0 auto;
    padding: 0 1.5rem;
}
.site-archetype-vcard-app-shell__infobar,
.site-archetype-vcard-app-shell__menubar {
    position: sticky;
    top: 1rem;
}
@media (max-width: 1023px) {
    /* Responsive: collapse to a single stacked column in DOM order
       (infobar → content → menubar). */
    .site-archetype-vcard-app-shell {
        grid-template-columns: 1fr;
        gap: 1.5rem;
    }
    .site-archetype-vcard-app-shell__infobar,
    .site-archetype-vcard-app-shell__menubar {
        position: static;
    }
}

/* ── horizontal-scroll archetype (D-TC-salimov-1) ────────────────────────
   salimov's signature: a left-to-right deck of full-viewport panels. The deck
   container owns overflow-x:auto (the horizontal scroll surface); each panel
   is a 100vw × 100vh flex item with scroll-snap. Desktop section titles render
   vertically (writing-mode:vertical-rl) — the horizontal-scroll signature.

   LOAD-BEARING (design decision #3 — the mobile-fallback overflow-lock
   scoping): the horizontal scroll is CONTAINED in the deck container. There
   is NO body{overflow:hidden} here. The desktop overflow "lock" is SCOPED to
   the deck (height:100vh + overflow-x:auto + overflow-y:hidden), so the page
   body scrolls normally OUTSIDE the deck (the header/footer chrome stay
   reachable) and the mobile collapse is AUTOMATIC: at ≤1024px the deck flips
   to flex-direction:column + overflow:visible + height:auto, and because
   nothing ever locked the body, the page reverts to normal vertical scroll
   with no override needed. (The original salimov locks body{overflow:hidden}
   desktop-side; we deliberately do NOT — the deck-local lock is simpler, safe
   under progressive enhancement, and keeps the §4.4 no-JS guarantee: without
   JS the body is never locked, so a no-JS visitor scrolls the page normally
   and the deck scrolls natively via overflow-x:auto.) */
.site-archetype-horizontal-scroll {
    display: flex;
    flex-direction: row;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x mandatory;
    height: 100vh;
    height: 100dvh; /* mobile-browser-safe viewport unit where supported */
    scrollbar-width: none;          /* Firefox — hide the scrollbar */
    -ms-overflow-style: none;       /* IE10+ */
}
.site-archetype-horizontal-scroll::-webkit-scrollbar { display: none; } /* Blink/WebKit */

.site-archetype-horizontal-scroll__panel {
    flex: 0 0 100vw;
    width: 100vw;
    height: 100vh;
    height: 100dvh;
    scroll-snap-align: start;
    /* A panel may hold more content than fits 100vh — let it scroll its own
       surface vertically (desktop) without breaking the deck's height. */
    overflow-y: auto;
}

/* Desktop vertical section titles (the horizontal-scroll signature). TC-2
   themes these per-template; TC-1 provides the hook + the writing-mode rule
   scoped to the archetype so the rule stays inert off-template. */
.site-archetype-horizontal-scroll__panel [data-vb-panel-title] {
    writing-mode: vertical-rl;
    text-orientation: mixed;
}

/* Responsive collapse (≤1024px): the deck becomes a normal vertical stack.
   Because the overflow lock was deck-local (not body), this is ALL that is
   needed — body scroll was never disabled, so the page scrolls vertically as
   a normal onepage. Section titles flip back to horizontal text. */
@media (max-width: 1024px) {
    .site-archetype-horizontal-scroll {
        flex-direction: column;
        overflow-x: visible;
        overflow-y: visible;
        height: auto;
        scroll-snap-type: none;
    }
    .site-archetype-horizontal-scroll__panel {
        flex-basis: auto;
        width: 100%;
        height: auto;
    }
    .site-archetype-horizontal-scroll__panel [data-vb-panel-title] {
        writing-mode: initial;
        text-orientation: initial;
    }
}

@media (prefers-reduced-motion: reduce) {
    /* The deck has no CSS transitions; scroll-behavior stays 'auto' so no
       native scrollbar-drag animates. The JS also drops smooth scrollTo. */
    .site-archetype-horizontal-scroll { scroll-behavior: auto; }
}

/* ── Horizontal-deck chrome (D-TC-salimov-2) — generic STRUCTURE ──────────
    The deck's signature chrome: the angled wedge separators between panels, the
    page-load split-panel preloader, + the bottom scroll-progress bar. These are
    GENERIC archetype chrome (reusable by any horizontal-deck template); the
    per-template COLORS / imagery live in the template's token stylesheet (e.g.
    salimov.css). The bundle injects/controls these elements via data-vb-* hooks
    (progressive enhancement — without JS the preloader/progress bar are NEVER
    injected, and the separators are optional [data-vb-separator] nodes a
    template authors; here we lay out their STRUCTURE only). */

/* Angled wedge separators — a full-height divider pinned to a panel seam. A
   template authors a `<div data-vb-separator="wedge">` (or wedge-opposite /
   copyright / mobile-up / mobile-down) where it wants the divider; this lays
   out the full-height positioning. The background-image is painted by the
   template token layer. Inert without the node. */
[data-vb-separator] {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 80px;                 /* the wedge band width (salimov ~80px) */
    pointer-events: none;
    z-index: 2;
}
[data-vb-separator="wedge"] { right: -1px; transform: translateX(0); }
[data-vb-separator="wedge-opposite"] { left: -1px; }
[data-vb-separator="copyright"],
[data-vb-separator="mobile-up"],
[data-vb-separator="mobile-down"] {
    left: 0; right: 0; width: 100%; height: 80px; top: auto; bottom: 0;
}
@media (max-width: 1024px) {
    /* On the mobile vertical stack the side wedges collapse away (no horizontal
       seam); the copyright/mobile wedges stay as horizontal band dividers. */
    [data-vb-separator="wedge"],
    [data-vb-separator="wedge-opposite"] { display: none; }
}

/* Page-load preloader veil — JS-injected (initPreloader) as
   `<div class="vb-preloader" data-vb-preloader><div class="vb-preloader__line">`.
   Without JS the node never exists → the page is NEVER hidden (the load-bearing
   safety property). The split-panel close is a two-pseudo-element animation:
   ::before + ::after are the left/right panels that shrink to nothing on
   .is-loaded. Template token layers paint the panel + line colors. */
.vb-preloader {
    position: fixed;
    inset: 0;
    z-index: 99999;
    display: flex;
    align-items: center;
    justify-content: center;
    pointer-events: none;
}
.vb-preloader::before,
.vb-preloader::after {
    content: '';
    position: absolute;
    top: 0;
    bottom: 0;
    width: 50%;
    background: #222;             /* default; salimov.css themes to --salimov-surface */
    transition: width 0.5s ease 0.1s;
}
.vb-preloader::before { left: 0; }
.vb-preloader::after { right: 0; transition-delay: 0.25s; }
.vb-preloader.is-loaded::before,
.vb-preloader.is-loaded::after { width: 0; }
.vb-preloader__line {
    position: relative;
    z-index: 1;
    width: 3px;
    height: 250px;                /* salimov `.line` height */
    border-radius: 4px;
    overflow: hidden;
    background: rgba(0, 0, 0, 0.3);
    transition: opacity 0.5s ease;
}
.vb-preloader.is-loaded .vb-preloader__line { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
    /* The bundle removes the veil instantly under reduced motion; these guards
       keep any residual transition from animating. */
    .vb-preloader,
    .vb-preloader::before,
    .vb-preloader::after,
    .vb-preloader__line { transition: none; }
}

/* Horizontal scroll-progress bar — JS-injected (initScrollProgress) as
   `<div class="vb-scroll-progress" data-vb-scroll-progress><div class="vb-scroll-progress__fill">`.
   A fixed track at the bottom; the fill width is driven by the deck's scroll
   position. Without JS the node never exists. Template token layers paint the
   track + fill. Hidden on the mobile vertical stack (no horizontal scroll to
   track). */
.vb-scroll-progress {
    position: fixed;
    bottom: 32px;
    left: 0;
    right: 0;
    z-index: 20;
    height: 8px;
    margin: 0 auto;
    border-radius: 4px;
    background: rgba(0, 0, 0, 0.2); /* default; salimov.css themes the tint */
    overflow: hidden;
    pointer-events: none;
}
.vb-scroll-progress__fill {
    height: 100%;
    width: 0;
    border-radius: 4px;
    background: #111827;           /* default; salimov.css themes the accent */
    transition: width 0.15s linear;
}
@media (max-width: 1024px) {
    /* On the mobile vertical stack there is no horizontal scroll → hide the bar. */
    .vb-scroll-progress { display: none; }
}
@media (prefers-reduced-motion: reduce) {
    /* The bar still UPDATES under reduced motion (a status readout), but the
       fill does not transition (no motion). */
    .vb-scroll-progress__fill { transition: none; }
}

/* ── Scroll-reveal (horizontal deck, JS-driven) — D-TC-salimov-2 ──────────
    ADDITIVE to the CSS-first `[data-vb-reveal]` (animation-timeline: view())
    above. The bundle marks deck-reveal nodes `.vb-reveal-managed` (only inside
    a horizontal-scroll deck, only when JS is active); this scoped rule owns
    their pre-reveal hidden state + the is-revealed reveal. Elsewhere the
    CSS-first path stands untouched. Reduced motion → content visible (the
    bundle also short-circuits to is-revealed). */
html.js-enabled .site-archetype-horizontal-scroll [data-vb-reveal].vb-reveal-managed {
    animation: none;             /* hand off from the CSS-first animation */
}
html.js-enabled .site-archetype-horizontal-scroll [data-vb-reveal].vb-reveal-managed:not(.is-revealed) {
    opacity: 0;
    transform: translateY(1.25rem);
}
html.js-enabled .site-archetype-horizontal-scroll [data-vb-reveal].vb-reveal-managed.is-revealed {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s ease, transform 0.5s ease;
}
@media (prefers-reduced-motion: reduce) {
    html.js-enabled .site-archetype-horizontal-scroll [data-vb-reveal].vb-reveal-managed:not(.is-revealed) {
        opacity: 1;
        transform: none;
    }
    html.js-enabled .site-archetype-horizontal-scroll [data-vb-reveal].vb-reveal-managed.is-revealed {
        transition: none;
    }
}

/* ════════════════════════════════════════════════════════════════════════
 * Commerce behaviors (D-TC-ninico-2b) — generic structure (sanitizer-safe).
 * Template layers (ninico.css) theme these under [data-template="…"]. The
 * markup uses only §4.4-admitted elements (button/dialog/details/time/img +
 * data-*/aria-*), so it survives the content sanitizer unchanged.
 * ════════════════════════════════════════════════════════════════════════ */

/* ── Product card (product_grid / shop_listing) ─────────────────────────── */
.vb-product { position: relative; }
.vb-product__media { position: relative; overflow: hidden; border-radius: 0.5rem; background: #f3f4f6; }
.vb-product__img-front,
.vb-product__img-back { display: block; width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.vb-product__img-back {
    position: absolute; inset: 0; opacity: 0;
    transition: opacity 0.5s cubic-bezier(0.15, 0.75, 0.5, 1);
}
.vb-product__img-placeholder { aspect-ratio: 4 / 5; width: 100%; background: #e5e7eb; }
.vb-product:hover .vb-product__img-back,
.vb-product:focus-within .vb-product__img-back { opacity: 1; }

.vb-product__badge {
    position: absolute; top: 0.75rem; left: 0.75rem; z-index: 2;
    padding: 4px 8px; border-radius: 5px; font-size: 12px; font-weight: 600;
    line-height: 1; color: #fff; background: #111827;
}
.vb-product__badge--sale { background: #dc2626; }
.vb-product__badge--new { background: #2563eb; }
.vb-product__badge--featured { background: #111827; }

/* Hover action row — JS-only chrome (hidden without the bundle, so a no-JS
   visitor sees a clean card; the title link is the no-JS path to the product). */
.vb-product__actions {
    display: none;
    position: absolute; left: 0; right: 0; top: 50%;
    transform: translateY(-50%);
    justify-content: center; gap: 0.5rem; z-index: 2;
    opacity: 0; transition: opacity 0.3s ease;
}
html.js-enabled .vb-product__actions { display: flex; }
.vb-product:hover .vb-product__actions,
.vb-product:focus-within .vb-product__actions { opacity: 1; }
.vb-product__action {
    width: 2.75rem; height: 2.75rem; border: 0; border-radius: 6px;
    background: #fff; color: #111827; font-size: 12px; font-weight: 600;
    cursor: pointer; box-shadow: 0 4px 11px rgba(0, 0, 0, 0.12);
    transition: background 0.2s ease, color 0.2s ease;
}
.vb-product__action:hover { background: #111827; color: #fff; }
.vb-product__action.is-wished { background: #dc2626; color: #fff; }

.vb-product__body { padding-top: 0.85rem; }
.vb-product__title { font-size: 15px; font-weight: 500; margin: 0 0 0.5rem; }
.vb-product__title a { color: inherit; text-decoration: none; }
.vb-product__title a:hover { color: #dc2626; }
.vb-product__rating { display: flex; align-items: center; gap: 0.35rem; font-size: 13px; color: #6b7280; margin-bottom: 0.35rem; }
.vb-product__star { color: #d1d5db; line-height: 1; }
.vb-product__star.is-filled { color: #f59e0b; }
.vb-product__reviews { color: #9ca3af; font-size: 12px; }
.vb-product__price { margin: 0 0 0.5rem; }
.vb-product__price-current { font-weight: 600; font-size: 16px; }
.vb-product__price-old { color: #9ca3af; text-decoration: line-through; margin-left: 0.5rem; font-size: 13px; }
.vb-product__colors { display: flex; gap: 6px; list-style: none; margin: 0.5rem 0 0; padding: 0; }
.vb-product__color { width: 12px; height: 12px; border-radius: 50%; border: 1px solid #d1d5db; background: #99c4ff; }

/* ── Tabs (product_grid layout=tabs + product_detail) ───────────────────── */
/* Panels: no `hidden` in markup → all visible without JS. The bundle sets the
   `hidden` property on inactive panels only when JS is active. */
.vb-tabs__nav { display: flex; flex-wrap: wrap; gap: 0.5rem; border-bottom: 1px solid #e5e7eb; margin-bottom: 1.25rem; }
.vb-tabs__trigger {
    appearance: none; background: transparent; border: 0; border-bottom: 2px solid transparent;
    padding: 0.5rem 0.25rem; margin-bottom: -1px; cursor: pointer;
    font-size: 15px; font-weight: 600; color: #6b7280;
    transition: color 0.2s ease, border-color 0.2s ease;
}
.vb-tabs__trigger:hover { color: #111827; }
.vb-tabs__trigger.is-active { color: #111827; border-bottom-color: #dc2626; }

/* ── Deal countdown timer ───────────────────────────────────────────────── */
.vb-countdown { display: flex; align-items: flex-start; gap: 0.5rem; flex-wrap: wrap; margin: 1rem 0; }
.vb-countdown__unit { display: inline-flex; flex-direction: column; align-items: center; justify-content: center;
    min-width: 4rem; padding: 0.5rem 0.75rem; border-radius: 6px; background: #fff;
    box-shadow: 0 10px 20px rgba(0, 0, 0, 0.04); }
.vb-countdown__num { font-size: 28px; font-weight: 700; line-height: 1; color: #111827; }
.vb-countdown__lbl { font-size: 12px; color: #6b7280; margin-top: 0.25rem; text-transform: uppercase; letter-spacing: 0.04em; }
.vb-countdown__sep { font-size: 24px; font-weight: 700; color: #9ca3af; align-self: center; }
.vb-countdown__bar { width: 100%; height: 6px; border-radius: 999px; background: #e5e7eb; overflow: hidden; margin-top: 0.75rem; }
.vb-countdown__bar-fill { height: 100%; width: 0; border-radius: 999px; background: #dc2626; transition: width 0.5s ease; }

/* ── Shop listing (filter sidebar + sort + grid) ────────────────────────── */
.vb-shop__layout { display: grid; grid-template-columns: minmax(14rem, 16rem) 1fr; gap: 2rem; align-items: start; margin-top: 1.5rem; }
@media (max-width: 1023px) { .vb-shop__layout { grid-template-columns: 1fr; gap: 1.5rem; } }
.vb-shop__group { border-bottom: 1px solid #f3f4f6; padding: 0 0 0.75rem; margin: 0 0 0.75rem; }
.vb-shop__group-title { cursor: pointer; font-size: 14px; font-weight: 600; list-style: none; }
.vb-shop__group-title::-webkit-details-marker { display: none; }
.vb-shop__group-options { list-style: none; margin: 0.75rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.vb-shop__filter {
    text-align: left; border: 1px solid #e5e7eb; border-radius: 6px; background: #fff;
    padding: 0.4rem 0.75rem; font-size: 13px; color: #374151; cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
.vb-shop__filter:hover { border-color: #9ca3af; }
.vb-shop__filter.is-active { background: #111827; color: #fff; border-color: #111827; }
.vb-shop__clear { margin-top: 0.75rem; border: 0; background: transparent; color: #6b7280; font-size: 13px; cursor: pointer; text-decoration: underline; }
.vb-shop__sort { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; margin-bottom: 1.25rem; }
.vb-shop__sort-label { font-size: 13px; color: #6b7280; }
.vb-shop__sort-btn { border: 1px solid #e5e7eb; border-radius: 6px; background: #fff; padding: 0.35rem 0.85rem; font-size: 13px; color: #374151; cursor: pointer; }
.vb-shop__sort-btn.is-active { background: #111827; color: #fff; border-color: #111827; }
.vb-shop__grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.5rem; list-style: none; margin: 0; padding: 0; }
@media (max-width: 1023px) { .vb-shop__grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 639px) { .vb-shop__grid { grid-template-columns: 1fr; } }
.vb-shop__item { list-style: none; }
.vb-shop__empty { color: #6b7280; font-size: 14px; padding: 2rem 0; }

/* ── PDP gallery + summary ──────────────────────────────────────────────── */
.vb-gallery__main { margin-bottom: 0.75rem; }
.vb-gallery__main-img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 0.5rem; background: #f3f4f6; display: block; }
.vb-gallery__main-img--placeholder { aspect-ratio: 1 / 1; background: #e5e7eb; }
.vb-gallery__thumbs { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.vb-gallery__thumb { padding: 0; border: 2px solid transparent; border-radius: 0.375rem; background: transparent; cursor: pointer; overflow: hidden; }
.vb-gallery__thumb img { width: 5rem; height: 5rem; object-fit: cover; display: block; }
.vb-gallery__thumb.is-active { border-color: #dc2626; }
.vb-product-detail__buy { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; margin: 1.25rem 0; }
.vb-product-detail__info { margin: 1.25rem 0; }
.vb-product-detail__info-row { display: flex; gap: 0.5rem; padding: 0.35rem 0; border-bottom: 1px solid #f3f4f6; font-size: 14px; }
.vb-product-detail__info-row dt { font-weight: 600; min-width: 7rem; color: #374151; margin: 0; }
.vb-product-detail__info-row dd { margin: 0; color: #6b7280; }
.vb-product-detail__cat { margin-right: 0.4rem; }
.vb-product-detail__cat a { color: inherit; text-decoration: none; }
.vb-product-detail__cat a:hover { color: #dc2626; }
.vb-product-detail__reviews-note { color: #6b7280; font-size: 14px; }

/* ── Qty stepper ────────────────────────────────────────────────────────── */
.vb-qty { display: inline-flex; align-items: center; border: 1px solid #e5e7eb; border-radius: 6px; overflow: hidden; }
.vb-qty__btn { width: 2.25rem; height: 2.5rem; border: 0; background: #f9fafb; color: #374151; font-size: 16px; cursor: pointer; }
.vb-qty__btn:hover { background: #f3f4f6; }
.vb-qty__value { min-width: 2.5rem; text-align: center; font-weight: 600; font-size: 14px; padding: 0 0.25rem; }
.vb-qty--sm .vb-qty__btn { width: 1.75rem; height: 2rem; font-size: 14px; }
.vb-qty--sm .vb-qty__value { min-width: 1.75rem; font-size: 13px; }

/* ── Cart (inline) + drawer ─────────────────────────────────────────────── */
.vb-cart__items { list-style: none; margin: 1.5rem 0; padding: 0; border-top: 1px solid #e5e7eb; }
.vb-cart__item { display: flex; align-items: center; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid #e5e7eb; }
.vb-cart__item-img { width: 4rem; height: 4rem; border-radius: 0.375rem; object-fit: cover; background: #f3f4f6; }
.vb-cart__item-img--placeholder { background: #e5e7eb; }
.vb-cart__item-body { flex: 1; }
.vb-cart__item-title { font-weight: 600; font-size: 14px; margin: 0 0 0.35rem; }
.vb-cart__item-price { font-weight: 700; font-size: 14px; }
.vb-cart__empty { color: #6b7280; padding: 2rem 0; }
.vb-cart__totals { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-top: 1.5rem; }
.vb-cart__subtotal { font-size: 18px; font-weight: 700; }
.vb-cart__count {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 1.25rem; height: 1.25rem; padding: 0 0.35rem; border-radius: 999px;
    background: #111827; color: #fff; font-size: 12px; font-weight: 600; line-height: 1;
    transition: transform 0.25s ease;
}
.vb-cart__count.is-bumped { transform: scale(1.35); }

/* The slide-in drawer (<dialog>). Ships translateX(100%); .is-open slides to 0.
   Reduced motion drops the transition (the bundle also closes instantly). */
dialog.vb-cart-drawer {
    margin: 0; margin-left: auto; padding: 0; border: 0; background: transparent;
    max-width: none; max-height: none; width: min(380px, 92vw); height: 100dvh;
    color: #111827; overflow: visible;
}
dialog.vb-cart-drawer::backdrop { background: rgba(0, 0, 0, 0.5); }
.vb-cart-drawer__panel {
    height: 100%; background: #fff; box-shadow: rgba(5, 13, 54, 0.05) 5px 15px 30px 0;
    display: flex; flex-direction: column; transform: translateX(100%);
    transition: transform 0.3s cubic-bezier(0.785, 0.135, 0.15, 0.86);
}
dialog.vb-cart-drawer.is-open .vb-cart-drawer__panel { transform: translateX(0); }
.vb-cart-drawer__head { display: flex; align-items: center; justify-content: space-between; padding: 1rem 1.25rem; border-bottom: 1px solid #f3f4f6; }
.vb-cart-drawer__title { font-size: 15px; font-weight: 700; text-transform: uppercase; margin: 0; }
.vb-cart-drawer__close { border: 0; background: transparent; font-size: 22px; line-height: 1; cursor: pointer; color: #6b7280; }
.vb-cart-drawer__items { list-style: none; margin: 0; padding: 0 1.25rem; overflow-y: auto; flex: 1 1 auto; }
.vb-cart-drawer__item { display: flex; gap: 0.75rem; padding: 0.85rem 0; border-bottom: 1px solid #f3f4f6; }
.vb-cart-drawer__item-img { width: 3.5rem; height: 3.5rem; border-radius: 0.375rem; object-fit: cover; background: #f3f4f6; }
.vb-cart-drawer__item-img--placeholder { background: #e5e7eb; }
.vb-cart-drawer__item-title { font-size: 14px; font-weight: 600; margin: 0 0 0.25rem; }
.vb-cart-drawer__item-meta { font-size: 13px; color: #6b7280; margin: 0; }
.vb-cart-drawer__empty { color: #6b7280; padding: 2rem 1.25rem; }
.vb-cart-drawer__foot { padding: 1rem 1.25rem; border-top: 1px solid #f3f4f6; }
.vb-cart-drawer__subtotal { font-weight: 700; margin: 0 0 0.75rem; }

/* ── Quickview modal (built by the bundle) ──────────────────────────────── */
dialog.vb-quickview { border: 0; background: transparent; padding: 0; margin: auto; max-width: min(92vw, 460px); max-height: 92vh; color: #111827; }
dialog.vb-quickview::backdrop { background: rgba(15, 15, 20, 0.7); }
.vb-quickview__panel { position: relative; background: #fff; border-radius: 0.5rem; padding: 1.25rem; }
.vb-quickview__close { position: absolute; top: 0.5rem; right: 0.5rem; border: 0; background: rgba(0,0,0,0.08); border-radius: 999px; width: 2.25rem; height: 2.25rem; font-size: 1.25rem; cursor: pointer; }
.vb-quickview__img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 0.375rem; background: #f3f4f6; }
.vb-quickview__title { font-size: 18px; font-weight: 700; margin: 0.75rem 0 0.35rem; }
.vb-quickview__price { font-size: 16px; font-weight: 600; margin: 0 0 1rem; }

/* ── Deal block layout ──────────────────────────────────────────────────── */
.vb-deal__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2rem; align-items: center; padding: 1.5rem; border: 1px solid #e5e7eb; border-radius: 0.5rem; }
@media (max-width: 767px) { .vb-deal__grid { grid-template-columns: 1fr; } }
.vb-deal__media img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 0.5rem; }
.vb-deal__media-placeholder { aspect-ratio: 1 / 1; background: #e5e7eb; border-radius: 0.5rem; }
.vb-deal__offer { font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: #dc2626; }
.vb-deal__title { font-size: 24px; font-weight: 700; margin: 0.35rem 0; }
.vb-deal__title a { color: inherit; text-decoration: none; }
.vb-deal__desc { color: #4b5563; margin: 0.5rem 0; }
.vb-deal__price-current { font-size: 24px; font-weight: 700; }
.vb-deal__price-old { color: #9ca3af; text-decoration: line-through; margin-left: 0.5rem; }
.vb-deal__ends { color: #6b7280; font-size: 14px; margin: 0.75rem 0; }
.vb-deal__stock-bar { width: 100%; height: 8px; border-radius: 999px; background: #e5e7eb; overflow: hidden; }
.vb-deal__stock-fill { height: 100%; border-radius: 999px; background: #dc2626; }

@media (prefers-reduced-motion: reduce) {
    .vb-product__img-back,
    .vb-product__actions,
    .vb-cart__count,
    .vb-countdown__bar-fill,
    .vb-cart-drawer__panel { transition: none !important; }
    dialog.vb-cart-drawer.is-open .vb-cart-drawer__panel { transform: none; }
    .vb-cart-drawer__panel { transform: none; }
}

/* ── Ninico header row-2 — the 3-column grid (categories LEFT | center | aside RIGHT).
 *    VE-006.15: Tailwind's arbitrary `lg:grid-cols-[260px_1fr_260px]` is NOT emitted
 *    by the pre-built served stylesheet (it ships only standard grid-cols-*), so the
 *    band fell back to grid-cols-1 and row-2 stacked. This scoped rule GUARANTEES the
 *    3-column layout on desktop (>=1024px) and collapses to one column on mobile
 *    (<1024px), per the design contract. (Structural only — colors/spacing deferred.) */
.vb-ninico-header__band {
    display: grid;
    grid-template-columns: 1fr; /* mobile (<1024px): stacked, one column */
    gap: 1rem;
}
@media (min-width: 1024px) {
    .vb-ninico-header__band {
        grid-template-columns: 260px minmax(0, 1fr) 260px; /* desktop: 3 columns */
    }
}

/* VE-006.15 — the categories-topbar hamburger rotates when collapsed (a visual
 * indicator only; the whole topbar is the click target). Structural behavior. */
.vb-ninico-header__categories-bar .vb-ninico-header__categories-icon {
    transition: transform 0.15s ease;
    display: inline-block;
}
.vb-ninico-header__categories-bar.is-collapsed .vb-ninico-header__categories-icon {
    transform: rotate(90deg);
}
/* VE-006.16 — the categories-topbar. The blade's arbitrary `bg-[#d51243]` is NOT
 * emitted by the served (pre-built) Tailwind sheet, so without this rule the bar
 * rendered white and the (standard) `text-white` label was invisible on it. This
 * scoped rule guarantees the red bar + white label render (structural: makes the
 * "Categories" label visible). Colors/shadow deferred to the styling pass. */
.vb-ninico-header__categories-bar {
    display: flex;
    align-items: center;
    padding: 9px 25px;
    min-height: 50px;
    width: 100%;
    font-size: 16px;
    color: #ffffff;
    font-weight: 600;
    border-radius: 6px 6px 0 0;
    background-color: #d51243;
}

/* VE-006.16 — promo cards: text OVERLAY the image + "Shop Now" as a button.
 * The blade previously stacked text below the image and rendered the CTA as
 * plain (arbitrary-class) red text. The overlay layout + the button background
 * are served here (arbitrary Tailwind classes aren't emitted by the served
 * sheet). Structural; fine spacing/fonts deferred to the styling pass. */
.vb-ninico-header__promo-card {
    position: relative;
    display: block;
    overflow: hidden;
    border-radius: 6px;
    border: 1px solid #e7e7e7;
    min-height: 150px; /* VE-006.16 — prevents image-less card collapse (absolutely-positioned text needs a container) */
}
.vb-ninico-header__promo-img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.vb-ninico-header__promo-scrim {
    position: absolute;
    inset: 0;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.62), rgba(0, 0, 0, 0.12));
}
.vb-ninico-header__promo-body {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    align-items: flex-start;
    gap: 2px;
    padding: 12px;
}
.vb-ninico-header__promo-title {
    font-weight: 600;
    line-height: 1.2;
    color: #ffffff;
}
.vb-ninico-header__promo-subtitle {
    font-size: 0.8rem;
    color: rgba(255, 255, 255, 0.92);
}
.vb-ninico-header__promo-cta {
    display: inline-block;
    margin-top: 6px;
    padding: 5px 12px;
    background-color: #d51243;
    color: #ffffff;
    border-radius: 6px;
    font-weight: 600;
    font-size: 0.8rem;
}

/* ── Ninico header SEPARATE sticky bar (VE-006.19). Rework of the .18 condense:
 *    the .18 approach (condense the main header via .is-sticky) was wrong — the
 *    header disappeared + the wrong elements showed. The ninico source uses a
 *    SEPARATE sticky component (HeaderSticky.js) that slides down from the top.
 *    This is a SEPARATE fixed element (its own DOM), initially hidden above the
 *    viewport, that slides into view via a CSS transition when the sentinel
 *    detects the main header has scrolled past. The main header scrolls away
 *    normally (NOT sticky). Structural only — colors/fonts/exact spacing deferred. */
.vb-ninico-header__sticky {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 99;                    /* ninico .header-sticky */
    background: #ffffff;            /* opaque surface (no content bleed-through) */
    box-shadow: 0 0 60px 0 rgba(0, 0, 0, 0.07); /* ninico .header-sticky shadow */
    transform: translateY(-100%);   /* hidden above the viewport */
    transition: transform 0.3s ease;
}
.vb-ninico-header__sticky.is-visible {
    transform: translateY(0);       /* slides down into view */
}
/* Reduced motion: instant slide (no transition) for users who prefer it. */
@media (prefers-reduced-motion: reduce) {
    .vb-ninico-header__sticky {
        transition: none;
    }
}
/* Internal layout: a single row (logo | menu | search/icons), horizontal, space-between. */
.vb-ninico-header__sticky-inner {
    max-width: 80rem;
    margin: 0 auto;
    padding: 0 1.5rem;
    min-height: 4rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

/* ════════════════════════════════════════════════════════════════════════
 *  VE-006.20 — NINICO HEADER DESIGN-SYSTEM STYLING PASS.
 *  Applies the ninico main.css design tokens (colors / fonts / spacing) to the
 *  `.vb-ninico-header*` hooks. The header STRUCTURE is fixed (B1/B2); this is
 *  the VISUAL layer only (served CSS — no render-path change). The signature
 *  accent is --tp-text-primary #d51243 (the ninico source header rules use
 *  #d51243 everywhere — the Categories bar, Shop Now link, cart/wishlist
 *  badges, nav hover — NOT the blue --tp-theme-1 #3D6CE7 which only appears in
 *  subtle box-shadows). Entirely scoped to `.vb-ninico-header*` so non-ninico
 *  pages are unaffected. (velius-blocks.css loads AFTER the served Tailwind
 *  sheet, so these single-class rules win the cascade against the standard
 *  utilities the blade also carries.)
 *  ════════════════════════════════════════════════════════════════════════ */

/* Font + base color for the whole header tree (font-family inherits to all
 * children; the blade's arbitrary `text-[#333]` is NOT emitted by the served
 * Tailwind sheet, so the base text color is set here). */
.vb-ninico-header,
.vb-ninico-header__sticky {
    font-family: 'Jost', sans-serif; /* ninico --tp-ff-body */
}
.vb-ninico-header {
    color: #040404;                  /* ninico --tp-text-body */
    background-color: #ffffff;
}

/* ── Row-1 search bar (inert styled box — ninico .header-search-bar input):
 *    grey-2 background, 6px radius, 50px tall, icon pinned left:20px centered,
 *    placeholder in grey-1. */
.vb-ninico-header__search {
    position: relative;
    height: 50px;
    background-color: #f3f4f7;       /* ninico --tp-grey-2 */
    border: none;
    border-radius: 6px;
    padding: 5px 45px 5px 45px;
}
.vb-ninico-header__search > span:first-child {
    position: absolute;
    left: 20px;
    top: 50%;
    transform: translateY(-50%);
}
.vb-ninico-header__search-placeholder {
    color: #a5a5a6;                  /* ninico --tp-grey-1 */
}

/* ── Row-1 utility cluster (lang / currency / wishlist / account / cart). */
.vb-ninico-header__utility {
    color: #040404;
}
.vb-ninico-header__lang,
.vb-ninico-header__currency,
.vb-ninico-header__account {
    font-size: 14px;
    font-weight: 600;
}
.vb-ninico-header__cart,
.vb-ninico-header__wishlist {
    font-size: 20px;                /* ninico .header-meta__social a icon size */
}

/* Cart / wishlist count badge (ninico .header-cart span): 15px round, red. */
.vb-ninico-header__cart-count,
.vb-ninico-header__wishlist-count {
    display: inline-block;
    height: 15px;
    min-width: 15px;
    line-height: 15px;
    border-radius: 50%;
    background-color: #d51243;
    color: #ffffff;
    font-size: 12px;
    text-align: center;
    position: absolute;
    top: -4px;
    right: -8px;
    padding: 0 3px;
}

/* ── Row-2 LEFT — the category link list (ninico .cat-menu__list). */
.vb-ninico-header__categories-list {
    padding: 10px 0;
    background-color: #ffffff;
    border: 1px solid #e7e7e7;       /* ninico --tp-border-1 */
    border-top: 0;
}
.vb-ninico-header__categories-link {
    display: flex;
    align-items: center;
    color: #040404;                  /* ninico --tp-text-body */
    font-size: 14px;
    font-weight: 400;
    padding: 9px 25px 5px 25px;
    transition: color 0.2s ease;
}
.vb-ninico-header__categories-link:hover {
    color: #d51243;
}
/* Offers-promo foot (mirrors the list density + accent hover). */
.vb-ninico-header__offers {
    background-color: #ffffff;
    border: 1px solid #e7e7e7;
    border-top: 0;
}
.vb-ninico-header__offers-link {
    color: #040404;
    font-size: 14px;
    transition: color 0.2s ease;
}
.vb-ninico-header__offers-link:hover {
    color: #d51243;
}

/* ── Row-2 CENTER — the main menu (live nav — ninico .main-menu ul>li>a). */
.vb-ninico-header__menu {
    column-gap: 48px;                /* ninico density (source uses 65px; 48px fits the 3-col center column without overflow) */
    row-gap: 8px;
}
.vb-ninico-header__menu-link {
    font-weight: 600;
    font-size: 14px;
    color: #040404;                  /* ninico --tp-text-body */
    line-height: 1;
    transition: color 0.2s ease;
}
.vb-ninico-header__menu-link:hover {
    color: #d51243;
}

/* ── Row-2 RIGHT — contact + store locator (ninico .menu-contact__info). */
.vb-ninico-header__contact {
    font-weight: 600;
    font-size: 14px;
    color: #040404;
}
.vb-ninico-header__store-locator {
    color: #d51243;                  /* signature accent */
    font-weight: 600;
    font-size: 14px;
}

/* Promo card refinements — the overlay layout is locked above (VE-006.16);
 * these tighten the spacing + type to the ninico aesthetic. */
.vb-ninico-header__promo-body {
    gap: 4px;
    padding: 14px;
}
.vb-ninico-header__promo-subtitle {
    font-size: 14px;
}
.vb-ninico-header__promo-cta {
    font-size: 13px;
}

/* ── Sticky bar content (VE-006.19 separate element) — mirrors the main
 *    header's menu / search / utility metrics. */
.vb-ninico-header__sticky-menu {
    column-gap: 40px;
    row-gap: 8px;
}
.vb-ninico-header__sticky-menu-link {
    font-weight: 600;
    font-size: 14px;
    color: #040404;
    line-height: 1;
    transition: color 0.2s ease;
}
.vb-ninico-header__sticky-menu-link:hover {
    color: #d51243;
}
/* Sticky search (inert box — ninico .header-meta__search-5 input). */
.vb-ninico-header__sticky-search {
    position: relative;
    height: 50px;
    width: 200px;
    background-color: transparent;
    border: 1px solid #e7e7e7;       /* ninico --tp-border-1 */
    border-radius: 6px;
    padding: 2px 10px 2px 45px;
}
.vb-ninico-header__sticky-search > span:first-child {
    position: absolute;
    left: 18px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 14px;
    color: #040404;
}
.vb-ninico-header__sticky-search-placeholder {
    font-size: 14px;
    color: #999999;                  /* ninico --tp-text-5 placeholder */
}
/* Sticky utility cluster + cart/wishlist badge (same metrics as main header). */
.vb-ninico-header__sticky-utility {
    color: #040404;
}
.vb-ninico-header__sticky-account {
    font-size: 14px;
    font-weight: 600;
}
.vb-ninico-header__sticky-cart,
.vb-ninico-header__sticky-wishlist {
    font-size: 20px;
}
.vb-ninico-header__sticky-cart-count,
.vb-ninico-header__sticky-wishlist-count {
    display: inline-block;
    height: 15px;
    min-width: 15px;
    line-height: 15px;
    border-radius: 50%;
    background-color: #d51243;
    color: #ffffff;
    font-size: 12px;
    text-align: center;
    position: absolute;
    top: -4px;
    right: -8px;
    padding: 0 3px;
}

/* ════════════════════════════════════════════════════════════════════════
 *  VE-006.21 — NINICO HEADER STYLING CORRECTIONS (7 areas).
 *  Pure CSS/markup pass — NO functional/wiring/data-model change. Applies the
 *  ninico main.css design tokens to the `.vb-ninico-header*` hooks: (1) category
 *  icons + offers bg, (2) the promo welcome strip, (3) slider dots/card/button,
 *  (4) FA utility icons + inline contact, (5) promo-card (CTA hidden, fonts,
 *  parallax), (6) band alignment, (7) slide copy. velius-blocks.css loads AFTER
 *  the served Tailwind sheet, so these single-class rules win the cascade. NO
 *  Tailwind arbitrary-value classes here (the .15 lesson — the served sheet does
 *  not emit bg-[#xxx] / text-[14px]); plain CSS scoped to the hooks only.
 *  ════════════════════════════════════════════════════════════════════════ */

/* The ninico btn-arrow nudge (main.css @keyframes iconarrow): the arrow shifts
 * right, blinks out, re-enters from the left. Reused by the slide-btn + the
 * promo "Shop Now" link on hover. */
@keyframes vb-iconarrow {
    49% { transform: translateX(30%); }
    50% { opacity: 0; transform: translateX(-30%); }
    51% { opacity: 1; }
}

/* VE-006.23 — fix the flash-before-animate bug. The .22 stagger keys off
 *    .vb-ninico-header__slide.is-active with animation-fill-mode:both, so the
 *    0% keyframe (opacity:0) only applies ONCE .is-active is present. But
 *    sync() (velius-blocks.js) mirrors is-active from a DEBOUNCED (80ms) scroll
 *    handler — not synchronously like real Swiper's .swiper-slide-active — so
 *    there is a visible window where the new slide is on-screen but is-active
 *    has not yet been toggled, and the content's default opacity:1 shows as a
 *    flash before the stagger's fill-mode:both hides it. Fix: make the content
 *    hidden by default; the stagger animation (or the reduced-motion path) is
 *    the ONLY thing that reveals it. No flash is then possible.
 *
 *    Scoped to html.js-enabled so a no-JS visitor still sees the copy (the §4.4
 *    progressive-enhancement guarantee — without JS, is-active is never added,
 *    so a blanket opacity:0 would hide the content permanently). Scoped to
 *    prefers-reduced-motion: no-preference so users who request reduced motion
 *    see the content immediately (the existing reduce-block sets
 *    animation:none on the stagger targets; without this no-preference guard,
 *    the default opacity:0 + animation:none would leave the content hidden). */
@media (prefers-reduced-motion: no-preference) {
    html.js-enabled .vb-ninico-header__slide .vb-ninico-header__slide-eyebrow,
    html.js-enabled .vb-ninico-header__slide .vb-ninico-header__slide-title,
    html.js-enabled .vb-ninico-header__slide .vb-ninico-header__slide-subtitle,
    html.js-enabled .vb-ninico-header__slide .vb-ninico-header__slide-btn {
        opacity: 0;
    }
}

/* VE-006.22 — ninico staggered fade-in/move-up on slide-become-active
 *    (ninico main.css 2824-2855 + animate.css fadeInUp 1176-1190). The
 *    active slide's eyebrow → title → subtitle → button animate in with a staggered
 *    fade + 20px upward move. Triggered by .is-active on the slide (added
 *    by velius-blocks.js sync()). */
@keyframes vb-fadeInUp {
    0%   { opacity: 0; transform: translateY(20px); }
    100% { opacity: 1; transform: translateY(0); }
}
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-eyebrow,
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-title,
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-subtitle,
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-btn {
    animation-fill-mode: both;
    animation-name: vb-fadeInUp;
}
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-eyebrow {
    animation-delay: 0.6s;
    animation-duration: 0.8s;
}
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-title {
    animation-delay: 0.8s;
    animation-duration: 1s;
}
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-subtitle {
    animation-delay: 0.9s;
    animation-duration: 1s;
}
.vb-ninico-header__slide.is-active .vb-ninico-header__slide-btn {
    animation-delay: 1s;
    animation-duration: 1.2s;
}

/* ── AREA 2 — the promo welcome strip. The canonical topbar is the SEPARATE
 *    `promo_bar` block (VE-006.11, rendered at header.promo ABOVE the ninico
 *    header), mirroring ninico's `.header-top.space-bg`. That block's blade
 *    emits arbitrary Tailwind classes (bg-[#f4eee7] / text-[#e23744]) which the
 *    served (pre-built) Tailwind sheet does NOT emit (the .15 lesson) — so the
 *    strip would render unstyled (white bg, default-color CTA). These served
 *    rules target the block's real `.block-promo-bar` class + its CTA `<a>` to
 *    reproduce the ninico source values (main.css lines 815-1199): cream
 *    --tp-theme-2 bg, 16px/600 welcome text, accent #d51243 "Shop Now" with a
 *    1px underline + an arrow that nudges on hover. velius-blocks.css loads
 *    AFTER the served Tailwind sheet, so these win the cascade. The block's
 *    markup (PromoBarBlockTest locks the arbitrary classes survive) is untouched.
 *    ───────────────────────────────────────────────────────────────────────── */
.block-promo-bar {
    background-color: #f3eee7;            /* ninico --tp-theme-2 (.space-bg) */
    color: #040404;                       /* ninico --tp-text-body */
    font-size: 16px;                      /* ninico .header-welcome-text span */
    font-weight: 600;
    border-bottom: 1px solid #ece6df;
}
.block-promo-bar p {
    font-size: 16px;
    font-weight: 600;
}
.block-promo-bar a {
    color: #d51243;                       /* ninico .header-welcome-text a (accent) */
    font-weight: 600;
    text-decoration: underline;           /* ninico a::before 1px underline */
    text-underline-offset: 2px;
    display: inline-flex;
    align-items: center;
}
/* Arrow appended via CSS (no blade change to promo_bar — stays in scope): the
 * ninico CTA carries `fal fa-long-arrow-right`; a Unicode arrow reproduces the
 * affordance without a markup edit. */
.block-promo-bar a::after {
    content: "\2192";                     /* →  (U+2192 RIGHTWARDS ARROW) */
    margin-left: 8px;                     /* ninico .header-welcome-text a i margin-left */
    transition: transform 0.25s ease;
}
.block-promo-bar a:hover::after {
    transform: translateX(3px);           /* subtle nudge toward the destination */
}

/* ── AREA 4 — search + utility FA icons. The blade swapped the emoji/`>span>:
 *    first-child` icon for an `<i>` hook, so the absolute positioning moves to
 *    the hook (the old `> span:first-child` selector no longer matches). ───── */
.vb-ninico-header__search-icon {
    position: absolute;
    left: 20px;
    top: 50%;
    transform: translateY(-50%);
    color: #040404;
    font-size: 16px;
}
.vb-ninico-header__sticky-search-icon {
    position: absolute;
    left: 18px;
    top: 50%;
    transform: translateY(-50%);
    color: #040404;
    font-size: 14px;
}
/* Utility icons (cart/wishlist/account) — uniform #040404 at ~20px (matches the
 * existing .vb-ninico-header__cart/.wishlist font-size). */
.vb-ninico-header__utility .fa-solid,
.vb-ninico-header__utility .fa-regular,
.vb-ninico-header__sticky-utility .fa-solid,
.vb-ninico-header__sticky-utility .fa-regular {
    color: #040404;
    font-size: 20px;
}

/* ── AREA 4 — inline contact row (ninico .menu-contact: phone + Find Store
 *    side by side). Phone first, Find Store to its right; both icons accent. */
.vb-ninico-header__contact-row {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    column-gap: 20px;
    row-gap: 6px;
}
.vb-ninico-header__contact {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.vb-ninico-header__store-locator {
    display: inline-flex;
    align-items: center;
    gap: 8px;
}
.vb-ninico-header__contact i,
.vb-ninico-header__store-locator i {
    color: #d51243;                       /* ninico .menu-contact__icon */
    font-size: 15px;
}

/* ── AREA 1 — per-category icons (ninico .cat-menu__list li>a i: 32px slot,
 *    accent #d51243) + the offers-section muted bg (ninico .daily-offer uses
 *    --tp-border-2 #f6f6f6, with 600-weight links). ────────────────────────── */
.vb-ninico-header__categories-link .vb-ninico-header__categories-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    flex: none;
    color: #d51243;
    font-size: 16px;
}
.vb-ninico-header__offers {
    padding: 14px 0;                       /* ninico .daily-offer — EQUAL top/bottom */
    background-color: #f6f6f6;             /* ninico --tp-border-2 */
}
.vb-ninico-header__offers-list {
    list-style: none;
    margin: 0;
    padding: 0;
}
.vb-ninico-header__offers-item {
    list-style: none;
    padding: 3px 20px 3px 25px;            /* ninico .daily-offer ul li — LEFT 25px aligns w/ category icon start */
    margin: 0;                             /* ninico li spacing is padding-only (no sibling margin) */
}
.vb-ninico-header__offers-link {
    font-weight: 600;                      /* ninico .daily-offer ul li a */
}

/* ── AREA 3 — slider dots, ninico Swiper bar style (main.css 2793-2822). The
 *    dots container now carries vb-carousel__dots so the html.js-enabled
 *    show-rule applies; this scoped override repositions it as an inside-bottom
 *    overlay (the base .vb-carousel__dots rule is a below-carousel strip with
 *    margin-top:0.75rem — overridden here) + restyles the dots as 25×4px bars.
 *    Inactive = common-black 10% opacity; active = accent #d51243, full. ────── */
.vb-hero-carousel .vb-carousel__dots {
    position: absolute;
    bottom: 15px;
    left: 0;
    right: 0;
    z-index: 9;
    justify-content: center;
    gap: 0;
    margin: 0;
    padding: 0;
    list-style: none;
    text-align: center;
}
.vb-hero-carousel .vb-carousel__dot {
    width: 25px;
    height: 4px;
    border-radius: 5px;
    background: #000000;                  /* ninico --tp-common-black */
    opacity: 0.1;
    margin: 0 5px;
    border: 0;
    padding: 0;
    transition: opacity 0.25s ease, background 0.25s ease;
}
.vb-hero-carousel .vb-carousel__dot.is-active {
    background: #d51243;                  /* ninico --tp-text-primary (accent) */
    opacity: 1;
}

/* ── AREAS 3 + 7 — the slide image-as-card + copy + "Shop Now" button. The
 *    image is a rounded card (border-radius:6px) filling the slide; the copy
 *    sits in an absolute left/vertically-centered block; the button is the
 *    ninico .tp-btn (white/dark base → accent/white on hover with the arrow
 *    nudge). A light LEFT scrim replaces the heavy black overlay so the dark
 *    copy stays legible on arbitrary data-driven images. ──────────────────── */
.vb-ninico-header__slide-media {
    position: relative;
    overflow: hidden;
    border-radius: 6px;                   /* ninico .tp-slide-item__img img */
    min-height: 450px;
    background-color: #f3f4f7;            /* placeholder surface while the image loads */
}
@media (max-width: 767px) {
    .vb-ninico-header__slide-media { min-height: 320px; }
}
.vb-ninico-header__slide-img {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 6px;
}
.vb-ninico-header__slide-scrim {
    position: absolute;
    inset: 0;
    border-radius: 6px;
    background: linear-gradient(90deg, rgba(255, 255, 255, 0.72) 0%, rgba(255, 255, 255, 0.32) 38%, rgba(255, 255, 255, 0) 62%);
}
.vb-ninico-header__slide-content {
    position: absolute;                   /* ninico .tp-slide-item__content */
    top: 50%;
    left: 0;
    transform: translateY(-50%);
    z-index: 2;
    max-width: 60%;
    padding: 0 20px 0 60px;
}
@media (max-width: 1199px) {
    .vb-ninico-header__slide-content { padding-left: 35px; max-width: 70%; }
}
@media (max-width: 767px) {
    .vb-ninico-header__slide-content { padding-left: 25px; max-width: 88%; }
}
.vb-ninico-header__slide-eyebrow {
    font-size: 20px;                      /* ninico .tp-slide-item__sub-title */
    font-weight: 400;
    color: #d51243;                       /* accent */
    margin: 0 0 5px;
    line-height: 1.2;
}
.vb-ninico-header__slide-title {
    font-size: 50px;                      /* ninico .tp-slide-item__title */
    font-weight: 600;
    line-height: 1.2;
    color: #040404;                       /* --tp-text-body */
    margin: 0;
}
.vb-ninico-header__slide-subtitle {
    font-size: 16px;
    color: #555555;
    margin: 12px 0 0;
    line-height: 1.5;
}
@media (max-width: 767px) {
    .vb-ninico-header__slide-eyebrow { font-size: 16px; }
    .vb-ninico-header__slide-title { font-size: 26px; }
}
.vb-ninico-header__slide-btn {
    display: inline-block;                /* ninico .tp-btn */
    margin-top: 24px;
    padding: 17px 33px;
    border-radius: 6px;
    background: #ffffff;
    color: #040404;
    font-size: 14px;
    font-weight: 600;
    line-height: 1.2;
    text-decoration: none;
    transition: color 0.2s ease, background-color 0.2s ease;
}
.vb-ninico-header__slide-btn:hover,
.vb-ninico-header__slide-btn:focus-visible {
    color: #ffffff;
    background-color: #d51243;            /* hover inverts to accent */
}
.vb-ninico-header__slide-btn i {
    margin-left: 10px;                    /* ninico .tp-btn i */
}
.vb-ninico-header__slide-btn:hover i,
.vb-ninico-header__slide-btn:focus-visible i {
    animation: vb-iconarrow 0.4s linear;
}

/* ── AREA 5 — promo cards. ninico .tpslider-banner has NO visible CTA (only
 *    sub-title + title overlay). The CTA span STAYS in the markup (a VE-006.16
 *    regression lock asserts its presence + its CSS rule) but is hidden here so
 *    the visual matches ninico. Sub-title accent 16px, title 20px/600 dark,
 *    content top-left. Parallax zoom on hover. ────────────────────────────── */
.vb-ninico-header__promo-cta {
    display: none;
}
.vb-ninico-header__promo-body {
    justify-content: flex-start;          /* ninico top-left overlay (was bottom-end) */
    padding: 30px;                        /* ninico .tpslider-banner__content */
}
/* FIELD-MAP NOTE: the seeder sends title=>'Hand made' (the red eyebrow — ninico's
 * .tpslider-banner__sub-title) + subtitle=>'New Modern Stylist Crafts' (the dark
 * headline — ninico's .tpslider-banner__title). So __promo-title is styled as the
 * EYEBROW (accent/16/400) + __promo-subtitle as the HEADLINE (dark/20/600) to
 * match the ninico visual. The blade renders title-first (eyebrow) then subtitle
 * (headline) — the correct reading order. ─────────────────────────────────── */
.vb-ninico-header__promo-title {
    font-size: 16px;                      /* ninico .tpslider-banner__sub-title (eyebrow) */
    font-weight: 400;
    color: #d51243;                       /* accent */
}
.vb-ninico-header__promo-subtitle {
    font-size: 20px;                      /* ninico .tpslider-banner__title (headline) */
    font-weight: 600;
    color: #040404;                       /* --tp-text-body */
}
/* Light scrim so the dark promo copy reads on arbitrary images (the prior dark
 * scrim suited white text; the ninico-faithful dark title needs a light wash). */
.vb-ninico-header__promo-scrim {
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.78), rgba(255, 255, 255, 0.32) 55%, rgba(255, 255, 255, 0.12));
}
.vb-ninico-header__promo-img {
    transition: transform 0.3s ease;      /* ninico .tpslider-banner__img */
}
.vb-ninico-header__promo-card:hover .vb-ninico-header__promo-img,
.vb-ninico-header__promo-card:focus-within .vb-ninico-header__promo-img {
    transform: scale(1.05);               /* ninico hover scale(1.05) */
}

/* ── AREA 6 — row-2 band alignment. align-items:stretch makes the 3 columns
 *    equal height (= the tallest, the slider). The categories nav is a flex
 *    column whose list grows (flex:1) to push the offers section to the bottom,
 *    aligning the sidebar bottom with the slider bottom. The aside's promo
 *    cards distribute (flex:1) to fill the column. ─────────────────────────── */
.vb-ninico-header__band {
    align-items: stretch;
}
.vb-ninico-header__categories {
    display: flex;
    flex-direction: column;
}
.vb-ninico-header__categories-list {
    flex: 1 1 auto;
}
.vb-ninico-header__aside {
    justify-content: flex-start;
}
.vb-ninico-header__promo-card {
    flex: 1 1 auto;
}

/* Reduced motion: drop the hover micro-animations + the parallax zoom + the slide stagger. */
@media (prefers-reduced-motion: reduce) {
    .vb-ninico-header__slide-btn:hover i,
    .vb-ninico-header__slide-btn:focus-visible i { animation: none; }
    .block-promo-bar a::after { transition: none; transform: none; }
    .vb-ninico-header__promo-img { transition: none; }
    .vb-ninico-header__promo-card:hover .vb-ninico-header__promo-img,
    .vb-ninico-header__promo-card:focus-within .vb-ninico-header__promo-img { transform: none; }
    .vb-ninico-header__slide.is-active .vb-ninico-header__slide-eyebrow,
    .vb-ninico-header__slide.is-active .vb-ninico-header__slide-title,
    .vb-ninico-header__slide.is-active .vb-ninico-header__slide-subtitle,
    .vb-ninico-header__slide.is-active .vb-ninico-header__slide-btn {
        animation: none;
    }
}

/* ══ VE-007.3 — SECTION HEADING ══
 * Ports the ninico `.tpsection__title` + `.tpsection__title span` + the
 * `section-animation` keyframes (ninico main.css lines 1088–1117 + 696). The
 * ninico source ships an <img> shape as the underline; since we cannot
 * redistribute the jpg, the EFFECT is replicated with a pure-CSS ::after bar
 * on the transition span, animated by the ported `vb-section-underline`
 * keyframes (named with the Velius `vb-` convention — NOT the ninico
 * `section-animation` name). ALL styling lives here (the partial emits
 * semantic class hooks only — no Tailwind arbitrary classes). */
.block-section-heading {
    font-family: 'Jost', sans-serif;       /* ninico --tp-ff-body (matches header) */
    margin: 0;                             /* fills .page-content; spacing is the placement wrapper's job (VE-007.2) */
}
.vb-section-heading--left   { text-align: left; }
.vb-section-heading--center { text-align: center; }
.vb-section-heading--right  { text-align: right; }

/* The title — ports `.tpsection__title`: near-black, 600 weight, 26px
 * (22px below 767px). The base `heading` span inherits this color + weight. */
.vb-section-heading__title {
    font-size: 26px;                       /* ninico .tpsection__title */
    color: #040404;                        /* ninico --tp-text-body */
    font-weight: 600;                      /* ninico .tpsection__title */
    line-height: 1.2;
    margin: 0;
}
@media (max-width: 767px) {
    .vb-section-heading__title { font-size: 22px; }   /* ninico @media (max-width: 767px) */
}

/* The base "heading" span inherits the title's #040404 / 600 — kept inline so
 * it sits on the same line as the transition span. */
.vb-section-heading__text {
    display: inline;
}

/* The accent "heading_transition" span — ports `.tpsection__title span`: the
 * ninico accent red, a LIGHTER weight (300), tightened letter-spacing. The
 * font-size inherits 26px/22px from the title. This span carries the looping
 * underline ::after. */
.vb-section-heading__transition {
    position: relative;
    display: inline-block;
    font-weight: 300;                      /* ninico .tpsection__title span (lighter than the base 600) */
    color: #d51243;                        /* ninico --tp-text-primary (the accent red) */
    letter-spacing: -1px;                  /* ninico .tpsection__title span */
}

/* The looping underline — a pure-CSS port of ninico's <img> shape. An
 * absolutely-positioned bar at the bottom of the transition span that grows
 * width 0→100%, holds, fades opacity→0, then resets (2s infinite), mirroring
 * the ninico `section-animation` keyframes verbatim. */
.vb-section-heading__transition::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: 2px;                           /* sits just under the accent text */
    width: 0;
    height: 3px;
    background-color: #d51243;
    animation: vb-section-underline 2s infinite;
}
@keyframes vb-section-underline {
    0%  { width: 0; }
    15% { width: 100%; }
    85% { opacity: 1; }
    90% { width: 100%; opacity: 0; }
    to  { width: 0; opacity: 0; }
}
/* ══ END VE-007.3 — SECTION HEADING ══ */

/* VE-007.3 — reduced motion: disable the section-heading underline loop.
 *    Placed AFTER the base animation rule (source order wins at equal
 *    specificity — the base rule is `.vb-section-heading__transition::after`
 *    = (0,1,1); this identical selector also = (0,1,1), so it must come LATER
 *    in source to override). Mirrors the slide-stagger guard's end-of-file
 *    placement. Degraded state = no underline; the accent color still shows
 *    emphasis. */
@media (prefers-reduced-motion: reduce) {
    .vb-section-heading__transition::after { animation: none; }
}

/* ══ VE-bx-7b — NAV BEHAVIOR FIELDS + THE {{menu}} MARKER FRAGMENT ══ */

/* (C) submenu_trigger = 'hover' (class vb-nav--hover): the submenu opens on
 *    item hover instead of the native <details> click. Uses the modern
 *    ::details-content pseudo (Chromium 131+/Gecko 130+) to surface the
 *    closed <details> content slot; the list itself is then shown only while
 *    the item is hovered. PROGRESSIVE ENHANCEMENT: without ::details-content
 *    support nothing changes — the native click-open lane keeps working
 *    (summary stays clickable). Static, hand-authored CSS (NOT a Vite input
 *    — rides the rsync with no build).
 */
.vb-nav--hover .vb-nav__item--has-children { position: relative; }
.vb-nav--hover .vb-nav__submenu::details-content { content-visibility: visible; }
.vb-nav--hover .vb-nav__submenu > summary { cursor: default; }
.vb-nav--hover .vb-nav__submenu > .vb-nav__submenu-list {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    z-index: 40;
}
.vb-nav--hover .vb-nav__item--has-children:hover .vb-nav__submenu > .vb-nav__submenu-list,
.vb-nav--hover .vb-nav__item--has-children:focus-within .vb-nav__submenu > .vb-nav__submenu-list {
    display: block;
}

/* (C) submenu_side = 'right' (class vb-nav--side-right): the open submenu
 *    list anchors to the parent item's right edge instead of dropping below
 *    it (the sidebar/vertical lane's natural companion). Composable with
 *    either trigger mode.
 */
.vb-nav--side-right .vb-nav__item--has-children { position: relative; }
.vb-nav--side-right .vb-nav__submenu-list {
    position: absolute;
    left: 100%;
    top: 0;
    z-index: 40;
}

/* (A) The {{menu}} marker fragment (custom code): the documented, class-
 *    stable MenuTree shape rendered at eligible text-node marker sites —
 *    ul.vb-menu / li.vb-menu__item / a.vb-menu__link / ul.vb-menu__submenu.
 *    Minimal, nav-vocabulary defaults; authors restyle via their scoped CSS.
 */
.vb-menu {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.25rem;
    list-style: none;
    margin: 0;
    padding: 0;
}
.vb-menu__item { margin: 0; position: relative; }
.vb-menu__link {
    display: inline-flex;
    align-items: center;
    padding: 0.5rem 0.75rem;
    color: inherit;
    text-decoration: none;
    font-size: 0.95rem;
    border-radius: 0.375rem;
}
.vb-menu__link:hover { background: rgba(0, 0, 0, 0.05); }
.vb-menu__submenu {
    list-style: none;
    margin: 0;
    padding: 0.25rem 0;
    min-width: 10rem;
    background: #fff;
    border: 1px solid rgba(0, 0, 0, 0.1);
    border-radius: 0.375rem;
    box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}
/* ══ END VE-bx-7b ══ */

/* ══ VE-brand2-4 — DARK-MODE LOGO PAIRING (2026-08-28) ══ */

/* (A) The nav block's site-logo lane (builder/blocks/nav.blade.php): when
 *    the dark slot is SET the partial renders BOTH plain imgs — the lane img
 *    gains .vb-brand-logo--light, a second img carries
 *    .vb-brand-logo--dark — and these static rules swap them on
 *    prefers-color-scheme: dark. JS-free (no Alpine on the served path; a
 *    <picture>/<source> swap is sanitizer-illegal — neither element is on
 *    the frozen allowlist). Both imgs keep .vb-nav__brand-logo (the sizing
 *    rule above), so the modifiers govern VISIBILITY only. Unset dark ⇒ the
 *    bare class renders byte-identically to the pre-rung shape (no second
 *    img, no modifier class — pinned in BrandDarkModePairingTest). The dark
 *    lane displays inline-block (an img's default) so the swap never
 *    collapses the <a> line box.
 */
.vb-brand-logo--dark { display: none; }
@media (prefers-color-scheme: dark) {
    .vb-brand-logo--light { display: none; }
    .vb-brand-logo--dark { display: inline-block; }
}

/* (B) The Breeze pair (layouts/guest + layouts/navigation) — the SAME swap
 *    for their own (non-vb-) class vocabulary, riding their existing
 *    utility-class styling. Canonical copy lives HERE (the contract's
 *    append); the Breeze layouts load a SELF-CONTAINED static vehicle
 *    (public/vendor/velius-brand-dark.css — 4 mirrored rules) instead of
 *    linking this 82KB vendor file with its GLOBAL [hidden] !important rule
 *    into the Breeze/admin-classic surfaces — the rung summary's
 *    router-directive-#2 route choice.
 */
.brand-logo--dark { display: none; }
@media (prefers-color-scheme: dark) {
    .brand-logo--light { display: none; }
    .brand-logo--dark { display: inline-block; }
}
/* ══ END VE-brand2-4 ══ */
