/* ==========================================================================
   Baylan design foundation (R1.1)

   Tokens are global; every component class is namespaced `.bl-` so that
   Yoga pages not yet migrated keep their original look.

   Two deliberate departures from the approved prototype:
   - color-mix() is never emitted. Every occurrence was a static colour at a
     static percentage, so the equivalent rgba() is pixel-identical and does
     not break below Chrome 111.
   - Barlow is replaced by Roboto / Roboto Condensed. Barlow ships no
     Cyrillic, so the prototype rendered ~90% of the copy in a system
     fallback rather than in Barlow.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Fonts
   Roboto 400/500/700 already ship with the theme. Only Condensed is new,
   and only at 600 — the single weight the headings scale uses.

   Split per unicode-range so /ru/ and /uk/ pages never pay for subsets they
   do not render. No local() src: a differently-versioned Roboto Condensed
   installed on the visitor's machine would silently change heading metrics.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Roboto Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/RobotoCondensed/RobotoCondensed-SemiBold.latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
                 U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
                 U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Roboto Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/RobotoCondensed/RobotoCondensed-SemiBold.latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304,
                 U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB,
                 U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* U+0490-0491 is Ґ/ґ — the letter that disqualified several candidates. */
@font-face {
  font-family: 'Roboto Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/RobotoCondensed/RobotoCondensed-SemiBold.cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

/* Carries ₴ (U+20B4); fetched only if a page actually prints the hryvnia sign. */
@font-face {
  font-family: 'Roboto Condensed';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('../fonts/RobotoCondensed/RobotoCondensed-SemiBold.cyrillic-ext.woff2') format('woff2');
  unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}

/* --------------------------------------------------------------------------
   2. Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Base roles */
  --bl-color-bg: #f2f2f3;
  --bl-color-surface: #e9e9ea;
  --bl-color-text: #1d1f20;
  --bl-color-accent: #5980a6;
  --bl-color-divider: rgba(29, 31, 32, 0.16);

  /* Neutral ramp — only the four steps the prototype actually uses */
  --bl-color-neutral-100: #f5f5f8;
  --bl-color-neutral-200: #e7e7ea;
  --bl-color-neutral-800: #424244;
  --bl-color-neutral-900: #2b2b2d;

  /* Accent ramp — 500 is unused in the prototype and is not carried over */
  --bl-color-accent-100: #eef6ff;
  --bl-color-accent-200: #d6ebff;
  --bl-color-accent-300: #b5d9fd;
  --bl-color-accent-400: #94bce3;
  --bl-color-accent-600: #597ea3;
  --bl-color-accent-700: #416180;
  --bl-color-accent-800: #2c455d;
  --bl-color-accent-900: #1d2d3d;

  /* Fixed brand values */
  --bl-color-logo: #d32027;
  --bl-color-photo-bg: #ffffff;

  /* Status. The approved prototype has no success/warning/danger anywhere, so
     these are derived rather than quoted: informational reuses the accent ramp
     outright, and the other three are the same lightness steps in a green,
     amber and red hue. `solid` is the filled variant the toast needs; text on
     it is always --bl-color-bg. */
  --bl-color-info-bg: var(--bl-color-accent-100);
  --bl-color-info-border: var(--bl-color-accent-300);
  --bl-color-info-fg: var(--bl-color-accent-800);
  --bl-color-info-solid: var(--bl-color-accent-800);

  --bl-color-success-bg: #e8f4ee;
  --bl-color-success-border: #a8d5bd;
  --bl-color-success-fg: #1b5e3a;
  --bl-color-success-solid: #1b5e3a;

  --bl-color-warning-bg: #fdf3e3;
  --bl-color-warning-border: #e8cd97;
  --bl-color-warning-fg: #7a4f06;
  --bl-color-warning-solid: #7a4f06;

  --bl-color-danger-bg: #fbeceb;
  --bl-color-danger-border: #e5b3af;
  --bl-color-danger-fg: #8c2420;
  --bl-color-danger-solid: #8c2420;

  /* Derived alpha values — precomputed replacements for color-mix() */
  --bl-alpha-text-04: rgba(29, 31, 32, 0.04);
  --bl-alpha-text-07: rgba(29, 31, 32, 0.07);
  --bl-alpha-text-08: rgba(29, 31, 32, 0.08);
  --bl-alpha-text-14: rgba(29, 31, 32, 0.14);
  --bl-alpha-text-45: rgba(29, 31, 32, 0.45);
  --bl-alpha-text-55: rgba(29, 31, 32, 0.55);
  --bl-alpha-text-60: rgba(29, 31, 32, 0.60);
  --bl-alpha-text-70: rgba(29, 31, 32, 0.70);
  --bl-alpha-accent-10: rgba(89, 128, 166, 0.10);
  --bl-alpha-accent-18: rgba(89, 128, 166, 0.18);
  --bl-alpha-accent-30: rgba(89, 128, 166, 0.30);
  --bl-alpha-scrim: rgba(43, 43, 45, 0.50);

  /* Typography */
  --bl-font-heading: 'Roboto Condensed', 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --bl-font-heading-weight: 600;
  --bl-font-body: 'Roboto', system-ui, -apple-system, 'Segoe UI', sans-serif;

  --bl-text-size: 15px;
  --bl-text-lh: 1.55;
  --bl-heading-lh: 1.12;
  --bl-heading-ls: -0.015em;

  --bl-h1: 42px;
  --bl-h2: 32px;
  --bl-h3: 25px;
  --bl-h4: 20px;
  --bl-h5: 16px;
  --bl-h6: 13px;

  /* Spacing — the prototype's 6 and 8 steps are unused */
  --bl-space-1: 3.4px;
  --bl-space-2: 6.8px;
  --bl-space-3: 10.2px;
  --bl-space-4: 13.6px;

  /* The approved design is square throughout */
  --bl-radius: 0;

  /* Elevation — the prototype's sm step is unused */
  --bl-shadow-md: 0 3px 10px rgba(43, 43, 45, 0.16);
  --bl-shadow-lg: 0 12px 32px rgba(43, 43, 45, 0.22);

  /* Layering — mirrors the prototype's stack */
  --bl-z-corner: 2;
  --bl-z-sticky: 20;
  --bl-z-sticky-bar: 25;
  --bl-z-fixed-bar: 28;
  --bl-z-header: 30;
  --bl-z-scrim: 60;
  --bl-z-drawer: 65;
  --bl-z-sheet: 70;
  --bl-z-toast: 80;

  /* Safe area — absent from the prototype, which is why its bottom tab bar
     collides with the iPhone home indicator */
  --bl-safe-top: 0px;
  --bl-safe-right: 0px;
  --bl-safe-bottom: 0px;
  --bl-safe-left: 0px;

  --bl-container: 1280px;
  --bl-hairline: 1px solid var(--bl-color-divider);
  --bl-focus-ring: 2px solid var(--bl-color-accent);

  /* Fixed-stack geometry. The prototype scatters 64/70/146/184px literals across
     four JS files; every fixed bottom layer must derive from these instead. */
  --bl-bottom-nav-height: 0px;
  --bl-fixed-bottom-offset: var(--bl-safe-bottom);

  /* First-paint value only: .bl-header wraps to a second row below 992px, so
     baylan-shell.js overwrites this with the measured height. */
  --bl-header-height: 56px;
}

@supports (padding: env(safe-area-inset-bottom)) {
  :root {
    --bl-safe-top: env(safe-area-inset-top);
    --bl-safe-right: env(safe-area-inset-right);
    --bl-safe-bottom: env(safe-area-inset-bottom);
    --bl-safe-left: env(safe-area-inset-left);
  }
}

/* --------------------------------------------------------------------------
   3. Base
   Scoped to `.bl-root` so it cannot reach unmigrated Yoga pages.
   -------------------------------------------------------------------------- */

.bl-root {
  background: var(--bl-color-bg);
  color: var(--bl-color-text);
  font-family: var(--bl-font-body);
  font-size: var(--bl-text-size);
  line-height: var(--bl-text-lh);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.bl-root *,
.bl-root *::before,
.bl-root *::after { box-sizing: border-box; }

.bl-root h1, .bl-root h2, .bl-root h3,
.bl-root h4, .bl-root h5, .bl-root h6 {
  margin: 0 0 var(--bl-space-2);
  font-family: var(--bl-font-heading);
  font-weight: var(--bl-font-heading-weight);
  line-height: var(--bl-heading-lh);
  letter-spacing: var(--bl-heading-ls);
}

.bl-root h1 { font-size: var(--bl-h1); }
.bl-root h2 { font-size: var(--bl-h2); }
.bl-root h3 { font-size: var(--bl-h3); }
.bl-root h4 { font-size: var(--bl-h4); }
.bl-root h5 { font-size: var(--bl-h5); }
.bl-root h6 {
  font-size: var(--bl-h6);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.bl-root p { margin: 0 0 var(--bl-space-3); }

/* No blanket underline: at (0,1,1) this rule outranks every single-class
   component rule, so it underlined the logo, nav, cards and buttons that the
   approved prototype draws clean. Chrome links read as links from their
   framing; only long-form copy, where a link has no other affordance, opts in. */
.bl-root a {
  color: var(--bl-color-accent);
  text-decoration: none;
  text-underline-offset: 2px;
}

/* Client-authored rich text: product descriptions, category intros and CMS
   pages. Links there sit in running copy with no other affordance. */
.bl-root .bl-prose a,
.bl-root .bl-listing-intro a,
.bl-root #information-information a { text-decoration: underline; }

.bl-root img { max-width: 100%; height: auto; }

/* The WebP <picture> wrappers must not become a layout box of their own.
   Without this, the chain is img -> picture -> a -> .bl-card-media: the picture
   has no sizing rules, so `height: 100%` on the image resolves against an
   indefinite parent, falls back to the thumbnail's intrinsic 250px, and the
   media band's overflow:hidden then crops the meter top and bottom. Taking the
   picture out of the box tree makes the image a direct child of the sized
   ancestor again, which is what every one of these layouts already assumed.
   Applies to the card, the home hero, the category tiles and the PDP gallery
   at once. */
.bl-root picture { display: contents; }

.bl-root ::selection { background: var(--bl-alpha-accent-30); }

.bl-root :focus { outline: none; }
.bl-root :focus-visible { outline: var(--bl-focus-ring); outline-offset: 2px; }

/* Keyboard focus is still visible where :focus-visible is unsupported,
   because the rule above is dropped together with the reset. */

.bl-text-muted { color: var(--bl-alpha-text-55); }

/* Deliberately unscoped: shell chrome outside `.bl-root` uses it too. */
.bl-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --------------------------------------------------------------------------
   4. Layout
   Container widths only. Breakpoints stay on the theme's Bootstrap 4 grid
   (576 / 768 / 992 / 1200) so the two systems do not diverge.
   -------------------------------------------------------------------------- */

.bl-container {
  width: 100%;
  max-width: var(--bl-container);
  margin-inline: auto;
  padding-left: var(--bl-space-4);
  padding-right: var(--bl-space-4);
}

/* The prototype's desktop gutter is 32px; --bl-space-4 resolves to 13.6px, so
   every shell row, breadcrumb and grid started 18px too far left. Desktop only:
   the mobile prototype really does use the tighter gutter, and .bl-container
   wraps every page, so widening it unconditionally would loosen phone layouts
   that are already approved. */
@media (min-width: 992px) {
  .bl-container {
    padding-left: 32px;
    padding-right: 32px;
  }
}

@supports not (margin-inline: auto) {
  .bl-container { margin-left: auto; margin-right: auto; }
}

.bl-stack { display: flex; flex-direction: column; }
.bl-row { display: flex; flex-wrap: wrap; }
.bl-center { display: flex; align-items: center; }

/* Explicit margins rather than `gap`: the theme ships zero `gap` declarations
   and 133 legacy flexbox prefixes, so gap support cannot be assumed. */
.bl-gap-1 > * + * { margin-top: var(--bl-space-1); }
.bl-gap-2 > * + * { margin-top: var(--bl-space-2); }
.bl-gap-3 > * + * { margin-top: var(--bl-space-3); }
.bl-gap-4 > * + * { margin-top: var(--bl-space-4); }

.bl-row.bl-gap-1 > * + *,
.bl-row.bl-gap-2 > * + *,
.bl-row.bl-gap-3 > * + *,
.bl-row.bl-gap-4 > * + * { margin-top: 0; }

.bl-row.bl-gap-1 > * + * { margin-left: var(--bl-space-1); }
.bl-row.bl-gap-2 > * + * { margin-left: var(--bl-space-2); }
.bl-row.bl-gap-3 > * + * { margin-left: var(--bl-space-3); }
.bl-row.bl-gap-4 > * + * { margin-left: var(--bl-space-4); }

/* --------------------------------------------------------------------------
   5. Blueprint primitive
   The signature hairline frame with four corner ticks. 29 uses in the
   prototype, so the corners are emitted by ::before/::after rather than by
   four hand-written spans.
   -------------------------------------------------------------------------- */

.bl-blueprint {
  position: relative;
  border: var(--bl-hairline);
  border-radius: var(--bl-radius);
}

/* All four corner ticks come from one pseudo-element, so a blueprint costs
   no extra markup. The prototype instead repeats four <span class="corner">
   in each of its 29 instances.
   The pseudo-element overhangs the box by 5.5px, which puts each 11x11 cross
   exactly on the border corner. */
.bl-blueprint::before {
  content: '';
  position: absolute;
  top: -5.5px;
  right: -5.5px;
  bottom: -5.5px;
  left: -5.5px;
  z-index: var(--bl-z-corner);
  pointer-events: none;
  background-image:
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55)),
    linear-gradient(var(--bl-alpha-text-55), var(--bl-alpha-text-55));
  background-repeat: no-repeat;
  background-size:
    11px 1px, 1px 11px,
    11px 1px, 1px 11px,
    11px 1px, 1px 11px,
    11px 1px, 1px 11px;
  background-position:
    left 0     top 5px,    left 5px    top 0,
    right 0    top 5px,    right 5px   top 0,
    left 0     bottom 5px, left 5px    bottom 0,
    right 0    bottom 5px, right 5px   bottom 0;
}

/* Opt out where the frame sits flush against another element. */
.bl-blueprint-plain::before { content: none; }

.bl-hr {
  height: 0;
  margin: var(--bl-space-4) 0;
  border: 0;
  border-top: var(--bl-hairline);
}

/* --------------------------------------------------------------------------
   6. Buttons
   -------------------------------------------------------------------------- */

.bl-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--bl-space-2);
  min-height: 40px;
  padding: var(--bl-space-2) calc(var(--bl-space-3) * 1.2);
  border: 1px solid transparent;
  border-radius: var(--bl-radius);
  background: none;
  color: var(--bl-color-text);
  font-family: var(--bl-font-heading);
  font-weight: var(--bl-font-heading-weight);
  font-size: var(--bl-h5);
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

.bl-btn > * + * { margin-left: var(--bl-space-2); }

.bl-btn-primary {
  background: var(--bl-color-accent);
  border-color: var(--bl-color-accent);
  color: var(--bl-color-bg);
}
.bl-btn-primary:hover { background: var(--bl-color-accent-600); border-color: var(--bl-color-accent-600); }
.bl-btn-primary:active { background: var(--bl-color-accent-700); border-color: var(--bl-color-accent-700); }

.bl-btn-secondary { border-color: var(--bl-color-divider); }
.bl-btn-secondary:hover { background: var(--bl-alpha-text-07); }
.bl-btn-secondary:active { background: var(--bl-alpha-text-14); }

.bl-btn-ghost {
  min-height: auto;
  padding: var(--bl-space-1) var(--bl-space-1);
  color: var(--bl-color-accent);
}
.bl-btn-ghost:hover { background: var(--bl-alpha-accent-10); }
.bl-btn-ghost:active { background: var(--bl-alpha-accent-18); }

/* `.bl-root a` (0,1,1) outranks the single-class button rules above, painting
   anchor CTAs accent-on-accent (invisible label). Restate colors for anchors. */
.bl-root a.bl-btn { color: var(--bl-color-text); }
.bl-root a.bl-btn-primary { color: var(--bl-color-bg); }
.bl-root a.bl-btn-ghost { color: var(--bl-color-accent); }

.bl-btn-block { display: flex; width: 100%; margin-top: var(--bl-space-2); }

.bl-btn[disabled],
.bl-btn.is-disabled {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.bl-btn.is-loading {
  position: relative;
  color: transparent;
  pointer-events: none;
}

.bl-btn.is-loading::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 16px;
  height: 16px;
  margin: -8px 0 0 -8px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: 50%;
  color: var(--bl-color-text);
  animation: bl-spin 0.6s linear infinite;
}

.bl-btn-primary.is-loading::after { color: var(--bl-color-bg); }

@keyframes bl-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .bl-btn { transition: none; }
  .bl-btn.is-loading::after { animation-duration: 2s; }
}

/* --------------------------------------------------------------------------
   7. Icon controls
   -------------------------------------------------------------------------- */

.bl-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  border-radius: var(--bl-radius);
  background: none;
  color: inherit;
  cursor: pointer;
}
.bl-icon-btn:hover { background: var(--bl-alpha-text-07); }
.bl-icon-btn:active { background: var(--bl-alpha-text-14); }

/* The legacy bundle declares `svg { fill: currentcolor }`, and a presentation
   attribute loses to any rule at all, so every stroked line icon in the
   redesign was being painted as a solid blob. This does not impose a fill on
   anything - it only gives back the one the markup already asked for. */
svg[fill="none"] { fill: none; }

.bl-icon { width: 20px; height: 20px; flex: 0 0 auto; display: block; }
.bl-icon-sm { width: 16px; height: 16px; }
.bl-icon-lg { width: 24px; height: 24px; }

.bl-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--bl-radius);
  background: var(--bl-color-accent);
  color: var(--bl-color-bg);
  font-family: var(--bl-font-body);
  font-size: 11px;
  line-height: 16px;
  text-align: center;
}

/* --------------------------------------------------------------------------
   8. Inputs
   -------------------------------------------------------------------------- */

.bl-field { display: block; margin-bottom: var(--bl-space-3); }

.bl-field > label {
  display: block;
  margin-bottom: var(--bl-space-1);
  color: var(--bl-alpha-text-70);
  font-size: var(--bl-h6);
}

.bl-input {
  display: block;
  width: 100%;
  min-height: 40px;
  padding: var(--bl-space-2) var(--bl-space-3);
  border: 1px solid var(--bl-color-divider);
  border-radius: var(--bl-radius);
  background: var(--bl-color-surface);
  color: var(--bl-color-text);
  caret-color: var(--bl-color-accent);
  font-family: var(--bl-font-body);
  font-size: var(--bl-text-size);
  line-height: var(--bl-text-lh);
}

.bl-input:hover { border-color: var(--bl-alpha-text-45); }
.bl-input:focus-visible { border-color: var(--bl-color-accent); outline-offset: 0; }

.bl-input[disabled],
.bl-input.is-disabled { opacity: 0.45; cursor: not-allowed; }

.bl-input.is-invalid { border-color: var(--bl-color-accent-800); }

.bl-field-error {
  margin-top: var(--bl-space-1);
  color: var(--bl-color-accent-800);
  font-size: var(--bl-h6);
}

textarea.bl-input { min-height: 96px; resize: vertical; }

/* Radio */

.bl-radio { display: inline-flex; align-items: center; cursor: pointer; }
.bl-radio > input { position: absolute; opacity: 0; width: 0; height: 0; }

.bl-radio .bl-dot {
  width: 16px;
  height: 16px;
  margin-right: var(--bl-space-2);
  border: 1px solid var(--bl-color-divider);
  border-radius: 50%;
  flex: 0 0 auto;
}

.bl-radio:hover .bl-dot { border-color: var(--bl-color-accent); }

.bl-radio > input:checked + .bl-dot {
  border-color: var(--bl-color-accent);
  background: var(--bl-color-accent);
  box-shadow: inset 0 0 0 3px var(--bl-color-bg);
}

.bl-radio > input:focus-visible + .bl-dot { outline: var(--bl-focus-ring); outline-offset: 2px; }

/* Segmented control.
   The prototype drives this entirely from :has(), which Firefox ESR 115 does
   not support — leaving the user unable to see which option is selected.
   The `.is-checked` class is authoritative here and is set by JS; the :has()
   rules below are progressive enhancement only. */

.bl-seg {
  display: inline-flex;
  border: 1px solid var(--bl-color-divider);
  border-radius: var(--bl-radius);
  overflow: hidden;
}

.bl-seg-opt {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 36px;
  padding: 0 var(--bl-space-3);
  color: var(--bl-color-text);
  font-size: var(--bl-text-size);
  cursor: pointer;
}

.bl-seg-opt > input { position: absolute; opacity: 0; width: 0; height: 0; }

.bl-seg-opt + .bl-seg-opt { border-left: 1px solid var(--bl-color-divider); }

.bl-seg-opt.is-checked { background: var(--bl-color-accent); color: var(--bl-color-bg); }
.bl-seg-opt:not(.is-checked):hover { background: var(--bl-alpha-text-07); }
.bl-seg-opt.is-focused { outline: var(--bl-focus-ring); outline-offset: -2px; }

@supports selector(:has(*)) {
  .bl-seg-opt:has(input:checked) { background: var(--bl-color-accent); color: var(--bl-color-bg); }
  .bl-seg-opt:not(:has(input:checked)):hover { background: var(--bl-alpha-text-07); }
  .bl-seg-opt:has(input:focus-visible) { outline: var(--bl-focus-ring); outline-offset: -2px; }
}

/* --------------------------------------------------------------------------
   9. Tags
   -------------------------------------------------------------------------- */

.bl-tag {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 0 var(--bl-space-2);
  border: 1px solid transparent;
  border-radius: var(--bl-radius);
  font-size: var(--bl-h6);
  line-height: 1.2;
  white-space: nowrap;
}

.bl-tag-accent { background: var(--bl-color-accent-100); color: var(--bl-color-accent-800); }
.bl-tag-neutral { background: var(--bl-color-neutral-100); color: var(--bl-color-neutral-800); }
.bl-tag-outline { background: var(--bl-color-bg); border-color: var(--bl-color-accent); color: var(--bl-color-accent); }

/* --------------------------------------------------------------------------
   10. Scrim
   `inset` is avoided so the overlay still stretches on older engines.
   -------------------------------------------------------------------------- */

.bl-scrim {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: var(--bl-z-scrim);
  background: var(--bl-alpha-scrim);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--bl-space-4);
}

.bl-scrim[hidden] { display: none; }

/* --------------------------------------------------------------------------
   11. Image placeholder
   29% of the catalogue has no photograph at all; the grid must not collapse.
   -------------------------------------------------------------------------- */

.bl-photo {
  display: block;
  width: 100%;
  background: var(--bl-color-photo-bg);
  object-fit: contain;
}

.bl-photo-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  background: var(--bl-color-accent-100);
  color: var(--bl-color-accent-400);
  font-size: var(--bl-h6);
  text-align: center;
}

.bl-photo-empty > svg { margin-bottom: var(--bl-space-1); }

/* --------------------------------------------------------------------------
   12. Product card
   The card is rendered inside the theme's Bootstrap 4 grid rather than a grid
   of its own, so that the list/grid toggle on the category page keeps working.
   `col-6` there already produces the two mobile columns the prototype has.

   Type sizes are the prototype's literals rather than the heading scale: the
   scale's next step down from --bl-h5 is 13px, which loses the size difference
   between the product name and the spec line at a 150px card width.
   -------------------------------------------------------------------------- */

.bl-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--bl-color-bg);
  color: var(--bl-color-text);
  font-family: var(--bl-font-body);
  font-size: var(--bl-text-size);
  line-height: var(--bl-text-lh);
}

.bl-card *,
.bl-card *::before,
.bl-card *::after { box-sizing: border-box; }

.bl-card-media {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  padding: var(--bl-space-3);
  background: var(--bl-color-photo-bg);
  border-bottom: var(--bl-hairline);
}

.bl-card-media > a {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
}

/* width/height are on the element for the aspect hint; the box is what sizes
   the image, so both axes stay auto and the artwork is never distorted. */
.bl-card-img {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  transition: transform 0.25s ease;
}

/* Hover strip. All slides sit in one box; the active one is visible, the
   others hold their space at zero opacity so the swap has nothing to reflow.
   Kept CSS-only for the fade — JS just moves the is-active class between
   siblings on a timer. */
.bl-card-strip {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

.bl-card-strip-slide {
  position: absolute;
  inset: 0;
  margin: auto;
  opacity: 0;
  transition: opacity 0.18s ease;
}
.bl-card-strip-slide.is-active { opacity: 1; }

/* Subtle hover feedback: elevation + a small primary-image scale so the tile
   reads as interactive. The is-unavailable opacity still applies, so an
   out-of-stock card stays visibly dimmed on hover. */
@media (hover: hover) {
  .bl-card:hover .bl-card-img { transform: scale(1.03); }
  .bl-card { transition: box-shadow 0.2s ease; }
  .bl-card:hover { box-shadow: 0 6px 20px -12px rgba(29, 31, 32, 0.35); }
}

.bl-card-dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: var(--bl-space-2);
  display: flex;
  justify-content: center;
  gap: 5px;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
@media (hover: hover) {
  .bl-card:hover .bl-card-dots { opacity: 1; }
}
.bl-card-dot {
  width: 18px;
  height: 2px;
  background: rgba(29, 31, 32, 0.22);
  transition: background 0.15s ease;
}
.bl-card-dot.is-active { background: var(--bl-color-accent); }

.bl-card-flags {
  position: absolute;
  top: var(--bl-space-2);
  left: var(--bl-space-2);
  z-index: var(--bl-z-corner);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--bl-space-1);
}

.bl-card-body {
  display: flex;
  flex: 1;
  flex-direction: column;
  gap: 5px;
  padding: 9px 10px 10px;
}

.bl-card-body > p { margin: 0; }

.bl-card-brand {
  font-size: 9.5px;
  letter-spacing: 0.1em;
  line-height: 1.4;
  text-transform: uppercase;
  color: var(--bl-color-accent-700);
}

/* `.bl-root` prefix is load-bearing, not decoration.
   `.bl-root h3` (line 240) is specificity (0,1,1); a bare `.bl-card-title` is
   (0,1,0) and loses, so every card title on home, category, search and the PDP
   related row rendered at --bl-h3 = 25px instead of 14/16px. Measured on
   /odnostrumenevi-sukhokhidni before this fix: computed font-size 25px, card
   height 523px against the prototype's ~290px. The heading defaults are shared
   with client-authored copy, so they are left alone and the component states
   its own weight instead. */
.bl-root .bl-card-title {
  margin: 0;
  font-size: 14px;
  line-height: 1.2;
}

/* Clamped rather than truncated server-side: the full name stays in the DOM
   for search engines and for the accessible name of the link. */
.bl-card-title > a {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: inherit;
  text-decoration: none;
}

.bl-card-title > a:hover { color: var(--bl-color-accent-700); }
.bl-card-title > a:focus-visible { outline: var(--bl-focus-ring); outline-offset: 2px; }

.bl-card-specs {
  font-size: 11px;
  line-height: 1.35;
  color: var(--bl-alpha-text-55);
}

/* Pinned to the body's bottom so the action rows line up across a row of
   cards whose names and spec lines differ in height. */
.bl-card-body > .bl-card-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--bl-space-2);
  margin-top: auto;
  padding-top: var(--bl-space-1);
}

.bl-price-now {
  font-family: var(--bl-font-heading);
  font-weight: var(--bl-font-heading-weight);
  font-size: 16.5px;
  line-height: 1.2;
}

.bl-price-old {
  font-size: 12px;
  color: var(--bl-alpha-text-45);
}

.bl-card-stock {
  font-size: 11px;
  line-height: 1.35;
  color: var(--bl-alpha-text-55);
}

/* 109 of the 199 active products currently carry quantity 0, so the
   unavailable state has to stay quiet - the disabled button and the stock line
   already state it, and a heavier treatment would grey out half the catalogue. */
.bl-card.is-unavailable .bl-card-media { opacity: 0.65; }

.bl-card-actions {
  display: flex;
  gap: 5px;
  margin-top: var(--bl-space-1);
}

.bl-card-actions > .bl-btn { flex: 1; }

.bl-card-actions > .bl-card-compare {
  flex: 0 0 auto;
  width: 40px;
  padding: 0;
}

@media (min-width: 768px) {
  .bl-card-body { gap: var(--bl-space-2); padding: var(--bl-space-3); }
  /* Same (0,2,0) reason as the base rule above. */
  .bl-root .bl-card-title { font-size: var(--bl-h5); }
  .bl-card-specs, .bl-card-stock { font-size: var(--bl-h6); }
  .bl-price-now { font-size: 19px; }
}

/* Desktop card media is a landscape band in the approved prototype, not the
   square the mobile prototype uses. A 250x250 square added ~118px of height to
   every card and is the second half of why the desktop grid read as much
   heavier than the target. Scoped to >=992px so the approved mobile geometry,
   which IS square, is untouched. */
@media (min-width: 992px) {
  .bl-card-media {
    aspect-ratio: 3 / 2;
    /* Without this the flex container's automatic minimum size lets the 250px
       thumbnail push the box to 271px and aspect-ratio never takes effect. */
    min-height: 0;
    overflow: hidden;
  }

  /* `max-height: 100%` alone did NOT hold here — the 250px thumbnail kept its
     intrinsic height inside a 193px band and `overflow: hidden` then cropped
     the meter top and bottom. Sizing the image to the box and letting
     object-fit letterbox it is immune to how the percentage resolves, and it is
     what the prototype does: the photo is contained in the band, never cut.
     `.bl-root` prefix because `.bl-root img` is (0,1,1) and would otherwise
     win the height declaration. */
  .bl-root .bl-card-img {
    width: 100%;
    height: 100%;
    max-height: 100%;
    object-fit: contain;
  }
}

/* List mode. custom.js implements the category page's list/grid toggle by
   rewriting the column class to col-12, so keying off that class is what keeps
   the toggle working without touching the minified bundle. */
@media (min-width: 576px) {
  .col-product.col-12 .bl-card { flex-direction: row; }

  .col-product.col-12 .bl-card-media {
    flex: 0 0 auto;
    width: 200px;
    border-right: var(--bl-hairline);
    border-bottom: 0;
  }

  .col-product.col-12 .bl-card-body { flex: 1; }
  .col-product.col-12 .bl-card-title > a { -webkit-line-clamp: 2; }
  .col-product.col-12 .bl-card-actions { max-width: 320px; }
}

/* --------------------------------------------------------------------------
   13. Quantity stepper
   Geometry from the prototype's fixed PDP bar: 40px controls, 44px tall, the
   value between two dividers. The stepping itself belongs to the theme's own
   `Quantity` class, so this only provides the surface it drives.
   -------------------------------------------------------------------------- */

.bl-qty {
  display: inline-flex;
  align-items: stretch;
  background: var(--bl-color-bg);
  border: 1px solid var(--bl-color-divider);
  border-radius: var(--bl-radius);
  overflow: hidden;
}

.bl-qty-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--bl-color-text);
  font-family: var(--bl-font-body);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}

.bl-qty-btn:hover { background: var(--bl-alpha-text-07); }
.bl-qty-btn:active { background: var(--bl-alpha-text-14); }
.bl-qty-btn:focus-visible { outline: var(--bl-focus-ring); outline-offset: -2px; }

.bl-qty-input {
  width: 44px;
  min-height: 44px;
  padding: 0 var(--bl-space-1);
  border: 0;
  border-left: 1px solid var(--bl-color-divider);
  border-right: 1px solid var(--bl-color-divider);
  border-radius: 0;
  background: transparent;
  color: var(--bl-color-text);
  font-family: var(--bl-font-body);
  font-size: var(--bl-text-size);
  font-weight: 500;
  text-align: center;
  /* The value is stepped by the buttons, and a spin box on top of them would
     offer a second control with a different minimum. */
  -moz-appearance: textfield;
  appearance: textfield;
}

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

.bl-qty-input:focus-visible { outline: var(--bl-focus-ring); outline-offset: -2px; }

.bl-qty.is-disabled { opacity: 0.45; }
.bl-qty.is-disabled .bl-qty-btn,
.bl-qty.is-disabled .bl-qty-input { pointer-events: none; }

/* --------------------------------------------------------------------------
   14. Inline notice
   For messages that belong to a place on the page. Transient feedback stays
   with the theme's toast, restyled in section 16.
   -------------------------------------------------------------------------- */

.bl-notice {
  display: flex;
  align-items: flex-start;
  gap: var(--bl-space-3);
  margin: 0 0 var(--bl-space-3);
  padding: var(--bl-space-3) var(--bl-space-4);
  border: 1px solid transparent;
  border-left-width: 3px;
  border-radius: var(--bl-radius);
  font-size: 14px;
  line-height: 1.45;
}

.bl-notice-icon { flex: 0 0 auto; margin-top: 1px; }
.bl-notice-body { flex: 1; min-width: 0; }
.bl-notice-body > :first-child { margin-top: 0; }
.bl-notice-body > :last-child { margin-bottom: 0; }

.bl-notice-title {
  margin: 0 0 var(--bl-space-1);
  font-family: var(--bl-font-heading);
  font-weight: var(--bl-font-heading-weight);
  font-size: var(--bl-h5);
  line-height: var(--bl-heading-lh);
  letter-spacing: var(--bl-heading-ls);
}

.bl-notice-info { background: var(--bl-color-info-bg); border-color: var(--bl-color-info-border); border-left-color: var(--bl-color-info-fg); color: var(--bl-color-info-fg); }
.bl-notice-success { background: var(--bl-color-success-bg); border-color: var(--bl-color-success-border); border-left-color: var(--bl-color-success-fg); color: var(--bl-color-success-fg); }
.bl-notice-warning { background: var(--bl-color-warning-bg); border-color: var(--bl-color-warning-border); border-left-color: var(--bl-color-warning-fg); color: var(--bl-color-warning-fg); }
.bl-notice-danger { background: var(--bl-color-danger-bg); border-color: var(--bl-color-danger-border); border-left-color: var(--bl-color-danger-fg); color: var(--bl-color-danger-fg); }

.bl-notice a { color: inherit; text-decoration: underline; }

/* --------------------------------------------------------------------------
   15. Empty state
   -------------------------------------------------------------------------- */

.bl-empty { padding: 40px 20px; text-align: center; }

.bl-empty-icon { display: block; margin: 0 auto var(--bl-space-3); color: var(--bl-color-accent-400); }

.bl-empty-title { margin: 0 0 6px; font-size: var(--bl-h4); }

.bl-empty-text { margin: 0 0 var(--bl-space-4); font-size: var(--bl-h6); color: var(--bl-alpha-text-55); }

.bl-empty .bl-btn { min-height: 44px; }

/* R1.7: 404 page (shares the empty-state vocabulary, no page-scoped CSS file) */
.bl-error-body { padding: var(--bl-space-5) 0 var(--bl-space-6); text-align: center; }

.bl-error-code { margin: 0 0 var(--bl-space-2); font-size: clamp(56px, 12vw, 96px); font-weight: 700; line-height: 1; color: var(--bl-color-accent); }

.bl-error-text { margin: 0 auto var(--bl-space-4); max-width: 480px; font-size: var(--bl-h6); color: var(--bl-alpha-text-55); }

.bl-error-actions { display: flex; justify-content: center; gap: var(--bl-space-3); flex-wrap: wrap; }

.bl-error-actions .bl-btn { min-height: 44px; min-width: 160px; }

/* --------------------------------------------------------------------------
   16. Toast
   A restyle of the theme's own `Alert` (assets/js/custom.js), not a
   replacement: the DOM below is the one that class builds, misspelt
   `nofity-message` included. Loading after bundle.min.css is what lets these
   win without raising specificity.
   -------------------------------------------------------------------------- */

/* main.css leaves this static, so the absolutely positioned close button
   resolves against the fixed wrapper and every toast but the first has it
   sitting outside itself. */
.notify {
  position: relative;
  border-radius: var(--bl-radius);
  padding: var(--bl-space-3) 34px var(--bl-space-3) var(--bl-space-3);
  background: var(--bl-color-neutral-900);
  color: var(--bl-color-bg);
  font-family: var(--bl-font-body);
  font-size: 14px;
  line-height: 1.4;
  box-shadow: var(--bl-shadow-md);
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

.notify[data-type='info'] { background: var(--bl-color-info-solid); }
.notify[data-type='success'] { background: var(--bl-color-success-solid); }
.notify[data-type='warning'] { background: var(--bl-color-warning-solid); }
/* Never emitted by the theme today; without it the white text would land on
   the transparent default. */
.notify[data-type='danger'] { background: var(--bl-color-danger-solid); }

.notify a { color: var(--bl-color-bg); text-decoration: underline; }

.notify-close { color: var(--bl-color-bg); }
.notify-close svg { color: currentColor; }
.notify-close:focus-visible { outline: 2px solid var(--bl-color-bg); outline-offset: -2px; }

.notification { padding: var(--bl-space-3); }

@media (prefers-reduced-motion: reduce) {
  .notify { transition: none; }
}
