/* styles.css — shared layout/elements/components foundation.
   Currently loaded only by /produkty (src/templates/page_products_catalog.php and its
   components), NOT sitewide — see plan for the future sitewide migration. Every rule here
   is written generically (no /produkty-specific assumptions) so it can be "lifted" as-is
   into that future shared layer without rewriting anything.

   Each section below is a verified-used subset copied from assets/css/common.css — not a
   full copy. If something looks missing, it's because this page genuinely doesn't use it,
   not an oversight. (No scroll-reveal animation here on purpose — not used going forward.) */

:root {
    --bg-body: #f8f4ee;
    --bg-card: #ffffff;
    --text-dark: #2d3436;
    --text-light: #636e72;
    --danger: #e0848f;
    --success: #00b894;
    --radius-card: 15px;
    --radius-btn: 50px;
    --shadow-card: 0 10px 30px rgba(0, 0, 0, 0.1);
    --shadow-hover: 0 15px 35px rgba(0, 0, 0, 0.2);
    --font-main: 'Quicksand', sans-serif;
    --max-width: 1400px;
    --header-height: 70px;

    --btn-green: #73b7ab;
    --btn-green-hover: #5fa095;
    --btn-green-text: #ffffff;

    --btn-white: #ffffff;
    --btn-white-hover: #f9f9f9;
    --btn-white-text: #2d3436;

    --btn-apricot: var(--clr-apricot);
    --btn-apricot-hover: #fab1a0;
    --btn-apricot-text: #ffffff;

    --clr-green: #73b7ab;
    --clr-apricot: #f5b6a3;
    --clr-blue: #80cacd;
    --clr-purple: #bdabc4;
    --clr-beige: #f8f4ee;
    --clr-beige-hover: #e0d8c8;

    --clr-strawberry-pink: #e0848f;
    --clr-beach-sand: #eeba9b;
    --clr-buzzing-bees: #f3ee9c;
    --clr-paradise-green: #bcd5a3;
    --clr-aruba-blue: #96cacb;
    --clr-baltic-sea: #87b4d4;
    --clr-scented-levander: #c9b6d4;
    --clr-flushing-pink: #f1cddb;
}

/* --- Reset & Global --- */
* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

html {
    scroll-behavior: smooth;
    scroll-padding-top: 100px;
}

html,
body {
    position: relative;
    width: 100%;
}

body {
    font-family: var(--font-main);
    background-color: var(--bg-body);
    color: var(--text-dark);
    line-height: 1.6;
    min-height: 100vh;
    display: flex;
    justify-content: center;
    padding: 2rem;
}

@media (max-width: 768px) {
    body {
        padding: 0;
        background-color: var(--bg-card);
    }
}

a {
    text-decoration: none;
    color: inherit;
    transition: color 0.3s ease;
}

ul {
    list-style: none;
}

img {
    max-width: 100%;
    display: block;
}

/* Hide number input spinners globally */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* --- App Card Container --- */
.app-card {
    background-color: var(--bg-card);
    width: 100%;
    max-width: var(--max-width);
    border-radius: var(--radius-card);
    box-shadow: var(--shadow-card);
    min-height: 90vh;
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: clip;
}

@media (max-width: 768px) {
    .app-card {
        border-radius: 0;
        box-shadow: none;
        min-height: 100vh;
    }
}

.container {
    width: 100%;
    margin: 0 auto;
    padding: 0 2rem;
}

@media (max-width: 768px) {
    .container {
        padding: 0 1rem;
    }
}

/* Shared page-section wrapper — vertical spacing only (every section puts its own .container
   inside for horizontal padding/width, so this never doubles up with it). Use on every
   <section> on the new-architecture pages (homepage, /kontakt, /odbornici, …) instead of a
   one-off class per section with its own slightly different padding number. */
.section {
    padding: 2rem 0;
}

.section--beige {
    background-color: var(--clr-beige);
}

@media (max-width: 768px) {
    .section {
        padding: 1rem 0;
    }
}

/* --- Typography --- */
.section-title {
    text-align: left;
    font-size: 2.5rem;
    font-weight: 700;
    margin-bottom: 2rem;
    margin-top: 2rem;
    position: relative;
    padding-left: 2rem;
    color: var(--text-dark);
}

@media (max-width: 768px) {
    .section-title {
        text-align: center;
        padding-left: 0;
        font-size: 1.8rem;
    }
}

/* Multi-line editable heading (one \n-separated line per <span>) — both lines text-dark,
   differentiated by weight: first line 900 and larger (the "hook"), every line after it 500
   and smaller. render_heading_lines() in helpers.php. Shared across every page that uses it
   (homepage sections + /kontakt), not homepage-only, so it lives here rather than in
   home.css. */
.heading-lines {
    display: flex;
    flex-direction: column;
    margin: 0 0 0.75rem;
}

.heading-line {
    font-size: 1.4rem;
    font-weight: 500;
    line-height: 1.3;
    color: var(--text-dark);
}

/* Name is a holdover from when this line was green — it's now just "the bolder/bigger first
   line", same color as the second line, differentiated by weight (900 vs 500) instead. */
.heading-line--green {
    font-size: 1.8rem;
    font-weight: 900;
    color: var(--text-dark);
}

/* Centered variant — same heading-lines/heading-line sizing/colors, just centered instead of
   the left-aligned default. */
.heading-lines--center {
    align-items: center;
    text-align: center;
    margin: 0 auto 1.5rem;
}

@media (max-width: 900px) {
    .heading-lines {
        align-items: center;
    }
}

@media (max-width: 768px) {
    /* .heading-line--green must come after the base .heading-line rule — equal specificity
       means source order decides which wins on an element carrying both classes, and with
       these reversed the green (main) line was actually rendering at the smaller size instead
       of bigger than the secondary line. */
    .heading-line {
        font-size: 1.15rem;
    }

    .heading-line--green {
        font-size: 1.6rem;
    }
}

/* --- Buttons --- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0.8rem 2rem;
    border-radius: 50px;
    font-weight: 600;
    font-family: var(--font-main);
    cursor: pointer;
    transition: all 0.3s ease;
    border: none;
    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
    font-size: 1rem;
    line-height: 1;
    text-decoration: none;
}

.btn-primary {
    background-color: var(--btn-green);
    color: var(--btn-green-text);
}

.btn-primary:hover {
    background-color: var(--btn-green-hover);
    transform: translateY(-2px);
}

.btn-large {
    padding: 1rem 3rem;
    font-size: 1.1rem;
}

.btn-secondary {
    background-color: var(--btn-white);
    color: var(--btn-white-text);
}

.btn-secondary:hover {
    background-color: var(--btn-white-hover);
    transform: translateY(-2px);
}

.btn-tertiary {
    background-color: var(--btn-apricot);
    color: var(--btn-apricot-text);
}

.btn-tertiary:hover {
    background-color: var(--btn-apricot-hover);
    transform: translateY(-2px);
}

/* --- Toast Notification --- */
.toast-container {
    position: fixed;
    top: 20px;
    right: 20px;
    z-index: 10000;
    display: flex;
    flex-direction: column;
    gap: 10px;
    pointer-events: none;
}

.toast {
    background-color: #fff;
    color: var(--text-dark);
    padding: 1rem 1.5rem;
    border-radius: 12px;
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.15);
    font-weight: 600;
    font-size: 0.95rem;
    opacity: 0;
    transform: translateY(-20px) scale(0.9);
    transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
    pointer-events: auto;
    display: flex;
    align-items: center;
    border-left: 5px solid var(--success);
    min-width: 250px;
}

.toast.show {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.toast.error {
    border-left-color: var(--danger);
}

/* --- Product Cards (Shared, "full" variant — ProductCardRenderer::renderFull) --- */
.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    gap: 2rem;
    justify-content: center;
    justify-items: center;
    margin: 0 auto;
    max-width: 1200px;
}

.product-card {
    background: var(--clr-beige);
    border-radius: 16px;
    border: 2px solid var(--clr-beige);
    transition: all 0.3s ease;
    display: flex;
    flex-direction: column;
    width: 100%;
    max-width: 350px;
    height: 100%;
}

.product-card:hover {
    transform: translateY(-5px);
}

.product-card-top {
    position: relative;
    width: 100%;
    aspect-ratio: 1/1;
    background: white;
    border-radius: 14px 14px 0 0;
    flex-shrink: 0;
}

.product-img-link {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    display: block;
    overflow: hidden;
    border-radius: 14px 14px 0 0;
}

.product-card-top img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
}

.product-card:hover .product-card-top img {
    transform: scale(1.05);
}

/* Label 1: horizontal ribbon from the right edge */
.label-ribbon-right {
    position: absolute;
    top: 20px;
    right: -8px;
    background: var(--clr-purple);
    color: white;
    padding: 6px 15px 6px 15px;
    font-size: 0.8rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    z-index: 10;
    filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.15));
}

.label-ribbon-right::before {
    content: '';
    position: absolute;
    top: 0;
    left: -15px;
    width: 15px;
    height: 100%;
    background: var(--clr-purple);
    clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 15px 50%);
}

.label-ribbon-right::after {
    content: '';
    position: absolute;
    bottom: -8px;
    right: 0;
    border-top: 8px solid #97889c;
    border-right: 8px solid transparent;
}

/* Label 2: pill exactly above the image/content boundary */
.label-pill-center {
    position: absolute;
    bottom: 0px;
    left: 50%;
    transform: translate(-50%, 50%);
    background: var(--clr-purple);
    color: white;
    padding: 6px 16px;
    font-size: 0.8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    border-radius: 50px;
    z-index: 5;
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
    white-space: nowrap;
}

.ribbon-wrapper {
    position: absolute;
    top: 0;
    left: 0;
    width: 200px;
    height: 200px;
    overflow: hidden;
    z-index: 2;
    pointer-events: none;
}

.ribbon {
    position: absolute;
    top: 30px;
    left: -60px;
    width: 240px;
    background: var(--clr-strawberry-pink);
    color: white;
    text-align: center;
    transform: rotate(-45deg);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
    padding: 10px 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    line-height: 1.1;
}

/* Label 3 — the only thing the ribbon shows now; discount info moved to the price pill. */
.ribbon-label3 {
    font-size: 0.7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: block;
    max-width: 80%;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Mobile-only compact pills for label_1/2/3, shown above the rating instead of the
   desktop ribbon/pill overlays on the image (see the mobile media query below, which
   hides those overlays and shows this instead). */
.product-card-labels-mobile {
    display: none;
}

.card-label-pill {
    display: inline-block;
    background: var(--clr-purple);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    padding: 2px 8px;
    border-radius: 20px;
    margin: 0 4px 4px 0;
}

/* Name/rating/price/stock block — desktop: normal vertical flow below the image.
   Mobile: sits to the right of the (now 40%-width) image, see the grid layout below. */
.product-card-info {
    padding: 1rem 1rem 0;
    text-align: center;
}

/* Review stars (half-star capable, CSS width-clip overlay) */
.star-rating {
    position: relative;
    display: inline-block;
    line-height: 1;
    white-space: nowrap;
}

.star-rating-bg { color: #ddd; }

.star-rating-fg {
    position: absolute;
    top: 0;
    left: 0;
    overflow: hidden;
    white-space: nowrap;
    color: #f5c518;
}

.prs-score {
    font-size: .9rem;
    font-weight: 700;
    color: #333;
    margin-left: 4px;
    line-height: 1;
}

.product-card-review-stars {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 3px;
    font-size: .85rem;
    margin-top: 8px;
    margin-bottom: 4px;
}

.prs-count-static {
    font-size: .8rem;
    color: #888;
    margin-left: 2px;
    line-height: 1;
}

.product-card-info h3 {
    font-size: 1rem;
    margin-bottom: 0.4rem;
    font-weight: 700;
    color: var(--text-dark);
    display: -webkit-box;
    -webkit-line-clamp: 1;
    line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    min-height: calc(1rem * 1.6 * 1);
}

.product-card-info h3 a {
    color: inherit;
}

/* Stock + buy button, grouped together and pinned to the card's bottom edge (via
   .product-price-row's margin-top: auto above it) so this group lands at the same
   position on every card in a row, regardless of how long the description runs. */
.product-card-bottom {
    width: 100%;
    padding: 0 1rem 1rem;
}

/* Price — its own flat row (not nested in .product-card-bottom) so mobile can move it
   right under the name while desktop keeps it pinned near the bottom together with the
   stock/button row that follows — see margin-top: auto below and the mobile grid areas.
   min-height reserves the tallest possible content (the 3-line discount pill) so a plain
   price or a shorter "Od" pill still sits vertically centered instead of bottom-anchored,
   and so every card's price row is the same height regardless of what it displays. */
.product-price-row {
    margin-top: auto;
    margin-bottom: 0.8rem;
    padding: 0 1rem;
    min-height: 4rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.price-plain {
    font-size: 1.2rem;
    font-weight: 800;
    color: var(--clr-strawberry-pink);
}

.price-original {
    text-decoration: line-through;
    color: #999;
    font-weight: 400;
    font-size: 0.85rem;
    margin-right: 6px;
}

/* Discounted price: a 3-line pill — discount name (own background), price, savings —
   instead of the plain single-line pill used before. Smaller corner radius since the
   fully-rounded "pill" shape doesn't read well over 3 lines. */
.price-pill {
    display: inline-flex;
    flex-direction: column;
    align-items: stretch;
    text-align: center;
    border-radius: 8px;
    overflow: hidden;
    vertical-align: middle;
    min-width: 120px;
}

.price-pill-top {
    background: var(--clr-buzzing-bees);
    color: var(--text-dark);
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    line-height: 1.15;
    padding: 3px 10px;
}

.price-pill-mid {
    background: var(--clr-strawberry-pink);
    color: #fff;
    font-weight: 800;
    font-size: 1.2rem;
    line-height: 1.15;
    padding: 5px 10px;
}

.price-pill-bottom {
    background: var(--clr-strawberry-pink);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.3px;
    line-height: 1.15;
    padding: 0 10px 4px;
}

/* "Od <price>" accessory-bundle pill — just the one line, so its price line needs its
   own (taller) padding instead of the 3-line pill's top-shaved .price-pill-mid padding. */
.price-pill--from .price-pill-mid {
    padding: 6px 10px;
}

/* Stock — 2 lines with icons: availability, then a static "ships in 24h" line. Rendered
   by ProductCardRenderer::stockHtml() (catalog-specific, not the shared
   format_stock_status() used elsewhere on the site). */
.stock {
    display: flex;
    flex-direction: column;
    gap: 2px;
    font-size: 0.78rem;
    font-weight: 700;
}

.stock-line {
    display: flex;
    align-items: center;
    gap: 4px;
}

.stock-line svg {
    flex-shrink: 0;
}

.stock.stock-in .stock-line:first-child {
    color: var(--clr-green);
}

.stock.stock-low .stock-line:first-child {
    color: #ff9800;
}

.stock.stock-out .stock-line:first-child {
    color: var(--clr-strawberry-pink);
}

.stock.stock-digital .stock-line:first-child {
    color: var(--clr-green);
}

.stock-ship {
    color: var(--text-light);
    font-weight: 500;
    font-size: 0.72rem;
}

/* Description — its own row below the image+info, full card width. */
.product-short-desc {
    padding: 0 1rem;
    font-size: 0.9rem;
    color: var(--text-light);
    line-height: 1.4;
    min-height: calc(0.9rem * 1.4 * 4);
    flex-shrink: 0;
    display: -webkit-box;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
}

.product-short-desc p {
    display: inline;
    margin: 0;
}

/* Card action row — stock left, "Do košíku" button right. .has-qty is toggled by
   common.js's renderQtyControl()/renderAddToCartButton() when the product is/isn't in
   the cart (swaps the button for a qty stepper in the same .js-cart-action-container). */
.product-actions-wrapper {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.product-actions-wrapper .stock {
    text-align: left;
}

/* Explicit height AND width (not content-driven) so .qty-control (height:100%/width:100%)
   can exactly fill the same box .btn-buy occupies when it swaps in, instead of each state
   sizing itself independently and visibly changing the row's shape. Card context only
   (the product detail page's buy-actions overrides this to flex:1 1 50%, see product.css) —
   a relative width here, not a fixed px one, so it stays proportionate to the card instead
   of ballooning toward half the card's width on narrow cards. */
.product-actions-wrapper .js-cart-action-container {
    flex-shrink: 0;
    margin-left: auto;
    height: 44px;
    width: 45%;
}

.product-actions-wrapper .btn-buy {
    width: 100%;
}

/* Sized to roughly match the height of the 2-line .stock block next to it, so the row
   reads as one even baseline instead of a tall label beside a small button. */
.btn-buy {
    height: 44px;
    padding: 0 1.5rem;
    font-size: 0.95rem;
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 0.4rem;
    white-space: nowrap;
    box-sizing: border-box;
}

.product-actions-wrapper .btn:hover {
    transform: none;
}

.product-actions-wrapper .qty-control {
    height: 100%;
    width: 100%;
}

/* --- Mobile layout: image left (square, ~40% width) + name/rating right, price right
   under the name, description below spanning full width, then stock+button. Grid areas
   instead of relying on DOM order, since the desktop order doesn't match this — the
   top-level children of .product-card are direct grid-item children for exactly this
   reason. "media" spans both the info and price rows (repeated area name = row span). */
@media (max-width: 600px) {
    .product-card {
        display: grid;
        grid-template-columns: 42% 1fr;
        grid-template-areas:
            "media info"
            "media price"
            "labels labels"
            "desc desc"
            "bottom bottom";
        max-width: none;
    }

    .product-card-top {
        grid-area: media;
        /* No bottom-left rounding — the card's own rounded corner is the top-left one.
           Square again (matches the product image everywhere else) now that
           .product-card-info/.product-price-row carry their own min-height — the grid
           rows this spans grow to fit the square instead of it overflowing into the
           "labels" row below, which is what forced the height:100% workaround before
           those reservations existed. */
        aspect-ratio: 1 / 1;
        border-radius: 14px 0 0 0;
    }

    .product-img-link {
        border-radius: 14px 0 0 0;
    }

    /* Desktop's ribbon/pill overlays are replaced by the compact pills below on mobile. */
    .ribbon-wrapper,
    .label-ribbon-right,
    .label-pill-center {
        display: none;
    }

    .product-card-info {
        grid-area: info;
        text-align: left;
        padding: 0.5rem 0.5rem 0;
        min-height: 3rem;
        display: flex;
        flex-direction: column;
        justify-content: center;
    }

    /* Its own row below the image/name/price header, above the description — not nested
       inside .product-card-info any more (see the PHP: flat sibling, own grid area). */
    .product-card-labels-mobile {
        grid-area: labels;
        display: flex;
        flex-wrap: wrap;
        padding: 0 1rem;
        margin-top: 0.75rem;
    }

    /* Name above rating on mobile — reversed from desktop's DOM order (rating, then name)
       via flex order, not a markup change, so desktop stays untouched. */
    .product-card-info .product-card-review-stars {
        order: 2;
        justify-content: flex-start;
        margin-top: 4px;
    }

    /* Name wraps up to 2 lines on mobile, not clamped to 1 — max-height, not a reserved
       min-height: a 1-line name shouldn't leave a gap before the rating below it. */
    .product-card-info h3 {
        order: 1;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        max-height: calc(1rem * 1.6 * 2);
        margin-bottom: 0;
    }

    .product-price-row {
        grid-area: price;
        margin-top: 0;
        margin-bottom: 0.5rem;
        padding: 0 1rem 0 0.75rem;
        align-items: center;
        justify-content: flex-start;
    }

    .price-original {
        font-size: 0.7rem;
    }

    .price-pill {
        min-width: 105px;
    }

    .price-pill-top {
        font-size: 0.62rem;
        padding: 3px 9px;
    }

    .price-pill-mid {
        font-size: 1.15rem;
        padding: 5px 9px;
    }

    .price-pill-bottom {
        font-size: 0.62rem;
        padding: 0 9px 3px;
    }

    .product-short-desc {
        grid-area: desc;
        padding: 0.75rem 1rem;
    }

    .product-card-bottom {
        grid-area: bottom;
        padding: 0 1rem 1rem;
    }
}

/* --- Qty Control (Generic) — the stepper that replaces the buy button once the
   product is in the cart (window.renderQtyControl in common.js) ---
   height:100% — not a fixed pixel value — so it exactly fills whichever context's own
   fixed-height wrapper it's swapped into (.product-actions-wrapper .js-cart-action-container
   on cards, .buy-actions .js-cart-action-container on the product detail page), instead of
   assuming one specific button height. The inner +/- circles stay a fixed comfortable size
   regardless of the container's exact height (card ~44px vs detail page ~50px). */
.qty-control {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0;
    width: fit-content;
    height: 100%;
    box-sizing: border-box;
    background-color: #fff;
    border: 2px solid var(--clr-green);
    border-radius: 30px;
    padding: 0 4px;
    min-width: 110px;
}

.qty-control button.btn-qty {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--clr-apricot);
    color: white;
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: bold;
    font-size: 1.2rem;
    cursor: pointer;
    padding: 0;
    flex-shrink: 0;
    transition: background-color 0.3s ease;
}

.qty-control button.btn-qty:hover {
    background-color: #fab1a0;
}

.qty-control input.qty-input {
    width: 40px;
    text-align: center;
    border: none;
    font-weight: bold;
    font-size: 1.05rem;
    background: transparent;
}

.qty-control input::-webkit-outer-spin-button,
.qty-control input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.qty-control input {
    -moz-appearance: textfield;
    appearance: none;
}

/* --- Contact Section (section_contact.php) ---
   White band (not beige — same white as the page), but the same container/card pattern as
   .catalog-cta-band (product-catalog.css): .container.container-large wrapper (not its own
   max-width — this used to be narrower than .catalog-cta-band's row above it, an
   inconsistent side inset), white cards, no shadow, matching padding/radius/breakpoints.
   Same values mirrored in common.css (every page but product/catalog); home.css no longer
   overrides this section at all. */
.contact-hero-section {
    width: 100%;
    padding: 60px 0;
    background-color: #fff;
}

@media (max-width: 768px) {
    .contact-hero-section {
        padding: 2rem 0;
    }
}

.contact-layout {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1.75rem;
    align-items: stretch;
}

@media (max-width: 900px) {
    .contact-layout {
        grid-template-columns: 1fr;
    }
}

.contact-info,
.contact-form {
    background: #fff;
    border-radius: 20px;
    padding: 2.5rem 2rem;
}

@media (max-width: 768px) {
    .contact-info,
    .contact-form {
        padding: 2rem 1.25rem;
    }
}

.contact-info {
    font-size: 1rem;
    color: var(--text-dark);
}

/* Right column (the form) gets a tinted background so it reads as its own card instead of
   blending into the white section behind it — .contact-info stays white/flat since it's
   already visually separated by its .contact-details boxes. Inputs get a white fill on top
   of that tint so they stand out as fillable fields, not just more background. */
.contact-form {
    background: var(--clr-beige);
}

.contact-form .form-control {
    background: #fff;
    border-color: var(--clr-beige-hover, #e0d8c8);
}

.contact-form label {
    color: var(--text-dark);
}

/* Heading is .section-title (see markup) — the same shared class every other section's
   heading uses (left-aligned/2.5rem desktop, centered/1.8rem mobile inside
   .container.container-large), not a bespoke size for this section alone. */

.contact-info p {
    margin-bottom: 1rem;
    line-height: 1.6;
    color: #666;
}

.contact-details {
    margin-top: 2rem;
    background: var(--clr-beige);
    padding: 1.5rem;
    border-radius: 16px;
    border: 2px solid var(--clr-beige);
}

.contact-details p {
    margin-bottom: 0.5rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-weight: 600;
}

/* Was 3x identical inline style="display:flex;align-items:center;gap:0.5rem;..." in
   section_contact.php — one rule instead. */
.contact-details a {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    color: inherit;
    text-decoration: none;
}

.contact-phone-hours-short {
    font-weight: 400;
    font-size: 0.85rem;
    color: var(--clr-text-muted, #6b6b6b);
}

@media (max-width: 480px) {
    .contact-details p:has(.contact-phone-hours-short) {
        flex-wrap: wrap;
    }

    .contact-phone-hours-short {
        flex-basis: 100%;
        margin-left: 1.75rem;
    }
}

.contact-phone-hours {
    font-weight: 400;
    font-size: 0.8rem;
    color: var(--clr-text-muted, #6b6b6b);
    margin-top: 0.35rem;
}

.contact-form .form-group {
    margin-bottom: 1.5rem;
}

.form-group label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: 600;
    font-size: 0.9rem;
    color: var(--text-dark);
}

.form-control {
    width: 100%;
    padding: 0.8rem 1rem;
    border: 1px solid #ddd;
    border-radius: 8px;
    font-family: var(--font-main);
    font-size: 1rem;
    transition: all 0.3s;
}

.form-control:focus {
    outline: none;
    border-color: var(--clr-green);
    box-shadow: 0 0 0 3px rgba(115, 183, 171, 0.2);
}

textarea.form-control {
    resize: vertical;
    min-height: 120px;
}

/* .contact-info/.contact-form stretch to equal height (.contact-layout's align-items:
   stretch) — the form's own fields are naturally shorter than the info column's text +
   detail boxes, which without this left a dead gap at the bottom of the (invisible,
   border/shadow-free) card. Filling the stretched height and pinning the submit button to
   the bottom makes both columns' visible content actually reach the same bottom edge. */
#contact-form {
    display: flex;
    flex-direction: column;
    height: 100%;
}

/* The message field (last .form-group, holding the textarea) grows into the leftover
   stretched height instead of leaving a dead gap above the button — a bigger textarea to
   write in, not empty space. */
#contact-form .form-group:last-of-type {
    flex: 1;
    display: flex;
    flex-direction: column;
}

#contact-form .form-group:last-of-type textarea {
    flex: 1;
}

.contact-form-submit {
    width: 100%;
}

/* --- Validation Errors (Global) --- */
input.error,
select.error,
textarea.error,
.error-border {
    border-color: #e74c3c !important;
}

.error-message {
    color: #e74c3c;
    font-size: 0.85rem;
    margin-top: 0.25rem;
}

/* =========================================================================
   Components — merged from the former assets/css/components/ folder
   (carousel.css, product-card-compact.css, tabs.css). Kept as clearly
   delimited blocks below so they're easy to eyeball independently. */

/* --- Component: Carousel (carousel.php, carousel.js) ---
   Full-width beige promo/product carousel band. Every card in a row shares one fixed
   "unit" width AND height — a product card is always exactly 1 unit wide (square image +
   text body underneath); an offer card is 1-3 units wide (image fills the entire card, no
   text body) but always the same total height as a product card, so the row never has
   ragged/misaligned cards regardless of which widths are mixed together. */
:root {
    --carousel-unit-w: 200px;
    /* Tall enough for a 3-line discount pill (name/price/savings) + full-height buy
       button in .product-card-compact without the button getting clipped. */
    --carousel-unit-h: 400px;
    --carousel-gap: 16px;
    /* Left/right inset for the first/last card — matches .container's padding so the row
       starts aligned with the heading above it, while the track itself spans edge-to-edge
       and cards scroll all the way out to the true edge. */
    --carousel-edge-inset: 2rem;
}

.carousel-band {
    width: 100%;
    background-color: var(--clr-beige, #f8f4ee);
    padding: 10px 0;
}

/* Per-carousel admin toggle ("Zobrazit béžové pozadí") — same band, no fill, for a
   carousel meant to blend into whatever section it's embedded in. */
.carousel-band--no-bg {
    background-color: transparent;
}

/* Per-carousel admin toggle ("Zrušit boční odsazení karet") — for a carousel embedded in
   narrower surrounding content (e.g. a product detail tab) where the default edge inset
   (matching .container's padding) would sit wider than everything around it. Overriding
   the variable itself (not just .carousel-track's padding) so .carousel-title-wrap, which
   also reads it, stays in sync automatically — a carousel with no edge inset shouldn't
   have its heading indented either. */
.carousel-band--no-edge-inset {
    --carousel-edge-inset: 0px;
}

/* Same side padding as .carousel-track's own edge inset (both read --carousel-edge-inset)
   so the heading always lines up with the row of cards under it, with or without the
   no-edge-inset toggle. Always rendered now (even with no title) since it's also the nav
   arrows' row — .carousel-nav-group's margin-left:auto pushes them to the right whether or
   not a title sits next to them. */
.carousel-title-wrap {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 0 var(--carousel-edge-inset);
    margin-bottom: 16px;
}

.carousel-title {
    font-size: 1.8rem;
    text-align: left;
    margin: 0;
}

.carousel-nav-group {
    display: flex;
    gap: 8px;
    margin-left: auto;
    flex-shrink: 0;
}

.carousel-wrapper {
    position: relative;
}


.carousel-track {
    display: flex;
    align-items: flex-start;
    gap: var(--carousel-gap);
    overflow-x: auto;
    /* No scroll-snap — combined with the track's own left/right padding it made some
       browsers settle a few pixels into the row on load instead of true scrollLeft:0,
       which read as "the carousel starts pre-scrolled". Carousel.js still forces
       scrollLeft:0 on load, but removing snap avoids fighting it. */
    scrollbar-width: none;
    padding: 4px var(--carousel-edge-inset);
}

.carousel-track::-webkit-scrollbar {
    display: none;
}

.carousel-item {
    flex: 0 0 auto;
    width: var(--carousel-unit-w);
    height: var(--carousel-unit-h);
}

.carousel-item-w2 { width: calc(var(--carousel-unit-w) * 2 + var(--carousel-gap)); }
.carousel-item-w3 { width: calc(var(--carousel-unit-w) * 3 + var(--carousel-gap) * 2); }

/* Shared circle-button look for every horizontal track's prev/next controls (and the
   lightbox/modal close button, same shape). .gallery-nav/.lightbox-nav/.sk-modal-close stay
   absolutely positioned overlays on their image; .carousel-nav is different — it sits inline
   in .carousel-title-wrap's row now (always visible, both breakpoints, disabled instead of
   hidden when there's nothing to scroll — see .carousel-nav-group above and carousel.js), so
   its own rule below overrides the shared position/inset with position:static. */
.carousel-nav,
.gallery-nav,
.lightbox-nav,
.sk-modal-close {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid #eee;
    background: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--clr-green, #73b7ab);
    transition: background-color 0.2s ease, color 0.2s ease;
}

.carousel-nav {
    position: static;
    transform: none;
}

.gallery-nav:hover,
.lightbox-nav:hover,
.sk-modal-close:hover {
    background: var(--clr-green, #73b7ab);
    color: #fff;
}

.carousel-nav:hover:not(:disabled) {
    background: var(--clr-green, #73b7ab);
    color: #fff;
}

/* Distinct, unmistakably "nothing to scroll this way" look — no hover color change (see
   :not(:disabled) above), not-allowed cursor, faded. */
.carousel-nav:disabled {
    opacity: 0.35;
    cursor: not-allowed;
    color: #999;
}

/* Offer card: the image fills the whole card edge-to-edge (its aspect ratio follows
   however many units wide it is — only product images are always square), CTA button
   overlaid at the bottom-right — no separate white content area, no shadow. */
.carousel-offer-card {
    position: relative;
    width: 100%;
    height: 100%;
    border-radius: var(--radius-card, 15px);
    overflow: hidden;
    background: var(--clr-beige-hover, #e0d8c8);
}

.carousel-offer-card img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Only present when a dedicated mobile image was picked (see carousel.php) — otherwise
   the single unclassed <img> above covers every breakpoint. */
.carousel-offer-img-mobile {
    display: none;
}

@media (max-width: 768px) {
    .carousel-offer-img-desktop {
        display: none;
    }
    .carousel-offer-img-mobile {
        display: block;
    }
}

.carousel-offer-cta {
    position: absolute;
    right: 12px;
    bottom: 12px;
    z-index: 1;
}

/* A single-position offer card (no room to justify a corner pill) gets the same
   full-width, centered button treatment as a product card, at each breakpoint
   independently (desktop width and mobile width are configured separately). */
.carousel-item-w1 .carousel-offer-cta {
    left: 12px;
    width: calc(100% - 24px);
    justify-content: center;
}

@media (max-width: 768px) {
    :root {
        --carousel-unit-w: 165px;
        --carousel-unit-h: 365px;
        --carousel-gap: 12px;
        --carousel-edge-inset: 1rem;
    }

    .carousel-title {
        font-size: 1.4rem;
    }

    /* An offer card's mobile width (1-3 positions) is configured separately from its
       desktop width in the admin — override the desktop .carousel-item-w2/w3 class here.
       !important because both rules have identical specificity and must win regardless
       of source order. */
    .carousel-item-mw1 { width: var(--carousel-unit-w) !important; }
    .carousel-item-mw2 { width: calc(var(--carousel-unit-w) * 2 + var(--carousel-gap)) !important; }
    .carousel-item-mw3 { width: calc(var(--carousel-unit-w) * 3 + var(--carousel-gap) * 2) !important; }

    .carousel-item-mw1 .carousel-offer-cta {
        left: 12px;
        width: calc(100% - 24px);
        justify-content: center;
    }
}

/* --- Component: Compact product card (carousel product items, ProductCardRenderer::renderCompact) ---
   Always exactly 1 carousel unit wide — image, rating, name, price, button. White, no
   shadow, square edge-to-edge image. (Unrelated to .product-card above, the "full" card.) */
.product-card-compact {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    background: #fff;
    border-radius: var(--radius-card, 15px);
    overflow: hidden;
    border: 1px solid #f0ece3;
}

.pcc-img-link {
    position: relative;
    display: block;
    width: 100%;
    /* Square, sized off the shared unit width so it lines up with the unit height
       budget (unit height - body height). */
    height: var(--carousel-unit-w, 200px);
    flex-shrink: 0;
}

.pcc-img-link img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.pcc-badge {
    position: absolute;
    top: 8px;
    left: 8px;
    background: var(--clr-strawberry-pink, #e0848f);
    color: #fff;
    font-weight: 700;
    font-size: 0.75rem;
    padding: 3px 8px;
    border-radius: 20px;
}

.pcc-body {
    padding: 6px 12px 12px;
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    flex: 1;
    min-height: 0;
    overflow: hidden;
}

/* Tighter than .product-card-review-stars's default margin above — this card is small,
   the full-size rating spacing looks too airy here. */
.pcc-body .product-card-review-stars {
    margin: 2px 0;
}

.pcc-name {
    font-size: 0.85rem;
    margin: 0;
    line-height: 1.3;
    min-height: 2.2em;
    width: 100%;
}

.pcc-name a {
    color: inherit;
    text-decoration: none;
}

/* Price + button anchored together at the bottom of the card (regardless of how tall
   the rating/name block above happens to be) so the price row lines up at the exact
   same height across every card in a row. */
.pcc-bottom {
    margin-top: auto;
    padding-top: 6px;
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* Fixed height, tall enough for the worst case (original price + 3-line discount pill,
   stacked) — so a plain/short price still lands vertically centered instead of sitting at
   the bottom right above the button. Without this, a discounted neighbor card (taller
   price block) pushes its button down further than an undiscounted one's, so the two
   buttons — and the prices right above them — never line up at the same height. */
.pcc-price {
    min-height: 76px;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
}

/* Reuses .price-plain/.price-original/.price-pill (same discounted-price pill treatment
   as the full card), just sized down to fit the compact carousel card. */
.pcc-price .price-plain {
    font-size: 0.95rem;
}

.pcc-price .price-original {
    font-size: 0.75rem;
}

.pcc-price .price-pill {
    min-width: 90px;
}

.pcc-price .price-pill-top {
    font-size: 0.55rem;
    padding: 2px 7px;
}

.pcc-price .price-pill-mid {
    font-size: 0.85rem;
    padding: 5px 7px;
}

.pcc-price .price-pill-bottom {
    font-size: 0.55rem;
    padding: 0 7px 2px;
}

/* Fixed height (not padding-derived) on both buttons below — a padding+line-height combo
   rendered a couple px taller on one than the other depending on whether an icon was
   present, which is exactly the kind of mismatch a shared explicit height avoids. */
.pcc-actions .btn,
.carousel-offer-cta {
    height: 38px;
    box-sizing: border-box;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 0 1.2rem;
    font-size: 0.85rem;
    line-height: 1;
}

.pcc-actions .btn {
    width: 100%;
}

.pcc-actions .btn svg {
    flex-shrink: 0;
}

/* Same 38px the buy button already uses (see .pcc-actions .btn above) — gives the swapped-in
   .qty-control (height:100%, see the generic rule) the same explicit height to fill, same
   convention .product-actions-wrapper uses for full cards. Width:100% too, so it exactly
   replaces the button it swapped out instead of shrinking to content. */
.pcc-actions .js-cart-action-container {
    height: 38px;
    width: 100%;
}

.pcc-actions .qty-control {
    width: 100%;
    min-width: 0;
    padding: 0 3px;
}

/* The generic .qty-control's +/- circles (32px) and input are sized for the full card's
   44px-tall container — scaled down here to match, same proportional top/bottom breathing
   room (~7px) inside this card's smaller 38px button, instead of nearly filling it edge to
   edge. */
.pcc-actions .qty-control button.btn-qty {
    width: 28px;
    height: 28px;
    font-size: 1rem;
}

.pcc-actions .qty-control input.qty-input {
    width: 28px;
    font-size: 0.8rem;
}

/* --- Component: Tabs (tabs.php pattern, tabs.js ResponsiveTabs) ---
   Same visual language as the product detail page tabs (assets/css/product.css
   .tabs-nav/.tab-btn/.tab-content) — kept in sync by eye if either changes.
   Responsive: when the tab row doesn't fit its container, .tabs-bar gets .is-collapsed
   and the row becomes a "Třídit" dropdown instead of wrapping/scrolling. */
.tabs-bar {
    position: relative;
}

/* Opts out of the collapse-to-dropdown behavior (see tabs.js — .tabs-bar--scroll is never
   handed to ResponsiveTabs at all) in favor of a plain horizontally scrollable row — for a
   handful of always-relevant content tabs (e.g. product detail's popis/recenze/...), not a
   "pick one" sort control, a dropdown just hides tabs the visitor would otherwise want to
   see directly. */
.tabs-bar--scroll .tabs-nav {
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.tabs-bar--scroll .tabs-nav::-webkit-scrollbar {
    display: none;
}

.tabs-nav {
    display: flex;
    justify-content: flex-start;
    flex-wrap: nowrap;
    overflow: hidden;
    gap: 0.5rem;
    margin-bottom: 0;
    padding: 0 1rem;
    border-bottom: 2px solid var(--clr-green, #73b7ab);
}

.tab-btn {
    display: flex;
    align-items: center;
    background: var(--clr-beige, #f8f4ee);
    border: none;
    border-radius: 8px 8px 0 0;
    font-family: var(--font-main);
    font-size: 0.95rem;
    font-weight: 600;
    padding: 0.7rem 1.4rem;
    cursor: pointer;
    color: var(--text-dark, #333);
    transition: all 0.3s;
    margin-right: 2px;
    white-space: nowrap;
    flex-shrink: 0;
}

.tab-btn:hover {
    background: var(--clr-beige-hover, #e0d8c8);
    color: var(--text-dark, #333);
}

.tab-btn.active {
    background: var(--clr-green, #73b7ab);
    color: #fff;
    position: relative;
    z-index: 2;
}

.tab-btn svg {
    width: 15px;
    height: 15px;
    margin-right: 6px;
    flex-shrink: 0;
    transition: stroke 0.3s;
}

/* Just a top border, no radius/side borders/padding — the product grid inside gets its
   horizontal spacing purely from the page's own .container, same as every other element
   on the page, instead of an extra padding layer of its own. */
.tab-content {
    max-width: 100%;
    margin-top: 1rem;
    position: relative;
}

.tab-content[aria-busy="true"] {
    opacity: 0.5;
    pointer-events: none;
}

.tabs-sort-toggle {
    display: none;
    align-items: center;
    gap: 8px;
    background: var(--clr-beige, #f8f4ee);
    border: none;
    border-radius: 8px 8px 0 0;
    padding: 0.7rem 1.4rem;
    font-family: var(--font-main);
    font-weight: 600;
    font-size: 0.95rem;
    color: var(--text-dark, #333);
    cursor: pointer;
    white-space: nowrap;
    flex-shrink: 0;
    margin-right: 2px;
}

.tabs-sort-toggle:hover {
    background: var(--clr-beige-hover, #e0d8c8);
}

.tabs-sort-toggle svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.tabs-bar.is-collapsed .tabs-sort-toggle {
    display: flex;
}

/* Dropdown holding whatever tabs aren't the currently active one, once collapsed. */
.tabs-sort-dropdown {
    display: none;
    position: absolute;
    top: 100%;
    left: 1rem;
    margin-top: 6px;
    background: #fff;
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.15);
    padding: 8px;
    gap: 8px;
    flex-direction: column;
    flex-wrap: nowrap;
    z-index: 20;
    min-width: 200px;
}

.tabs-bar.is-open .tabs-sort-dropdown {
    display: flex;
}

.tabs-sort-dropdown .tab-btn {
    border-radius: 8px;
    width: 100%;
    justify-content: flex-start;
    margin-right: 0;
}

/* --- Component: Product gallery + lightbox (components/product_gallery.php,
   components/gallery.js) — sitewide-shared, not page-specific, since any product image
   gallery on any page should look/behave identically. --- */
.detail-gallery {
    position: relative;
    width: 100%;
}

.main-image-wrapper {
    position: relative;
    border-radius: 20px;
    overflow: hidden;
    margin-bottom: 1rem;
    background: #fff;
}

/* Horizontally scroll-snapping track — one slide per image. Native touch/swipe momentum
   gives a smooth, animated transition between images on mobile with zero custom drag
   code; nav buttons (desktop only, see .gallery-nav below) just scroll by one slide. */
.main-image-track {
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.main-image-track::-webkit-scrollbar {
    display: none;
}

/* Height is set per-breakpoint further down (bigger on desktop, a bit smaller on mobile,
   see the media queries below) — a tall product photo no longer stretches the whole
   gallery column; if the image ends up narrower than the column because of the height
   cap, it's centered rather than left-aligned. */
.main-image-slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 600px;
}

.main-image-slide img {
    width: auto;
    max-width: 100%;
    max-height: 100%;
    height: auto;
    display: block;
    object-fit: contain;
    cursor: zoom-in;
}

@media (max-width: 900px) {
    /* Square, not a fixed height — product photos are always square-cropped (same
       convention as the catalog/carousel cards), so a fixed height either let-boxed a big
       blank strip above/below the photo (indistinguishable from real page whitespace,
       since both are plain white) or cropped a taller photo. Aspect-ratio always matches
       the actual image with zero gap either way. */
    .main-image-slide {
        height: auto;
        aspect-ratio: 1 / 1;
    }
}

/* Desktop only — swiping carries the "change image" job on mobile instead. Shares its
   look with .carousel-nav (same white circle + SVG chevron, see that rule above) instead
   of a separate style. .gallery-main (not .main-image-wrapper, which clips to its rounded
   corners via overflow:hidden) is these buttons' positioning parent, so they can sit
   further outside the image without being clipped. */
.gallery-main {
    position: relative;
}

.gallery-nav.prev {
    left: -18px;
}

.gallery-nav.next {
    right: -18px;
}

@media (max-width: 900px) {
    .gallery-nav {
        display: none;
    }
}

/* Small "current/total" badge, mobile only (see .gallery-thumbs below) — bottom-right
   corner of the main image. */
.gallery-counter {
    display: none;
    position: absolute;
    right: 10px;
    bottom: 10px;
    z-index: 2;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    padding: 2px 10px;
    border-radius: 20px;
}

@media (max-width: 900px) {
    .gallery-counter {
        display: block;
    }
}

.gallery-thumbs {
    display: flex;
    gap: 10px;
    overflow-x: auto;
    padding-bottom: 5px;
}

.gallery-thumbs img {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: 10px;
    cursor: pointer;
    opacity: 0.6;
    transition: all 0.3s;
    border: 2px solid transparent;
}

.gallery-thumbs img.thumb-active,
.gallery-thumbs img:hover {
    opacity: 1;
    border-color: var(--clr-green);
}

/* Mobile: swipe + the .gallery-counter badge (see above) replace the thumbnail strip. */
@media (max-width: 900px) {
    .gallery-thumbs {
        display: none;
    }
}

/* --- Component: Modal (components/modal.php + components/modal.js) ---
   Generic modal shell — any page's Modal instance gets this look for free. Two desktop
   size variants (.sk-modal--fullscreen, .sk-modal--dialog); on mobile both always collapse
   to the same bottom sheet (slides up on open, swipe-down or the close button to dismiss —
   see modal.js) instead of their own desktop treatment. No shadow anywhere by design —
   .sk-modal-card gets a hairline-free flat look, distinguished from the backdrop only by
   being opaque white against the dark scrim. */
.sk-modal {
    display: none;
    position: fixed;
    z-index: 10000;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.6);
    justify-content: center;
    align-items: center;
    box-sizing: border-box;
}

.sk-modal.active {
    display: flex;
}

.sk-modal-card {
    position: relative;
    display: flex;
    background: #fff;
    overflow: hidden;
    animation: skModalZoomIn 0.3s;
}

@keyframes skModalZoomIn {
    from {
        transform: scale(0.96);
        opacity: 0;
    }

    to {
        transform: scale(1);
        opacity: 1;
    }
}

/* Floats over the card's top-right corner. transform:none cancels the shared
   .carousel-nav/.gallery-nav/.lightbox-nav rule's top:50%/translateY(-50%) (a vertical
   centering rule that doesn't apply here and would push this half out of the card). */
.sk-modal-close {
    position: absolute;
    top: 16px;
    right: 16px;
    transform: none;
    z-index: 5;
}

.sk-modal-body {
    width: 100%;
    height: 100%;
    min-height: 0;
    overflow: auto;
}

/* Near-fullscreen: the margin around the card (all 4 sides desktop) is the dark scrim. */
.sk-modal--fullscreen {
    padding: 15px;
}

.sk-modal--fullscreen .sk-modal-card {
    width: 100%;
    height: 100%;
    border-radius: 20px;
}

/* Centered, content-sized dialog (e.g. an info box) — not full height. */
.sk-modal--dialog .sk-modal-card {
    width: 90%;
    max-width: 560px;
    max-height: 85vh;
    border-radius: 16px;
    padding: 32px;
    box-sizing: border-box;
}

@media (max-width: 900px) {
    /* Bottom sheet, app-style, for BOTH desktop size variants: flush to the bottom/sides, a
       gap (and rounded corners) only at the top so it visibly reads as a card that slid up
       rather than a plain swap of the whole screen — slides up on open and back down on
       close/swipe-down (see .sk-modal-open below and modal.js) instead of the desktop
       zoom/fade. */
    .sk-modal--fullscreen,
    .sk-modal--dialog {
        padding: 20px 0 0;
        align-items: flex-end;
    }

    .sk-modal--fullscreen .sk-modal-card,
    .sk-modal--dialog .sk-modal-card {
        width: 100%;
        height: auto;
        /* Not calc(100% - 20px) — the parent's content box already excludes its own
           20px padding-top, so subtracting it again here left a doubled 40px gap. */
        max-height: 100%;
        max-width: none;
        border-radius: 20px 20px 0 0;
        animation: none;
        transform: translateY(100%);
        transition: transform 0.3s ease;
    }

    .sk-modal--fullscreen.active .sk-modal-card {
        height: 100%;
    }

    .sk-modal.sk-modal-open .sk-modal-card {
        transform: translateY(0);
    }
}

.lightbox-body {
    display: flex;
    width: 100%;
    height: 100%;
}

.lightbox-main {
    position: relative;
    flex: 1;
    min-width: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    box-sizing: border-box;
}

.lightbox-image-wrap {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Same scroll-snap mechanism as .main-image-track/.main-image-slide — see gallery.js. */
.lightbox-track {
    display: flex;
    width: 100%;
    height: 100%;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.lightbox-track::-webkit-scrollbar {
    display: none;
}

.lightbox-slide {
    flex: 0 0 100%;
    scroll-snap-align: start;
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.lightbox-content {
    max-width: 100%;
    max-height: 100%;
    object-fit: contain;
}

.lightbox-counter {
    position: absolute;
    left: 12px;
    bottom: 12px;
    background: rgba(0, 0, 0, 0.55);
    color: #fff;
    font-size: 0.8rem;
    font-weight: 600;
    padding: 3px 12px;
    border-radius: 20px;
}

.lightbox-nav.prev {
    left: 12px;
}

.lightbox-nav.next {
    right: 12px;
}

/* Right side panel (desktop) — the other images in this gallery, click to jump. Wider,
   with generous side padding, and thumbs themselves render smaller than the panel (not
   100% of it) — see .lightbox-thumbs img's fixed width below. */
.lightbox-thumbs {
    width: 200px;
    flex-shrink: 0;
    border-left: 1px solid #eee;
    padding: 20px 30px;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 12px;
    overflow-y: auto;
    box-sizing: border-box;
}

.lightbox-thumbs img {
    width: 90px;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 10px;
    cursor: pointer;
    opacity: 0.6;
    border: 2px solid transparent;
    transition: all 0.3s;
}

/* Clears .sk-modal-close, which floats over this corner of the card. */
.lightbox-thumbs img:first-child {
    margin-top: 40px;
}

.lightbox-thumbs img.thumb-active,
.lightbox-thumbs img:hover {
    opacity: 1;
    border-color: var(--clr-green);
}

@media (max-width: 900px) {
    /* Shell (padding/radius/slide animation) comes from .sk-modal--fullscreen's own mobile
       rules now (see the Modal component section above) — this just adjusts the gallery's
       own content layout for the bottom-sheet shape. Nav arrows hidden — swipe carries the
       "change image" job (native scroll-snap on .lightbox-track, same mechanism as the
       inline gallery below this breakpoint), not a JS src-swap. */
    .lightbox-nav {
        display: none;
    }

    .lightbox-body {
        flex-direction: column;
    }

    .lightbox-main {
        padding: 12px;
    }

    .lightbox-thumbs {
        width: 100%;
        flex-direction: row;
        align-items: stretch;
        border-left: none;
        border-top: 1px solid #eee;
        overflow-y: visible;
        overflow-x: auto;
        padding: 12px;
    }

    .lightbox-thumbs img {
        width: 64px;
        flex-shrink: 0;
    }

    .lightbox-thumbs img:first-child {
        margin-top: 0;
    }
}

/* --- Component: Clamp + toggle (components/clamp-toggle.js) ---
   Generic "clamp to N lines, show Zobrazit více/méně only if it actually overflows".
   --clamp-lines is set inline per instance by the JS from data-lines. */
.clamp-toggle-content {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: var(--clamp-lines, 2);
    overflow: hidden;
}

.clamp-toggle-content.is-expanded {
    -webkit-line-clamp: unset;
    overflow: visible;
}

.clamp-toggle-btn {
    display: inline-block;
    margin-top: 4px;
    background: none;
    border: none;
    padding: 0;
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--clr-green, #73b7ab);
    cursor: pointer;
}

/* An author-stylesheet rule of equal specificity to the UA [hidden] rule always wins over
   it — without this, the button above stays visible even with the hidden attribute set. */
.clamp-toggle-btn[hidden] {
    display: none;
}

/* --- Component: Slider (components/slider.php, components/slider.js) ---
   Full-bleed hero-style image slider, reused wherever a slider is picked by id (homepage
   sections 1/2/9 — see SliderManager). Deliberately namespaced .sk-slider* rather than
   reusing the old common.css hero/slider rules (.hero-*, .slider-*), which stay put for page_b2b.php's
   own unrelated hero. Desktop: absolutely-stacked slides cross-fade via opacity; nav
   arrows reuse .carousel-nav's exact look (white circle + SVG chevron, see that rule
   above) instead of duplicating it. Mobile: the desktop track is swapped out entirely for
   a second, native scroll-snap track (same mechanism as .main-image-track) — swipe alone
   drives it, no arrows, no cross-fade. */
.sk-slider {
    position: relative;
    width: 100%;
    overflow: hidden;
}

.sk-slider-track-desktop {
    position: relative;
    width: 100%;
    aspect-ratio: 1400 / 600;
}

.sk-slider-slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.6s ease;
}

.sk-slider-slide.active {
    opacity: 1;
    visibility: visible;
}

.sk-slider-bg {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Free px/% position, set inline per-slide from cta_top/cta_left (see
   slide_elem_pos_style() in helpers.php) — desktop only, same mechanism the old hero
   slider's button row used. */
.sk-slider-buttons {
    display: flex;
    gap: 12px;
    z-index: 2;
}

/* Fully self-contained (not just an override layered on .carousel-nav) — common.css also
   defines an older .carousel-nav (box-shadow, left:-20px/right:-20px, z-index:10, used by
   the old section_products.php grid) that loads before styles.css on the homepage; relying
   on cascade order alone to win per-property was fragile, so every property the button
   needs is declared here explicitly instead. */
.sk-slider-nav-desktop {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 5;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 1px solid #eee;
    background: #fff;
    box-shadow: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--clr-green, #73b7ab);
    transition: background-color 0.2s ease, color 0.2s ease;
}

.sk-slider-nav-desktop:hover {
    background: var(--clr-green, #73b7ab);
    color: #fff;
}

.sk-slider-nav-desktop.prev { left: 24px; }
.sk-slider-nav-desktop.next { right: 24px; }

.sk-slider-dots {
    position: absolute;
    left: 50%;
    bottom: 16px;
    transform: translateX(-50%);
    z-index: 2;
    display: flex;
    gap: 8px;
}

.sk-slider-dot {
    width: 9px;
    height: 9px;
    border-radius: 999px;
    border: none;
    padding: 0;
    background: rgba(255, 255, 255, 0.6);
    cursor: pointer;
    transition: background-color 0.2s ease, width 0.2s ease;
}

.sk-slider-dot.active {
    width: 22px;
    background: #fff;
}

/* Mobile-only swipe track — hidden on desktop, where the cross-fade track above is used
   instead. No arrows: native touch momentum carries the swipe, same as .main-image-track. */
.sk-slider-track-mobile {
    display: none;
}

@media (max-width: 768px) {
    .sk-slider-track-desktop,
    .sk-slider-nav-desktop,
    .sk-slider-dots {
        display: none;
    }

    .sk-slider-track-mobile {
        display: flex;
        width: 100%;
        aspect-ratio: 16 / 9;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        scrollbar-width: none;
    }

    .sk-slider-track-mobile::-webkit-scrollbar {
        display: none;
    }

    .sk-slider-slide-mobile {
        position: relative;
        flex: 0 0 100%;
        scroll-snap-align: start;
    }

    /* Same button as desktop, overlaid on the slide image instead of below it — fixed
       bottom-left corner, same 12px offset convention as a carousel offer card's CTA
       (.carousel-offer-cta above), no per-slide top/left config on mobile. Sized down to
       match .carousel-offer-cta too (overrides the .btn-large sizing it inherits for
       desktop) — a full-size hero button reads oversized pinned in a small mobile corner. */
    .sk-slider-cta-mobile {
        position: absolute;
        left: 12px;
        bottom: 12px;
        z-index: 2;
        height: 38px;
        padding: 0 1.2rem;
        font-size: 0.85rem;
    }
}

/* --- Shared text primitives (ported from common.css for the homepage, which no longer
   loads it — see header.php) --- */
.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;
}

.hero-title {
    font-size: 2.5rem;
    font-weight: 700;
    color: var(--text-dark);
    line-height: 1.2;
    margin-bottom: 0.5rem;
}

.hero-subtitle {
    font-size: 1.2rem;
    color: var(--text-dark);
    font-weight: 600;
    margin-bottom: 1.5rem;
    margin-top: 0.5rem;
}

.hero-subtitle span {
    display: block;
}

