/* Tendlio - storefront (Experimental).
   Self-contained recommendation cards (.tendlio-bt-card): we render our own card
   markup, NOT the theme's product template, so no theme grid can override the slider
   width (the "how many to show" count now works on every theme). We still show the
   real image, the theme's price HTML and the standard WC / YITH / theme badges, so it
   stays integrated. Kept small: just the card + slider styling.
   Card baseline last audited under sweep 7f1d-s2. */

.tendlio-bt {
    margin: 2.5em 0;
    clear: both;
    /* Anchor for the desktop prev/next pair (2026-09-01). It has to be the SECTION and
       not the carousel: .swiper is overflow:hidden, which is what drew the old buttons
       6px short of a circle at left:-6px. Nothing else in here re-anchors - the card
       badges and the add-to-cart circle sit on .tendlio-bt-card / __media, both of
       which are positioned already, and the gift band's arrows on .tendlio-bt__slider. */
    position: relative;
}
.tendlio-bt__title {
    margin: 0 0 1em;
}

/* Slider layout. Recommendation rows use Swiper (bundled locally, no CDN) - it owns the
   slidesPerView / breakpoints / dots / touch + drag, exactly like the old plugin, so it
   works the same on every theme. The free-gift band keeps a tiny native scroll row.
   The cards themselves are our own markup (never the theme's li.product), so no theme
   grid can fight the layout - that's what fixes "how many to show" everywhere. */
.tendlio-bt__slider { position: relative; }

/* --- Free-gift band: a lightweight native scroll-snap row (Swiper is overkill for a
   few reward cards). The recommendation rows use Swiper instead - see .tendlio-swiper. --- */
.tendlio-bt__list.tendlio-bt--slider {
    display: flex !important;
    flex-wrap: nowrap !important;
    align-items: flex-start;
    gap: 10px;
    margin: 0;
    padding: 0 0 6px;
    list-style: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.tendlio-bt__list.tendlio-bt--slider::-webkit-scrollbar { width: 0; height: 0; display: none; }
.tendlio-bt__list.tendlio-bt--slider > li.tendlio-gift-card { scroll-snap-align: start; }

/* --- Recommendation carousel (Swiper sizes the slides via slidesPerView). We only add
   room for the dots and tidy the slide box + the bullet color (theme-inherit).
   The .swiper.tendlio-swiper prefix is load-bearing (R14-GLOB-02): the vendor
   stylesheet is now the SCOPED copy, whose selectors all gained one .tendlio-swiper
   class - a plain .tendlio-swiper override would lose to them on shared properties
   (the vendor's padding:0 would eat our dots room, its bottom:8px would lift the
   dots). One extra class on ours restores the old "ours loads later, ours wins". */
.swiper.tendlio-swiper { position: relative; padding-bottom: 26px; }
.swiper.tendlio-swiper .swiper-wrapper { align-items: flex-start; } /* natural slide heights, don't stretch */
.swiper.tendlio-swiper .swiper-slide { height: auto; box-sizing: border-box; }
.swiper.tendlio-swiper .swiper-pagination { bottom: 0; line-height: 0; }
/* Inactive dots are CONTROLS (clickable), so they answer to non-text contrast:
   .35 could not pass 3:1 on any light theme; .55 still reads "inactive" and can
   (R14-A11Y-03). Active stays .9. */
.swiper.tendlio-swiper .swiper-pagination-bullet { background: currentColor; opacity: .55; }
.swiper.tendlio-swiper .swiper-pagination-bullet-active { opacity: .9; }
/* Pre-init slide width (R14-CWV-02): before storefront.js runs, Swiper's own CSS
   lays ONE slide across the full row (width:100%), and init then collapses it to
   1/N - the section's height jumped by a screenful and everything below moved.
   Size the slides from the same counts the init will use (inline --tendlio-cols
   vars printed by ProductRow::render), so the pre-init layout already matches.
   Swiper stamps swiper-initialized on the container at init; after that its
   inline per-slide widths take over and these rules stop matching. */
.tendlio-swiper:not(.swiper-initialized) .swiper-wrapper { display: flex; }
.tendlio-swiper:not(.swiper-initialized) .swiper-slide {
    flex-shrink: 0;
    width: calc(100% / var(--tendlio-cols, 6));
}
@media (max-width: 768px) {
    .tendlio-swiper:not(.swiper-initialized) .swiper-slide {
        width: calc(100% / (var(--tendlio-cols-m, 2) + 0.2));
    }
}

/* The card itself (theme-independent): centered, image on top with the standard
   badges absolutely placed over it, a two-line clamped name and the price below. */
.tendlio-bt-card { position: relative; box-sizing: border-box; }
.tendlio-bt-card__imglink { display: block; }
.tendlio-bt-card__body { display: block; text-align: center; text-decoration: none; color: inherit; }
.tendlio-bt-card__media { position: relative; display: block; overflow: hidden; border-radius: 8px; }
.tendlio-bt-card__media img {
    width: 100%; height: auto; display: block; border-radius: 8px; transition: opacity .2s;
    -webkit-user-drag: none; user-select: none; /* so a desktop image-drag doesn't fight drag-to-scroll */
}
.tendlio-bt-card:hover .tendlio-bt-card__media img { opacity: .92; }
/* Place 3rd-party / core loop badges (YITH WCBM, WC .onsale) over the image. On cart /
   checkout some badge plugins don't enqueue their own CSS, so anchor them here. */
.tendlio-bt-card__media > .yith-wcbm-badge,
.tendlio-bt-card__media > .onsale {
    position: absolute; top: 8px; left: 8px; z-index: 5; margin: 0; pointer-events: none;
}
/* Add-to-cart overlay (bottom-right of the image). Hover-reveal on desktop, always
   visible on touch. Reuses the WC ajax loop button under the hood (see ProductRow). */
.tendlio-bt-card__atc {
    position: absolute; bottom: 8px; right: 8px; z-index: 4;
    width: 36px; height: 36px; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    background: rgba(255, 255, 255, 0.96); border: 0;
    color: #333; font-size: 18px; line-height: 1; text-decoration: none; cursor: pointer;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
    opacity: 0; transform: translateY(4px); transition: opacity .2s, transform .2s, background .2s;
}
.tendlio-bt-card:hover .tendlio-bt-card__atc { opacity: 1; transform: translateY(0); }
.tendlio-bt-card__atc:hover { background: #fff; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.18); }
.tendlio-bt-card__atc.added { background: #e8f5e9; color: #2e7d32; }
@media (hover: none) { .tendlio-bt-card__atc { opacity: 0.92; transform: none; } }
/* While WooCommerce (or the theme) ajax-adds, some themes inject their loading spinner
   positioned for THEIR loop card - on XStore it landed bottom-left of our card instead
   of on the button. Clip anything to our circle, hide the cart glyph while loading, and
   force the injected spinner pseudo to sit in the centre of the button (the button
   already centres flex items; we leave transform alone so the spin keeps running). */
.tendlio-bt-card__atc { overflow: hidden; }
.tendlio-bt-card__atc.loading > span:not(.tendlio-spin),
.tendlio-bt-card__atc.loading > svg { display: none; }
/* The theme's / WooCommerce's own ajax spinner is a ::after pseudo that the theme
   positions for ITS loop card, so on our round button it landed in a corner (Alex).
   Kill it and show our own spinner element, dead-centre, in place of the cart glyph.
   storefront.js injects/removes .tendlio-spin on the clicked button. */
.tendlio-bt-card__atc.loading::before,
.tendlio-bt-card__atc.loading::after { display: none !important; content: none !important; }
.tendlio-bt-card__atc .tendlio-spin {
    display: block;
    width: 16px; height: 16px; border-radius: 50%;
    border: 2px solid currentColor; border-top-color: transparent;
    animation: tendlio-spin .6s linear infinite;
}
@keyframes tendlio-spin { to { transform: rotate(360deg); } }
/* The "View cart" text link WooCommerce appends after a successful add looks broken on a
   small round icon button; hide it (the button turns green via .added to confirm). */
.tendlio-bt-card__media a.added_to_cart { display: none !important; }
.tendlio-bt-card__name {
    font-size: 14px; margin: 8px 4px 4px; line-height: 1.35;
    overflow: hidden; text-overflow: ellipsis;
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
}
/* Weight 600 on the price, 400 on the name: the price is the one number a shopper
   scans a row for, and at the same size and weight as the name it read as a
   footnote (measured on Alex's own home page 2026-09-07: theme cards carried
   18px/600 prices next to our 14px/400, on the same screen). Size stays with the
   name so the card keeps its height on every theme; only the weight separates them. */
.tendlio-bt-card__price { display: block; font-size: 14px; font-weight: 600; margin: 0 4px; opacity: .9; }
/* .75, not .6 (R14-A11Y-03): the struck old price is information, and .6 of a
   typical theme's text color lands around 3.5:1 on white - under the 4.5:1 bar.
   Regular weight, so the old price does not compete with the one that applies. */
.tendlio-bt-card__price del { opacity: .75; margin-right: 4px; font-weight: 400; }
.tendlio-bt-card__price ins { text-decoration: none; }

/* Desktop prev/next arrows. Hidden by default; storefront.js shows them only when
   the row overflows and toggles [hidden] at each end. Mobile uses swipe, no arrows.
   The mark inside is an SVG and the box centres it with flex, not with line-height:
   see .tendlio-chev below for why a character could never be centred here. */
.tendlio-bt__arrow {
    position: absolute; top: 38%; z-index: 2;
    align-items: center; justify-content: center;
    width: 36px; height: 36px; padding: 0; border-radius: 50%;
    border: 1px solid rgba(0, 0, 0, 0.12); background: #fff; color: #111;
    cursor: pointer;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15); display: none;
}
.tendlio-bt__slider.tendlio-bt--has-overflow .tendlio-bt__arrow:not([hidden]) { display: flex; }
.tendlio-bt__arrow--prev { left: -6px; }
.tendlio-bt__arrow--next { right: -6px; }

/* Swiper carousels: a prev/next pair in the heading, beside the title (Alex, from four
   rendered variants 2026-08-23). They used to hover-reveal over the row's edges, and
   that cost three things at once: until the pointer arrived nothing said the row
   scrolled, phones got no arrows at all (no hover), and .swiper is overflow:hidden so a
   button at left:-6px rendered 6px short of a circle. Here they cover no product, need
   no gutter the theme may not have, and behave the same on a phone.

   Swiper's Navigation module drives them (initSwiper looks them up from the section
   now, not the carousel). It adds swiper-button-disabled at either end and, through
   watchOverflow, swiper-button-lock when everything already fits: locked means the row
   does not scroll, so the pair goes away entirely. */
.tendlio-bt__title--nav {
    display: flex; align-items: center; gap: 8px;
}
/* The title keeps the theme's heading look and takes only the width its words need, so
   the pair sits BESIDE it - which is the whole point of moving them here.
   flex: 1 1 auto did the opposite on a wide screen: the span stretched across the row
   and pushed the buttons to the far right. Measured on Alex's own product page at
   2048px: the title ended at x=557 and the arrows began at x=1695, 1138px of empty
   space between a control and the thing it belongs to (2026-08-30). It still SHRINKS,
   so a long title on a phone gives way rather than pushing the pair off the line. */
.tendlio-bt__titletext { flex: 0 1 auto; min-width: 0; }
.tendlio-swiper__arrow {
    display: inline-flex; align-items: center; justify-content: center;
    position: static; flex: 0 0 auto;
    width: 34px; height: 34px;
    transition: opacity .2s, background-color .2s;
}
.tendlio-swiper__arrow:hover { background: #f5f5f5; }
/* The chevron itself. It used to be the character U+2039 / U+203A, and that can never
   sit in the middle of a round button: a character is placed by the FONT, and every
   CSS way of centring one (line-height, flex) centres the LINE BOX, not the ink.
   Measured on Alex's store 2026-09-01: the theme's Futura drew it 3.5px ABOVE centre
   in a 34px circle, while the identical markup in the WP admin font drew it 2px BELOW
   - opposite errors from one build, because the theme owns the font and we do not.
   The SVG's path is centred on its viewBox instead, so the flex box above centres the
   ink exactly, on any theme (measured after: 0.00px on both axes). */
.tendlio-chev { display: block; flex: none; width: 16px; height: 16px; }
/* Disabled at an end: dimmed and inert, but still there, so the pair does not jump
   between one and two buttons as you scroll the row. */
.tendlio-swiper__arrow.swiper-button-disabled { opacity: .3; pointer-events: none; }
.tendlio-swiper__arrow.swiper-button-lock { display: none; }
/* Scoped to the gift band's own slider (2026-08-23): the carousel's pair carries the
   same look class, and a phone is where it matters most - there is no hover to reveal
   anything, and the swipe is the one gesture a shopper may not think to try. */
@media (max-width: 768px) {
    .tendlio-bt__slider .tendlio-bt__arrow { display: none !important; }
}

/* Desktop, pointer devices only: the pair leaves the heading and hover-reveals on the
   row's own left and right edges (Alex 2026-09-01, revising the 2026-08-23 "always
   beside the title" call). This is a SPLIT by device, not a return to the old look.
   The three costs that moved them into the heading are each answered:
     - a phone had no arrows at all, because there is no hover to reveal them. That is
       why this is gated on (hover: hover) and not on width: a touch laptop in a
       desktop-sized window reports hover: none and keeps the visible pair beside its
       title, which is exactly the device that used to lose it.
     - .swiper is overflow:hidden, so a button at left:-6px was drawn 6px short of a
       circle. These are positioned from the SECTION, which clips nothing, and they sit
       inside the row's edges rather than in a gutter the theme may not have.
     - nothing on screen says the row scrolls until the pointer arrives. Alex took that
       trade knowingly for the desktop; the dots still show, and touch keeps the pair.
   769 is the far side of the 768 line the rest of this file already draws (the gift
   band's mobile rule and the gift card's mobile flex-basis) and that storefront.js
   draws in Swiper's own breakpoint map. A narrow desktop WINDOW keeps the title pair
   too: a hover-reveal over a two-card row hides more than it offers. */
@media (hover: hover) and (min-width: 769px) {
    .tendlio-swiper__arrow {
        position: absolute;
        /* The band to sit in the middle of is the CARD row, not the section, whose top
           is the merchant's heading set in the merchant's font. CSS cannot name another
           element's box, so storefront.js measures the slide wrapper and writes
           --tendlio-arrow-y here. The 50% fallback (no JS, no ResizeObserver) lands on
           the section's middle, which is off by half the heading - the same "a margin
           living inside the wrapper made top:50% measure the wrong box" class that put
           the admin tab strip's arrows 10px low (0.1.870). */
        top: var(--tendlio-arrow-y, 50%);
        transform: translateY(-50%);
        z-index: 3;
        pointer-events: none;
    }
    /* Hidden by opacity, never by display or visibility: both of those take the button
       out of the tab order, and Swiper's lock/disabled handling with it.
       The disabled dim below is a TWO-class selector, so it outranks a one-class
       opacity:0 and left the prev arrow sitting at 30% with no pointer within reach of
       the row - measured at rest in the harness before this line existed. Naming it
       here costs one selector and removes a whole state that looked like a bug. */
    .tendlio-swiper__arrow,
    .tendlio-swiper__arrow.swiper-button-disabled { opacity: 0; }
    /* Positive insets on purpose: negative ones need a gutter the theme may not have. */
    .tendlio-swiper__arrow--prev { left: 8px; }
    .tendlio-swiper__arrow--next { right: 8px; }
    /* :focus alongside :focus-visible (R14-A11Y-06): a control hidden by opacity is
       still focusable, so the keyboard has to be able to reveal it - programmatic focus
       and browsers that apply :focus-visible narrowly included. */
    .tendlio-bt:hover .tendlio-swiper__arrow,
    .tendlio-swiper__arrow:focus,
    .tendlio-swiper__arrow:focus-visible { opacity: 1; pointer-events: auto; }
    /* Revealing is more specific than the base disabled rule, so an end arrow would
       come back to full strength on hover without this line. A row that already fits
       shows nothing at all: watchOverflow's swiper-button-lock is display:none above,
       and no opacity here can undo that. */
    .tendlio-bt:hover .tendlio-swiper__arrow.swiper-button-disabled { opacity: .3; pointer-events: none; }
}

/* In-stock alternatives: the out-of-stock prompt near the (greyed-out) cart area.
   Theme-inherit: currentColor picks up the theme's own text color. */
.tendlio-alt-oos { margin: 12px 0; }
.tendlio-alt-oos a {
    display: inline-block; font-weight: 600; text-decoration: none;
    padding: 8px 14px; border: 1px solid currentColor; border-radius: 6px;
}
.tendlio-alt-oos a:hover { text-decoration: underline; }

/* Deals page [tendlio_deals]: the grid is the theme's own ul.products; we only
   add the count / Load more controls around it. */
.tendlio-deals__summary { margin: 0 0 1em; opacity: 0.75; font-size: 0.95em; }
/* Our own grid (not the theme's ul.products), so columns tile cleanly on every theme
   and Load more sits centered below. --tendlio-deals-cols comes from the
   shortcode/setting (prefixed, R14-GLOB-05: every name entering the merchant's
   document carries ours). */
.tendlio-deals__grid {
    display: grid;
    grid-template-columns: repeat(var(--tendlio-deals-cols, 4), minmax(0, 1fr));
    gap: 18px;
    margin: 0 0 8px;
}
@media (max-width: 880px) { .tendlio-deals__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 640px) { .tendlio-deals__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tendlio-deals__more { text-align: center; margin: 22px 0; }
.tendlio-deals__btn.is-loading { opacity: 0.75; cursor: progress; }
.tendlio-deals__fallback { margin: 8px 0 0; opacity: 0.72; font-size: 0.9em; }
.tendlio-deals-empty { padding: 20px 0; }

/* Smart 404 suggestions block. */
.tendlio-404-suggest { margin: 28px auto; max-width: 1200px; }

/* "Claim your free gift" on the cart. A distinct REWARD band (not a recommendation
   row): a tinted, bordered box at the top of the cart with smaller cards, so it
   reads as "you've earned a gift", not a third "shop more" slider. Reuses the slider
   shell so the scroll-snap + arrows still work; cards are custom (free + Claim). */
.tendlio-giftslider {
    margin: 0 0 1.8em;
    padding: 14px 18px 10px;
    /* Edge derived from the theme's own text colour, not from black: on a dark theme a
       black-on-dark border is invisible and the band loses the frame that makes it read
       as "you've earned a gift" (3.81). The rgba line above stays as the fallback for
       browsers without color-mix, where a light theme is the safer assumption. */
    border: 1px solid rgba(0, 0, 0, 0.12);
    border-color: color-mix(in srgb, currentColor 20%, transparent);
    border-radius: 12px;
    background: rgba(255, 196, 0, 0.07); /* faint gift tint, subtle on most themes */
}
.tendlio-giftslider .tendlio-bt__title { margin: 0 0 0.6em; font-size: 1.05em; }
.tendlio-bt__list.tendlio-bt--slider > li.tendlio-gift-card {
    flex: 0 0 calc(100% / var(--tendlio-cols, 6));
    max-width: none; width: auto; margin: 0;
    scroll-snap-align: start; list-style: none;
    text-align: center; padding: 4px;
}
@media (max-width: 768px) {
    .tendlio-bt__list.tendlio-bt--slider > li.tendlio-gift-card {
        flex-basis: calc(100% / (var(--tendlio-cols-m, 2) + 0.3));
    }
}
.tendlio-gift-card__img img { width: 100%; height: auto; border-radius: 6px; display: block; }
.tendlio-gift-card__name { font-size: 14px; margin: 5px 0 2px; line-height: 1.2; }
.tendlio-gift-card__free { font-size: 14px; font-weight: 700; letter-spacing: .05em; opacity: .85; margin-bottom: 5px; }
.tendlio-gift-claim { width: 100%; padding: 4px 6px; font-size: 14px; }
/* Why a gift claim was refused. Printed by storefront.js from the server's own
   sentence; sits under the row so a long line can't distort the card slider. */
.tendlio-gift-msg {
    margin: 10px 0 0;
    padding: 8px 12px;
    border-radius: 6px;
    /* Same reason as the band's border: a black tint on a dark theme is no tint at all,
       and the box explaining why a gift was refused disappears into the page (3.81). The
       text keeps the theme's own colour, so tinting from currentColor keeps the pair
       related instead of half-inherited. */
    background: rgba(0, 0, 0, .05);
    background: color-mix(in srgb, currentColor 8%, transparent);
    font-size: 14px;
    line-height: 1.45;
    text-align: center;
}
.tendlio-cartrec-note {
    margin: 16px 0;
    padding: 10px 12px;
    border-left: 3px solid #4a90d9;
    background: #f4f8fc;
    color: #3c434a;
    font-size: 14px;
}
/* The link inside this note is the whole point of it, and on plenty of themes a
   link in body copy is the same color as the text around it, so it read as plain
   words (Alex). This note is admin-only and already paints its own colors, so it
   states the link's look instead of inheriting one. */
.tendlio-cartrec-note a {
    color: #2271b1;
    text-decoration: underline;
    font-weight: 600;
}
.tendlio-cartrec-note a:hover,
.tendlio-cartrec-note a:focus { color: #135e96; }

/* Kept LAST so these overrides win (same rule as admin.css): a visitor who turned
   motion off at the OS level gets no slide/hover/reveal animation from our widgets
   (R14-A11Y-02). The add-to-cart spinner keeps spinning - it is the only signal an
   add is in flight, and it is brief. Swiper's slide speed + the gift band's smooth
   scroll are handled in storefront.js (JS-driven motion ignores CSS here). */
@media (prefers-reduced-motion: reduce) {
    .tendlio-bt-card__media img,
    .tendlio-bt-card__atc,
    .tendlio-swiper__arrow,
    .tendlio-swiper .swiper-wrapper { transition: none; }
    .tendlio-bt-card__atc { transform: none; }
}
