/* ============================================================
   KOBA — style.css
   Stack: vanilla HTML/CSS/JS, no frameworks
   All colours via CSS variables — never hardcode
   Base spacing unit: 4px
   Direction: dark + gold — see the header of css/variables.css
   for the rules that are accessibility constraints, not style opinions.

   Sections:
     0 RESET · 1 TOKENS · 2 BASE · 3 TYPOGRAPHY · 4 A11Y
     5 (removed) · 6 HEADER · 7 FOOTER · 8 BUTTONS · 9 PRODUCT CARD
     10 CATALOG GRID · 11 FILTER SIDEBAR · 12 TAGS / BADGES · 13 FORMS
     14 QUANTITY · 15 CART · 16 PRODUCT PAGE · 17 CHECKOUT
     18 PAYMENT STEP · 19 SUCCESS SCREEN · 20 CONTACT · 21 BREADCRUMB
     22 HERO · 23 TOAST · 24 UTILITIES · 25 SKELETON · 26 LANDING
     27 FAQ · 28 REVIEWS · 29 LIGHTBOX · 30 SEO COPY · 31 RESPONSIVE
   ============================================================ */

/* ── 0. RESET ─────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
/* Ensure [hidden] always wins over any display: rule in author styles */
[hidden] { display: none !important; }
img, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button, input, select, textarea { font: inherit; }
ul, ol { list-style: none; }

/* ── 1. TOKENS ────────────────────────────────────────────── */
/* Moved to css/variables.css — the single place with literal colours.
   Every page loads it BEFORE this file. */

/* ── 2. BASE ──────────────────────────────────────────────── */
body {
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--ff-body);
  font-size: 16px;
  line-height: 1.65;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  -webkit-font-smoothing: antialiased;
}

main { flex: 1; }

.container {
  /* The gutter as a property, not just a padding: components that bleed to the
     screen edge (the chip row) read it, so they can never out-bleed the
     container at a breakpoint the container switches on. */
  --gutter: var(--sp-6);
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--gutter);
}

/* The vertical rhythm of the whole site hangs on this one token.
   Do NOT revert to var(--sp-10) to fit more above the fold. */
.section { padding: var(--sp-section) 0; }

/* ── 3. TYPOGRAPHY ────────────────────────────────────────── */
.t-display {
  font-family: var(--ff-head);
  font-size: clamp(38px, 6vw, 60px);
  font-weight: 800;
  letter-spacing: -.02em;
  line-height: 1.05;
  text-wrap: balance;
}
h1, .t-h1 {
  font-family: var(--ff-head);
  font-size: clamp(28px, 4vw, 38px);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.15;
}
h2, .t-h2 {
  font-family: var(--ff-head);
  font-size: clamp(24px, 3vw, 30px);
  font-weight: 700;
  letter-spacing: -.01em;
  line-height: 1.2;
}
h3, .t-h3 {
  font-family: var(--ff-head);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  letter-spacing: 0;
}
h4, .t-h4 {
  font-family: var(--ff-head);
  font-size: 18px;
  font-weight: 600;
  line-height: 1.35;
}
/* Section heading inside the SSR product page (Опис / Як приймати /
   Характеристики). It had no rule at all before. */
.section-title-sm {
  font-family: var(--ff-head);
  font-size: 20px;
  font-weight: 600;
  line-height: 1.3;
  color: var(--c-text);
  margin-bottom: var(--sp-3);
}
.section-title {
  font-family: var(--ff-head);
  font-size: clamp(20px, 2.5vw, 24px);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--c-text);
}
.page-heading {
  font-family: var(--ff-head);
  font-size: clamp(28px, 5vw, 38px);
  font-weight: 700;
  letter-spacing: -.02em;
  margin: 0 0 var(--sp-6);
}
.t-body  { font-size: 16px; line-height: 1.75; color: var(--c-text-soft); max-width: var(--content-w); }
.t-small { font-size: 13px; line-height: 1.5; color: var(--c-muted); }
.t-meta  {
  font-family: var(--ff-ui);
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--c-muted);
}
/* The thousands separator inside a price, narrowed.
   =================================================
   formatPrice() joins thousands with U+00A0, a non-breaking space, so «1 250 ₴»
   cannot wrap between the 1 and the 250. In a PROPORTIONAL face that space is
   narrow and reads as a grouping mark. In the numeric face it is a full
   monospace cell — measured on the stand: 12px against a 20px glyph — and at
   that width it stops reading as a separator and starts reading as a gap between
   two numbers.

   word-spacing rather than switching the character to U+202F: the narrow space
   would come out of whichever font happens to have it, and the whole point of
   the numeric face is that the figures do not shift. Minus 0.3em brings the
   separator to 6px — the same as a thin space would be — using the font we ship.

   Scoped to a variable so every numeric element below gets it from one place;
   `--num-gap` is applied wherever --ff-num is. */
.t-price,
.card-price, .card-old-price,
.product-price, .product-old-price,
.cart-item-price, .order-summary-item-price, .success-item-price,
.spec-val {
  word-spacing: -0.3em;
}

/* Price is a fact, so it is set in ink, not in a colour, and in the numeric
   face with tabular figures so digits do not jump between cards. */
.t-price {
  font-family: var(--ff-num);
  font-size: 20px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
}

/* ── 4. ACCESSIBILITY ─────────────────────────────────────── */
/* The ring sits OUTSIDE the element (outline-offset), so it stays visible on a
   gold button too: blue on gold is only ~2:1, and it is the ring drawn on the
   dark page background (9.6:1) that carries the required 3:1, not the ring
   against the button it surrounds. */
:focus-visible {
  outline: 2px solid var(--c-focus);
  outline-offset: 2px;
  box-shadow: var(--ring-focus);
  border-radius: inherit;
}
:focus:not(:focus-visible) { outline: none; }

/* Links inside prose.
   The reset above strips colour and underline from every <a> so navigation and
   cards can style themselves — but inside running text that leaves a link at
   1.00:1 against the words around it, with no second cue either. WCAG 1.4.1 is
   explicit that colour alone is not enough, so prose links get BOTH the action
   colour and an underline. Scoped to text containers only: a blanket `a{}` rule
   would put underlines back under every card and nav item.

   The four selectors after .legal-body are not prose containers, they are the
   places a measurement found still failing: the seller's phone and e-mail in the
   footer of the public offer and of the privacy policy (.t-small), the only
   contacts on the contact page (a.contact-value), the Telegram and e-mail on the
   order-success screen (.success-tg-note), and the links to the privacy policy
   and the offer in the consent line above the submit button
   (.checkout-submit-note). Every one of them was gold-on-gold at 1.00:1 against
   the sentence around it with no underline — the contacts and the consent links
   in a contract, which is the worst possible place for a link nobody can see.
   Measured in the browser across 13 pages, not read off the markup. */
.t-body a,
.t-small a,
.info-card a,
.info-steps a,
.faq-a a,
.catalog-seo-block a,
.product-desc a,
.legal-body a,
.spec-val a,
a.contact-value,
.success-tg-note a,
.checkout-submit-note a {
  color: var(--c-primary);
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.t-body a:hover,
.t-small a:hover,
.info-card a:hover,
.info-steps a:hover,
.faq-a a:hover,
.catalog-seo-block a:hover,
.product-desc a:hover,
.legal-body a:hover,
.spec-val a:hover,
a.contact-value:hover,
.success-tg-note a:hover,
.checkout-submit-note a:hover { color: var(--c-primary-dark); text-decoration-thickness: 2px; }
/* Never add `outline: none` to an element's BASE state to hide the ring from
   mouse users — the rule above already does that, once, for everything. A base
   `outline: none` sits later in the file at equal specificity and silently wins,
   which is how every input on the site lost its focus ring. */

/* Dark surfaces: blue would give ~2.6:1 there. */
.site-footer :focus-visible {
  outline-color: var(--c-focus-dark);
  box-shadow: var(--ring-focus-dark);
}

.skip-link {
  position: absolute;
  left: var(--sp-4);
  top: var(--sp-2);
  z-index: 300;
  transform: translateY(-200%);
  background: var(--c-card);
  color: var(--c-text);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r);
  padding: var(--sp-2) var(--sp-4);
  font-weight: 600;
  transition: transform var(--t);
}
.skip-link:focus { transform: translateY(0); }

/* The only justified !important in the project: this block exists to override
   author animations wholesale, and a specificity race would defeat it. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
  .skeleton, .skel-line { background: var(--c-skeleton-a); }
}

/* ── 5. PROMO BAR ─────────────────────────────────────────── */
/* Removed 2026-10-08 at the client's request: the header is the first thing on
   every page and the only sticky element of the chrome, so --sticky-top is the
   header alone. The number is kept so the table of contents does not renumber. */

/* ── 6. HEADER ────────────────────────────────────────────── */
/* The ONLY sticky element of the chrome, pinned at 0 from the first pixel. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  height: var(--header-h);
  background: var(--c-card);
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  box-shadow: none;
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  max-width: var(--max-w);
  margin: 0 auto;
  padding: 0 var(--sp-6);
}

/* Logo lockup: the client's mark (sprite #logo-mark, in currentColor) and the
   KOBA wordmark beside it — the mark has no lettering of its own.
   The mark is sized by HEIGHT only. Its outer <svg> carries the mark's own
   211:261 viewBox, so the width follows and the drawing is never letterboxed
   into a square box, which is what a fixed width × height did to it. */
.logo {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 0;
  color: var(--c-text);
}
.logo-mark {
  display: block;
  flex-shrink: 0;
  height: 42px;
  width: auto;
  color: var(--c-logo);
}
.logo-text {
  font-family: var(--ff-head);
  font-size: 1.25rem;
  font-weight: 800;
  letter-spacing: .04em;
  line-height: 1;
  white-space: nowrap;
}

/* Nav */
.header-nav-list {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  list-style: none;
  margin: 0;
  padding: 0;
}
.header-nav-list > li > a {
  display: block;
  font-family: var(--ff-ui);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--c-muted);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r);
  transition: color var(--t), background var(--t);
}
.header-nav-list > li > a:hover  { color: var(--c-text); background: var(--c-card-hover); }
.header-nav-list > li > a.active { color: var(--c-primary); background: var(--c-primary-wash); }

/* Header actions */
.header-actions {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 0;
}

/* Cart button */
.btn-cart {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  background: var(--c-cta);
  color: var(--c-on-cta);
  border: none;
  border-radius: var(--r);
  padding: var(--sp-2) var(--sp-4);
  min-height: 40px;
  font-family: var(--ff-ui);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0;
  cursor: pointer;
  transition: background var(--t);
  white-space: nowrap;
}
.btn-cart:hover { background: var(--c-cta-hover); }

/* ── Nav dropdown ─────────────────────────────────────────── */
/* «Каталог» is a link AND a disclosure: the link always goes to /catalog, the
   chevron button beside it opens the category list — by tap, click or keyboard,
   with aria-expanded. Hover still opens it where there IS hover; a touch screen
   has none, and before the button existed a tablet could not open it at all. */
.nav-dropdown {
  position: relative;
  display: flex;
  align-items: center;
}
.nav-dropdown-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 32px;
  margin-left: calc(-1 * var(--sp-2));
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--r);
  color: var(--c-muted);
  cursor: pointer;
}
.nav-dropdown-toggle:hover { color: var(--c-text); background: var(--c-card-hover); }
.nav-dropdown-toggle svg { transition: transform var(--t); }
.nav-dropdown.open .nav-dropdown-toggle svg { transform: rotate(180deg); }
.nav-dropdown-menu {
  position: absolute;
  top: 100%;           /* flush with the trigger; the visual gap comes from padding-top */
  left: 0;
  list-style: none;
  margin: 0;
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r);
  padding: var(--sp-1) 0;
  padding-top: 10px;   /* 6px of visual gap + 4px of inner breathing room */
  min-width: 180px;
  display: none;
  z-index: 200;
  box-shadow: var(--sh-2);
}
/* Invisible bridge over the gap: without it the menu closes while the pointer
   travels from the trigger down to the first item. */
.nav-dropdown-menu::before {
  content: '';
  position: absolute;
  bottom: 100%;
  left: 0;
  right: 0;
  height: 10px; /* must match the padding-top above */
}
.nav-dropdown.open .nav-dropdown-menu { display: block; }
.nav-dropdown-menu a {
  display: block;
  padding: var(--sp-2) var(--sp-4);
  font-family: var(--ff-ui);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--c-muted);
  border-radius: 0;
  white-space: nowrap;
}
.nav-dropdown-menu a:hover { color: var(--c-text); background: var(--c-card-hover); }
.nav-dropdown-menu a[aria-current="page"] { color: var(--c-primary); }

/* Hover only where a pointer can hover — on touch a "sticky" hover would open
   the menu on the tap meant for the link. */
@media (hover: hover) {
  .nav-dropdown:hover .nav-dropdown-menu { display: block; }
}
@media (pointer: coarse) {
  .nav-dropdown-toggle { width: 44px; height: 44px; margin-left: calc(-1 * var(--sp-3)); }
}

.cart-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  background: var(--c-on-dark-20);
  border-radius: 3px;
  font-size: 0.6875rem;
}
.cart-count[data-count="0"] { display: none; }

/* Mobile burger — the X is the .open class components.js sets with aria-expanded */
.burger {
  display: none;
  flex-direction: column;
  gap: 5px;
  background: none;
  border: none;
  cursor: pointer;
  padding: 11px;
}
.burger span {
  display: block;
  width: 22px;
  height: 2px;
  background: var(--c-text);
  border-radius: 2px;
  transition: transform var(--t-md), opacity var(--t);
}
.burger.open span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.burger.open span:nth-child(2) { opacity: 0; }
.burger.open span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Mobile menu, right under the header: nothing sits above the header, so its
   bottom edge is always --header-h and there is nothing to measure. It scrolls on its own: at 320×568 the links and the categories are taller
   than the space under the header, and before it scrolled the «Кошик» button
   sat below the screen with the page locked behind it — unreachable. */
.mobile-nav {
  display: none;
  position: fixed;
  top: var(--header-h);
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 99;
  flex-direction: column;
  gap: var(--sp-4);
  padding: var(--sp-2) var(--sp-4) 0;
  background: var(--c-bg);
  border-top: 1px solid var(--c-border);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.mobile-nav.open { display: flex; }
.mobile-nav-list,
.mobile-nav-sub { list-style: none; margin: 0; padding: 0; }
.mobile-nav-list > li { border-bottom: 1px solid var(--c-border); }
.mobile-nav-list a {
  display: flex;
  align-items: center;
  min-height: 48px;
  padding: var(--sp-2);
  font-family: var(--ff-ui);
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--c-muted);
  border-radius: var(--r);
  transition: color var(--t);
}
/* The categories, nested under «Каталог» — a real list, one per line. */
.mobile-nav-sub { padding: 0 0 var(--sp-2) var(--sp-4); }
.mobile-nav-sub a {
  min-height: 44px;
  font-size: 1rem;
  font-weight: 500;
  color: var(--c-text-soft);
}
.mobile-nav-list a:hover { color: var(--c-text); }
.mobile-nav-list a[aria-current="page"] { color: var(--c-primary); }
/* The cart is pinned to the bottom of the scrolling menu, so it is on screen
   whatever the length of the category list. The ring in the page colour hides
   the links scrolling underneath it. */
.mobile-nav-cart {
  position: sticky;
  bottom: var(--sp-4);
  flex-shrink: 0;
  margin: auto 0 var(--sp-4);
  box-shadow: 0 0 0 var(--sp-4) var(--c-bg);
}

/* ── 7. FOOTER ────────────────────────────────────────────── */
.site-footer {
  background: var(--grad-dark);
  color: var(--c-on-dark-75);
  border-top: none;
  padding: var(--sp-section) 0 var(--sp-6);
  margin-top: var(--sp-section);
}
.site-footer .logo { color: var(--c-on-dark); }
.site-footer .logo-mark { height: 36px; }
.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr repeat(3, 1fr);
  gap: var(--sp-8);
  margin-bottom: var(--sp-8);
}
.footer-brand .logo { margin-bottom: var(--sp-3); }
.footer-brand p { font-size: 0.875rem; color: var(--c-on-dark-60); line-height: 1.7; max-width: 260px; }
/* Column titles are labels, not headings: three more <h2>s on every page would
   outnumber the page's own structure. Each column is a labelled <nav>. */
.footer-col-title {
  font-family: var(--ff-ui);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-on-dark);
  margin-bottom: var(--sp-3);
}
.footer-col ul { display: flex; flex-direction: column; gap: var(--sp-2); list-style: none; margin: 0; padding: 0; }
.footer-col a { font-size: 0.875rem; color: var(--c-on-dark-75); transition: color var(--t); }
.footer-col a:hover { color: var(--c-on-dark); text-decoration: underline; text-underline-offset: 2px; }

/* ── Newsletter subscribe form (footer) ── */
.footer-newsletter-desc { font-size: 0.8125rem; color: var(--c-on-dark-60); margin-bottom: var(--sp-3); line-height: 1.5; }
.footer-subscribe-form { width: 100%; }
.footer-subscribe-row { display: flex; gap: 6px; }
.footer-subscribe-row input {
  /* `width: 0` is load-bearing, not redundant with min-width: 0. An <input>
     keeps an intrinsic min-content width of ~20 characters (205px at 16px),
     which the grid track honours — at 320px that pushed the footer to 330px
     and gave the whole page a horizontal scrollbar. */
  flex: 1 1 0;
  width: 0;
  min-width: 0;
  padding: 10px 12px;
  font-size: 1rem;
  font-family: var(--ff-body);
  border: 1px solid var(--c-on-dark-40);
  border-radius: var(--r);
  background: var(--c-on-dark-10);
  color: var(--c-on-dark);
  transition: border-color var(--t);
}
.footer-subscribe-row input::placeholder { color: var(--c-on-dark-60); }
.footer-subscribe-row input:focus { border-color: var(--c-primary); }
.footer-subscribe-row button {
  padding: 10px 16px;
  font-size: 0.875rem;
  font-family: var(--ff-ui);
  font-weight: 700;
  letter-spacing: 0;
  white-space: nowrap;
  background: var(--c-on-dark);
  color: var(--c-dark);
  border: none;
  border-radius: var(--r);
  cursor: pointer;
  transition: opacity var(--t);
}
.footer-subscribe-row button:hover { opacity: .88; }
.footer-subscribe-row button:disabled { opacity: .6; cursor: default; }
/* --c-success / --c-error are tuned for light surfaces; on the dark footer
   they would give ~2.6:1, so the message carries on-dark text plus an icon. */
.footer-subscribe-msg     { font-size: 0.8125rem; margin-top: 6px; min-height: 18px; }
.footer-subscribe-msg.ok  { color: var(--c-success); }
.footer-subscribe-msg.err { color: var(--c-on-dark); font-weight: 600; }

.footer-bottom {
  border-top: 1px solid var(--c-on-dark-20);
  padding-top: var(--sp-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  flex-wrap: wrap;
}
.footer-bottom p { font-size: 0.8125rem; color: var(--c-on-dark-60); }
/* Legal identity line — the E-E-A-T signal a YMYL shop must carry. */
.footer-legal { font-size: 0.8125rem; color: var(--c-on-dark-60); line-height: 1.7; margin-top: var(--sp-3); }

/* ── 8. BUTTONS ───────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  font-family: var(--ff-ui);
  font-weight: 600;
  font-size: 15px;
  letter-spacing: 0;
  border: 1.5px solid transparent;
  border-radius: var(--r);
  padding: 10px 20px;
  cursor: pointer;
  transition: background var(--t), border-color var(--t), color var(--t), opacity var(--t), transform var(--t), box-shadow var(--t);
  white-space: nowrap;
  user-select: none;
}
.btn:disabled { opacity: .45; pointer-events: none; }

.btn-primary       { background: var(--c-cta); color: var(--c-on-cta); }
.btn-primary:hover { background: var(--c-cta-hover); }

.btn-outline       { background: var(--c-card); color: var(--c-text); border-color: var(--c-border-strong); }
.btn-outline:hover { border-color: var(--c-primary); color: var(--c-primary); }

.btn-ghost         { background: transparent; color: var(--c-muted); }
.btn-ghost:hover   { color: var(--c-text); background: var(--c-card-hover); }

.btn-accent        { background: var(--c-accent); color: var(--c-on-accent); }
.btn-accent:hover  { background: var(--c-accent-dark); }

.btn-danger        { background: var(--c-error); color: var(--c-on-dark); }
.btn-danger:hover  { opacity: .88; }

.btn-sm { padding: 8px 14px; font-size: 13px; }
.btn-lg { padding: 13px 28px; font-size: 16px; }
.btn-full { width: 100%; }

.btn-icon {
  padding: 9px;
  background: var(--c-card);
  border-color: var(--c-border-strong);
  color: var(--c-muted);
  border-radius: var(--r);
}
.btn-icon:hover { color: var(--c-primary); border-color: var(--c-primary); }

/* WCAG 2.5.5 / platform guidelines: 44px on touch, desktop density untouched. */
@media (pointer: coarse) {
  .btn, .btn-sm, .btn-icon, .burger { min-height: 44px; }
}

/* ── 9. PRODUCT CARD ──────────────────────────────────────── */
.product-card {
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  box-shadow: var(--sh-card);
  transition: border-color var(--t), transform var(--t-md), box-shadow var(--t-md);
}
.product-card:hover {
  border-color: var(--c-primary);
  transform: translateY(-3px);
  box-shadow: var(--sh-card-hover);
}

.card-img-link { display: block; text-decoration: none; color: inherit; }

/* The plate. A supplement packshot is always shot on white, so a dark frame
   turned it into a white blob; `contain` also stops the lid and the label from
   being cropped, which is exactly what the shopper is looking at. */
.card-img-wrap {
  position: relative;
  aspect-ratio: 1;
  background-color: var(--c-plate);
  /* The pedestal sits ON the hatch: the client's packshots arrive with the
     background cut out (CLAUDE.md, the dark-catalogue warning), and a cut-out
     object with nothing under it reads as pasted on. Shared component, so the
     catalogue and the SSR pages get the same floor as the homepage shelf. */
  background-image: var(--grad-pedestal), var(--grad-plate);
  border-bottom: 1px solid var(--c-img-bdr);
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  flex-shrink: 0;
}
.card-img-wrap img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: var(--sp-3);
  transition: transform var(--t-md);
}
.product-card:hover .card-img-wrap img { transform: scale(1.03); }

.card-body { padding: var(--sp-4); display: flex; flex-direction: column; flex: 1; gap: 2px; }
.card-name {
  font-family: var(--ff-ui);
  font-size: 16px;
  font-weight: 700;
  line-height: 1.25;
  letter-spacing: -.01em;
  color: var(--c-text);
}
.card-sub  { font-size: 13px; color: var(--c-muted); margin-bottom: var(--sp-3); }
.card-footer { margin-top: auto; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2); }
.card-price-wrap { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.card-price {
  font-family: var(--ff-num);
  font-size: 20px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
}
.card-price.muted { color: var(--c-muted); font-size: 16px; }
.card-old-price   { font-family: var(--ff-num); font-size: 13px; color: var(--c-placeholder); text-decoration: line-through; }

/* Measured at 390px: the card is 173px wide, so .card-footer gets 139px while
   a nowrap button plus the price needs 145–186px. A column fixes both the
   overflow and the touch target. */
@media (max-width: 600px) {
  .card-footer {
    flex-direction: column;
    align-items: stretch;
    gap: var(--sp-2);
  }
  .card-footer .btn { width: 100%; }
}

/* ── 10. CATALOG GRID ─────────────────────────────────────── */
/* The grid and its empty state are shared components: the homepage shelf, the
   product page's related products and the catalogue view all render them. The
   catalogue view's own furniture (toolbar, heading row, chips, its 2/3/4
   column modifier) lives in css/catalog.css, loaded by that view only. */
.catalog-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: var(--sp-4);
}

/* With an empty database this is not an edge case but the shipping state of
   /catalog and of every category landing, so it gets an icon, a heading and a
   way out instead of one grey line. */
.catalog-empty,
.product-not-found {
  grid-column: 1 / -1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  text-align: center;
  padding: var(--sp-16) var(--sp-4);
  background-color: var(--c-plate);
  background-image: var(--grad-plate);
  border: 1px solid var(--c-border-light);
  border-radius: var(--r-lg);
}
.catalog-empty svg,
.product-not-found svg { width: 56px; height: 56px; color: var(--c-plate-ink); }
.catalog-empty h3,
.product-not-found h1,
.product-not-found h2 {
  /* Both, because the 404 body renders h1 (it is the page's only heading) while
     the empty-catalogue state renders h2 inside a page that already has one. */
  font-size: clamp(22px, 3.4vw, 28px);
  color: var(--c-text);
  margin: 0;
}
.catalog-empty p,
.product-not-found p { color: var(--c-muted); font-size: 15px; max-width: 44ch; margin: 0; }
.catalog-empty .btn,
.product-not-found .btn { margin-top: var(--sp-2); }

/* ── 11. FILTER SIDEBAR ───────────────────────────────────── */
/* Removed 2026-10-08 with the static catalogue page: the catalogue view has a
   toolbar instead (css/catalog.css). The number is kept so the table of
   contents does not renumber. */

/* ── 12. TAGS / BADGES ────────────────────────────────────── */
/* One model for all four states: light tint + dark text + 1px border of the
   same hue. Uppercase survives ONLY here and in .badge. */
.badge, .tag {
  font-family: var(--ff-ui);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  line-height: 1.4;
  padding: 3px 8px;
  border-radius: var(--r-pill);
  border: 1px solid transparent;
  white-space: nowrap;
}
.tag { display: inline-flex; align-items: center; }
.badge {
  position: absolute;
  top: 8px;
  left: 8px;
  /* A card is 173px wide in the two-column grid at 390px. "Акція" (54px) and
     "Немає в наявності" (141px) cannot share that row, and they were
     overlapping by 40px — measured, not guessed. Capping the width keeps a long
     label inside the card; --badge-out below moves the availability flag to the
     other end of the image so the two can never meet. */
  max-width: calc(100% - 16px);
  /* …and inside that cap a long label wraps instead of being clipped: a 138px
     card at 320 is narrower than «НЕМАЄ В НАЯВНОСТІ» on one line. On a card
     wide enough nothing changes. Tags stay on one line (.badge, .tag above). */
  white-space: normal;
  pointer-events: none;
}

/* ⚠️ NOT DEAD, although every grep says they are. The class is composed at
   runtime — `badge badge-${p.badge}` in js/product-card.js — from the `badge`
   column in D1, whose three allowed values are BADGE_LABELS in js/utils.js:
   new, sale, hit. So the strings "badge-new", "badge-sale" and "badge-hit" exist
   nowhere in the markup, a dead-class sweep lists all three, and deleting them
   takes the styling off every badge in the shop with nothing to show for it.
   A review already proposed exactly that. If a fourth badge value is ever added
   to BADGE_LABELS, it needs a rule here in the same commit. */
.badge-new,  .tag-new  { background: var(--c-badge-new-bg);  color: var(--c-badge-new-fg);  border-color: var(--c-badge-new-bdr); }
.badge-sale, .tag-sale { background: var(--c-badge-sale-bg); color: var(--c-badge-sale-fg); border-color: var(--c-badge-sale-bdr); }
.badge-hit,  .tag-hit  { background: var(--c-badge-hit-bg);  color: var(--c-badge-hit-fg);  border-color: var(--c-badge-hit-bdr); }
.badge-out,  .tag-out  { background: var(--c-badge-out-bg);  color: var(--c-badge-out-fg);  border-color: var(--c-badge-out-bdr); }
/* Availability sits at the BOTTOM of the photo, not opposite the promo badge:
   opposite corners still collide once both labels are long. */
.badge-out { top: auto; bottom: 8px; left: 8px; right: auto; }

/* ── 13. FORMS ────────────────────────────────────────────── */
.form-group { margin-bottom: var(--sp-4); }
.form-label {
  display: block;
  font-family: var(--ff-ui);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-text-soft);
  margin-bottom: 6px;
}
.form-label .required { color: var(--c-error); margin-left: 3px; }
.form-label-hint { font-size: 12px; font-weight: 400; color: var(--c-muted); margin-left: var(--sp-1); }

.form-input {
  width: 100%;
  background: var(--c-card);
  border: 1.5px solid var(--c-border-strong);
  border-radius: var(--r);
  padding: 11px 14px;
  color: var(--c-text);
  font-size: 16px; /* prevents iOS Safari auto-zoom on focus */
  transition: border-color var(--t);
  appearance: none;
}
.form-input:focus { border-color: var(--c-primary); }
.form-input::placeholder { color: var(--c-placeholder); }
.form-input.error { border-color: var(--c-error); }
.form-error { font-size: 13px; color: var(--c-error); margin-top: 4px; display: none; }
.form-error.show { display: block; }
textarea.form-input { resize: vertical; min-height: 100px; }

select.form-input {
  cursor: pointer;
  background-image: var(--img-select-arrow);
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-4); }

/* ── 14. QUANTITY INPUT ───────────────────────────────────── */
.qty-input {
  display: inline-flex;
  align-items: center;
  gap: 0;
  border: 1.5px solid var(--c-border-strong);
  border-radius: var(--r);
  overflow: hidden;
  background: var(--c-card);
}
.qty-btn {
  width: 44px;
  height: 44px;
  background: none;
  border: none;
  color: var(--c-muted);
  font-size: 16px;
  cursor: pointer;
  transition: color var(--t), background var(--t);
  display: flex;
  align-items: center;
  justify-content: center;
}
.qty-btn:hover { color: var(--c-primary); background: var(--c-primary-wash); }
/* The old .qty-val rules were written for exactly this element and then went
   dead when the SSR template shipped a different class name. Revived here. */
.qty-field {
  width: 46px;
  text-align: center;
  background: none;
  border: none;
  border-left: 1px solid var(--c-border);
  border-right: 1px solid var(--c-border);
  color: var(--c-text);
  font-family: var(--ff-num);
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  font-size: 15px;
  height: 40px;
  appearance: none;
  -moz-appearance: textfield;
}
.qty-field::-webkit-outer-spin-button,
.qty-field::-webkit-inner-spin-button { appearance: none; margin: 0; }

/* ── 15. CART ─────────────────────────────────────────────── */
.cart-layout {
  display: grid;
  grid-template-columns: 1fr 340px;
  gap: var(--sp-8);
  align-items: start;
  padding-bottom: var(--sp-16);
}
.cart-items-wrap { display: flex; flex-direction: column; gap: var(--sp-3); }
.cart-item {
  display: grid;
  grid-template-columns: 72px 1fr auto;
  gap: var(--sp-4);
  align-items: center;
  padding: var(--sp-4);
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
}
.cart-item-img-link {
  display: block;
  width: 72px;
  height: 72px;
  border-radius: var(--r);
  background-color: var(--c-plate);
  background-image: var(--grad-plate);
  border: 1px solid var(--c-img-bdr);
  overflow: hidden;
  flex-shrink: 0;
}
.cart-item-img-link img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
.cart-item-body { flex: 1; min-width: 0; }
.cart-item-name { font-family: var(--ff-ui); font-size: 15px; font-weight: 700; margin-bottom: 3px; }
.cart-item-sub  { font-size: 13px; color: var(--c-muted); margin-bottom: var(--sp-2); }
.cart-item-unit { font-size: 13px; color: var(--c-muted); margin-top: 2px; }
.cart-item-qty  { display: flex; align-items: center; }
.cart-item-actions { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.cart-item-price {
  font-family: var(--ff-num);
  font-size: 18px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
  white-space: nowrap;
  text-align: right;
}
.cart-item-remove { background: none; border: none; color: var(--c-muted); cursor: pointer; padding: 4px; transition: color var(--t); line-height: 1; border-radius: var(--r); }
.cart-item-remove:hover { color: var(--c-error); background: var(--c-badge-sale-bg); }

.cart-empty { text-align: center; padding: var(--sp-16) 0; color: var(--c-muted); }
/* Emoji render in three different colour fonts across platforms and clash with
   the monochrome stroke set; this is an icon from the sprite now. */
.cart-empty-icon { width: 64px; height: 64px; margin: 0 auto var(--sp-4); color: var(--c-plate-ink); font-size: 0; }
.cart-empty h2 { color: var(--c-text); margin-bottom: var(--sp-3); }
.cart-empty p  { margin-bottom: var(--sp-6); font-size: 16px; }

/* ── 16. PRODUCT PAGE ─────────────────────────────────────── */
.product-layout {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-10);
  align-items: start;
}
.product-img-wrap {
  background-color: var(--c-plate);
  background-image: var(--grad-plate);
  border: 1px solid var(--c-img-bdr);
  border-radius: var(--r-xl);
  aspect-ratio: 1;
  overflow: hidden;
  position: sticky;
  top: var(--sticky-top);
}
.product-img-wrap img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: var(--sp-6);
  display: block;
}
.product-img-wrap.skeleton { height: auto; }

@media (max-width: 900px) {
  .product-img-wrap { position: static; }
}

.product-info { min-width: 0; }
.product-tags {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
  margin-bottom: var(--sp-3);
}
.product-name {
  font-family: var(--ff-head);
  font-size: clamp(26px, 3.4vw, 36px);
  font-weight: 700;
  letter-spacing: -.02em;
  line-height: 1.15;
  margin-bottom: var(--sp-1);
}
/* Renamed from .product-name-en: it carries dose and form now, not an English
   name. worker/ssr-product.js renames the class in the same commit. */
.product-subline { font-size: 15px; color: var(--c-muted); margin-bottom: var(--sp-5); }

.product-price-row { display: flex; align-items: baseline; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.product-price {
  font-family: var(--ff-num);
  font-size: 34px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
  line-height: 1;
}
.product-old-price { font-family: var(--ff-num); font-size: 18px; color: var(--c-placeholder); text-decoration: line-through; }

.product-add-row {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}

.product-benefits {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  margin-bottom: var(--sp-6);
  max-width: var(--content-w);
}
.product-benefits li {
  position: relative;
  padding-left: var(--sp-6);
  font-size: 16px;
  line-height: 1.6;
  color: var(--c-text-soft);
}
.product-benefits li::before {
  content: '';
  position: absolute;
  left: 0;
  top: .45em;
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--c-primary);
  border-bottom: 2px solid var(--c-primary);
  transform: rotate(-45deg);
}

.product-details {
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  margin-top: var(--sp-10);
  padding-top: var(--sp-8);
  border-top: 1px solid var(--c-border);
}
.product-desc-block p,
.product-usage p,
.product-desc {
  font-size: 16px;
  line-height: 1.75;
  color: var(--c-text-soft);
  max-width: var(--content-w);
}
.product-desc-block p + p,
.product-usage p + p { margin-top: var(--sp-4); }
.product-usage {
  background: var(--c-primary-wash);
  border: 1px solid var(--c-badge-new-bdr);
  border-radius: var(--r-lg);
  padding: var(--sp-5) var(--sp-6);
}
.product-specs-block { max-width: var(--content-w); }
.product-specs-wrap { margin-bottom: var(--sp-4); }

.specs-table {
  width: 100%;
  border-collapse: collapse;
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  overflow: hidden;
  font-size: 15px;
}
.specs-table td { padding: var(--sp-3) var(--sp-4); }
.specs-table tr + tr td { border-top: 1px solid var(--c-border-light); }
.specs-table tr:nth-child(even) td { background: var(--c-accent-wash); }
.spec-key { color: var(--c-muted); width: 45%; }
/* Mono on VALUES only. Cyrillic mono is wider and would wrap a long key on
   320px; and never below 14px, where mono Cyrillic loses to proportional. */
.spec-val { font-family: var(--ff-num); font-weight: 500; font-variant-numeric: tabular-nums; color: var(--c-text); }
/* Prose cell in a spec-style table. Same grid as .spec-val, but the body face:
   --ff-num is reserved for fact VALUES (dose, pack count, price, order id), and
   a Ukrainian sentence set in IBM Plex Mono reads badly and spends the one
   typographic signal this direction has. */
.spec-text {
  padding: 10px 0;
  color: var(--c-text-soft);
  font-size: 15px;
  line-height: 1.6;
}


.product-trust {
  display: flex;
  gap: var(--sp-5);
  flex-wrap: wrap;
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-border);
  font-family: var(--ff-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--c-text-soft);
  letter-spacing: 0;
}
.product-trust span { display: flex; align-items: center; gap: 5px; }
.product-trust svg { color: var(--c-accent-ink); }

.out-of-stock-msg {
  padding: var(--sp-3) var(--sp-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-placeholder);
  border-radius: var(--r);
  font-size: 14px;
  color: var(--c-text-soft);
}

/* Related products on the server-rendered /p/<slug> pages.
   Grid and cards reuse .catalog-grid / .product-card — only spacing here. */
.related-section { margin-top: var(--sp-10); padding-top: var(--sp-8); border-top: 1px solid var(--c-border); }
.related-section .section-title { margin-bottom: var(--sp-4); }
@media (max-width: 600px) {
  .related-section { margin-top: var(--sp-8); padding-top: var(--sp-6); }
}

/* ── 17. CHECKOUT ─────────────────────────────────────────── */
.checkout-layout {
  display: grid;
  grid-template-columns: 1fr 380px;
  gap: var(--sp-8);
  align-items: start;
}
.cart-summary-box,
.order-summary-box,
.checkout-card {
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.checkout-card { margin-bottom: var(--sp-4); }
.cart-summary-box,
.order-summary-box {
  position: sticky;
  top: var(--sticky-top);
}

/* One canonical summary heading. It replaced .cart-summary-title (13px/1.5px
   caps) and the inline-margined .checkout-card-title in the summary column. */
.summary-title,
.cart-summary-title {
  font-family: var(--ff-ui);
  font-size: 16px;
  font-weight: 700;
  letter-spacing: 0;
  text-transform: none;
  color: var(--c-text);
  margin-bottom: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--c-border);
}
.checkout-card-title {
  font-family: var(--ff-ui);
  font-size: 17px;
  font-weight: 700;
  letter-spacing: 0;
  margin-bottom: var(--sp-4);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--c-border);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.checkout-card-title .step-num {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--c-primary);
  color: var(--c-on-dark);
  font-family: var(--ff-num);
  font-size: 12px;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  margin-bottom: 0;
  opacity: 1;
}

.cart-summary-row,
.order-summary-item { font-size: 15px; }
.cart-summary-row {
  display: flex;
  justify-content: space-between;
  padding: var(--sp-2) 0;
}
.order-summary-items { display: flex; flex-direction: column; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.order-summary-item {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
}
.order-summary-item-name { color: var(--c-text-soft); flex: 1; }
.cart-summary-row span:last-child,
.order-summary-item-price,
.success-item-price { font-family: var(--ff-num); font-variant-numeric: tabular-nums; }
.order-summary-item-price { white-space: nowrap; font-weight: 500; }
.cart-summary-total,
.order-summary-total-row,
.success-item-total {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-family: var(--ff-num);
  font-size: 22px;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  color: var(--c-text);
  border-top: 2px solid var(--c-border);
}
.cart-summary-total { padding-top: var(--sp-4); margin: var(--sp-2) 0 var(--sp-4); }
.order-summary-total-row { padding-top: var(--sp-4); margin-top: var(--sp-2); }

/* Step indicator */
.steps {
  display: flex;
  align-items: center;
  gap: 0;
  margin-bottom: var(--sp-8);
}
.step {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--ff-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--c-placeholder);
  flex: 1;
  position: relative;
}
.step::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--c-border);
  margin: 0 var(--sp-2);
}
.step:last-child::after { display: none; }
.step.active { color: var(--c-text); }
.step.done   .step-num { background: var(--c-success); }
.step.active .step-num { background: var(--c-primary); }

.checkout-submit-wrap {
  display: none; /* shown on mobile */
  flex-direction: column;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}
.checkout-submit-note {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 13px;
  color: var(--c-muted);
  margin-top: var(--sp-2);
}
.checkout-submit-note svg { color: var(--c-accent-ink); flex-shrink: 0; }

@media (max-width: 900px) {
  .checkout-submit-wrap { display: flex; }
  #submit-btn-desk      { display: none; }
}

/* ── 18. PAYMENT STEP ─────────────────────────────────────── */
.payment-options {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  margin-bottom: var(--sp-2);
}
.payment-option {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1.5px solid var(--c-border-strong);
  border-radius: var(--r);
  cursor: pointer;
  transition: border-color var(--t), background var(--t);
  user-select: none;
}
.payment-option:hover    { border-color: var(--c-primary); background: var(--c-accent-wash); }
.payment-option.selected { border-color: var(--c-primary); background: var(--c-primary-wash); }
.payment-option input[type="radio"] {
  margin-top: 3px;
  flex-shrink: 0;
  accent-color: var(--c-primary);
  width: 16px;
  height: 16px;
}
.payment-option-inner {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.payment-option-title { font-weight: 600; font-size: 16px; color: var(--c-text); line-height: 1.2; }
.payment-option-desc  { font-size: 13px; color: var(--c-muted); line-height: 1.4; }

/* Prepay details panel */
.payment-details {
  margin-top: var(--sp-4);
  background: var(--c-surface);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  text-align: left; /* Needed when used inside centered containers (success screen) */
}
.payment-details-title {
  font-size: 13px;
  font-weight: 700;
  color: var(--c-text-soft);
  text-transform: none;
  letter-spacing: 0;
  margin-bottom: var(--sp-3);
}
.payment-detail-item {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border);
}
.payment-detail-item:last-of-type { border-bottom: none; }
.payment-detail-icon {
  width: 24px;
  height: 24px;
  object-fit: contain;
  flex-shrink: 0;
  border-radius: 4px;
}
.payment-detail-label {
  font-weight: 600;
  font-size: 13px;
  width: 78px;
  flex-shrink: 0;
  color: var(--c-text);
}
.payment-detail-value {
  font-family: var(--ff-num);
  font-size: 13px;
  color: var(--c-text);
  flex: 1;
  word-break: break-all;
  line-height: 1.4;
}
.copy-btn {
  margin-left: auto;
  padding: 3px 10px;
  font-size: 12px;
  background: var(--c-card);
  border: 1px solid var(--c-border-strong);
  border-radius: var(--r);
  cursor: pointer;
  color: var(--c-text-soft);
  transition: border-color var(--t), color var(--t);
  flex-shrink: 0;
  white-space: nowrap;
}
.copy-btn:hover  { border-color: var(--c-primary); color: var(--c-primary); }
.copy-btn.copied { border-color: var(--c-success); color: var(--c-success); }

.payment-details-warning {
  margin-top: var(--sp-3);
  font-size: 13px;
  color: var(--c-text-soft);
  font-weight: 500;
  line-height: 1.5;
}
.payment-details-warning a { color: var(--c-primary); text-decoration: underline; }

/* Replaces the two inlined warning panels in checkout.html — the only place on
   the site that carried colours outside the palette. */
.notice-warn {
  background: var(--c-warn-bg);
  border: 1px solid var(--c-warn-bdr);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4);
  margin-bottom: var(--sp-3);
  font-size: 14px;
  line-height: 1.6;
  color: var(--c-text);
}
.notice-warn a { color: var(--c-warning); font-weight: 600; text-decoration: underline; }
.notice-warn svg { color: var(--c-warning); flex-shrink: 0; vertical-align: -2px; }

/* ── 19. SUCCESS SCREEN ───────────────────────────────────── */
.order-success {
  text-align: center;
  padding: var(--sp-16) 0;
  width: 100%;
}
.order-success:not([hidden]) {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
}
.order-success-icon { font-size: 64px; width: 100%; }
.order-success h1,
.order-success .success-title { width: 100%; }
.order-success p { color: var(--c-muted); max-width: 440px; margin: 0 auto var(--sp-8); }
/* The success block is not a separate document: its heading is an h2 now. */
.success-title { font-family: var(--ff-head); font-size: clamp(28px, 4vw, 38px); font-weight: 700; letter-spacing: -.02em; }

.order-id-box {
  background: var(--c-card);
  border: 2px solid var(--c-primary);
  border-radius: var(--r-lg);
  padding: var(--sp-5) var(--sp-6);
  width: 100%;
  max-width: 420px;
  text-align: center;
}
.order-id-label { font-size: 14px; color: var(--c-muted); margin: 0 0 var(--sp-2); }
/* Was --c-accent on --c-surface: 1.33:1 for the one number the buyer must copy. */
.order-id-value {
  font-family: var(--ff-num);
  font-size: 24px;
  font-weight: 500;
  color: var(--c-text);
  letter-spacing: .02em;
  margin: 0;
  word-break: break-all;
}
.order-id-save { font-size: 13px; color: var(--c-muted); margin: var(--sp-2) 0 0; }

.success-order-summary {
  width: 100%;
  max-width: 480px;
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-4);
  margin: 0 0 var(--sp-4);
  text-align: left;
}
.success-item-row {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--sp-3);
  padding: var(--sp-2) 0;
  border-bottom: 1px solid var(--c-border-light);
  font-size: 14px;
}
.success-item-name  { color: var(--c-text); flex: 1; }
.success-item-qty   { color: var(--c-muted); font-size: 13px; }
.success-item-price { color: var(--c-text); font-weight: 500; white-space: nowrap; }
.success-item-total { padding: var(--sp-3) 0 0; font-size: 18px; }

#success-cod,
#success-prepay {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  width: 100%;
  max-width: 520px;
}
.success-message { font-size: 16px; color: var(--c-text); line-height: 1.6; }
.success-tg-note {
  text-align: center;
  font-size: 14px;
  width: 100%;
  color: var(--c-text);
  background: var(--c-primary-wash);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4);
  line-height: 1.7;
}
/* Colour and underline come from the prose-link rule near the top; only the
   weight is specific to this block. The pair that used to live here set the
   colour without an underline and put the underline on :hover — which is no help
   to anyone who is not already pointing at the link. */
.success-tg-note a { font-weight: 600; }

/* ── 20. CONTACT ──────────────────────────────────────────── */
.contact-layout {
  display: grid;
  grid-template-columns: 1fr 1.2fr;
  gap: var(--sp-10);
  align-items: start;
}
.contact-item {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-3);
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--c-border);
}
.contact-item:last-child { border-bottom: none; }
.contact-icon {
  width: 40px;
  height: 40px;
  background: var(--c-primary-wash);
  border: 1px solid var(--c-badge-new-bdr);
  border-radius: var(--r);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--c-accent-ink);
  flex-shrink: 0;
}
.contact-icon svg { color: var(--c-accent-ink); }
.contact-label {
  font-family: var(--ff-ui);
  font-size: 13px;
  font-weight: 600;
  color: var(--c-muted);
  text-transform: none;
  letter-spacing: 0;
  margin-bottom: 3px;
}
.contact-value { font-size: 16px; font-weight: 600; }

.info-card {
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  margin-bottom: var(--sp-4);
}
.info-card-title {
  font-family: var(--ff-ui);
  font-size: 18px;
  font-weight: 700;
  margin-bottom: var(--sp-3);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--c-border);
  color: var(--c-text);
}
.info-steps {
  list-style: decimal;
  padding-left: var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}
.info-steps li { font-size: 16px; color: var(--c-text-soft); line-height: 1.6; }
.info-steps a  { color: var(--c-primary); }
.info-steps a:hover { text-decoration: underline; }

/* ── 21. BREADCRUMB ───────────────────────────────────────── */
.breadcrumb {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-4) 0;
  font-size: 0.875rem;
  color: var(--c-muted);
  flex-wrap: wrap;
}
.breadcrumb a, .breadcrumb-link { color: var(--c-muted); transition: color var(--t); }
.breadcrumb a:hover, .breadcrumb-link:hover { color: var(--c-primary); }
.breadcrumb-sep { color: var(--c-placeholder); }
.breadcrumb-current { color: var(--c-text-soft); }

/* ── 22. HERO ─────────────────────────────────────────────── */
/* Moved to css/home.css with the homepage redesign of 2026-10-07: the hero, the
   trust bar, the category cards, the step grids, the brand block and the review
   tiles were homepage-only and are now that page's own stylesheet. What stays in
   this section of the file is nothing — the number is kept so the table of
   contents does not renumber. */

/* ── 23. TOAST ────────────────────────────────────────────── */
.toast-container {
  position: fixed;
  bottom: var(--sp-6);
  right: var(--sp-6);
  z-index: 999;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  pointer-events: none;
}
.toast {
  background: var(--c-card);
  border: 1px solid var(--c-border);
  border-left: 3px solid var(--c-primary);
  border-radius: var(--r);
  padding: var(--sp-3) var(--sp-4);
  font-family: var(--ff-ui);
  font-size: 14px;
  font-weight: 600;
  color: var(--c-text);
  min-width: 220px;
  box-shadow: var(--sh-float);
  opacity: 0;
  transform: translateX(20px);
  transition: opacity var(--t-md), transform var(--t-md);
  pointer-events: auto;
}
.toast.show    { opacity: 1; transform: translateX(0); }
.toast.success { border-left-color: var(--c-success); }
.toast.warning { border-left-color: var(--c-warning); }
.toast.error   { border-left-color: var(--c-error); }

/* ── 24. UTILITIES ────────────────────────────────────────── */
.visually-hidden { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0,0,0,0); white-space: nowrap; }
.text-muted { color: var(--c-muted); }
.divider { height: 1px; background: var(--c-border); margin: var(--sp-4) 0; }
.page-title-wrap { padding: var(--sp-8) 0 var(--sp-4); }

/* Cart page title count badge */
.cart-title-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 7px;
  background: var(--c-primary);
  color: var(--c-on-cta);
  border-radius: var(--r-pill);
  font-family: var(--ff-num);
  font-size: 13px;
  font-weight: 500;
  margin-left: 10px;
  vertical-align: middle;
}

/* ── 25. SKELETON LOADER ──────────────────────────────────── */
/* background-size: 200% 100% keeps the sweep period independent of the element
   width; the old 800px band crossed a 200px card in a fraction of the cycle. */
@keyframes shimmer {
  0%   { background-position: 100% 0; }
  100% { background-position: -100% 0; }
}
@keyframes spin {
  to { transform: rotate(360deg); }
}
.skel-line {
  border-radius: var(--r);
  background: var(--grad-skeleton);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite linear;
}
/* Structural silhouette, built from the same tokens as .product-card so the
   swap to a real card causes no layout shift. */
.skeleton {
  border-radius: var(--r-lg);
  border: 1px solid var(--c-border-light);
  background: var(--c-card);
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.skeleton::before {
  content: '';
  display: block;
  aspect-ratio: 1;
  background: var(--grad-skeleton);
  background-size: 200% 100%;
  animation: shimmer 1.4s infinite linear;
}
/* The body block has to be about as tall as a REAL card body, or the grid jumps
   when products arrive — which is the CLS this element exists to prevent.
   Measured before: card 384 vs skeleton 265 at 390px, a 119px shift per row.
   Measured after: 0px at >=800px, within 19px on mobile. The remainder is
   honest and unavoidable — a product name wraps to one or two lines depending
   on the name, and a skeleton cannot know the name. Do not chase it with more
   breakpoints; measure again if the card's box model changes. */
.skeleton::after {
  content: '';
  display: block;
  height: 140px;
  margin: var(--sp-4);
  border-radius: var(--r);
  background:
    linear-gradient(var(--c-skeleton-a) 0 0) 0 0    / 82% 18px no-repeat,
    linear-gradient(var(--c-skeleton-a) 0 0) 0 28px / 58% 14px no-repeat,
    linear-gradient(var(--c-skeleton-a) 0 0) 0 74px / 34% 22px no-repeat,
    linear-gradient(var(--c-skeleton-a) 0 0) 100% 70px / 42% 36px no-repeat;
}
/* Below 600px .card-footer stacks (price over a full-width button), so a real
   card grows by ~36px and the skeleton has to grow with it. Two numbers, not a
   formula: the card has two layouts, and this mirrors them. */
@media (max-width: 600px) {
  .skeleton::after { height: 176px; }
}


/* ── 26. LANDING ──────────────────────────────────────────── */
/* The homepage's blocks live in css/home.css. Three things stay here because
   other pages use them: the FAQ band (also on /delivery-payment), the section
   title (FAQ, /delivery-payment) and the step number (checkout). */
.home-faq {
  background: var(--c-bg);
  border-top: 1px solid var(--c-border-light);
}
.home-section-title {
  font-family: var(--ff-head);
  font-size: clamp(24px, 3vw, 32px);
  font-weight: 700;
  letter-spacing: -.02em;
  color: var(--c-text);
  margin-bottom: var(--sp-6);
}
.step-num {
  font-family: var(--ff-num);
  font-size: 28px;
  font-weight: 500;
  color: var(--c-primary);
  opacity: .35;
  line-height: 1;
  margin-bottom: var(--sp-2);
}

/* ── 27. FAQ ──────────────────────────────────────────────── */
.faq-layout {
  display: grid;
  grid-template-columns: 280px 1fr;
  gap: var(--sp-12);
  align-items: start;
}
.faq-heading { position: sticky; top: var(--sticky-top); }
.faq-heading .home-section-title { margin-bottom: var(--sp-3); }
.faq-heading-sub { font-size: 15px; color: var(--c-muted); line-height: 1.6; }

.faq-list { display: flex; flex-direction: column; }
.faq-item { border-bottom: 1px solid var(--c-border-light); }
.faq-item:first-child { border-top: 1px solid var(--c-border-light); }

.faq-q {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-4) 0;
  font-size: 16px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  user-select: none;
  color: var(--c-text);
  transition: color var(--t);
}
.faq-q::-webkit-details-marker { display: none; }
.faq-item[open] > .faq-q { color: var(--c-primary); }

.faq-q::after {
  content: '';
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background-color: var(--c-surface);
  background-image: var(--img-qty-plus);
  background-repeat: no-repeat;
  background-position: center;
  transition: background-color var(--t-md);
}
.faq-item[open] > .faq-q::after {
  background-color: var(--c-primary);
  background-image: var(--img-qty-minus);
}

.faq-a {
  padding: 0 var(--sp-10) var(--sp-4) 0;
  font-size: 16px;
  line-height: 1.75;
  color: var(--c-text-soft);
  max-width: var(--content-w);
}
.faq-a a { color: var(--c-primary); text-decoration: underline; }

/* ── 28. REVIEWS ──────────────────────────────────────────── */
.reviews-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--sp-3);
}
@media (min-width: 640px) {
  .reviews-grid { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }
}

/* Full page card */
.review-pub-card {
  background: var(--c-card);
  border-radius: var(--r-lg);
  overflow: hidden;
  border: 1px solid var(--c-border);
}
.review-pub-img-wrap {
  display: block;
  aspect-ratio: 1;
  overflow: hidden;
  background-color: var(--c-plate);
  background-image: var(--grad-plate);
}
.review-pub-img-wrap img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--t-md);
}
.review-pub-img-wrap:hover img { transform: scale(1.04); }

.review-pub-card--video .review-pub-video-wrap {
  aspect-ratio: 16/9;
  background: var(--c-backdrop);
  overflow: hidden;
  position: relative;
}
.review-pub-card--video .review-pub-video-wrap iframe {
  width: 100%; height: 100%; border: 0;
}

/* Lazy YouTube embed */
.yt-lazy { display: block; width: 100%; height: 100%; }
.yt-lazy img { display: block; width: 100%; height: 100%; object-fit: cover; }
.yt-play-btn {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  pointer-events: none;
  filter: drop-shadow(var(--sh-text-sm));
  opacity: .9;
  transition: opacity var(--t);
}
.yt-lazy:hover .yt-play-btn { opacity: 1; }

.review-pub-meta { padding: var(--sp-3) var(--sp-4) var(--sp-4); }
.review-pub-author { font-size: 14px; font-weight: 600; display: block; }
.review-pub-date { font-size: 13px; color: var(--c-muted); display: block; margin-top: 2px; }
.review-pub-text { font-size: 14px; color: var(--c-muted); margin: var(--sp-2) 0 0; line-height: 1.5; }

/* Spinner for public pages */
.spinner-pub {
  display: inline-block;
  width: 32px; height: 32px;
  border: 3px solid var(--c-border);
  border-top-color: var(--c-primary);
  border-radius: 50%;
  animation: spin .7s linear infinite;
}

/* Home section preview cards */
/* Reserve one row so the reviews arriving from the API do not push the FAQ. */
#home-reviews-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-3);
  min-height: 200px;
}
#home-reviews-grid > * {
  flex: 0 1 calc(20% - var(--sp-3));
  min-width: 140px;
  max-width: 260px;
}


/* YouTube thumbnail — absolute fill, with play + meta stacked on top */


/* ── 29. LIGHTBOX ─────────────────────────────────────────── */
.lightbox-backdrop {
  position: fixed;
  inset: 0;
  background: var(--c-overlay-strong);
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--sp-4);
  animation: lb-fade-in .15s ease;
}
.lightbox-backdrop[hidden] { display: none; }
@keyframes lb-fade-in { from { opacity: 0 } to { opacity: 1 } }

.lightbox-content {
  position: relative;
  max-width: min(92vw, 960px);
  max-height: 90vh;
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox-content img {
  max-width: 100%;
  max-height: 90vh;
  border-radius: var(--r-lg);
  display: block;
  object-fit: contain;
  box-shadow: var(--sh-modal);
}
.lb-video {
  width: min(90vw, 840px);
  aspect-ratio: 16/9;
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--sh-modal);
}
.lb-video iframe { width: 100%; height: 100%; border: none; display: block; }
/* Not a YouTube address: a link out, on a panel the size of the player. */
.lb-video--link {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--c-surface);
}
.lb-video-link {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--c-text);
  font-size: 1rem;
  text-decoration: none;
}
.lb-video-link:hover { text-decoration: underline; }
.lightbox-close {
  position: fixed;
  top: 14px;
  right: 14px;
  background: var(--c-on-dark-10);
  backdrop-filter: blur(4px);
  border: 1px solid var(--c-on-dark-20);
  color: var(--c-on-dark);
  width: 42px;
  height: 42px;
  border-radius: 50%;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--t);
  z-index: 1001;
}
.lightbox-close:hover { background: var(--c-on-dark-20); }

/* ── 30. SEO COPY ─────────────────────────────────────────── */
/* The catalogue's SEO block and the category description below the grid moved
   to css/catalog.css with the catalogue view, the only page that renders them. */

/* ── 33. SECTION FURNITURE ────────────────────────────────── */
/* The pieces that make a section read as one: the chapter kicker, the title,
   the lead, the quiet text link. Born on the homepage as .home-*, shared since
   the catalogue and the blog use them too — one look for "a section starts here"
   across the site. */
.sec-kicker {
  font-family: var(--ff-num);
  font-size: 0.75rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--c-muted);
  margin-bottom: var(--sp-3);
}
.sec-kicker-num { color: var(--c-accent-ink); }

.sec-title {
  font-family: var(--ff-head);
  font-weight: 800;
  font-size: clamp(1.5rem, 1.15rem + 1.8vw, 2.5rem);
  line-height: 1.12;
  letter-spacing: -.015em;
  color: var(--c-text);
  text-wrap: balance;
}

.sec-lead {
  margin-top: var(--sp-3);
  font-size: 1rem;
  line-height: 1.6;
  color: var(--c-text-soft);
  max-width: 40rem;
}

/* A link that is a quiet next step, not a button. Underlined, because on a
   dark page colour alone does not make a link (WCAG 1.4.1); --c-link is the one
   gold allowed as a foreground, and only with the underline. */
.sec-link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--c-link);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.sec-link:hover { color: var(--c-link-hover); text-decoration-thickness: 2px; }

/* In-page targets land below the sticky header, not under it: article
   headings (#s-…, the table of contents) and any #fragment. */
:target,
[id^="s-"] { scroll-margin-top: var(--sticky-top); }

/* ── 34. CHIP ROW ─────────────────────────────────────────── */
/* A row of LINKS styled as chips — catalogue categories, blog topics. Real
   <a href> in an <ul>, so every one is crawlable; the current one carries
   aria-current="page". On phones the row scrolls sideways instead of wrapping
   into a block of chips that pushes the content off the first screen; the
   edges fade so a cut-off chip reads as "there is more", not as a bug. */
/* Below 768 the row scrolls and bleeds to the screen edges by exactly the
   container's gutter: the fade sits over the gutter, so the first chip — and
   its focus ring — start fully visible, aligned with the text above. */
.chip-row {
  display: flex;
  gap: var(--sp-2);
  list-style: none;
  margin: 0 calc(-1 * var(--gutter, var(--sp-4)));
  padding: var(--sp-1) var(--gutter, var(--sp-4));
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  scroll-padding-inline: var(--gutter, var(--sp-4));
  -webkit-mask-image: var(--mask-fade-x);
  mask-image: var(--mask-fade-x);
}
.chip-row::-webkit-scrollbar { display: none; }
.chip-row > li { flex-shrink: 0; }

.chip-link {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 6px 14px;
  background: var(--c-card);
  border: 1.5px solid var(--c-border-strong);
  border-radius: var(--r-pill);
  color: var(--c-text-soft);
  font-family: var(--ff-ui);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}
.chip-link:hover { color: var(--c-text); border-color: var(--c-primary); }
/* --c-primary-wash is only 1.10:1 against --c-card, so the current chip carries
   the border and the text colour as well — the fill alone is not a signal. */
.chip-link[aria-current="page"] {
  color: var(--c-primary);
  background: var(--c-primary-wash);
  border-color: var(--c-primary);
}
.chip-link-count { font-family: var(--ff-num); font-size: 0.75rem; color: var(--c-placeholder); }

@media (pointer: coarse) {
  .chip-link { min-height: 44px; }
}
/* Wide enough for the whole row: it wraps, and nothing is cut, so no fade. */
@media (min-width: 768px) {
  .chip-row {
    margin-inline: 0;
    padding-inline: 0;
    flex-wrap: wrap;
    overflow: visible;
    -webkit-mask-image: none;
    mask-image: none;
  }
}

/* ── 32. MOTION ───────────────────────────────────────────────
   Progressive enhancement only. Four rules keep it from becoming a liability,
   and each one is here because breaking it was measured, not imagined:

   1. ONLY transform and opacity are animated. Anything else forces layout or
      paint every frame. The first version of this section animated `box-shadow`
      on the sticky header: measured at 120 paints and 170 raster tasks over the
      first 90px of scroll, against 0 and 0 with the animation off.
   2. NOTHING above the fold is revealed on scroll. The hero is the LCP element.
   3. Every reveal is wrapped in `@media screen`, `@supports` AND
      `prefers-reduced-motion: no-preference`. Without the first one the print
      layout has no scroll timeline, the animation resolves to its 0% keyframe,
      and the page prints blank.
   4. `animation` never takes `--t` / `--t-md`. Those are "duration easing" pairs
      for `transition`; in an `animation` shorthand they expand to
      `.25s ease ease-out` and the whole declaration is dropped without a word.
      Use `--dur-*` and `--ease-*`.
*/

/* ── Cross-document page transitions ──────────────────────────
   One declaration, no JavaScript, same-origin only — which every link here is,
   now that the storefront and the Worker are one origin.

   No `view-transition-name` on the header or footer, deliberately. They are
   inserted by components.js AFTER the new document is snapshotted, so naming
   them produced groups with no counterpart: instead of being carried across,
   they animated out and the real header appeared ~40ms later. Measured in the
   pagereveal handler: the new document contained only `root` at capture time.
   Naming them becomes correct the day the shell is server-rendered. */
@view-transition { navigation: auto; }

@media (prefers-reduced-motion: reduce) {
  /* `navigation: auto` cannot be undone per-media, so the animation is removed
     instead. The navigation still happens; it just happens instantly. */
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}

/* ── Scroll-driven reveals ────────────────────────────────────
   Tied to the element's own progress through the viewport: no
   IntersectionObserver, no scroll listener, no layout reads on the main thread.

   Attached to COMPONENTS rather than to sections of the homepage. The earlier
   version listed `.home-featured .product-card` and friends, so the two pages
   that matter most for search — the category landing and the product page —
   had no motion at all, and the same card behaved differently depending on
   which page it was on. */
@keyframes reveal-rise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

@media screen {
  @supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
      .product-card,
      .info-card,
      .faq-item,
      .catalog-seo-block {
        animation: reveal-rise linear both;
        animation-timeline: view();
        /* Bound to the element's own ENTRY, not to `cover`. With a cover-based
           range the bottom third of a phone screen is permanently occupied by
           half-faded cards — which on a dark palette reads as a rendering fault,
           because the card fill is barely distinguishable from the page. */
        animation-range: entry 0% entry 55%;
      }

      /* Keyboard focus can land on an element the scroll timeline still has
         mid-fade: the browser scrolls focus into view with `nearest`, which
         stops exactly inside the range. Anything focused, or containing focus,
         opts out of the animation entirely. */
      .product-card:focus-within,
      .info-card:focus-within,
      .faq-item:focus-within,
      .catalog-seo-block:focus-within {
        animation: none;
        opacity: 1;
        transform: none;
      }
    }
  }
}

/* ── Header elevation — deliberately absent ───────────────────
   There was a scroll-driven version here: a gold hairline fading in over the
   first 90px via `animation-timeline: scroll(root)`. It is gone for two
   reasons, in this order:

   1. The first attempt animated `box-shadow`, which is a paint property.
      Measured at 120 paints and 170 raster tasks over those 90px, against 0 and
      0 with the animation off — a repaint of a full-width sticky layer on every
      frame of the very first gesture a visitor makes.
   2. The rewritten version animated only opacity on a pseudo-element, which is
      correct, but `scroll()` progress could not be made to resolve in any
      environment available here — including a ScrollTimeline built by hand in
      JavaScript against document.documentElement. A rule that cannot be
      observed working is a rule that gets shipped on faith.

   The header already has a permanent border-bottom, which separates it from the
   content at every scroll position. That is enough. Bring the effect back only
   together with a way to see it working.
*/

/* ── Micro-interactions ───────────────────────────────────────
   Press feedback on everything tappable. `:active` rather than a JS class, so it
   fires on touch without the 300ms gap and cannot get stuck if a pointer event
   is missed.

   Note what is NOT here: a second `transition` declaration for .btn or
   .cat-card. Re-declaring the shorthand further down the file silently drops
   whatever the original listed — that is how `.btn` lost `opacity` (breaking
   the fade on :disabled) and `.cat-card` lost `border-color`. Transitions are
   declared once, where the component is. */
.btn:active,
.qty-btn:active,
.btn-icon:active { transform: translateY(1px) scale(.985); }

/* The gold CTA gets a glow rather than a lighter fill: on a dark page a lighter
   fill reads as "disabled", a glow reads as "raised". */
.btn-primary:hover { box-shadow: 0 0 0 1px var(--c-cta-hover), 0 6px 20px var(--c-primary-glow); }

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

/* ── FAQ ──────────────────────────────────────────────────────
   The answer fades in; the panel opens instantly.

   The tempting version is the grid-template-rows 0fr → 1fr trick, which animates
   "to the content's own height" without measuring anything. It was written here
   first and taken out: it still animates a LAYOUT property, so the subtree
   reflows every frame — exactly what rule 1 forbids. A rule that gets an
   exception the first time it is inconvenient is not a rule. */
@media (prefers-reduced-motion: no-preference) {
  .faq-item[open] > .faq-a {
    animation: reveal-rise var(--dur-md) var(--ease-out) both;
  }
}

/* ── 31. RESPONSIVE ───────────────────────────────────────── */
/* Tablet: ≤900px */
@media (max-width: 900px) {
  .product-layout   { grid-template-columns: 1fr; }
  .checkout-layout  { grid-template-columns: 1fr; }
  .contact-layout   { grid-template-columns: 1fr; }
  .footer-grid      { grid-template-columns: 1fr 1fr; }
  .cart-layout      { grid-template-columns: 1fr; }
  .cart-summary-box,
  .order-summary-box { position: static; }
  .faq-layout       { grid-template-columns: 1fr; gap: var(--sp-6); }
  .faq-heading      { position: static; }
}

/* Tablet: nav collapse at 768px */
@media (max-width: 768px) {
  .header-nav         { display: none; }
  .burger             { display: flex; }
  .faq-q { padding: var(--sp-3) var(--sp-4); }
  .faq-a { padding: 0 var(--sp-4) var(--sp-3); }
}

/* Mobile: ≤600px */
@media (max-width: 600px) {
  .header-inner { padding: 0 var(--sp-3); gap: var(--sp-2); }
  .logo-text    { font-size: 1.0625rem; }
  .logo-mark    { height: 36px; }
  .btn-cart     { padding: var(--sp-1) var(--sp-2); gap: 5px; }

  .container { --gutter: var(--sp-4); }
  .section   { padding: var(--sp-section) 0; }

  .catalog-grid { grid-template-columns: repeat(2, 1fr); gap: var(--sp-3); }

  .cart-item       { grid-template-columns: 56px 1fr; }
  .cart-item-price { grid-column: 2; }

  .form-row    { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr; }

  .product-add-row { flex-wrap: wrap; }
  .product-img-wrap img { padding: var(--sp-4); }
  .steps { gap: var(--sp-1); }
  .step  { font-size: 12px; }

  .toast-container { right: var(--sp-3); left: var(--sp-3); bottom: var(--sp-3); }
  .toast           { min-width: unset; }
}

/* Extra small: ≤380px */
@media (max-width: 380px) {
  .catalog-grid { grid-template-columns: 1fr; }
}
