/* =============================================================
   ROLIC — stylesheet
   Off-white / warm grey base palette with a single near-black accent.
   Primary typeface: Inter. Accent typeface: Sora.
   ============================================================= */

/* ---------- Design tokens ---------- */
:root {
    /* --color-white is the base surface (page background, cards, header,
       popups) AND the text color on every dark surface. It's a barely
       off-white rather than #FFF on purpose, with a faint warm cast that
       keeps it in the same family as the greys below (which are warm
       too) — a neutral tint reads slightly cold beside them over large
       areas.

       NOTE: the whole light end of the scale is pinned to this value —
       it's the original scale shifted down by the same amount. The greys
       must always step *downward* from it; if they don't, every "subtle
       grey" section renders lighter than the page it sits on and the
       hierarchy inverts. Re-check grey-50/100/200 whenever this changes.

       Product/component pages override this to pure #FFFFFF — see
       body.bg-white further down for why. */
    --color-white: #FAF9F7;
    /* grey-50 is the .section--grey band tint. Kept only a few steps
       below --color-white on purpose (the client wanted it much
       lighter) — it reads as a very soft band, and on product pages,
       where --color-white becomes pure #FFFFFF, the contrast is a
       little clearer. It must still stay *below* --color-white. */
    --color-grey-50: #F6F5F2;
    --color-grey-100: #EAE8E4;
    --color-grey-200: #DCD9D3;
    --color-grey-300: #C7C2BA;
    --color-grey-400: #A39C93;
    --color-grey-600: #6E675F;
    --color-grey-700: #4C463F;
    --color-grey-800: #332F2A;
    --color-charcoal: #22201C;
    --color-charcoal-soft: #2C2925;

    /* Near-black accent (was terracotta #B5654A). Warm-tinted rather
       than pure #000 so it belongs to the same family as the greys, and
       a step darker than --color-charcoal so the two stay distinguishable
       where they meet. -light and -tint are now neutral greys rather than
       pale terracottas: -light must stay readable as TEXT on the dark
       footer (.newsletter-msg--ok) as well as working as a border on
       light surfaces, and -tint is the pale hover/active background used
       across the quickbar, dealer listing, download pills and modals. */
    --color-accent: #1C1A17;
    --color-accent-dark: #000000;
    --color-accent-light: #C9C5BE;
    --color-accent-tint: #E7E5E1;

    /* The accent's stand-in on the site's dark surfaces (charcoal footer,
       .cta-banner, .section--dark, the gallery lightbox). Needed only
       because the accent went near-black: as a terracotta it was the one
       bright thing on those surfaces and worked as-is, but near-black on
       charcoal is invisible. Anything that used the accent to stand OUT
       against a dark background uses this instead — buttons on dark
       surfaces additionally flip to dark-text-on-light. */
    --color-accent-on-dark: #FAF9F7;

    /* The one non-greyscale brand color on the site (2026-09-15, was the
       literal #EABC42 in ~8 places — buttons' hover state, the home page
       card icon chip + stat-row numbers, the CTA banner button hover, the
       pd-hero-bar "Consultar" button, and the footer newsletter button
       hover). Pulled into a token so future palette changes are one edit.
       Not the same value as includes/header.php's ?color=2 preview gold
       (#E8BB42) — that's a separate, throwaway comparison toggle, left as
       its own literal on purpose (see the comment there). */
    --color-yellow: #FFDE00;

    --font-primary: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    /* Sora (2026-09-08, replaced Playfair Display). Note the fallback is
       sans-serif now, not serif — Sora is a geometric sans, so a serif
       fallback would render as a completely different kind of face while
       the webfont loads or if it fails. Sora has no italic; nothing uses
       --font-accent italic today, so any future italic would be a
       browser-synthesised oblique. */
    --font-accent: 'Sora', sans-serif;
    /* Every rule that sets font-family: var(--font-accent) also sets
       font-weight: var(--font-accent-weight), so the accent face's weight
       is decided here in one place. Stated explicitly even on the rules
       that would otherwise inherit 400 — several of them style headings
       (.catalog-group__title, .blog-detail__intro), which would default
       to bold and quietly ignore this. 300 = Sora Light; the Google
       Fonts link in includes/header.php loads 100–800, so any value in
       that range works without touching the URL. */
    --font-accent-weight: 300;

    --container-width: 1280px;
    --radius-sm: 2px;
    --radius-md: 4px;
    --radius-lg: 6px;
    --shadow-md: 0 20px 45px rgba(34, 32, 28, 0.10);
    --shadow-sm: 0 4px 14px rgba(34, 32, 28, 0.08);
    --transition: 200ms ease;

    /* Toggle: the thin border around content photos (.photo-hero,
       .photo-4x3/.photo-9x7, .stacked-media img, .video-embed) and the
       producto-detalle.php / componente-detalle.php product-image
       displays (.pd-strip__item, .pd-version-item__media,
       .pd-gallery-row1__big, .pd-gallery-row1__side > div,
       .pd-gallery-row2 > div). Set to `none` to remove the border
       everywhere it's used, or back to the 1px value to restore it —
       nothing else needs to change. Doesn't affect card/panel/button
       borders (.card, .product-tile, .panel-card, .pd-tabs, etc.) —
       those are separate UI chrome, not image borders. */
    --content-image-border: 0px solid var(--color-grey-200);

    /* Extra sticky offset every element stacked below .main-header
       (.main-header itself, .pd-tabs) needs to add to its own `top` value
       when .topbar is also sticky — see body.dealer-logged-in below,
       right after the topbar rules. 0 by default (.topbar isn't sticky
       for a logged-out visitor, so nothing needs to shift down). */
    --sticky-header-offset: 0px;

    /* Height of producto-detalle.php's sticky product-title bar
       (.pd-hero-bar — the hero's line/product name condensed into a strip
       under the header once the hero scrolls past). One number drives
       three things that must agree: the bar's own height, how far
       .pd-tabs has to drop so it stacks *below* the bar, and .pd-section's
       scroll-margin-top so anchor jumps clear both. Change it here only. */
    --pd-hero-bar-h: 56px;
    /* How much .pd-tabs/.pd-section shift down to make room for that bar.
       0 by default — producto-detalle-tab.php uses the same .pd-tabs /
       .pd-section classes but has no bar, so the offset is opted into per
       page via .pd-tabs-scope--with-hero-bar rather than applied global. */
    --pd-tabs-extra: 0px;
}

/* ---------- Reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body, h1, h2, h3, h4, p, ul, ol, figure { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; padding: 0; }
input { font: inherit; }

html { overflow-x: hidden; }

body {
    font-family: var(--font-primary);
    color: var(--color-grey-800);
    background: var(--color-white);
    -webkit-font-smoothing: antialiased;
    line-height: 1.55;
}

/* Pure-white pages (set $whiteBackground = true; before requiring
   includes/header.php). Two reasons it's used:
     - product/component pages: the photography is shot on pure white,
       which on the slightly-off-white base leaves a visible grey box
       around every product;
     - the home page: the client wants its content areas white.

   Redefining the *token* rather than just `body { background }` is
   deliberate: it also whitens the card, tile and panel surfaces that sit
   on the page — on product pages those are what the photo edges actually
   touch, so setting only the page background would fix nothing.

   Grey section bands (--color-grey-50) are intentionally left alone; they
   still read as a deliberate band against pure white. */
body.bg-white { --color-white: #FFFFFF; }

h1, h2, h3, h4 {
    font-weight: 600;
    line-height: 1.2;
    color: var(--color-charcoal);
}

.visually-hidden {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

/* Subtle scroll-reveal. The .reveal class is written directly into the
   markup (blog.php / productos.php); assets/js/main.js just toggles
   .visible via IntersectionObserver as each card scrolls into view. */
.reveal {
    opacity: 0;
    transform: translateY(40px);
    transition: opacity 0.7s ease, transform 0.7s ease !important;
}
.reveal.visible { opacity: 1 !important; transform: translateY(0); }
.delay-1 { transition-delay: 0.1s !important; }
.delay-2 { transition-delay: 0.2s !important; }
.delay-3 { transition-delay: 0.3s !important; }
.delay-4 { transition-delay: 0.4s !important; }
.delay-5 { transition-delay: 0.5s !important; }
.delay-6 { transition-delay: 0.6s !important; }
.delay-7 { transition-delay: 0.7s !important; }
.delay-8 { transition-delay: 0.8s !important; }
.delay-9 { transition-delay: 0.9s !important; }
.delay-10 { transition-delay: 1s !important; }
.delay-11 { transition-delay: 1.1s !important; }
.delay-12 { transition-delay: 1.2s !important; }
@media (prefers-reduced-motion: reduce) {
    .reveal { transition: none; }
}

.skip-link {
    position: absolute;
    left: 12px;
    top: -60px;
    background: var(--color-charcoal);
    color: var(--color-white);
    padding: 10px 18px;
    border-radius: var(--radius-sm);
    z-index: 200;
    transition: top var(--transition);
}
.skip-link:focus { top: 12px; }

.container {
    width: 100%;
    max-width: var(--container-width);
    margin: 0 auto;
    padding: 0 24px;
}

/* ---------- Accent / cursive text ---------- */
.accent-text {
    font-family: var(--font-accent);
    color: var(--color-accent);
    font-weight: var(--font-accent-weight);
    font-size: 1.35em;
    line-height: 1;
}

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 13px 28px;
    border-radius: 0;
    font-weight: 500;
    font-size: 0.95rem;
    letter-spacing: .2px;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
    border: 1px solid transparent;
}
.btn svg { width: 18px; height: 18px; flex-shrink: 0; transition: transform var(--transition); }
.btn--sm { padding: 9px 20px; font-size: 0.85rem; }
.btn--sm svg { width: 16px; height: 16px; }
.btn--logout:hover svg { transform: translateX(3px); }
.btn--primary {
    background: var(--color-accent);
    color: var(--color-white);
}
.btn--primary:hover { background: var(--color-accent-dark); }
.btn--outline {
    background: transparent;
    border-color: var(--color-grey-300);
    color: var(--color-charcoal);
}
.btn--outline:hover { border-color: var(--color-accent); color: var(--color-accent); }
/* Solid near-black, square-cornered button that goes yellow on hover —
   used by the "Ver Todos" grid expanders on productos.php and the
   "Cerrar sesión" button on distribuidores-home.php. Distinct from
   .btn--primary, whose hover is a near-imperceptible shift to pure black
   now that the accent is itself near-black. The yellow is the same
   var(--color-yellow) as .newsletter-form button:hover and the .pd-hero-bar__cta.
   Note the square corners rely on source order: .btn--dark and .btn are
   both single-class selectors, and this rule sits later in the file. */
.btn--dark {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-white);
    border-radius: var(--radius-sm);
}
.btn--dark:hover,
.btn--dark:focus-visible {
    background: var(--color-yellow);
    border-color: var(--color-yellow);
    color: var(--color-charcoal);
}
/* Fully square corners. Must stay AFTER .btn and .btn--dark — all three are
   single-class selectors, so whichever comes last sets border-radius. */
.btn--square { border-radius: 0; }

/* =============================================================
   HEADER
   ============================================================= */
.site-header {
    /* Keeps the <header> landmark for accessibility without letting its
       (short) box constrain how far .main-header (and, for a logged-in
       dealer, .topbar too — see body.dealer-logged-in near the :root
       tokens) can stay sticky below — position:sticky is bounded by its
       containing block's height, and .site-header is only ever as tall
       as topbar + main-header. display:contents removes .site-header
       from the box tree entirely, so each sticky child's containing
       block becomes <body> instead — which is why two stacked sticky
       children with different `top` offsets works here at all. */
    display: contents;
}

/* ---- top utility bar ---- */
/* Pure #FFFFFF, not var(--color-white): the header sits above the
   slightly-off-white page on most pages, and the client wants it white
   regardless of which base the page is using — except .topbar itself,
   which is back to a slight grey (2026-08-31, was plain white; briefly
   tan, same day, before the client asked for grey instead) as its own
   deliberate band above the white .main-header; the two now read as two
   rows again rather than one unbroken block, which is the point of the
   tint. Reuses --color-grey-50, the site's existing subtle-band token
   (same one .section--grey uses) rather than a new hardcoded hex. */
.topbar {
    background: var(--color-grey-50);
}
/* Only sticky while a dealer is logged in AND the quickbar is enabled
   (body.dealer-logged-in — see includes/header.php) — a logged-out
   visitor's topbar keeps scrolling away as before. z-index above
   .main-header's 100 so it's never accidentally layered under it. */
body.dealer-logged-in .topbar {
    position: sticky;
    top: 0;
    z-index: 101;
}
/* .topbar is 40px tall (.topbar__inner's fixed height, below) at every
   breakpoint, so this one value covers both — unlike .main-header's own
   height, which does change at ≤1024px. */
body.dealer-logged-in { --sticky-header-offset: 40px; }
.topbar__inner {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 16px;
    height: 40px;
}
/* (.topbar__social was removed 2026-09-08 along with its markup — the
   topbar is dealer-only and functional now, and the footer carries the
   social links for everyone. .topbar__inner's justify-content: flex-end
   keeps the remaining actions cluster right-aligned on its own.) */

.topbar__actions {
    display: flex;
    align-items: center;
    gap: 16px;
}

.topbar__login,
.topbar__logout {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--color-grey-700);
    transition: color var(--transition);
}
.topbar__login svg,
.topbar__logout svg { width: 20px; height: 20px; }
.topbar__login:hover,
.topbar__logout:hover { color: var(--color-accent); }

.topbar__logout {
    padding-left: 16px;
    border-left: 1px solid var(--color-grey-200);
}

/* ---- main header row ---- */
.main-header {
    position: sticky;
    top: var(--sticky-header-offset);
    z-index: 100;
    /* Pure white, same as .topbar above — see the note there. */
    background: #FFFFFF;
}
.main-header__inner {
    display: flex;
    align-items: center;
    gap: 32px;
    height: 84px;
}

.brand { flex-shrink: 0; }
.brand img { height: 44px; width: auto; }

.main-nav { flex: 1; }
.main-nav__list {
    display: flex;
    align-items: center;
    gap: 8px;
}

.main-nav__link-row {
    display: flex;
    align-items: center;
}

.main-nav__link {
    display: inline-flex;
    align-items: center;
    padding: 30px 16px;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--color-grey-800);
    position: relative;
    transition: color var(--transition);
}
.main-nav__link::after {
    content: '';
    position: absolute;
    left: 16px;
    right: 16px;
    bottom: 26px;
    height: 2px;
    background: var(--color-accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--transition);
}
.main-nav__link:hover,
.main-nav__link.is-active { color: var(--color-accent); }
.main-nav__link:hover::after,
.main-nav__link.is-active::after { transform: scaleX(1); }

.mega-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    color: var(--color-grey-600);
}
.main-nav__caret { width: 16px; height: 16px; transition: transform var(--transition); }

/* ---- dealer quick-access bar (logged-in distributors only — see
   DEALER_QUICKBAR_ENABLED in includes/functions.php). Lives inside
   .topbar__actions, between .topbar__login and .topbar__logout. When
   present, .topbar itself becomes sticky (body.dealer-logged-in, further
   down) so the icons stay reachable while scrolling — see
   --sticky-header-offset near the :root tokens for how .main-header (and
   anything else stacked below it, e.g. .pd-tabs) shifts down to
   accommodate the now-also-sticky topbar above it. ---- */
.topbar__quickbar {
    display: flex;
    align-items: center;
    gap: 4px;
    padding-left: 16px;
    border-left: 1px solid var(--color-grey-200);
}
.topbar__quickbar-item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    color: var(--color-grey-600);
    transition: background var(--transition), color var(--transition);
}
.topbar__quickbar-item:hover { background: var(--color-accent-tint); color: var(--color-accent); }
.topbar__quickbar-item svg { width: 21px; height: 21px; }
/* Cart item-count badge (Pedido Actual Sillas/Componentes) — solid red,
   same semantic red the status pills/errors use elsewhere (#B23A3A), just
   as a solid chip rather than their usual pale-wash treatment, since a
   badge needs to read at a glance rather than sit quietly in a form. */
.topbar__quickbar-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    min-width: 16px;
    height: 16px;
    padding: 0 3px;
    border-radius: 999px;
    background: #B23A3A;
    color: #FFFFFF;
    font-size: 0.62rem;
    font-weight: 700;
    line-height: 16px;
    text-align: center;
    pointer-events: none;
}

/* ---- mega menu (desktop: hover-driven) ---- */
.has-mega { position: static; }

.mega-menu {
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
    background: var(--color-white);
    border-top: 1px solid var(--color-grey-200);
    box-shadow: var(--shadow-md);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-8px);
    transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
    z-index: 90;
}

.has-mega:hover .mega-menu,
.has-mega:focus-within .mega-menu {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}

.mega-menu__inner {
    display: flex;
    padding: 44px 24px;
    min-height: 260px;
}

.mega-menu__categories {
    width: 300px;
    flex-shrink: 0;
    border-right: 1px solid var(--color-grey-200);
    padding-right: 40px;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.mega-menu__category {
    display: flex;
    align-items: center;
    justify-content: space-between;
    text-align: left;
    width: 100%;
    padding: 15px 18px;
    border-radius: var(--radius-sm);
    font-size: 1.02rem;
    font-weight: 500;
    color: var(--color-grey-800);
    transition: background var(--transition), color var(--transition);
}
.mega-menu__category::after {
    content: '\2192';
    opacity: 0;
    transform: translateX(-6px);
    color: var(--color-accent);
    transition: opacity var(--transition), transform var(--transition);
}
.mega-menu__category:hover,
.mega-menu__category.is-active {
    background: var(--color-grey-50);
    color: var(--color-accent);
}
.mega-menu__category:hover::after,
.mega-menu__category.is-active::after {
    opacity: 1;
    transform: translateX(0);
}

.mega-menu__panel {
    display: none;
    padding-left: 56px;
    flex: 1;
}
.mega-menu__panel.is-visible { display: block; }
.mega-menu__panel ul {
    columns: 2;
    column-gap: 48px;
}
.mega-menu__panel li { break-inside: avoid; }
.mega-menu__panel a {
    display: block;
    padding: 11px 14px;
    margin-left: -14px;
    border-radius: var(--radius-sm);
    font-size: 0.98rem;
    color: var(--color-grey-700);
    transition: background var(--transition), color var(--transition), padding-left var(--transition);
}
/* Solid black chip on hover, white text — a much stronger read than the
   old pale tint, which went nearly invisible once the accent became
   near-black (accent-tint is now just a light grey). */
.mega-menu__panel a:hover,
.mega-menu__panel a:focus-visible {
    background: var(--color-accent);
    color: #FFFFFF;
    padding-left: 20px;
}
/* Final "Ver Todos" item in each panel — set apart from the category
   list above it, since it goes somewhere different (the full catalogue
   rather than one category). `break-inside` doesn't apply to it usefully
   in a multi-column list, so it just reads as the last entry. */
.mega-menu__panel a.mega-menu__all {
    font-weight: 600;
    color: var(--color-accent);
}
/* Leading arrow, marking it as a "go somewhere else" item rather than
   another category. A pseudo-element rather than markup so both panels
   get it from one place and it can't end up in the link's accessible
   name. It inherits `color` from the anchor, so the hover rule below
   (white on the black chip) carries it automatically — no separate
   ::before:hover rule needed. */
.mega-menu__panel a.mega-menu__all::before {
    content: "\2192";
    margin-right: 8px;
}
/* Needed, and NOT redundant with the generic `a:hover` above.
   `.mega-menu__panel a.mega-menu__all` and `.mega-menu__panel a:hover`
   have the SAME specificity (two classes + one element each — :hover
   counts as a class), and this block comes later in the file, so without
   this rule the base colour above wins during hover: the chip turned
   black while the text stayed near-black, i.e. black on black. Same
   equal-specificity/source-order trap as the .reveal/.delay-N cases
   documented in CLAUDE.md. */
.mega-menu__panel a.mega-menu__all:hover,
.mega-menu__panel a.mega-menu__all:focus-visible { color: #FFFFFF; }

/* Both mega-menu panels are DB-driven lists (product_lines, component_categories)
   long enough that the base 2-column layout would grow too tall — denser
   3-column layout with a smaller type size keeps the flyout compact.
   Sillas y Sillones and Componentes both use this modifier so their
   distribution/type size match. */
.mega-menu__panel.mega-menu__panel--dense ul {
    columns: 3;
    column-gap: 28px;
}
.mega-menu__panel.mega-menu__panel--dense a {
    padding: 8px 12px;
    margin-left: -12px;
    font-size: 0.88rem;
}

/* ---- search ---- */
.main-header__tools {
    display: flex;
    align-items: center;
    gap: 0;
    flex-shrink: 0;
}

.search {
    display: flex;
    flex-direction: row-reverse;
    align-items: center;
    justify-content: flex-end;
}
.search__toggle {
    width: 46px;
    height: 46px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-700);
    border-radius: 50%;
    transition: color var(--transition), background var(--transition);
    flex-shrink: 0;
}
.search__toggle svg { width: 28px; height: 28px; }
.search__toggle:hover { color: var(--color-accent); background: var(--color-grey-50); }
/* When open, the opener collapses — the mag glass now lives inside the
   field as the submit button (.search__submit), with .search__close (X)
   to its right. */
.search.is-active .search__toggle { display: none; }

.search__form {
    display: flex;
    align-items: center;
    width: 0;
    overflow: hidden;
    border-radius: var(--radius-sm);
    transition: width var(--transition);
}
.search.is-active .search__form {
    width: 300px;
    border: 1px solid var(--color-grey-300);
    background: var(--color-grey-50);
}
.search.is-active .search__form:focus-within { border-color: var(--color-accent); }

.search__form input {
    flex: 1;
    min-width: 0;
    border: none;
    background: transparent;
    padding: 9px 6px 9px 16px;
    font-size: 0.9rem;
    outline: none;
}

.search__submit,
.search__close {
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-700);
    border-radius: 50%;
    transition: color var(--transition), background var(--transition);
}
.search__submit svg,
.search__close svg { width: 20px; height: 20px; }
.search__submit:hover,
.search__close:hover { color: var(--color-accent); }
.search__close { margin-right: 4px; }

/* ---- mobile nav toggle (hamburger) ---- */
.nav-toggle {
    display: none;
    flex-direction: column;
    justify-content: center;
    align-items: flex-end;
    gap: 5px;
    width: 40px;
    height: 40px;
}
.nav-toggle span {
    display: block;
    width: 22px;
    height: 2px;
    background: var(--color-charcoal);
    transition: transform var(--transition), opacity var(--transition);
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* =============================================================
   MAIN CONTENT — shared section utilities
   ============================================================= */
.site-main { min-height: 50vh; }

.section { padding: 88px 0; }
.section--tight { padding: 56px 0; }
.section--grey { background: var(--color-grey-50); }
.section--dark {
    background: var(--color-charcoal);
    color: var(--color-grey-200);
}
.section--dark h1, .section--dark h2, .section--dark h3 { color: var(--color-white); }

.eyebrow {
    display: block;
    font-family: var(--font-accent);
    color: var(--color-accent);
    font-weight: var(--font-accent-weight);
    font-size: 1.5rem;
    margin-bottom: 10px;
}

.section-title { font-size: clamp(1.8rem, 3vw, 2.6rem); margin-bottom: 20px; }

/* Dealer ordering + order-history flow (pedido-sillas / pedido-componentes
   pages and their historial-pedido* detail/list pages) — same grey
   page-title band as the rest of the site, just thinner/smaller: these
   pages are a repeated tool a dealer clicks through several times per
   order, not a one-off landing section. */
.section--order-title { padding: 20px 0; }
.section--order-title .eyebrow { font-size: 1.1rem; margin-bottom: 4px; }
.section--order-title .section-title { font-size: clamp(1.3rem, 1.8vw, 1.7rem); margin-bottom: 8px; }

/* productos.php: the page-title block above "Sillas y Sillones" was
   removed on request, so #sillas-sillones is now the first thing on the
   page and doesn't need a full 88px of top air; its bottom is halved too
   so #componentes (which needs none of its own — see below) doesn't sit
   too far below it. IDs are unique to this page, so no other .section
   is affected. */
#sillas-sillones { padding-top: 44px; padding-bottom: 44px; }
#componentes { padding-top: 0; }
#sillas-sillones .section-title,
#componentes .section-title { margin-bottom: 10px; }

/* distribuidores-home.php's main section — less top air than the site's
   default 88px, per request. ID is unique to this page. */
#distribuidor-home { padding-top: 44px; }

/* Per-category heading on the full-catalogue pages (/productos/todos and
   /componentes/todos). Uses the accent serif rather than Inter so the
   many repeated group headings read as editorial dividers instead of
   competing with each product's own <h3>. Replaces an inline
   `font-size:1.5rem` those pages used to carry. */
.catalog-group__title {
    font-family: var(--font-accent);
    font-weight: var(--font-accent-weight);
    font-size: 1.75rem;
    margin-bottom: 4px;
}
.section-lead {
    color: var(--color-grey-600);
    font-size: 1.08rem;
}
/* busqueda.php's "N en productos y M en componentes." line, sitting under
   the results summary. Its own paragraph so the count can never be left
   orphaned at the end of the sentence above it; slightly smaller and
   spaced so the two read as a heading and its detail rather than one
   broken sentence. */
.search-breakdown {
    margin-top: 6px;
    font-size: 0.98rem;
}

/* ---- hero ---- */
/* ---- home hero slider (index.php, slideshow_photos album 1) ----
   Full-bleed: deliberately NOT wrapped in a .container, since the source
   images are full-width 2800x1080 banners. The aspect-ratio (not a fixed
   height) is what gives the section its height, and it matches the
   assets exactly, so nothing is ever cropped or letterboxed at any
   width — including on phones, where it just becomes a shorter band. */
.hero-slider {
    position: relative;
    /* Only ever visible in the moment before the first slide's image
       paints — the slides cover it completely. Follows the page base
       rather than sitting at grey-100, so it can't flash a grey block on
       a page whose content areas are meant to be white. */
    background: var(--color-white);
    /* No bottom border (removed on request 2026-09-14, once the counter
       row moved below the image). */
}
/* position: relative makes this the arrows' containing block (they're
   inside it in the markup), so top: 50% centres them on the image and
   not on image + the counter row below it. */
.hero-slider__viewport { position: relative; overflow: hidden; }
/* Crossfade, not a sliding track: every slide is stacked in the same box
   and only the active one is opaque. The box gets its height from the
   aspect-ratio here, since absolutely-positioned slides contribute none. */
.hero-slider__track {
    position: relative;
    aspect-ratio: 2800 / 1080;
}
.hero-slider__slide {
    position: absolute;
    inset: 0;
    opacity: 0;
    /* Slides are links; without this the stacked, invisible ones would
       still swallow clicks meant for the visible one. main.js also drops
       them out of the tab order and hides them from screen readers. */
    pointer-events: none;
    transition: opacity 0.6s ease;
}
.hero-slider__slide.is-active { opacity: 1; pointer-events: auto; }
.hero-slider__slide img { width: 100%; height: 100%; object-fit: cover; }

.hero-slider__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 46px;
    height: 46px;
    border-radius: 50%;
    color: var(--color-charcoal);
    background: rgba(255, 255, 255, 0.82);
    box-shadow: var(--shadow-sm);
    transition: background var(--transition), color var(--transition);
}
.hero-slider__arrow:hover,
.hero-slider__arrow:focus-visible { background: var(--color-white); color: var(--color-accent); }
.hero-slider__arrow svg { width: 22px; height: 22px; }
.hero-slider__arrow--prev { left: 24px; }
.hero-slider__arrow--next { right: 24px; }

/* Slide counter + countdown line in one row: "01 ——— 09" (2026-09-14 —
   replaced a row of square dot markers stacked above the line). It sits
   in normal flow right BELOW the image, centred, rather than overlaid on
   it (same day, client request) — so it's on the page background, not
   photography, and needs no halo or translucent track. The bar has an
   explicit width (.hero-slider__progress below). */
.hero-slider__nav {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 12px;
    padding: 14px 0;
}
/* Charcoal to match the bar's fill. Tabular figures keep "01" and "09"
   the same width, so the bar doesn't shift as the number changes. */
.hero-slider__count {
    font-size: 0.8rem;
    font-weight: 600;
    letter-spacing: .5px;
    font-variant-numeric: tabular-nums;
    line-height: 1;
    color: var(--color-charcoal);
}

/* Autoplay countdown: drains left-ward over one interval, restarted by
   main.js on every slide change and paused with the autoplay itself.
   Its `animation-duration` is set from JS so AUTOPLAY_MS stays the single
   source of truth for both the timer and this bar. */
.hero-slider__progress {
    height: 2px;
    width: 120px;
    flex: 0 0 auto;
    /* Light grey track: it's on the white page now, where the old
       translucent-white track (made for over-photo) would be invisible. */
    background: var(--color-grey-200);
    overflow: hidden;
}
.hero-slider__progress span {
    display: block;
    height: 100%;
    background: var(--color-charcoal);
    /* Collapses toward the left edge, so the visible length shortens from
       the right — reads as time running out. */
    transform-origin: left center;
    transform: scaleX(0);
}
.hero-slider__progress span.is-running { animation: hero-countdown linear forwards; }

@keyframes hero-countdown {
    from { transform: scaleX(1); }
    to { transform: scaleX(0); }
}
@media (prefers-reduced-motion: reduce) {
    .hero-slider__slide { transition: none; }
}

/* ---- text hero ----
   Currently unused (index.php's hero became the slider above), but kept
   as one of the site's reusable content-page patterns — see the class
   list in CLAUDE.md. */
.hero {
    background: var(--color-grey-50);
    border-bottom: 1px solid var(--color-grey-200);
}
.hero__inner {
    padding: 96px 0 88px;
    max-width: 760px;
}
.hero__title {
    font-size: clamp(2.2rem, 4.5vw, 3.4rem);
    letter-spacing: -.5px;
}
.hero__lead {
    margin-top: 22px;
    font-size: 1.15rem;
    color: var(--color-grey-600);
    max-width: 600px;
}
.hero__actions {
    margin-top: 36px;
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
}

/* ---- card grid ---- */
.card-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
    margin-top: 48px;
}
.card {
    /* Square corners, no border, very subtle solid grey fill (client
       request 2026-09-14 — the home page's three cards are the only .card
       on the site). #F8F7F4 is the midpoint of the two values the client
       has bracketed: grey-50 (#F6F5F2) was "a bit lighter" please, and
       #FAF9F7 then "a bit darker". It's a literal, not var(--color-white)
       or a grey token, because index.php sets $whiteBackground, which
       redefines --color-white to pure #FFFFFF. Tried the same day and
       reverted: a white-to-grey gradient, and white with a light border. */
    background: #F8F7F4;
    border: 0;
    border-radius: 0;
    padding: 32px;
    transition: box-shadow var(--transition), transform var(--transition);
}
.card:hover {
    box-shadow: var(--shadow-sm);
    transform: translateY(-3px);
}
/* Icon + title on one row (2026-08-31, was icon stacked above title) -
   .card__head wraps both; the 10px gap-to-paragraph that used to live on
   h3's own margin-bottom moved here so it isn't also pulling the icon's
   vertical centering off by half that margin. */
.card__head {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 14px;
}
.card__icon {
    width: 46px;
    height: 46px;
    border-radius: 50%;
    /* Site yellow (client request 2026-09-14) — var(--color-yellow), the same
       token the buttons' hover states use. The chip is the yellow part and
       the glyph stays near-black: a yellow glyph on the old light-grey chip
       measured ~1.4:1 and its thin strokes all but vanished. */
    background: var(--color-yellow);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}
.card__icon svg { width: 22px; height: 22px; }
.card h3 { font-size: 1.1rem; margin-bottom: 10px; }
.card__head h3 { margin-bottom: 0; }
.card p { color: var(--color-grey-600); font-size: 0.96rem; }

/* ---- busqueda.php: the two catalogue buttons under a no-results
   message. A flex row rather than the <p> this started as, so the gap
   between the buttons and the space above them are both explicit
   (inline-block anchors in a <p> only ever got a single word-space
   between them, and the paragraph's own margin left them nearly touching
   the sentence above). ---- */
.search-empty__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 24px;
    margin-top: 32px;
}

/* ---- dashboard messages (distribuidores-home.php, above the tile grid)
   Ported from the old site's area-clientes.php: a CMS-wide widget, the
   dealer's own free-text note, then any group-targeted messages — see
   dealer_group_messages() in includes/functions.php. No border, matching
   the rest of this session's dealer-area work. ---- */
.dealer-messages { margin-top: 32px; display: flex; flex-direction: column; gap: 16px; }
.dealer-message {
    background: var(--color-grey-50);
    border-radius: 0;
    padding: 20px 24px;
}
.dealer-message__alert {
    background: var(--color-yellow);
    color: var(--color-charcoal);
    text-align: center;
    font-weight: 600;
    font-size: 0.9rem;
    padding: 10px 16px;
    margin-bottom: 14px;
}
.dealer-message__title { font-weight: 700; color: var(--color-charcoal); margin-bottom: 6px; }

/* ---- dashboard tile grid (distributor area launcher cards) ---- */
.dash-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
    margin-top: 40px;
}
.dash-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: 16px;
    padding: 32px 20px;
    background: var(--color-white);
    border-radius: 0;
    transition: box-shadow var(--transition), transform var(--transition);
}
.dash-tile:hover {
    box-shadow: var(--shadow-sm);
    transform: translateY(-3px);
}
.dash-tile__icon {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: var(--color-accent-tint);
    color: var(--color-accent);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background var(--transition), transform var(--transition);
}
.dash-tile:hover .dash-tile__icon {
    background: var(--color-accent);
    color: var(--color-white);
    transform: scale(1.08);
}
.dash-tile__icon svg { width: 26px; height: 26px; }
.dash-tile__label {
    font-size: 0.82rem;
    font-weight: 600;
    letter-spacing: .3px;
    text-transform: uppercase;
    color: var(--color-grey-700);
    line-height: 1.4;
    transition: color var(--transition);
}
.dash-tile:hover .dash-tile__label { color: var(--color-accent-dark); }

/* ---- home "Últimos Lanzamientos" banner row (index.php) ----
   Four square banners in one row. All four sources are 1080x1080, and
   the aspect-ratio below matches, so `cover` never actually crops —
   it's there so an off-ratio replacement degrades gracefully instead of
   breaking the row's alignment. */
.launch-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 20px;
    margin-top: 40px;
}
.launch-tile {
    display: block;
    overflow: hidden;
    /* Square corners (client request 2026-09-14, same as .cta-banner). */
    border-radius: 0;
    /* Same reasoning as .hero-slider above — a lazy-loaded banner's
       placeholder, covered as soon as the image paints. */
    background: var(--color-white);
}
.launch-tile img {
    width: 100%;
    /* height:auto is load-bearing — the markup carries width="900"
       height="900" (good for CLS), and that height attribute becomes a
       presentational `height: 900px`. With both dimensions definite,
       aspect-ratio is ignored and the images render 900px tall. */
    height: auto;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    /* Slow zoom on hover, echoing the old site's own
       "imagezoom-container" behaviour these banners come from. Scaling
       the <img> inside an overflow:hidden link (rather than the link
       itself) keeps the tile's own box still. */
    transition: transform 600ms ease;
}
.launch-tile:hover img,
.launch-tile:focus-visible img { transform: scale(1.06); }

@media (prefers-reduced-motion: reduce) {
    .launch-tile img { transition: none; }
    .launch-tile:hover img,
    .launch-tile:focus-visible img { transform: none; }
}

/* "Ver Todos" row under a truncated grid (productos.php) — bottom-right
   of the grid it expands. */
.grid-more {
    display: flex;
    justify-content: flex-end;
    margin-top: 32px;
}
/* .product-tile sets no `display`, so the hidden attribute's UA
   `display: none` applies on its own — but this codebase has been bitten
   twice by an author `display` rule silently defeating `hidden` (see
   .form-status and the dealer listing), so state it explicitly rather
   than depend on that staying true. */
.product-tile[hidden] { display: none; }

/* ---- product category placeholder tiles (no photography yet) ---- */
.product-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 24px;
    margin-top: 40px;
}
/* Borderless by design — the product photos sit directly on the page
   with nothing outlining them. The hover shadow is what gives the tile
   its edge on interaction. */
.product-tile {
    position: relative;
    border-radius: 0;
    overflow: hidden;
    background: var(--color-white);
    transition: box-shadow var(--transition), transform var(--transition);
}
.product-tile:hover { box-shadow: var(--shadow-sm); transform: translateY(-3px); }
/* Stretched-link overlay used by tiles that link out to a real detail page
   (currently only Working Mesh -> producto-detalle.php) without changing
   the tile's own tag from <div> to <a>. */
.product-tile__stretched-link {
    position: absolute;
    inset: 0;
    z-index: 2;
}
.product-tile__media {
    aspect-ratio: 4 / 3;
    background:
        repeating-linear-gradient(135deg, var(--color-grey-100) 0 2px, var(--color-grey-50) 2px 14px);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-400);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: .4px;
}
.product-tile__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
/* Catalog/product-shot tiles (component category tiles, and
   producto-categoria.php's individual product photos) — these photos are
   plain-background product shots, not full-bleed lifestyle renders like
   the Sillas y Sillones category tiles, so they get a padded, white-backed
   inset with object-fit: contain instead of cover — object-fit: cover
   crops a plain-background product shot in a way that often cuts off part
   of the chair; contain shows the whole product instead, letterboxed on
   the white inset rather than against the tile's default diagonal-stripe
   placeholder background. */
.product-tile__media--padded {
    padding: 16px;
    background: var(--color-white);
}
.product-tile__media--padded img {
    object-fit: contain;
    border-radius: var(--radius-sm);
}
.product-tile__body { padding: 18px 20px 22px; text-align: center; }
.product-tile__body h3 { font-size: 1rem; margin-bottom: 4px; }
.product-tile__body p { font-size: 0.85rem; color: var(--color-grey-600); }

/* ---- component card, borderless-image variant (componente-categoria.php
   only — every other card/tile on the site keeps using .product-tile
   above, untouched. Swap the classes back in that one file to revert). ----
   Client wants the photo to sit with no outline around it; only the
   bottom (title/SKU) block is boxed, and the box border fades in rather
   than cutting in with a hard line. Uses the same .product-grid layout
   grid as .product-tile, just a different card/media/body treatment. */
.component-card {
    position: relative;
    border-radius: 0;
    background: var(--color-white);
    transition: transform var(--transition), box-shadow var(--transition);
}
.component-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-sm); }
.component-card__media {
    aspect-ratio: 4 / 3;
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: var(--color-white);
    color: var(--color-grey-400);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: .4px;
}
.component-card__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    position: relative;
    z-index: 1;
}
/* the "fade instead of a hard line" border: the area behind the photo
   stays solid white (no tint) — only the left/right edge lines fade in,
   from transparent near the top down to the border color right where
   .component-card__body's solid border begins, so the two joins seamlessly. */
.component-card__media::before,
.component-card__media::after {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: linear-gradient(to bottom, transparent 0%, var(--color-grey-200) 100%);
    pointer-events: none;
}
.component-card__media::before { left: 0; }
.component-card__media::after { right: 0; }
.component-card__body {
    padding: 18px 20px 22px;
    border: 1px solid var(--color-grey-200);
    border-top: none;
    border-radius: 0;
}
.component-card__body h3 { font-size: 1rem; margin-bottom: 4px; }
.component-card__body p { font-size: 0.85rem; color: var(--color-grey-600); }

/* ---- generic content blocks (Lorem Ipsum pages) ---- */
.prose { max-width: 760px; }
.prose p { margin-bottom: 18px; color: var(--color-grey-700); }
.prose p:last-child { margin-bottom: 0; }

/* Lists inside CMS-authored rich text get their normal browser bullets
   and indent back. The global reset near the top of this file
   (`ul, ol { padding: 0; list-style: none }`) exists for the site's
   structural lists — nav, mega menu, footer links, card grids — but it
   also silently flattened any <ul> an editor writes in CKEditor: the
   bullets vanished and the items ran together as unindented lines.
   Scoped to .prose, so every structural list still gets the reset.

   Applies anywhere .prose is used, not just componente-detalle.php:
   producto-detalle.php's Descripción General blocks, la-empresa.php and
   blog-detail.php all render editor content through the same class, and
   a list should look the same in all of them.

   `revert` restores the user-agent value, which also gets nested levels
   right (disc -> circle -> square) for free. The plain disc/decimal
   before it is the fallback for browsers without `revert`, which would
   otherwise drop the declaration and leave the list unbulleted. */
.prose ul { list-style: disc; list-style: revert; }
.prose ol { list-style: decimal; list-style: revert; }
.prose ul,
.prose ol {
    padding-left: 22px;
    margin-bottom: 18px;
    color: var(--color-grey-700);
}
.prose li { margin-bottom: 6px; }
.prose li:last-child { margin-bottom: 0; }
.prose ul:last-child,
.prose ol:last-child { margin-bottom: 0; }

.two-col {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 64px;
    align-items: start;
}

/* Feature rows variant (Descripción General, producto-detalle.php) — image
   sized down and both cells centered (vertically + horizontally) within
   their grid column, instead of the base .two-col's full-width/top-aligned
   default (still used as-is elsewhere: la-empresa.php, contacto.php). */
.two-col--centered { align-items: center; }
.two-col--centered .media { max-width: 82%; margin: 0 auto; }
.two-col--centered .prose { max-width: 80%; margin: 0 auto; }

/* One Descripción General content-block row (product_txt_blocks) on
   producto-detalle.php: tighter than the base .two-col's 64px gutter, and
   a much smaller gap between consecutive rows than the 64px inline
   margin-bottom these used to carry.

   Its own class rather than a change to .two-col--centered, because that
   modifier is also used by acceso-distribuidores.php, where an 8px gutter
   would jam the two columns together. The gap rule is written as a
   two-class selector (0,2,0) so it beats .two-col's own `gap` on
   specificity rather than on source order — the equal-specificity trap
   this stylesheet has been bitten by repeatedly. .media/.prose already
   cap themselves at 82%/80% of their column, so most of the visual
   breathing room between the two halves comes from those, not the gap. */
.two-col--centered.pd-block-row { gap: 8px; }
.pd-block-row { margin-bottom: -12px; }
/* A YouTube block in Descripción General (product_txt_blocks.youtube_url)
   does NOT use the two-column row above — a video in half a column reads
   as an aside rather than the thing you're meant to watch. It gets the
   full content width, capped and centred, with any title/text stacked
   above it.

   The width here is also load-bearing, not just cosmetic. An earlier pass
   put the embed in a .media cell and it rendered at literally 0x0: the
   `margin: 0 auto` above makes those grid cells shrink-to-fit (auto
   margins in an axis override the default `stretch`), an <img>/<video>
   has an intrinsic width to shrink to, but .video-embed is a div whose
   only child is `position: absolute` — content width 0, and its
   `padding-bottom: 56.25%` then resolved against that 0 for 0 height.
   As a plain block child of .container it has a definite width, so the
   ratio resolves normally. Keep it out of any shrink-to-fit context. */
.pd-block-video {
    max-width: 900px;
    margin-left: auto;
    margin-right: auto;
}
.pd-block-video__text { max-width: 80%; margin: 0 auto 24px; text-align: center; }

/* Equal-height variant (contacto.php) — cells stretch to the row height
   instead of the base .two-col's `align-items: start`, so a child with
   `flex: 1` (contacto's map) can grow to fill the taller column. */
.two-col--stretch { align-items: stretch; }

/* contacto.php's details column: a flex column purely so .contact-map can
   claim the leftover height. margin-top replaces the map's own former
   fixed one — a flex child's margin isn't absorbed by `flex: 1`. */
.contact-info { display: flex; flex-direction: column; }
.contact-info .contact-map { margin-top: 32px; }

/* ---- maps (mapa-distribuidores.php + contacto.php) ----
   Both maps plot the same dealer markers with the same look, so they
   share every rule below; only their sizing differs (see .dealer-map /
   .contact-map right after). */
.dealer-map-meta { color: var(--color-grey-600); font-size: 0.92rem; margin: 0 0 20px; }
.dealer-map,
.contact-map {
    border-radius: 0;
    border: 1px solid var(--color-grey-200);
    overflow: hidden;
}
/* L.map('dealers-map') turns this exact element into .leaflet-container
   itself (Leaflet doesn't nest a separate child) — so overrides of
   Leaflet's own base styles need this compound selector to reliably win
   the cascade regardless of stylesheet load order. Leaflet's internal
   panes/controls default to z-index up to 1000, which otherwise beats
   .main-header's sticky z-index (100) once this map scrolls underneath
   it; setting z-index here (position is already relative via leaflet.css)
   makes this element establish its own stacking context, capping
   everything inside it below the header. */
.dealer-map.leaflet-container,
.contact-map.leaflet-container {
    font-family: inherit;
    background: var(--color-grey-50);
    z-index: 1;
}
.dealer-map .leaflet-tile-pane,
.contact-map .leaflet-tile-pane { filter: grayscale(1) contrast(1.05) brightness(1.02); }

/* mapa-distribuidores.php: map left (~40%), searchable dealer listing
   right (~60%). `align-items: start` is load-bearing, not cosmetic — a
   grid item stretches to the row height by default, which would make
   .dealer-locator__map as tall as the 92-row listing and leave its
   `position: sticky` nothing to do. Keeping the item short lets it stick
   within the (tall) grid content box instead. */
.dealer-locator {
    /* One source of truth for the two columns' height: the map is exactly
       this tall, and the listing beside it is capped at the same value so
       the pair reads as one block. Tied to the viewport rather than a
       fixed height so the sticky map never ends up taller than the space
       it's pinned into (which would make its lower half permanently
       unreachable); the max keeps it sane on very tall screens. */
    --dealer-panel-height: min(640px, calc(100vh - 140px));
    display: grid;
    grid-template-columns: 2fr 3fr;
    gap: 32px;
    align-items: start;
}
.dealer-locator__map {
    position: sticky;
    /* Same 84px .main-header + 8px breathing room as .pd-tabs, plus
       --sticky-header-offset for the logged-in dealer's sticky topbar —
       see the long comment on .pd-tabs for the full reasoning. */
    top: calc(92px + var(--sticky-header-offset));
}
.dealer-map { height: var(--dealer-panel-height); }

/* The search field stays put while only the rows below it scroll: the
   column is a flex column capped at the map's height, with the field as a
   fixed-size first child and .dealer-locator__scroll taking the rest. */
.dealer-locator__list {
    min-width: 0;
    display: flex;
    flex-direction: column;
    max-height: var(--dealer-panel-height);
}
.dealer-locator__scroll {
    flex: 1;
    overflow-y: auto;
    /* Keeps rows off the scrollbar. */
    padding-right: 10px;
}

/* The `hidden` attribute only gets `display: none` from the browser's
   *user-agent* stylesheet, which ANY author rule beats regardless of
   specificity — so a `display` declaration on a row or group would
   silently defeat the search filter's `el.hidden = true`. That is exactly
   what happened here the first time (rows kept showing while their
   province heading vanished). Declared once for the whole listing so it
   can't regress the next time something in here gets a display value.
   Same trap as .form-status[hidden] on contacto.php. */
.dealer-locator__list [hidden] { display: none !important; }

.dealer-search {
    display: flex;
    align-items: center;
    gap: 10px;
    background: var(--color-white);
    border: 1px solid var(--color-grey-300);
    border-radius: var(--radius-sm);
    padding: 0 14px;
    margin-bottom: 20px;
    transition: border-color var(--transition);
}
.dealer-search:focus-within { border-color: var(--color-accent); }
.dealer-search__icon { width: 18px; height: 18px; flex-shrink: 0; color: var(--color-grey-400); }
.dealer-search input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    background: none;
    padding: 12px 0;
    font-size: 0.95rem;
}
/* WebKit's own round clear button sits awkwardly against the field's
   flush-right padding and doesn't match anything else on the site. */
.dealer-search input::-webkit-search-cancel-button { -webkit-appearance: none; }

.dealer-list-empty { color: var(--color-grey-600); font-size: 0.92rem; }

.dealer-group { margin-bottom: 24px; }
.dealer-group:last-child { margin-bottom: 0; }
/* Sticky so you always know which province the rows you're looking at
   belong to while scrolling the listing. */
.dealer-group__title {
    position: sticky;
    top: 0;
    background: var(--color-white);
    font-family: var(--font-accent);
    font-size: 1.05rem;
    font-weight: var(--font-accent-weight);
    color: var(--color-accent);
    padding-bottom: 8px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--color-grey-200);
}

.dealer-item { border-bottom: 1px solid var(--color-grey-100); }
.dealer-item:last-child { border-bottom: 0; }

/* The whole row is the button — icon, name and street address all sit
   inside it, so there's no small target to aim at. */
.dealer-item__btn {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 10px 10px;
    border-radius: var(--radius-sm);
    text-align: left;
    transition: background var(--transition);
}
.dealer-item__btn:hover,
.dealer-item__btn:focus-visible { background: var(--color-grey-50); }
.dealer-item__icon { width: 16px; height: 16px; flex-shrink: 0; color: var(--color-grey-400); }
/* min-width:0 lets the name shrink (and ellipsize) instead of shoving the
   address off the row — flex items refuse to shrink below their content
   width without it. */
.dealer-item__name {
    min-width: 0;
    font-size: 0.92rem;
    color: var(--color-grey-700);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
/* Right-aligned via margin-left:auto rather than justify-content, so the
   icon and name stay grouped together on the left. */
.dealer-item__address {
    margin-left: auto;
    padding-left: 12px;
    max-width: 45%;
    font-size: 0.8rem;
    color: var(--color-grey-400);
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.dealer-item.is-active .dealer-item__btn { background: var(--color-accent-tint); }
.dealer-item.is-active .dealer-item__icon,
.dealer-item.is-active .dealer-item__name { color: var(--color-accent); }
.dealer-item.is-active .dealer-item__name { font-weight: 600; }

/* contacto.php's map fills whatever height is left in its column once the
   contact details above it are laid out, so the column ends up as tall as
   the form alongside it — see .two-col--stretch / .contact-info below.
   min-height keeps it reasonable if the form is ever the shorter of the
   two (its Pipedrive iframe sizes itself at runtime). */
.contact-map { flex: 1; min-height: 460px; }

.dealer-marker svg { display: block; filter: drop-shadow(0 2px 3px rgba(34, 32, 28, 0.4)); }

.dealer-popup .leaflet-popup-content-wrapper {
    padding: 0;
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-md);
    overflow: hidden;
}
.dealer-popup .leaflet-popup-content { margin: 0; width: 320px !important; }
.dealer-popup .leaflet-popup-tip { box-shadow: var(--shadow-sm); }
.dealer-popup .leaflet-popup-close-button { color: var(--color-grey-400); top: 8px; right: 8px; }
.dealer-popup .leaflet-popup-close-button:hover { color: var(--color-accent); }

.dealer-card { padding: 0px 20px 20px; }
.dealer-card__name {
    font-size: 1rem;
    font-weight: 700;
    color: var(--color-grey-800);
    margin: 0 0 12px;
    padding: 0 24px 12px 0;
    border-bottom: 1px solid var(--color-grey-200);
}
.dealer-card__list { display: flex; flex-direction: column; gap: 9px; }
.dealer-popup .dealer-card__row {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 0.86rem;
    color: var(--color-grey-700);
    line-height: 1.4;
    text-decoration: none;
}
.dealer-card__row svg { flex-shrink: 0; width: 18px; height: 18px; margin-top: 0; color: var(--color-accent-dark); }
.dealer-card__row span { min-width: 0; word-break: break-word; }
.dealer-popup a.dealer-card__row:hover,
.dealer-popup a.dealer-card__row:hover span { color: var(--color-accent); }

.stat-row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 24px;
    margin-top: 48px;
}
/* No lead paragraph above it on index.php (it's the section's only
   content) — margin-top: 48px was there to clear one, so drop it here
   and centre the row vertically within the section's own padding. Each
   item's own text is centred too (2026-09-08), so the number sits over
   the middle of its label rather than both ragging left.
   Scoped to the modifier, not the base .stat-row__item, so the reusable
   pattern keeps its left-aligned default for any other page that picks
   it up (la-empresa.php's placeholder stats, say). Two classes here vs.
   one below, so this wins on specificity regardless of source order. */
.stat-row--centered { margin-top: 0; align-items: center; }
.stat-row--centered .stat-row__item { text-align: center; }
.stat-row__item { text-align: left; }
.stat-row__number {
    font-size: 2.4rem;
    font-weight: 700;
    /* Site yellow (client request 2026-09-14), same var(--color-yellow) as .card__icon. */
    color: var(--color-yellow);
}
.stat-row__label { color: var(--color-grey-600); font-size: 0.92rem; margin-top: 4px; }
/* White labels on the home counters' dark band (2026-09-14). The yellow
   numbers need no override — they already read well on charcoal. */
.section--dark .stat-row__label { color: var(--color-white); }

/* The banner itself stays the dark charcoal panel — the light-grey band
   (same token as the counters area, .section--grey / --color-grey-50)
   belongs on its *wrapping* .section instead (each page adds
   .section--grey to that wrapper), not on .cta-banner. */
.cta-banner {
    background: var(--color-charcoal);
    /* Square corners, banner and its button (client request 2026-09-14). */
    border-radius: 0;
    padding: 64px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 32px;
    flex-wrap: wrap;
    color: var(--color-white);
}
.cta-banner h2 { color: var(--color-white); margin-bottom: 8px; }
.cta-banner p { color: var(--color-grey-300); }

/* Primary buttons on the site's dark surfaces flip to light-on-dark.
   With the near-black accent, an unmodified .btn--primary here would be
   a charcoal button on a charcoal panel — see --color-accent-on-dark. */
.cta-banner .btn--primary,
.section--dark .btn--primary {
    background: var(--color-accent-on-dark);
    color: var(--color-charcoal);
    border-radius: var(--radius-sm);
}
/* Square button inside the CTA banner, matching the banner's own corners.
   Split out rather than edited in the shared rule above so .section--dark
   buttons keep their radius; same specificity and later in the file, so
   it wins. */
.cta-banner .btn--primary { border-radius: 0; }
.cta-banner .btn--primary:hover { background: var(--color-yellow); }
.section--dark .btn--primary:hover { background: #FFFFFF; }

/* ---- company/about page: photo hero, stacked media, steps, video ---- */
.photo-hero {
    width: 100%;
    height: 440px;
    object-fit: cover;
    display: block;
    border-radius: 0;
    border: var(--content-image-border);
}
/* Full-bleed variant (producto-detalle.php's hero) — the img simply isn't
   wrapped in a .container in the markup, so it's already full viewport
   width on its own; this modifier only strips the rounded corners/border
   that make sense for the contained variant but look wrong flush against
   the viewport edges. Deliberately not a width:100vw trick — that can
   overflow by the vertical scrollbar's width (see html's overflow-x:hidden
   comment elsewhere in this file for why that class of bug is taken
   seriously here). */
.photo-hero--bleed {
    border-radius: 0;
    border: none;
}
/* producto-detalle.php's render hero: show the whole uploaded render at its
   own aspect ratio, edge to edge — no fixed height, no object-fit: cover
   crop. The two-class selector (and the matching one in the ≤720px block)
   is deliberate: it has to beat both the base `.photo-hero { height }` and
   its single-class mobile override. */
.photo-hero.photo-hero--bleed {
    height: auto;
    object-fit: contain;
}

/* ---- producto-detalle.php hero: render with the line + product name
   overlaid, centred in white (2026-08-31 — replaced a grey intro block
   that sat above the image, see that file's own note). ---- */
.pd-hero {
    position: relative;
    display: block;
}
.pd-hero__img { display: block; }
.pd-hero__caption {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 24px;
    text-align: center;
    /* Scrim — load-bearing, not decoration. One template renders ~30
       different products' heroes, and their centre-band luminance was
       measured across the whole set at 12 (near-black, Grou) to 222
       (near-white, Cool Gray): white text on the raw image is unreadable
       on the bright half. A centred ellipse puts the darkening only
       where the words are and fades to nothing well before the edges, so
       the render itself still reads. If this is ever lightened, re-check
       it against the brightest hero, not against a mid-tone one. */
    background: radial-gradient(ellipse 75% 65% at 50% 50%,
                rgba(0, 0, 0, 0.62) 0%,
                rgba(0, 0, 0, 0.44) 45%,
                rgba(0, 0, 0, 0) 78%);
    /* Only the line link should be clickable — the scrim itself must not
       swallow clicks meant for the image beneath it. */
    pointer-events: none;
}
.pd-hero__caption > * { pointer-events: auto; }
.pd-hero__line {
    font-family: var(--font-accent);
    font-weight: var(--font-accent-weight);
    font-size: 1.15rem;
    letter-spacing: .5px;
    /* Beats the global `a { color: inherit }` (0,0,1) on specificity, so
       no inline style is needed the way the old .eyebrow link had one. */
    color: var(--color-white);
    text-shadow: 0 1px 12px rgba(0, 0, 0, .55);
    transition: opacity var(--transition);
}
.pd-hero__line:hover { opacity: .75; }
.pd-hero__title {
    /* Beats the global `h1,h2,h3,h4 { color: var(--color-charcoal) }`. */
    color: var(--color-white);
    font-size: clamp(2.1rem, 5.5vw, 4.2rem);
    line-height: 1.05;
    letter-spacing: -0.02em;
    text-shadow: 0 2px 20px rgba(0, 0, 0, .5);
    margin: 0;
}

/* ---- Sticky product-title bar. Once the hero has scrolled fully past,
   the same two lines reappear as a compact strip pinned under the header:
   grey, black text, left-aligned, smaller. .pd-tabs then stacks below it
   (see --pd-tabs-extra near the tokens).

   `position: fixed` rather than sticky on purpose: a sticky element has
   to sit in normal flow, which would leave a permanent empty strip under
   the hero while the bar is hidden. Fixed takes no layout space, and the
   space .pd-tabs needs is reserved separately via --pd-tabs-extra.

   Only shown once the hero is fully past — deliberately NOT while the
   render is still on screen. The bar would then be sitting over product
   photography, and the hero renders run from near-black to near-white
   (see the .pd-hero__caption scrim note), so black-on-grey over one of
   the dark ones would be unreadable. Past the hero it only ever overlays
   the page's own light background. Same reason the background is solid
   rather than translucent. ---- */
.pd-hero-bar {
    position: fixed;
    /* 84px = .main-header's height; + the topbar's own height when a
       dealer is logged in and it's sticky too. Flush under the header —
       no gap to paint over, unlike .pd-tabs' ::before backdrop. */
    top: calc(84px + var(--sticky-header-offset));
    left: 0;
    right: 0;
    /* Under .main-header (100) so it slides up behind it when hiding,
       below .whatsapp-fab (150) and the modals. */
    z-index: 90;
    background: var(--color-grey-50);
    transform: translateY(-100%);
    opacity: 0;
    /* visibility (not just opacity) so the section-nav links this bar now
       carries are out of the tab order while it's hidden. */
    visibility: hidden;
    pointer-events: none;
    transition: transform .3s ease, opacity .3s ease, visibility .3s ease;
}
.pd-hero-bar.is-visible {
    transform: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
    .pd-hero-bar { transition: none; }
}
.pd-hero-bar__inner {
    display: flex;
    align-items: center;
    gap: 20px;
    min-height: var(--pd-hero-bar-h);
}
.pd-hero-bar__title {
    flex: 0 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 1.02rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--color-charcoal);
}
/* The section-nav (<nav class="pd-tabs">) lives inside this bar now — it
   pins along with the bar and sits to the right of the title, rather than
   being its own sticky bordered box. The standalone .pd-tabs chrome below
   (sticky position, bordered/shadowed box, container-width sizing, the
   ::before gap backdrop) is still used verbatim by producto-detalle-tab.php,
   so it's overridden here rather than changed. */
.pd-hero-bar .pd-tabs {
    position: static;
    /* Sits immediately to the right of the title (the __inner gap is the
       only space between them), not pushed out to the far edge. */
    flex: 0 1 auto;
    width: auto;
    max-width: none;
    min-width: 0;
    margin: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    overflow: hidden;
}
.pd-hero-bar .pd-tabs::before { display: none; }
.pd-hero-bar .pd-tabs__inner { padding: 0; }
.pd-hero-bar .pd-tabs__link { padding: 8px 14px; font-size: 0.86rem; }
/* Share toggle + strip (2026-09-10) — the "3 connected circles" button
   just left of "Consultar". Clicking it drops a small strip of the actual
   share targets below it (WhatsApp, Facebook, X, Telegram, LinkedIn, copy
   link); open/close is main.js's [data-share] block. The wrapper now
   carries the margin-left:auto that used to be on the CTA, so the two stay
   pushed to the far right as a pair.

   The strip's open state sets visibility: INHERIT, not visible. When
   .pd-hero-bar hides it goes visibility:hidden, and an explicit `visible`
   on a descendant would override that and leave the strip floating on
   screen with no bar under it. (main.js also closes it at that point.) */
.pd-share {
    position: relative;
    flex: 0 0 auto;
    margin-left: auto;
    /* 12px to the CTA rather than the bar's 20px gap — they read as one group. */
    margin-right: -8px;
}
/* Back button, immediately left of the share toggle and styled as its twin.
   It takes over the margin-left:auto that pushes the group right, so the
   share widget following it gives its own up; 4px between the two. */
.pd-hero-bar__back {
    flex: 0 0 auto;
    margin-left: auto;
    margin-right: -16px;
}
.pd-hero-bar__back + .pd-share { margin-left: 0; }
.pd-hero-bar__back,
.pd-share__toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-sm);
    background: none;
    color: var(--color-grey-700);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}
.pd-hero-bar__back svg,
.pd-share__toggle svg { width: 20px; height: 20px; }
/* Same grey chip as the active .pd-tabs__link beside it. */
.pd-hero-bar__back:hover,
.pd-hero-bar__back:focus-visible,
.pd-share__toggle:hover,
.pd-share__toggle:focus-visible,
.pd-share.is-open .pd-share__toggle {
    background: var(--color-grey-100);
    color: var(--color-charcoal);
}
.pd-share__strip {
    position: absolute;
    top: calc(100% + 12px);
    right: 0;
    display: flex;
    gap: 6px;
    padding: 8px;
    background: #FFFFFF;
    border-radius: var(--radius-lg);
    box-shadow: 0 0 0 1px rgba(34, 32, 28, 0.06), var(--shadow-md);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity var(--transition), transform var(--transition), visibility var(--transition);
}
/* Caret pointing up at the toggle — centred under its box (36px wide, so
   right = 18 - 5). */
.pd-share__strip::before {
    content: '';
    position: absolute;
    top: -5px;
    right: 13px;
    width: 10px;
    height: 10px;
    background: #FFFFFF;
    box-shadow: -1px -1px 0 rgba(34, 32, 28, 0.06);
    transform: rotate(45deg);
}
.pd-share.is-open .pd-share__strip {
    opacity: 1;
    visibility: inherit;
    transform: none;
    pointer-events: auto;
}
@media (prefers-reduced-motion: reduce) {
    .pd-share__strip { transition: none; }
}
/* Monochrome at rest, each network's own colour on hover/focus — the same
   "third-party brand mark" exception .whatsapp-fab makes to the palette. */
.pd-share__item {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--color-grey-100);
    color: var(--color-charcoal);
    cursor: pointer;
    transition: background var(--transition), color var(--transition);
}
.pd-share__item svg { width: 18px; height: 18px; }
.pd-share__item:hover,
.pd-share__item:focus-visible { color: #FFFFFF; }
.pd-share__item--whatsapp:hover, .pd-share__item--whatsapp:focus-visible { background: #25D366; }
.pd-share__item--facebook:hover, .pd-share__item--facebook:focus-visible { background: #1877F2; }
.pd-share__item--x:hover,        .pd-share__item--x:focus-visible        { background: #000000; }
.pd-share__item--telegram:hover, .pd-share__item--telegram:focus-visible { background: #26A5E4; }
.pd-share__item--linkedin:hover, .pd-share__item--linkedin:focus-visible { background: #0A66C2; }
.pd-share__item--copy:hover,     .pd-share__item--copy:focus-visible     { background: var(--color-charcoal); }

/* Inline variant (blog-detail.php — after the intro and after the article):
   the toggle sits in normal flow as a round outlined button, and the strip
   opens to its RIGHT, vertically centred on it, instead of dropping below.
   Selectors are doubled up so they beat the ≤1024px block's single-class
   .pd-share / .pd-share__strip::before overrides further down the file. */
.pd-share.pd-share--inline {
    width: max-content;
    margin-left: 0;
    margin-right: 0;
}
.pd-share--inline .pd-share__toggle {
    border: 1px solid var(--color-grey-200);
    border-radius: 50%;
}
.pd-share--inline .pd-share__strip {
    top: 50%;
    right: auto;
    left: calc(100% + 12px);
    z-index: 5;
    transform: translate(-6px, -50%);
}
.pd-share--inline.is-open .pd-share__strip { transform: translateY(-50%); }
/* Caret on the strip's left edge, pointing back at the toggle. */
.pd-share--inline .pd-share__strip::before {
    top: 50%;
    right: auto;
    left: -5px;
    margin-top: -5px;
    box-shadow: -1px 1px 0 rgba(34, 32, 28, 0.06);
}

/* "Consultar" CTA — pushed to the far right of the bar (by .pd-share's
   margin-left:auto just before it), compact near-black button that
   transitions to the site's yellow (var(--color-yellow), same as the home CTA banner /
   footer newsletter buttons) on hover, with dark text for contrast on the
   yellow. */
.pd-hero-bar__cta {
    flex: 0 0 auto;
    padding: 8px 18px;
    border-radius: var(--radius-sm);
    font-size: 0.85rem;
    font-weight: 600;
    line-height: 1.2;
    white-space: nowrap;
    cursor: pointer;
    background: var(--color-accent);
    color: #FFFFFF;
    border: 1px solid var(--color-accent);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.pd-hero-bar__cta:hover,
.pd-hero-bar__cta:focus-visible {
    background: var(--color-yellow);
    border-color: var(--color-yellow);
    color: var(--color-charcoal);
}
.photo-4x3, .photo-9x7 {
    width: 100%;
    object-fit: cover;
    display: block;
    border-radius: 0;
    border: var(--content-image-border);
}
.photo-4x3 { aspect-ratio: 4 / 3; }
.photo-9x7 { aspect-ratio: 9 / 7; }

/* Modifier: keeps the box's own size/aspect-ratio (so it still fits the
   card like .photo-4x3/.photo-9x7 do) but shows the whole image with no
   cropping — used by producto-detalle.php's product_txt_blocks images,
   which can be any source ratio. */
.photo-4x3--contain, .photo-9x7--contain {
    object-fit: contain;
}

.stacked-media { display: flex; flex-direction: column; gap: 20px; }
.stacked-media img {
    width: 100%;
    display: block;
    border-radius: 0;
    border: var(--content-image-border);
}
.stacked-media__logos {
    padding: 20px 24px;
    background: var(--color-grey-50);
}

.steps { list-style: none; margin: 24px 0 0; padding: 0; display: flex; flex-direction: column; gap: 16px; }
.steps li { padding-left: 16px; border-left: 3px solid var(--color-accent); }
.steps__label { font-weight: 600; color: var(--color-charcoal); margin-bottom: 4px; }
.steps p { color: var(--color-grey-600); font-size: 0.94rem; margin: 0; }

.video-embed {
    position: relative;
    padding-bottom: 56.25%;
    height: 0;
    border-radius: 0;
    overflow: hidden;
    border: var(--content-image-border);
    background: var(--color-grey-100);
}
.video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ---- blog ---- */
.blog-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 28px;
    margin-top: 48px;
}
.blog-card {
    display: flex;
    flex-direction: column;
    border-radius: 0;
    overflow: hidden;
    transition: box-shadow var(--transition), transform var(--transition);
}
.blog-card:hover { box-shadow: var(--shadow-sm); transform: translateY(-3px); }
.blog-card__media {
    aspect-ratio: 16 / 10;
    background:
        repeating-linear-gradient(135deg, var(--color-grey-100) 0 2px, var(--color-grey-50) 2px 14px);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-400);
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: .4px;
    text-align: center;
}
.blog-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.blog-card__body { padding: 24px; }
.blog-card__date {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: .4px;
    color: var(--color-accent);
    margin-bottom: 10px;
    display: block;
}
.blog-card h3 { font-size: 1.08rem; margin-bottom: 10px; }
.blog-card h3 a:hover { color: var(--color-accent); }
.blog-card p { color: var(--color-grey-600); font-size: 0.92rem; margin-bottom: 16px; }
.blog-card__link { font-weight: 600; font-size: 0.9rem; color: var(--color-charcoal); }
.blog-card__link:hover { color: var(--color-accent); }

/* ---- blog detail (single post) ---- */
.blog-detail__back {
    display: inline-block;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-grey-600);
    margin-bottom: 18px;
}
.blog-detail__back:hover { color: var(--color-accent); }
.blog-detail__meta {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--color-grey-500);
    font-size: 0.9rem;
    margin-bottom: 18px;
}
.blog-detail__intro {
    font-family: var(--font-accent);
    font-weight: var(--font-accent-weight);
    color: var(--color-accent);
    font-size: 1.9rem;
    line-height: 1.3;
    text-align: center;
    max-width: 700px;
    margin: 40px auto 0;
}
.blog-detail__article h2 { margin-top: 12px; }
.blog-detail__share { margin-top: 24px; }
.blog-detail__article .accent-text {
    display: block;
    margin: 32px 0;
    line-height: 1.4;
}

/* ---- forms (contact / login) ---- */
.form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px 20px;
}
.form-grid .field--full { grid-column: 1 / -1; }
.field { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
/* .form-grid's own row-gap already spaces rows out — without this,
   fields inside a grid also carry the base .field's margin-bottom on
   top of it, compounding to a much bigger (and inconsistent-looking)
   gap than either value alone. Fields outside a grid (e.g. the plain
   stacked .field list on acceso-distribuidores.php's login form) keep
   the base margin-bottom untouched. */
.form-grid .field { margin-bottom: 0; }
.field label { font-size: 0.88rem; font-weight: 500; color: var(--color-grey-700); }
.field input,
.field textarea {
    border: 1px solid var(--color-grey-300);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-size: 0.95rem;
    background: var(--color-white);
    outline: none;
    transition: border-color var(--transition);
    resize: vertical;
}
.field input:focus,
.field textarea:focus { border-color: var(--color-accent); }

/* ---- floating-label fields (contacto.php) ---- */
.field--floating {
    position: relative;
    display: block;
}
.field--floating input,
.field--floating textarea {
    width: 100%;
    /* Extra top padding (vs. the base .field's flat 12px) clears the
       label once it's floated to the top — without this the shrunk
       label would sit on top of the typed text instead of above it. */
    padding: 22px 14px 10px;
}
.field--floating label {
    position: absolute;
    left: 15px;
    top: 17px;
    font-size: 0.95rem;
    color: var(--color-grey-400);
    font-weight: 400;
    pointer-events: none;
    transition: top var(--transition), font-size var(--transition), color var(--transition);
}
/* :placeholder-shown only matches when the field is empty — every
   .field--floating input/textarea below has placeholder=" " (a single
   space, never empty-string) specifically so this pseudo-class has
   something to key off without needing JS to track "has a value". */
.field--floating input:focus ~ label,
.field--floating input:not(:placeholder-shown) ~ label,
.field--floating textarea:focus ~ label,
.field--floating textarea:not(:placeholder-shown) ~ label {
    top: 7px;
    font-size: 0.72rem;
    color: var(--color-accent);
}

/* ---- inline field validation (contacto.php) ---- */
.field--invalid input,
.field--invalid textarea { border-color: #B23A3A; }
.field--invalid label { color: #B23A3A; }
.field__error {
    display: block;
    margin-top: 6px;
    padding: 3px 9px;
    border-radius: 999px;
    background: #FBEAEA;
    font-size: 0.7rem;
    font-weight: 500;
    color: #B23A3A;
    width: fit-content;
    /* Visible whenever the [hidden] attribute is absent — see the
       [hidden] override right below for why visibility (not display)
       is what actually toggles this. */
    visibility: visible;
}
/* Keeps reserving this element's layout space even while "hidden" —
   overrides the browser's default [hidden] -> display:none (same
   [hidden]-vs-author-rule situation documented elsewhere in this
   project for .form-status, just solved in the opposite direction:
   there the goal was to restore display:none, here it's to cancel it).
   visibility:hidden is what actually hides the content instead, so a
   field's error slot is always the same height whether or not an error
   is currently shown — fields below never jump down when one appears. */
.field__error[hidden] {
    display: block;
    visibility: hidden;
}
.form-error {
    background: #FBEAEA;
    border: 1px solid #E3B8B8;
    color: #B23A3A;
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-size: 0.88rem;
    margin: 0 0 20px;
}
.form-notice {
    background: var(--color-grey-50);
    border: 1px solid var(--color-grey-200);
    color: var(--color-grey-700);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-size: 0.88rem;
    margin: 0 0 20px;
}

/* ---- form submit status (contacto.php: spinner while sending, then
   confirmation/error in place) ---- */
.form-status {
    display: flex;
    align-items: center;
    gap: 10px;
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-size: 0.88rem;
    font-weight: 500;
    margin: 0 0 20px;
}
/* The [hidden] attribute's display:none only comes from the browser's
   user-agent stylesheet, which any author rule (like .form-status's own
   `display: flex` above) always overrides regardless of specificity —
   so without this, [data-contact-status] rendered as a visible empty
   box (complete with its spinner, which has no hidden-state of its
   own) on every page load, before any submission. */
.form-status[hidden] { display: none; }
.form-status--loading {
    background: var(--color-grey-50);
    border: 1px solid var(--color-grey-200);
    color: var(--color-grey-700);
}
.form-status--ok {
    background: var(--color-accent-tint);
    border: 1px solid var(--color-accent-light);
    color: var(--color-accent-dark);
}
.form-status--error {
    background: #FBEAEA;
    border: 1px solid #E3B8B8;
    color: #B23A3A;
}
.form-status__spinner {
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 2px solid rgba(76, 70, 63, 0.25);
    border-top-color: var(--color-grey-700);
    animation: form-status-spin 0.7s linear infinite;
}
.form-status--ok .form-status__spinner,
.form-status--error .form-status__spinner { display: none; }
@keyframes form-status-spin {
    to { transform: rotate(360deg); }
}

.panel-card {
    background: var(--color-white);
    border: 1px solid var(--color-grey-200);
    border-radius: 0;
    padding: 48px;
    box-shadow: var(--shadow-sm);
}
.panel-card--grey { background: var(--color-grey-50); box-shadow: none; }

/* ---- password field with show/hide toggle (Mi Clave modal) ---- */
.field__input-wrap { position: relative; display: flex; }
.field__input-wrap input { flex: 1; padding-right: 44px; }
.field__toggle {
    position: absolute;
    top: 50%;
    right: 6px;
    transform: translateY(-50%);
    width: 32px;
    height: 32px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    color: var(--color-grey-400);
    transition: color var(--transition), background var(--transition);
}
.field__toggle:hover { color: var(--color-accent); background: var(--color-accent-tint); }
.field__toggle svg { width: 18px; height: 18px; }
.field__toggle .icon-eye-off { display: none; }
.field__toggle[aria-pressed="true"] .icon-eye { display: none; }
.field__toggle[aria-pressed="true"] .icon-eye-off { display: block; }

/* ---- modal (generic — currently only Mi Clave's password-change form) ---- */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(34, 32, 28, 0.55);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    z-index: 300;
    opacity: 0;
    visibility: hidden;
    /* visibility deliberately left out of this list — on open it should
       flip to visible the instant .is-open is toggled (so the field
       inside is immediately focusable), not animate in. Only opacity
       (and the .modal panel's own transform, below) actually transition.
       Closing is handled separately via .is-closing (see main.js's
       closeModal()) — visibility has to stay "visible" for the fade-out
       to be seen at all; without that class as a bridge, removing
       .is-open alone would drop straight to this rule's own
       visibility:hidden before the opacity transition had a chance to
       play, which is exactly the "just disappears" bug this fixes. */
    transition: opacity var(--transition);
}
.modal-overlay.is-open,
.modal-overlay.is-closing { visibility: visible; }
.modal-overlay.is-open { opacity: 1; }
.modal-overlay.is-closing { pointer-events: none; }
.modal {
    background: var(--color-white);
    border-radius: 0;
    box-shadow: var(--shadow-md);
    width: 100%;
    max-width: 440px;
    max-height: calc(100vh - 48px);
    overflow-y: auto;
    padding: 36px 32px 32px;
    position: relative;
    transform: translateY(16px);
    transition: transform var(--transition);
}
.modal-overlay.is-open .modal { transform: translateY(0); }
.modal__close {
    position: absolute;
    top: 14px;
    right: 14px;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-600);
    background: var(--color-grey-50);
    border: 1px solid var(--color-grey-200);
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.modal__close:hover { background: var(--color-accent-tint); color: var(--color-accent); border-color: var(--color-accent-light); }
.modal__close svg { width: 18px; height: 18px; }
.modal__title { font-size: 1.3rem; margin-bottom: 8px; padding-right: 36px; }
.modal__lead { color: var(--color-grey-600); font-size: 0.92rem; margin-bottom: 24px; }
.modal .field:last-of-type { margin-bottom: 24px; }
body.modal-open { overflow: hidden; }

/* PDF preview modal (producto-detalle.php) — much larger than the base
   .modal (sized for a short form), near-fullscreen so the embedded PDF is
   actually readable. overflow:hidden on the panel itself (rather than the
   base modal's overflow-y:auto) since the iframe fills all remaining
   space via flex:1 — nothing here should ever need the panel itself to
   scroll. */
.modal--pdf {
    max-width: 960px;
    width: calc(100% - 48px);
    height: calc(100vh - 48px);
    max-height: none;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.modal--pdf .modal__title { flex-shrink: 0; }
/* The grey backdrop lives on the wrapper and the iframe is transparent, so
   the loading message underneath shows through until the PDF viewer paints
   over it. That layering is the safety net: if the iframe's `load` event
   never fires (it isn't guaranteed for a plugin-rendered PDF), the document
   still covers the spinner instead of sitting under it. */
.modal__pdf-body {
    position: relative;
    flex: 1;
    min-height: 0;
    display: flex;
    border-radius: var(--radius-sm);
    background: var(--color-grey-50);
    overflow: hidden;
}
.modal__pdf-frame {
    position: relative;
    flex: 1;
    width: 100%;
    border: none;
    background: transparent;
}
.modal__pdf-loading {
    position: absolute;
    inset: 0;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 14px;
    color: var(--color-grey-600);
    font-size: 0.92rem;
}
.modal__pdf-body.is-loading .modal__pdf-loading { display: flex; }
.modal__pdf-spinner {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 3px solid rgba(76, 70, 63, 0.2);
    border-top-color: var(--color-grey-700);
    animation: form-status-spin 0.7s linear infinite;
}

/* Form modal (producto-detalle.php "Consultar" — embeds a Pipedrive web
   form). Wider than the base .modal; keeps the base's overflow-y:auto so
   a tall form scrolls inside the panel. */
.modal--form { max-width: 600px; }
.modal--form .pipedriveWebForms { margin-top: 4px; }
.modal--form .pipedriveWebForms iframe { width: 100% !important; }

/* Download success toast (producto-detalle.php) — deliberately a literal
   green rather than the site's terracotta accent (see CLAUDE.md: one
   accent color, used sparingly) — a save-confirmation toast is closer to
   generic "success" UI than an on-brand moment, so it borrows the more
   universal green convention instead. Visibility is class-only
   (.is-visible), same reasoning as .modal-overlay above: no [hidden]
   attribute, since author display/opacity rules would silently defeat it
   (see the .form-status[hidden] bug elsewhere in this project). */
.download-toast {
    position: fixed;
    bottom: 28px;
    left: 50%;
    transform: translateX(-50%) translateY(12px);
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 22px;
    background: #2E7D46;
    color: var(--color-white);
    border-radius: var(--radius-sm);
    box-shadow: var(--shadow-md);
    font-size: 0.92rem;
    font-weight: 600;
    z-index: 400;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--transition), transform var(--transition);
}
.download-toast.is-visible {
    opacity: 1;
    visibility: visible;
    transform: translateX(-50%) translateY(0);
}
.download-toast svg { width: 20px; height: 20px; flex-shrink: 0; }

/* =============================================================
   PRODUCT DETAIL (producto-detalle.php)
   ============================================================= */

/* ---- photo strip (5 images, 3 visible, arrow nav) ---- */
.pd-strip { position: relative; margin-top: 32px; }
/* Full-bleed variant (producto-detalle.php, right below the full-bleed
   hero) — not wrapped in a .container, so it's already edge-to-edge. A
   slight top margin (was 0 / flush against the hero) gives it a little
   breathing room, per request. */
.pd-strip--bleed { margin-top: 8px; }
.pd-strip__track {
    display: flex;
    gap: 20px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
}
.pd-strip__track::-webkit-scrollbar { display: none; }
.pd-strip__item {
    position: relative;
    flex: 0 0 calc((100% - 40px) / 3);
    scroll-snap-align: start;
    aspect-ratio: 16 / 10;
    border-radius: 0;
    overflow: hidden;
    border: var(--content-image-border);
    /* Items are <button>s (they open the lightbox) — strip the UA chrome. */
    padding: 0;
    background: none;
    color: inherit;
    font: inherit;
    cursor: pointer;
}
/* Bleed variant: a slight gap between items (was flush edge-to-edge,
   unlike the contained strip's 20px gap above) and the slight top margin
   above, both per request. Still no rounded corners, and still smaller
   tiles whose flex-basis is deliberately NOT an even divisor of 100%
   (22% ≈ 4.5 visible instead of a clean 4 or 5), so the last one in view
   is intentionally cut off mid-image as a "there's more, scroll" cue. */
.pd-strip--bleed .pd-strip__track { gap: 8px; }
.pd-strip--bleed .pd-strip__item { flex-basis: 22%; border-radius: 0; }
.pd-strip__item img { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform var(--transition); }
.pd-strip__item:hover img { transform: scale(1.04); }
/* Magnifier chip, bottom-right, revealed on hover/focus — signals the
   tile opens in the lightbox. */
.pd-strip__zoom {
    position: absolute;
    right: 8px;
    bottom: 8px;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.92);
    color: var(--color-charcoal);
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transform: translateY(4px);
    transition: opacity var(--transition), transform var(--transition);
    pointer-events: none;
}
.pd-strip__item:hover .pd-strip__zoom,
.pd-strip__item:focus-visible .pd-strip__zoom { opacity: 1; transform: none; }
.pd-strip__zoom svg { width: 16px; height: 16px; }
.pd-strip__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--color-white);
    border: 1px solid var(--color-grey-200);
    box-shadow: var(--shadow-md);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-charcoal);
    z-index: 5;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.pd-strip__arrow:hover { background: var(--color-accent); color: var(--color-white); border-color: var(--color-accent); }
.pd-strip__arrow--prev { left: 12px; }
.pd-strip__arrow--next { right: 12px; }
.pd-strip__arrow svg { width: 20px; height: 20px; }

/* ---- sticky section tabs ---- */
.pd-tabs {
    /* Not nested inside a .container div (see producto-detalle.php for
       why — it needs to be a direct child of .pd-tabs-scope for the
       sticky fix to actually take effect), so it replicates .container's
       content-area width/centering itself: a width:100%,max-width:1280px,
       margin:auto,padding:0 24px box has a content area that's always
       exactly "min(100%, 1280px) - 48px", centered the same way — this
       reproduces that same box without an outer wrapper or padding of its
       own (padding would sit *inside* this element's own border, making
       the visible bordered box wider than the rest of the page's content
       instead of matching it). */
    width: calc(100% - 48px);
    max-width: calc(var(--container-width) - 48px);
    position: sticky;
    /* .main-header is 84px tall (72px at the ≤1024px breakpoint below) —
       the extra 8px here is a deliberate visual gap so the tab bar
       doesn't sit flush against the header once both are stuck; keep the
       matching .pd-section scroll-margin-top values (further down) and
       the ::before backdrop's offsets (right below) in sync if this
       changes. The + var(--sticky-header-offset) accounts for .topbar
       also being sticky for a logged-in dealer (see body.dealer-logged-in
       near the :root tokens) — 0px otherwise, so this is a no-op for
       everyone else. */
    top: calc(92px + var(--pd-tabs-extra) + var(--sticky-header-offset));
    z-index: 60;
    background: var(--color-white);
    border: 1px solid var(--color-grey-200);
    border-radius: var(--radius-md);
    margin: 32px auto 0;
    box-shadow: var(--shadow-sm);
}
/* The 8px gap above (between the stuck header and top:92px here) isn't
   covered by either sticky box on its own, so scrolling content was
   visible through it. This backdrop rides along with .pd-tabs (same
   sticky box, so it moves with it, not a separately-positioned element)
   and paints that gap solid. `top`/`height` stop 1px short of the box's
   own top border (an absolutely-positioned child is placed relative to
   the *padding* box, not the border box, so without that 1px offset the
   backdrop paints directly over — and visually erases — .pd-tabs' own
   top border) — keep both the "8"s and the "1" in sync with the gap and
   border-width above if either changes. */
.pd-tabs::before {
    content: '';
    position: absolute;
    top: -9px;
    left: 0;
    right: 0;
    height: 8px;
    background: var(--color-white);
}
.pd-tabs__inner {
    display: flex;
    gap: 4px;
    overflow-x: auto;
    padding: 8px;
    scrollbar-width: none;
}
.pd-tabs__inner::-webkit-scrollbar { display: none; }
.pd-tabs__link {
    flex: 0 0 auto;
    border: none;
    background: none;
    font: inherit;
    cursor: pointer;
    padding: 12px 20px;
    border-radius: var(--radius-sm);
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--color-grey-600);
    white-space: nowrap;
    transition: background var(--transition), color var(--transition);
}
.pd-tabs__link:hover { color: var(--color-charcoal); }
.pd-tabs__link.is-active { background: var(--color-grey-100); color: var(--color-charcoal); }

.pd-section { scroll-margin-top: calc(164px + var(--pd-tabs-extra)); }
/* A bit of extra breathing room between consecutive sections, on top of
   their own top/bottom padding (32px, or 24px mobile — see
   .pd-tabs-scope--with-hero-bar .pd-section below). margin-bottom only,
   not padding: padding-top is what a tab-click anchor jump lands under
   (see the comment on that rule), so touching it back would reopen the
   "sliver of hero visible, bar flickers" issue that padding was
   deliberately tightened to avoid. */
.pd-section { margin-bottom: 24px; }
/* Product detail's own section titles (Descripción General, Especificaciones,
   etc.) read smaller/lighter than the site-wide .section-title default (used
   on blog.php, la-empresa.php, contacto.php, ...) — scoped to .pd-section so
   only this page (and its unrouted producto-detalle-tab.php variant, which
   shares the same classes) is affected. font-weight needs its own rule since
   the site-wide h1,h2,h3,h4 { font-weight: 600 } is what .section-title
   actually inherits it from — .section-title itself sets no weight. */
.pd-section .section-title {
    font-size: clamp(1.5rem, 2.4vw, 2rem);
    font-weight: 500;
    margin-bottom: 12px;
}
/* producto-detalle.php folds its section-nav into .pd-hero-bar, so the
   only sticky stack above a section is .main-header (84) + that bar (56).
   An anchor jump lands the section flush under both (the section's own
   88px top padding is the visual breathing room) — no gap, so a tab click
   that scrolls near the top can't leave a sliver of the hero showing and
   flicker the bar back off. producto-detalle-tab.php keeps the base value
   above (its .pd-tabs is a separate sticky element). --pd-tabs-extra stays
   defined and referenced but nothing sets it to non-zero any more. */
.pd-tabs-scope--with-hero-bar .pd-section { scroll-margin-top: 140px; padding: 32px 0; }
/* First section sits right under the full-bleed render strip (whose own
   section padding is zeroed in the markup) — hug it close rather than
   leave the default section gap. */
.pd-tabs-scope--with-hero-bar .pd-section:first-child { padding-top: 16px; }
/* Click-to-show tab panels (producto-detalle-tab.php only — see the
   data-tabs-panels branch in main.js). producto-detalle.php's .pd-section
   elements never get this class, since that page keeps every section
   always rendered and just scrolls to it. */
.pd-section.is-hidden { display: none; }

/* ---- "versiones disponibles" band ----
   Full-bleed grey band rather than the bordered .panel-card--grey box it
   used to be. The band element sits OUTSIDE .container in the markup (see
   producto-detalle.php) so its background reaches both viewport edges,
   and carries a .container of its own inside so the label and image strip
   stay aligned with every other block on the page. Done structurally
   rather than with the `width:100vw; margin-left:calc(50% - 50vw)`
   trick — 100vw includes the scrollbar, which would push the page
   sideways (masked here only by html{overflow-x:hidden}, which this
   codebase already relies on for the mobile nav and shouldn't have to
   cover for a second, avoidable case).
   The vertical padding lives on the band, not on .pd-versions, so the
   grey extends past the content the same amount top and bottom. */
.pd-versions-band {
    background: var(--color-grey-50);
    padding: 48px 0;
    margin-top: 24px;
}
.pd-versions {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 32px;
    align-items: center;
}
.pd-versions__label h3 { font-size: 1.5rem; margin-top: 6px; }
.pd-versions__list {
    display: flex;
    gap: 20px;
    overflow-x: auto;
    padding-bottom: 4px;
}
/* Each version is just the image now — no caption below it. The box
   mirrors the home page's .launch-tile ("Últimos Lanzamientos"): 1:1,
   rounded, overflow-clipped, white placeholder, with the same slow zoom
   on the image itself (not the box, so the rounded corners hold) on
   hover/focus. Applies to both the linked (<a>) and non-linked (<div>)
   variants. */
.pd-version-item { flex: 0 0 190px; }
.pd-version-item__media {
    aspect-ratio: 1 / 1;
    border-radius: 0;
    background: var(--color-white);
    overflow: hidden;
}
.pd-version-item__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 600ms ease;
}
.pd-version-item:hover .pd-version-item__media img,
.pd-version-item:focus-visible .pd-version-item__media img { transform: scale(1.06); }

@media (prefers-reduced-motion: reduce) {
    .pd-version-item__media img { transition: none; }
    .pd-version-item:hover .pd-version-item__media img,
    .pd-version-item:focus-visible .pd-version-item__media img { transform: none; }
}

/* ---- specs (measurements list + video) ---- */
.pd-specs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 40px;
    margin-top: 24px;
    align-items: start;
}
.pd-specs__media { display: flex; flex-direction: column; gap: 20px; }
.pd-specs__row {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 14px 0;
    border-bottom: 1px solid var(--color-grey-200);
    font-size: 0.94rem;
}
.pd-specs__row:first-child { padding-top: 0; }
.pd-specs__row span:first-child { color: var(--color-grey-600); }
.pd-specs__row span:last-child { font-weight: 600; color: var(--color-charcoal); text-align: right; }

/* ---- real measurement-diagram images (producto-detalle.php,
   product_medidas) — one fixed height for every image, side by side,
   wrapping to as many rows as needed; width is whatever that height
   works out to for each image's own aspect ratio (object-fit:contain,
   not stretched/cropped), which is why there's no explicit width here.
   Replaces .pd-specs above (still kept for producto-detalle-tab.php /
   "producto-detalle - STATIC.php", the two standalone comparison pages
   that still use it — not touched here, this is a new, separate
   ruleset). */
/* Slightly smaller than a normal .section-lead — it's a unit caption, not
   an intro paragraph. */
.pd-medidas__note { font-size: 0.95rem; }
.pd-medidas { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 20px; margin-top: 24px; }
.pd-medidas__img {
    height: 380px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    background: var(--color-white);
    border: var(--content-image-border);
    border-radius: var(--radius-md);
    display: block;
}
/* Measurement video (product_medidas.youtube_url) — shown after every
   measurement image, never interleaved among them (see the PHP split
   into $medidaImages/$medidaVideos). Full-bleed grey band, same
   structural pattern as .pd-versions-band below (the band sits OUTSIDE
   .container in the markup so its background reaches both viewport
   edges; it carries its own inner .container so the video still lines
   up with the rest of the page) — see that rule's comment for why this
   is done structurally rather than with a width:100vw trick. */
.pd-medidas__video-band {
    background: var(--color-grey-50);
    padding: 48px 0;
    margin-top: 32px;
}
/* The video itself: same centered, 900px-max .video-embed box as the
   Descripción General blocks' video (.pd-block-video). */
.pd-medidas__video {
    max-width: 900px;
    margin: 0 auto;
}

/* ---- ficha técnica download pills ---- */
.pd-downloads {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-auto-flow: row;
    gap: 20px;
    margin-top: 24px;
}
.pd-download-btn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 22px;
    background: var(--color-grey-50);
    border: none;
    border-radius: var(--radius-sm);
    font-weight: 600;
    font-size: 0.88rem;
    /* No text-transform: titles show exactly as loaded in the CMS
       (product_docs / product_recursos), per client request 2026-09-14. */
    color: var(--color-charcoal);
    transition: background var(--transition);
    /* A grid item's default min-width is auto (= its content's natural
       width), not 0 — without this, a long title forces the whole pill
       wider than its .pd-downloads grid track instead of wrapping/eliding
       within it, pushing later columns/pills off-screen. Same fix as
       .pd-download-btn__label's own min-width:0 below, just one level up
       the box tree (grid item, not flex item). */
    min-width: 0;
}
/* :not(--split) — the split (PDF) pill isn't a single click target
   anymore (view/download are two separate icon buttons inside it), so it
   shouldn't hover as a whole; only its .pd-download-btn__icon children
   get their own hover state, further down. */
.pd-download-btn:not(.pd-download-btn--split):hover { background: var(--color-accent-tint); }
.pd-download-btn svg { width: 20px; height: 20px; color: var(--color-accent); flex-shrink: 0; }

/* Split variant: PDF docs get two separate actions (view in modal /
   force-download) instead of one link filling the whole pill, so the pill
   itself is a plain <div> — .pd-download-btn's own padding/background/
   border/hover are all still shared with the single-link variant above,
   only the inner layout differs. */
/* Wraps to 2 (or more) lines rather than eliding — the label sits in a
   flex row next to the icon(s), and min-width:0 is what lets it actually
   shrink/wrap within that row instead of pushing them out. */
.pd-download-btn__label { min-width: 0; }
.pd-download-btn__actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.pd-download-btn__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    border-radius: 50%;
    background: transparent;
    border: none;
    cursor: pointer;
    flex-shrink: 0;
    transition: background var(--transition), color var(--transition);
}
.pd-download-btn__icon svg { width: 18px; height: 18px; color: var(--color-accent); }
.pd-download-btn__icon:hover { background: var(--color-accent); }
.pd-download-btn__icon:hover svg { color: var(--color-white); }

/* ---- imágenes gallery grid ---- */
.pd-gallery-row1 {
    display: grid;
    grid-template-columns: 1.4fr 1fr;
    gap: 20px;
    margin-top: 32px;
}
.pd-gallery-row1__big {
    height: 520px;
    border-radius: 0;
    overflow: hidden;
    border: var(--content-image-border);
}
.pd-gallery-row1__side { display: flex; flex-direction: column; gap: 20px; height: 520px; }
.pd-gallery-row1__side > div {
    flex: 1;
    min-height: 0;
    border-radius: 0;
    overflow: hidden;
    border: var(--content-image-border);
}
.pd-gallery-row1__big img,
.pd-gallery-row1__side img { width: 100%; height: 100%; object-fit: cover; display: block; }

.pd-gallery-row2 {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 20px;
    margin-top: 20px;
}
.pd-gallery-row2 > div {
    aspect-ratio: 4 / 3;
    border-radius: 0;
    overflow: hidden;
    border: var(--content-image-border);
}
.pd-gallery-row2 img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---- real product-photos gallery (producto-detalle.php) ----
   One row of uniform tiles that scrolls sideways, with the render strip's
   arrows (.pd-strip__arrow) on each side (2026-09-14). It used to be a
   wrapping auto-fill grid that showed every photo at once — products range
   from 1 to 45+ photos — and before that a large 3:4 "featured" first photo
   beside a grid of thumbnails; both were changed at the client's request.

   4 tiles in view on desktop, 3 at ≤1024px, 2 at ≤720px — a clean divisor
   each time, since the arrows (not a cut-off tile) signal there's more.
   Deliberately no cloning/infinite loop like .pd-strip: a product with 3
   photos would show duplicates side by side. The arrows wrap to the other
   end instead (main.js, [data-gallery-row]). */
.pd-gallery {
    position: relative;
    margin-top: 24px;
}
.pd-gallery__track {
    display: flex;
    gap: 20px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
}
.pd-gallery__track::-webkit-scrollbar { display: none; }
/* Arrows ship `hidden` and are shown only when the row overflows; the
   arrow's own display:flex would otherwise beat the attribute. */
.pd-gallery .pd-strip__arrow[hidden] { display: none; }
.pd-gallery__thumb {
    display: block;
    flex: 0 0 calc((100% - 60px) / 4);
    scroll-snap-align: start;
    aspect-ratio: 4 / 3;
    padding: 0;
    /* Fill colour for the letterbox space object-fit:contain leaves around
       a photo whose real aspect ratio doesn't match this box's 4:3 — shows
       the whole photo uncropped instead of cover's crop-to-fill. White (not
       the grey-50 this started as) so the whole gallery reads as one
       consistent white surface, section background included. */
    background: var(--color-white);
    border: var(--content-image-border);
    border-radius: 0;
    overflow: hidden;
    cursor: zoom-in;
}
.pd-gallery__thumb img { width: 100%; height: 100%; object-fit: contain; display: block; transition: transform var(--transition); }
.pd-gallery__thumb:hover img { transform: scale(1.06); }
/* The track clips its overflow, which would cut off an outside focus ring. */
.pd-gallery__thumb:focus-visible { outline-offset: -3px; }

/* ---- image lightbox modal (#modal-gallery) — white stage so product
   photos (shot on white) sit centered via object-fit:contain with no
   visible box around them, and any aspect ratio still fits without being
   cropped. */
.modal--gallery {
    max-width: 1100px;
    width: calc(100% - 48px);
    height: calc(100vh - 48px);
    max-height: none;
    padding: 0;
    background: #FFFFFF;
    position: relative;
    overflow: hidden;
}
.modal--gallery .modal__close {
    z-index: 3;
}
.pd-gallery-modal__stage {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
}
.pd-gallery-modal__stage img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.pd-gallery-modal__arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--color-grey-50);
    border: 1px solid var(--color-grey-200);
    color: var(--color-grey-600);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 2;
    transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.pd-gallery-modal__arrow:hover { background: var(--color-accent); border-color: var(--color-accent); color: var(--color-white); }
.pd-gallery-modal__arrow[hidden] { display: none; }
.pd-gallery-modal__arrow--prev { left: 16px; }
.pd-gallery-modal__arrow--next { right: 16px; }
.pd-gallery-modal__arrow svg { width: 22px; height: 22px; }
.pd-gallery-modal__counter {
    position: absolute;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    color: var(--color-white);
    font-size: 0.82rem;
    background: rgba(0, 0, 0, 0.4);
    padding: 6px 14px;
    border-radius: 999px;
    z-index: 2;
}

/* ---- recursos cards ---- */
.pd-resources {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 28px;
    margin-top: 32px;
}
.pd-resource-card {
    border: 1px solid var(--color-grey-200);
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--color-white);
    transition: box-shadow var(--transition), transform var(--transition);
}
.pd-resource-card:hover { box-shadow: var(--shadow-sm); transform: translateY(-3px); }
.pd-resource-card__media { position: relative; aspect-ratio: 4 / 3; background: var(--color-grey-50); overflow: hidden; }
.pd-resource-card__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    filter: grayscale(1) contrast(0.9) brightness(1.15) opacity(0.5);
}
.pd-resource-card__icon {
    position: absolute;
    right: 16px;
    top: 16px;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--color-white);
    border: 1px solid var(--color-grey-200);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-600);
}
.pd-resource-card__icon svg { width: 16px; height: 16px; }
.pd-resource-card__body { padding: 22px 24px 26px; }
.pd-resource-card__body h3 { font-size: 1.1rem; margin-bottom: 14px; }
.pd-resource-card__btn {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: var(--color-charcoal);
    color: var(--color-white);
    padding: 10px 18px;
    border-radius: 999px;
    font-size: 0.85rem;
    font-weight: 500;
    transition: background var(--transition);
}
/* Inverts on hover rather than going to the accent: this button is
   already charcoal, and near-black-on-charcoal is not a visible state
   change. Same treatment as .cta-banner .btn--primary. */
.pd-resource-card__btn:hover { background: var(--color-accent-on-dark); color: var(--color-charcoal); }
.pd-resource-card__btn svg { width: 14px; height: 14px; }

/* =============================================================
   DISTRIBUTOR ORDER HISTORY
   (historial-pedidos.php / historial-pedido-detalle.php)
   ============================================================= */

/* ---- status pill (shared by the list row and the detail header) ---- */
.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 5px 14px;
    border-radius: 999px;
    font-size: 0.76rem;
    font-weight: 600;
    letter-spacing: .3px;
    text-transform: uppercase;
    white-space: nowrap;
}
.status-pill::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex-shrink: 0; }
.status-pill--pending { background: var(--color-grey-100); color: var(--color-grey-700); }
/* Solid dark chip rather than the old pale-terracotta one: with a
   near-black accent, accent-tint is a light grey all but identical to
   --pending's grey-100, which would have made "En Curso" and "Sin
   Procesar" near-indistinguishable at a glance. Inverting it keeps the
   two clearly apart without introducing a colour the palette doesn't
   have. (--success/--danger keep their green/red — those are semantic
   status colours, not the brand accent.) */
.status-pill--progress { background: var(--color-charcoal); color: var(--color-white); }
.status-pill--success { background: #E8F3EB; color: #2E7D46; }
.status-pill--danger { background: #FBEAEA; color: #B23A3A; }
/* Sits in a 1fr grid column in .order-row — without this it stretches to
   fill that column's full width instead of just wrapping its text. */
.status-pill { justify-self: start; }

/* ---- filter toolbar + collapsible date-filter panel (historial-pedidos.php) ---- */
.order-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}
.order-toolbar__summary { color: var(--color-grey-600); font-size: 0.95rem; }
.order-toolbar .btn svg { width: 16px; height: 16px; }
/* No border on the Filtrar toggle — scoped here rather than on the shared
   .btn--outline, which every other outline button on the site still uses. */
.order-toolbar__toggle { border: none; }
.order-toolbar__chevron { transition: transform var(--transition); }
.order-toolbar__toggle[aria-expanded="true"] .order-toolbar__chevron { transform: rotate(180deg); }

.order-filter-panel {
    max-height: 0;
    opacity: 0;
    overflow: hidden;
    margin-top: 16px;
    transition: max-height var(--transition), opacity var(--transition);
}
.order-filter-panel.is-open { max-height: 260px; opacity: 1; }

.order-filter {
    display: flex;
    align-items: flex-end;
    gap: 14px;
    flex-wrap: wrap;
    padding: 16px 18px;
    background: var(--color-grey-50);
    border-radius: 0;
}
.order-filter .field { margin-bottom: 0; gap: 4px; }
.order-filter .field label { font-size: 0.78rem; }
.order-filter .field input {
    min-width: 130px;
    padding: 8px 38px 8px 10px;
    font-size: 0.88rem;
}
/* Calendar-picker trigger, sized down a bit from its default (also used
   by the Mi Clave modal's password eye-toggle) to match this compact
   filter. Sits over .field__input-wrap's padding-right:38px above. */
.order-filter .field__toggle { width: 26px; height: 26px; right: 4px; }
.order-filter .field__toggle svg { width: 15px; height: 15px; }
/* The real <input type="date"> behind the calendar button — invisible
   but still a real, sizeable element (not display:none) so
   .showPicker()/.focus() keep working across browsers. Its own value
   is never submitted (no [name]); assets/js/main.js copies a picked
   date into the visible dd/mm/aaaa text field instead. */
.field__input-wrap .date-field__native {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    border: 0;
    opacity: 0;
    pointer-events: none;
}

/* ---- order list rows ---- */
.order-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 32px;
}
.order-row {
    display: grid;
    grid-template-columns: 140px 110px 1fr auto;
    align-items: center;
    gap: 20px;
    padding: 18px 24px;
    background: var(--color-white);
    border-radius: 0;
    transition: box-shadow var(--transition), transform var(--transition);
}
.order-row:hover {
    box-shadow: var(--shadow-sm);
    transform: translateY(-2px);
}
.order-row__id { font-weight: 600; color: var(--color-charcoal); }
.order-row__date { color: var(--color-grey-600); font-size: 0.92rem; }
.order-row__action {
    display: flex;
    align-items: center;
    gap: 6px;
    justify-self: end;
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--color-accent);
}
.order-row__action svg { width: 16px; height: 16px; transition: transform var(--transition); }
.order-row:hover .order-row__action svg { transform: translateX(3px); }

/* ---- pagination ---- */
.pagination {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 24px;
    margin-top: 40px;
}
.pagination__link {
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-charcoal);
    transition: color var(--transition);
}
.pagination__link:hover { color: var(--color-accent); }
.pagination__link--disabled { color: var(--color-grey-300); pointer-events: none; }
.pagination__status { color: var(--color-grey-500); font-size: 0.88rem; }

/* ---- detail page ---- */
.historial-detail__back {
    display: inline-block;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--color-grey-600);
    margin-bottom: 18px;
}
.historial-detail__back:hover { color: var(--color-accent); }
.historial-detail__meta {
    display: flex;
    align-items: center;
    gap: 14px;
    color: var(--color-grey-500);
    font-size: 0.9rem;
    margin-top: 6px;
}
.order-detail-summary { margin-bottom: 40px; border: none; }
.order-detail-summary p { margin: 0 0 8px; }
.order-detail-summary p:last-child { margin-bottom: 0; }

.order-detail-list {
    display: flex;
    flex-direction: column;
    gap: 12px;
    margin-top: 24px;
}
.order-detail-item {
    display: flex;
    align-items: flex-start;
    gap: 20px;
    padding: 20px 24px;
    background: var(--color-white);
    border-radius: 0;
}
.order-detail-item__qty {
    flex-shrink: 0;
    background: var(--color-grey-100);
    color: var(--color-grey-700);
    font-weight: 700;
    font-size: 0.85rem;
    border-radius: var(--radius-sm);
    padding: 6px 12px;
}
.order-detail-item__title { font-weight: 600; color: var(--color-charcoal); margin-bottom: 4px; }
.order-detail-item__code {
    font-size: 0.82rem;
    color: var(--color-accent-dark);
    font-weight: 600;
    letter-spacing: .3px;
    margin-bottom: 4px;
}
.order-detail-item__options { font-size: 0.88rem; color: var(--color-grey-600); }
/* Price breakdown + line total, right of the description — same type
   treatment as the cart's .cart-row__price / .cart-row__total, so an order
   reads the same in the history as it did at checkout. The body takes the
   free space so both stay pinned right; the fixed-width total keeps every
   row's figures in one column. */
.order-detail-item__body { flex: 1; min-width: 0; }
.order-detail-item__price {
    flex-shrink: 0;
    font-size: 0.82rem;
    color: var(--color-grey-600);
    line-height: 1.5;
    white-space: nowrap;
    text-align: right;
}
.order-detail-item__unit { color: var(--color-charcoal); }
.order-detail-item__noprice { color: var(--color-grey-500); font-size: 0.85rem; }
.order-detail-item__total {
    flex-shrink: 0;
    min-width: 130px;
    font-weight: 700;
    color: var(--color-charcoal);
    white-space: nowrap;
    text-align: right;
}

/* =============================================================
   DEALER ORDERING (pedido-sillas.php / pedido-componentes.php and
   their agregar/confirmar/confirmado/guardar handlers)

   Row layout deliberately mirrors .order-row/.order-detail-item above
   (white card, no border, --radius-sm accents, no .card-grid/.product-tile
   reuse) since these are the same family of "one line per record" list as
   the order history, just editable. See CLAUDE.md's dealer-cart-port
   notes for the page-by-page rationale.
   ============================================================= */

.cart-empty { border: none; }

.cart-flash {
    background: var(--color-accent-tint);
    border: none;
    color: var(--color-accent-dark);
    border-radius: var(--radius-sm);
    padding: 12px 14px;
    font-size: 0.88rem;
    margin: 0 0 20px;
}

.cart-list { display: flex; flex-direction: column; gap: 12px; margin-top: 8px; }

.cart-row {
    display: grid;
    /* media, body, price, qty, total, controls (Eliminar) — 6 tracks for
       6 children. Previously only 5 were declared, so the 6th (controls)
       overflowed onto an implicit new row instead of sitting in the same
       row as the last column. */
    grid-template-columns: 96px 1fr auto auto auto auto;
    align-items: center;
    gap: 20px;
    padding: 18px 24px;
    background: var(--color-white);
    border-radius: 0;
}
/* The review screen (pedido-*-confirmar.php) drops the qty input and the
   remove/update controls, so it only ever needs 4 of the 5 columns — a
   plain form has none, so this variant collapses to a <div>-only grid. */
.cart-list--review .cart-row { grid-template-columns: 96px 1fr auto auto; }

.cart-row__media {
    width: 96px;
    height: 64px;
    border-radius: var(--radius-sm);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.cart-row__media img { width: 100%; height: 100%; object-fit: contain; }
.cart-row__media-placeholder {
    width: 100%;
    height: 100%;
    background: repeating-linear-gradient(45deg, var(--color-grey-100), var(--color-grey-100) 6px, var(--color-grey-50) 6px, var(--color-grey-50) 12px);
}

.cart-row__title { font-weight: 600; color: var(--color-charcoal); margin-bottom: 3px; }
.cart-row__options { font-size: 0.85rem; color: var(--color-grey-600); margin-bottom: 3px; }
.cart-row__code {
    font-size: 0.78rem;
    color: var(--color-accent-dark);
    font-weight: 600;
    letter-spacing: .3px;
}

.cart-row__price { font-size: 0.82rem; color: var(--color-grey-600); line-height: 1.5; white-space: nowrap; }
.cart-row__unit { margin-top: 2px; color: var(--color-charcoal); }
.cart-row__noprice { color: var(--color-grey-500); font-size: 0.85rem; }
.cart-discount { color: #B23A3A; }

.cart-row__qty { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.cart-row__qty label { font-size: 0.72rem; color: var(--color-grey-500); }
.cart-row__qty input {
    width: 68px;
    border: 1px solid var(--color-grey-300);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
    font-size: 0.9rem;
    text-align: center;
}
.cart-row__qty input:focus { border-color: var(--color-accent); outline: none; }
.cart-row__units { display: block; font-size: 0.72rem; color: var(--color-grey-500); margin-top: 2px; }
.cart-row__qty--static { font-size: 0.88rem; color: var(--color-grey-700); white-space: nowrap; }

.cart-row__total { font-weight: 700; color: var(--color-charcoal); white-space: nowrap; text-align: right; }

.cart-row__controls { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
/* Real submit buttons so both actions work with JS off — main.js hides
   this one once it wires up auto-save-on-change (see the [hidden] rule
   right below, same trap documented elsewhere in this file: an author
   `display` rule on .btn/.cart-row__update always beats the bare
   [hidden] attribute unless it's restated explicitly here). */
.cart-row__update[hidden] { display: none; }
.cart-row__remove {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.82rem;
    font-weight: 600;
    color: var(--color-grey-500);
    padding: 8px 4px;
    transition: color var(--transition);
}
.cart-row__remove:hover { color: #B23A3A; }
.cart-row__remove svg { width: 22px; height: 22px; }

.cart-qty-status { margin-top: 0; margin-bottom: 20px; border: none; }

.cart-summary {
    margin-top: 24px;
    padding: 20px 24px;
    background: var(--color-grey-50);
    text-align: right;
}
.cart-summary p { margin: 0 0 6px; }
.cart-summary p:last-of-type { margin-bottom: 0; }
.cart-summary__units { color: var(--color-grey-700); }
.cart-summary__warning { color: #B23A3A; font-weight: 600; font-size: 0.88rem; }
.cart-summary__total { font-size: 1.1rem; }
.cart-summary__total strong { color: var(--color-charcoal); }
.cart-summary__legend { margin-top: 10px; font-size: 0.78rem; color: var(--color-grey-500); text-align: right; }
.cart-summary__legend p { margin: 0; }

.cart-edit-link { margin-top: 16px; font-size: 0.88rem; }
.cart-edit-link a { color: var(--color-accent); font-weight: 600; }

.cart-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 16px;
    margin-top: 32px;
}
/* pedido-sillas.php / pedido-componentes.php's cart-listing action row
   only (Agregar producto/componente + Revisar pedido) — right-aligned per
   request. Scoped rather than changing the shared .cart-actions, which
   the empty-cart state and the agregar/confirmar/confirmado/historial
   pages' "Volver"-style rows also use and should stay left-aligned. */
.cart-actions--end { justify-content: flex-end; }

.order-form__note {
    font-size: 0.8rem;
    color: var(--color-grey-500);
    margin-top: 10px;
}

/* ---- add-to-cart picker (state 1/2: product/variant/component grid) ----
   Sized to match the public catalogue's .product-grid / .product-tile
   (/productos/todos) on request: same 4/2/1 columns and 24px gap, a 4:3
   media box with the --padded 16px inset, and the same body padding and
   title size. Keep the two in step if either changes. */
.picker-grid {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 24px;
    margin: 24px 0 40px;
}
.picker-tile {
    display: flex;
    flex-direction: column;
    text-align: center;
    background: var(--color-white);
    border-radius: 0;
    overflow: hidden;
    transition: box-shadow var(--transition), transform var(--transition);
}
.picker-tile:hover {
    box-shadow: var(--shadow-sm);
    transform: translateY(-3px);
}
.picker-tile__media {
    aspect-ratio: 4 / 3;
    padding: 16px;
    background: var(--color-white);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.picker-tile__media img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
    border-radius: var(--radius-sm);
}
.picker-tile__title {
    padding: 18px 20px 22px;
    font-size: 1rem;
    font-weight: 600;
    line-height: 1.2;
    color: var(--color-charcoal);
}

/* ---- add-to-cart form (state 3: options + qty) ---- */
/* Border dropped on request — this is the one .panel-card use with no
   border; the shared .panel-card rule (contacto.php, product-detail, etc.)
   is untouched. */
/* Widened from 720px so a product's option fields (Mecanismo/Base/
   Apoyabrazo/Tela-Color — 3 or 4 depending on orders_enable_mec_selection)
   all fit on one row instead of an odd count wrapping the last one onto
   its own line — see .add-cart__options below. */
.add-cart { max-width: 960px; border: none; }
.add-cart__head {
    display: flex;
    align-items: center;
    gap: 20px;
    margin-bottom: 28px;
    padding-bottom: 24px;
    border-bottom: 1px solid var(--color-grey-200);
}
.add-cart__media {
    width: 120px;
    height: 120px;
    object-fit: contain;
    background: var(--color-white);
    border-radius: var(--radius-sm);
    flex-shrink: 0;
}
.add-cart__line { font-size: 0.78rem; color: var(--color-grey-500); text-transform: uppercase; letter-spacing: .5px; }
.add-cart__title { font-size: 1.3rem; margin: 2px 0; }
.add-cart__code { font-size: 0.82rem; color: var(--color-accent-dark); font-weight: 600; }

.add-cart__options {
    display: grid;
    /* Up to 4 fields (Mecanismo/Base/Apoyabrazo/Tela-Color); a product
       with only 3 (Mecanismo disabled) just leaves the 4th track empty
       rather than wrapping onto a lone second row. */
    grid-template-columns: repeat(4, 1fr);
    gap: 20px 20px;
}
.add-cart__option label {
    display: flex;
    align-items: center;
    gap: 8px;
}
.add-cart__help {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--color-grey-100);
    color: var(--color-grey-600);
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1;
    flex-shrink: 0;
    transition: background var(--transition), color var(--transition);
}
.add-cart__help:hover { background: var(--color-accent-tint); color: var(--color-accent); }
.add-cart__option select {
    border: 1px solid var(--color-grey-300);
    border-radius: var(--radius-sm);
    padding: 11px 14px;
    font-size: 0.92rem;
    background: var(--color-white);
    width: 100%;
}
.add-cart__option select:focus { border-color: var(--color-accent); outline: none; }
.add-cart__preview { margin-top: 10px; }
/* Same [hidden]-vs-author-rule trap as elsewhere in this file — restated
   explicitly since .add-cart__preview sets no display of its own but a
   future rule easily could. */
.add-cart__preview[hidden] { display: none; }
.add-cart__preview img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-sm);
}

.add-cart__footer {
    display: flex;
    align-items: flex-end;
    gap: 20px;
    margin-top: 28px;
    padding-top: 24px;
    border-top: 1px solid var(--color-grey-200);
}
.add-cart__qty { margin-bottom: 0; width: 120px; }
.add-cart__qty input { width: 100%; }

/* ---- option-help catalogue modal ---- */
.modal--wide { max-width: 640px; }
.opt-catalog {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 16px;
    max-height: 60vh;
    overflow-y: auto;
}
.opt-catalog__item { display: flex; flex-direction: column; align-items: center; gap: 8px; text-align: center; }
.opt-catalog__media {
    width: 100%;
    aspect-ratio: 1 / 1;
    background: var(--color-grey-50);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.opt-catalog__media img { width: 100%; height: 100%; object-fit: contain; }
.opt-catalog__title { font-size: 0.8rem; color: var(--color-grey-700); }

/* ---- order-confirmed panel (pedido-*-confirmado.php) ---- */
.order-confirmed { max-width: 560px; text-align: center; margin: 0 auto; }
.order-confirmed__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #E8F3EB;
    color: #2E7D46;
    margin-bottom: 20px;
}
.order-confirmed__icon svg { width: 28px; height: 28px; }
.order-confirmed__lead { font-size: 1.05rem; color: var(--color-charcoal); margin-bottom: 12px; }

/* =============================================================
   FOOTER
   ============================================================= */
.site-footer {
    background: var(--color-charcoal);
    color: var(--color-grey-300);
}

.footer__grid {
    display: grid;
    grid-template-columns: 1.4fr 1fr 1.2fr 1fr;
    gap: 48px;
    padding: 72px 24px 56px;
}

.footer__logo {
    height: 26px;
    width: auto;
    filter: brightness(0) invert(1);
    margin-bottom: 20px;
}

.footer__address {
    font-size: 0.92rem;
    color: var(--color-grey-300);
    margin-bottom: 16px;
    max-width: 240px;
}

.footer__whatsapp {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 0.92rem;
    font-weight: 500;
    color: var(--color-white);
    transition: color var(--transition);
}
.footer__whatsapp svg { width: 18px; height: 18px; color: #3FBF5F; }
.footer__whatsapp:hover { color: var(--color-accent-on-dark); }

.footer__social {
    display: flex;
    gap: 14px;
    margin-top: 24px;
}
.footer__social a {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    border: 1px solid rgba(255,255,255,0.18);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--color-grey-300);
    transition: border-color var(--transition), color var(--transition);
}
.footer__social svg { width: 15px; height: 15px; }
.footer__social a:hover { border-color: var(--color-accent-on-dark); color: var(--color-accent-on-dark); }

/* ---- "Login Distribuidores" (includes/footer.php) ----
   The public site's distributor-login entry point since .topbar became
   dealer-only (2026-09-08). Deliberately the same solid, square-cornered
   button as the newsletter's "Suscribirse" (off-white fill, charcoal
   text) rather than an outlined variant — no border at all.
   Hover fills with the site's yellow, var(--color-yellow) — the same
   token used by .newsletter-form button:hover and .pd-hero-bar__cta.

   Left alignment: align-self/margin-right keep the box pinned to the
   column's left edge rather than stretching or centring, whatever
   display type .footer__col--brand ends up with (it has no rule of its
   own today, so this is belt-and-braces). The label itself is inset by
   the button's own padding — that's intended, the *box* is what lines
   up with the logo/address/social above.

   The 36px top margin (vs. .footer__social's own 24px) is deliberate
   spacing so the button reads as its own thing below the icon row
   rather than part of it. */
.footer__login {
    display: inline-flex;
    align-items: center;
    align-self: flex-start;
    margin-top: 36px;
    margin-right: auto;
    gap: 8px;
    padding: 11px 18px;
    border: 0;
    border-radius: 0;
    font-size: 0.85rem;
    font-weight: 500;
    background: var(--color-accent-on-dark);
    color: var(--color-charcoal);
    transition: background var(--transition), color var(--transition);
}
.footer__login svg { width: 18px; height: 18px; flex-shrink: 0; }
.footer__login:hover,
.footer__login:focus-visible {
    background: var(--color-yellow);
    color: var(--color-charcoal);
}

.site-footer h3 {
    color: var(--color-white);
    font-size: 0.95rem;
    text-transform: uppercase;
    letter-spacing: .4px;
    margin-bottom: 20px;
}

.footer__col--links ul { display: flex; flex-direction: column; gap: 8px; }
.footer__col--links a { font-size: 0.92rem; transition: color var(--transition); }
.footer__col--links a:hover { color: var(--color-accent-on-dark); }

.footer__col--newsletter p { font-size: 0.88rem; margin-bottom: 18px; color: var(--color-grey-400); }
.newsletter-msg {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 0.85rem;
    font-weight: 600;
    border-radius: var(--radius-sm);
    opacity: 0;
    transform: translateY(-6px) scale(0.98);
    max-height: 0;
    margin: 0;
    padding: 0 14px;
    overflow: hidden;
    transition: opacity 0.3s ease, transform 0.3s ease, max-height 0.3s ease, padding 0.3s ease, margin 0.3s ease;
}
.newsletter-msg.is-visible {
    opacity: 1;
    transform: translateY(0) scale(1);
    max-height: 60px;
    margin: -6px 0 14px;
    padding: 10px 14px;
}
.newsletter-msg::before {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    font-size: 0.72rem;
    line-height: 1;
}
/* Sits on the dark charcoal footer, so this one can't use the accent:
   near-black on near-black is invisible. Was a pale terracotta wash
   (hardcoded rgba of the old accent) — now a neutral light wash, with
   the check mark inverted (light chip, dark glyph) instead of the old
   accent-filled chip. */
.newsletter-msg--ok {
    background: rgba(255, 255, 255, 0.12);
    border: 1px solid rgba(255, 255, 255, 0.32);
    color: var(--color-accent-light);
}
.newsletter-msg--ok::before { content: '\2713'; background: var(--color-accent-light); color: var(--color-charcoal); }
.newsletter-msg--error {
    background: rgba(211, 92, 92, 0.18);
    border: 1px solid rgba(211, 92, 92, 0.5);
    color: #F0BFBF;
}
.newsletter-msg--error::before { content: '!'; background: #D35C5C; color: var(--color-white); }
.newsletter-form { display: flex; flex-direction: column; gap: 10px; }
.newsletter-form input {
    border: 1px solid rgba(255,255,255,0.18);
    background: rgba(255,255,255,0.05);
    border-radius: var(--radius-sm);
    padding: 11px 14px;
    color: var(--color-white);
    font-size: 0.9rem;
    outline: none;
}
.newsletter-form input::placeholder { color: var(--color-grey-400); }
.newsletter-form input:focus { border-color: var(--color-accent-on-dark); }
/* Inverted against the charcoal footer — see --color-accent-on-dark. */
.newsletter-form button {
    background: var(--color-accent-on-dark);
    color: var(--color-charcoal);
    border-radius: 0;
    padding: 11px 14px;
    font-weight: 500;
    font-size: 0.9rem;
    transition: background var(--transition);
}
.newsletter-form button:hover { background: var(--color-yellow); }

/* ---- floating WhatsApp button (includes/footer.php, every page) ----
   z-index 150 sits above the sticky header (100/101) but below the modal
   overlay (300) and the toast (400), so it never floats over a dialog.
   Official WhatsApp green rather than a palette token — it's a
   third-party brand mark, and the near-black accent would make it
   unrecognisable. */
.whatsapp-fab {
    position: fixed;
    right: 24px;
    bottom: 24px;
    z-index: 150;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border-radius: 50%;
    background: #25D366;
    color: #FFFFFF;
    box-shadow: 0 6px 20px rgba(34, 32, 28, 0.28);
    transition: transform var(--transition), box-shadow var(--transition), background var(--transition);
}
.whatsapp-fab svg { width: 30px; height: 30px; }
.whatsapp-fab:hover,
.whatsapp-fab:focus-visible {
    background: #1EBE5A;
    transform: translateY(-3px);
    box-shadow: 0 10px 26px rgba(34, 32, 28, 0.34);
}
@media (prefers-reduced-motion: reduce) {
    .whatsapp-fab { transition: none; }
    .whatsapp-fab:hover,
    .whatsapp-fab:focus-visible { transform: none; }
}

.grecaptcha-badge { visibility: hidden; }

p.recaptcha-disclaimer {
    font-size: 0.66rem;
    line-height: 1.4;
    color: var(--color-grey-400);
    margin-top: 12px;
}
.recaptcha-disclaimer a { color: inherit; text-decoration: underline; }
.recaptcha-disclaimer a:hover { color: var(--color-white); }

.footer__seals {
    display: flex;
    align-items: center;
    gap: 16px;
    flex-wrap: wrap;
}
.footer__seals img {
    max-height: 64px;
    width: auto;
    border-radius: 0;
}

.footer__bottom { border-top: 1px solid rgba(255,255,255,0.12); }
.footer__bottom-inner {
    padding: 22px 24px;
    font-size: 0.82rem;
    color: var(--color-grey-400);
}

/* =============================================================
   RESPONSIVE
   ============================================================= */
@media (max-width: 1024px) {
    /* 8 quickbar icons + login/logout + 3 social icons is too much for a
       40px-tall topbar below tablet width — hidden rather than shrunk
       further; logged-in mobile users can still reach everything from
       distribuidores-home.php. */
    .topbar__quickbar { display: none; }

    .main-header__inner { height: 72px; gap: 16px; justify-content: space-between; }

    .main-nav {
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        background: var(--color-white);
        max-height: 0;
        overflow-y: auto;
        transition: max-height var(--transition);
        padding: 0;
        border-top: 1px solid var(--color-grey-200);
    }
    .main-nav.is-open { max-height: calc(100vh - 112px); padding: 12px 0 40px; }

    .main-nav__list { flex-direction: column; align-items: stretch; gap: 0; }
    .main-nav__item { padding: 0 24px; }
    .main-nav__item.has-mega { padding: 0; }
    .main-nav__link-row { padding: 0 24px; }
    .main-nav__link { padding: 16px 0; width: 100%; }
    .main-nav__link::after { display: none; }

    .mega-toggle { display: flex; }
    .mega-toggle[aria-expanded="true"] .main-nav__caret { transform: rotate(180deg); }

    .mega-menu {
        position: static;
        box-shadow: none;
        border-top: none;
        opacity: 1;
        visibility: visible;
        transform: none;
        max-height: 0;
        overflow: hidden;
        transition: max-height var(--transition);
        background: var(--color-grey-50);
    }
    .has-mega.is-open .mega-menu { max-height: 900px; }
    .has-mega .mega-menu { display: block; }

    .mega-menu__inner { flex-direction: column; padding: 8px 24px 24px; min-height: 0; }
    /* Unwrap the categories column (display:contents) so each category
       button and its own panel become direct flex children of
       .mega-menu__inner and can be interleaved with `order` — a category's
       sub-items open directly below THAT category, not after both. */
    .mega-menu__categories { display: contents; }
    .mega-menu__category[data-panel-target="sillas-sillones"] { order: 1; }
    .mega-menu__panel[data-panel="sillas-sillones"] { order: 2; }
    .mega-menu__category[data-panel-target="componentes"] { order: 3; }
    .mega-menu__panel[data-panel="componentes"] { order: 4; }
    /* padding-left aligns the sub-item text with the parent category label
       (which sits 18px inside its button via the base .mega-menu__category
       padding); the links' own -14px/-12px margin nets to 0, so this is
       what does the aligning. */
    .mega-menu__panel { padding-left: 18px; padding-top: 8px; }
    .mega-menu__panel ul,
    .mega-menu__panel.mega-menu__panel--dense ul { columns: 1; }

    .nav-toggle { display: flex; }

    /* Expanded search spans the row and covers the logo, stopping just
       short of the hamburger, instead of stretching the flex row —
       .search__form leaves normal flow entirely (anchored to
       .main-header, already a positioned ancestor via its sticky rule —
       see the sticky-header note above), so the opener icon and
       hamburger never move when it opens. The mag-glass submit + X close
       live inside the pill at its right end. */
    .search__form {
        position: absolute;
        top: 50%;
        right: 74px;
        transform: translateY(-50%);
        z-index: 60;
    }
    .search.is-active .search__form {
        width: calc(100vw - 98px);
        background: var(--color-white);
        box-shadow: var(--shadow-md);
    }
    .search__form input { padding-top: 13px; padding-bottom: 13px; }

    .card-grid, .product-grid, .blog-grid, .dash-grid { grid-template-columns: repeat(2, 1fr); }
    /* 2x2 rather than one row — four squares across a tablet/phone width
       would be too small to read. The "one row" brief is the desktop
       layout. */
    .launch-grid { grid-template-columns: repeat(2, 1fr); gap: 16px; }
    .two-col { grid-template-columns: 1fr; gap: 40px; }
    /* producto-detalle.php's Descripción General rows alternate
       image-first/text-first on desktop, but .two-col's mobile
       single-column stack otherwise just follows DOM order — so a
       text-first row stayed text-first on mobile too. Force image always
       first here, per request. (.pd-block-video also carries
       .pd-block-row but isn't a grid/flex container, so `order` is a
       no-op there — harmless.) */
    .pd-block-row .media { order: -1; }
    .pd-block-row .prose { order: 2; }
    /* Media keeps a fixed 4:3 box on desktop (.photo-4x3, cover/contain),
       so a wide or tall source image sits letterboxed inside it — the box
       bottom always lands in the same place, but the visible image
       content inside doesn't, so the gap to the text below reads as
       inconsistent from block to block. On mobile, drop the fixed ratio
       so the box always matches the image's own rendered height instead —
       per request. */
    .pd-block-row .photo-4x3 { aspect-ratio: auto; }
    /* The -12px .pd-block-row margin (see that rule's own comment) is
       fine on desktop's two-column rows, but on mobile's single-column
       stack it was overlapping real content — most visibly where a tall
       text block sat right above the video block. Back to a small
       positive gap here instead. */
    .pd-block-row { margin-bottom: 12px; }
    /* .prose is always the last thing in its row now (order: 2 above), so
       the 12px row gap is all that separated a row's text from the next
       row's image — per request, a little extra breathing room specific
       to text-then-image transitions. */
    .pd-block-row .prose { margin-bottom: 40px; }

    /* Map stacks above the listing and stops being sticky — a pinned map
       would eat most of the viewport with the list scrolling in a sliver
       underneath. Rows scroll it back into view when tapped instead (see
       mapa-distribuidores.php). Both panes keep their shared height via
       --dealer-panel-height, so the listing stays a self-contained
       scroller rather than turning the page into 92 rows of scrolling. */
    .dealer-locator { --dealer-panel-height: 420px; grid-template-columns: 1fr; gap: 24px; }
    .dealer-locator__map { position: static; }
    .footer__grid { grid-template-columns: 1fr 1fr; }

    .pd-tabs { top: calc(80px + var(--pd-tabs-extra) + var(--sticky-header-offset)); }
    .pd-section { scroll-margin-top: calc(140px + var(--pd-tabs-extra)); margin-bottom: 16px; }
    /* .main-header is 72px at this breakpoint, not 84px — the bar sits
       flush under it. */
    :root { --pd-hero-bar-h: 50px; }
    .pd-hero-bar { top: calc(72px + var(--sticky-header-offset)); }
    .pd-tabs-scope--with-hero-bar .pd-section { scroll-margin-top: 122px; padding: 24px 0; }
    .pd-tabs-scope--with-hero-bar .pd-section:first-child { padding-top: 12px; }
    .pd-hero-bar__inner { gap: 12px; }
    .pd-hero-bar__title { font-size: 0.95rem; }
    /* Section-nav tab links hidden on mobile, per request — the user just
       scrolls there instead, and dropping them frees up room in the bar
       for the title/CTA. Only the in-bar copy (producto-detalle.php); the
       standalone .pd-tabs on producto-detalle-tab.php is untouched. */
    .pd-hero-bar .pd-tabs__inner { display: none; }
    .pd-hero-bar__cta { padding: 6px 13px; font-size: 0.78rem; }
    /* Smaller chips so the 6-item strip (anchored under the toggle, which
       sits left of the CTA) still fits inside a ~360px phone: 6x32 + gaps
       + padding = 224px. */
    .pd-share { margin-right: -4px; }
    .pd-hero-bar__back { margin-right: -8px; }
    .pd-hero-bar__back,
    .pd-share__toggle { width: 32px; height: 32px; }
    .pd-hero-bar__back svg,
    .pd-share__toggle svg { width: 18px; height: 18px; }
    .pd-share__strip { gap: 4px; padding: 6px; }
    .pd-share__strip::before { right: 11px; }
    .pd-share__item { width: 32px; height: 32px; }
    .pd-share__item svg { width: 16px; height: 16px; }
    .pd-strip__item { flex-basis: calc((100% - 20px) / 2); }
    .pd-strip--bleed .pd-strip__item { flex-basis: 36%; }
    .pd-versions { grid-template-columns: 1fr; }
    .pd-specs { grid-template-columns: 1fr; }
    .pd-downloads { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-flow: row; }
    .pd-gallery-row1 { grid-template-columns: 1fr; }
    .pd-gallery-row1__big { height: auto; aspect-ratio: 4 / 3; }
    .pd-gallery-row1__side { height: auto; flex-direction: row; }
    .pd-gallery-row1__side > div { aspect-ratio: 4 / 3; }
    .pd-gallery-row2 { grid-template-columns: repeat(2, 1fr); }
    /* Imágenes row: 3 tiles in view. */
    .pd-gallery__thumb { flex-basis: calc((100% - 40px) / 3); }
    .pd-resources { grid-template-columns: 1fr; }

    .order-row { grid-template-columns: 1fr auto; row-gap: 10px; }
    .order-row__action { grid-column: 2; grid-row: 1 / 3; align-self: center; }

    .picker-grid { grid-template-columns: repeat(2, 1fr); }
    .add-cart__options { grid-template-columns: 1fr; }
    .opt-catalog { grid-template-columns: repeat(2, 1fr); }
    .cart-row {
        grid-template-columns: 84px 1fr auto;
        row-gap: 10px;
    }
    .cart-list--review .cart-row { grid-template-columns: 84px 1fr auto; }
    .cart-row__price { grid-column: 2 / -1; }
    .cart-row__controls { grid-column: 1 / -1; justify-content: flex-end; }
}

@media (max-width: 720px) {
    .topbar__login span,
    .topbar__logout span { display: none; }
    .topbar__logout { padding-left: 0; border-left: 0; }
    .section { padding: 64px 0; }
    .hero__inner { padding: 64px 0 56px; }
    .card-grid, .product-grid, .blog-grid { grid-template-columns: 1fr; }
    .dash-grid { grid-template-columns: repeat(2, 1fr); gap: 14px; }
    .stat-row { grid-template-columns: repeat(2, 1fr); }
    .footer__grid { grid-template-columns: 1fr; padding: 56px 24px 40px; }
    .form-grid { grid-template-columns: 1fr; }
    .cta-banner { padding: 40px 28px; }
    .panel-card { padding: 32px 24px; }
    /* The banner band is only ~200px tall at this width (it keeps the
       assets' 2800x1080 ratio rather than cropping), so full-size arrows
       and the counter would dominate the image. */
    .hero-slider__arrow { width: 34px; height: 34px; }
    .hero-slider__arrow svg { width: 17px; height: 17px; }
    .hero-slider__arrow--prev { left: 10px; }
    .hero-slider__arrow--next { right: 10px; }
    .hero-slider__nav { padding: 10px 0; gap: 8px; }
    .hero-slider__count { font-size: 0.7rem; }
    .hero-slider__progress { width: 64px; }
    .whatsapp-fab { right: 16px; bottom: 16px; width: 50px; height: 50px; }
    .whatsapp-fab svg { width: 27px; height: 27px; }
    .photo-hero { height: 240px; }
    .photo-hero.photo-hero--bleed { height: auto; }
    /* The bleed hero keeps its render's own ratio, so on a phone it can
       be a fairly short band — tighten the overlay so the two lines
       always clear it. */
    .pd-hero__caption { gap: 6px; padding: 16px; }
    .pd-hero__line { font-size: 1rem; }
    .pd-medidas__img { height: 200px; }
    .dealer-locator { --dealer-panel-height: 380px; }
    /* Single-column here, so there's no second column left to match — the
       map just goes back to a sensible standalone height. */
    .contact-map { min-height: 380px; }
    .pd-strip__item { flex-basis: 82%; }
    .pd-gallery-row2 { grid-template-columns: 1fr; }
    /* Phone: 2 tiles in view, and smaller arrows so they don't cover most
       of a ~160px tile. */
    .pd-gallery__track { gap: 12px; }
    .pd-gallery__thumb { flex-basis: calc((100% - 12px) / 2); }
    .pd-gallery .pd-strip__arrow { width: 36px; height: 36px; }
    .pd-gallery .pd-strip__arrow svg { width: 18px; height: 18px; }
    .pd-gallery .pd-strip__arrow--prev { left: 8px; }
    .pd-gallery .pd-strip__arrow--next { right: 8px; }
    .pd-gallery-modal__arrow { width: 40px; height: 40px; }
    .pd-gallery-modal__arrow--prev { left: 8px; }
    .pd-gallery-modal__arrow--next { right: 8px; }
    .two-col--centered .media,
    .two-col--centered .prose { max-width: 100%; }

    .order-filter { padding: 20px; gap: 16px; }
    .order-filter .field input { min-width: 0; width: 100%; }
    .order-filter .btn { width: 100%; }
    .order-row {
        grid-template-columns: 1fr;
        justify-items: start;
        row-gap: 10px;
    }
    .order-row__action { grid-column: 1; grid-row: auto; justify-self: start; }
    .pagination { gap: 16px; }
    .order-detail-item { flex-direction: column; gap: 10px; }
    .order-detail-item__price,
    .order-detail-item__total { text-align: left; min-width: 0; }

    .picker-grid { grid-template-columns: 1fr; }
    .opt-catalog { grid-template-columns: 1fr; }
    .add-cart__footer { flex-direction: column; align-items: stretch; }
    .add-cart__qty { width: 100%; }
    .cart-row {
        grid-template-columns: 1fr;
        justify-items: start;
        row-gap: 8px;
    }
    .cart-list--review .cart-row { grid-template-columns: 1fr; }
    .cart-row__price,
    .cart-row__controls,
    .cart-row__total { grid-column: 1; }
    .cart-row__controls { justify-content: flex-start; width: 100%; }
    .cart-summary { text-align: left; }
    .cart-summary__legend { text-align: left; }
}
