/* Phase 2 step 2.2 — Category shop pages (shop/*.php).
   Scoped classes only, per .claude/rules/coding-style.md — css/style.css
   itself is untouched (Homepage FINAL_LOCKED); this file only reads its
   :root design tokens via var(--...). */

.shop-breadcrumb ol {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  padding: 0;
  margin: 0;
  list-style: none;
  font-size: 13px;
  color: var(--color-ink-soft);
}

.shop-breadcrumb a {
  color: var(--color-ink-soft);
  text-decoration: none;
}

.shop-breadcrumb a:hover {
  color: var(--color-forest);
}

.shop-breadcrumb-sep {
  color: var(--color-line);
}

.shop-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
  justify-content: space-between;
}

.shop-filter,
.shop-sort {
  min-width: 0;
}

.shop-filter input,
.shop-sort select {
  height: 44px;
  padding: 0 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
  background: var(--surface-raised);
  font-size: 14px;
  color: var(--color-ink);
  font-family: inherit;
}

.shop-filter input {
  width: 100%;
  min-width: 220px;
}

.shop-filter input:focus-visible,
.shop-sort select:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.shop-result-count {
  font-size: 13px;
  color: var(--color-ink-soft);
  margin: 0 0 16px;
}

.category-product-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

@media (min-width: 640px) {
  .category-product-grid { gap: 24px; }
}

@media (min-width: 768px) {
  .category-product-grid { grid-template-columns: repeat(3, 1fr); }
}

@media (min-width: 1024px) {
  .category-product-grid { grid-template-columns: repeat(4, 1fr); }
}

.product-card {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--surface-raised);
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.35s ease, border-color 0.2s ease;
}

.product-card:hover {
  box-shadow: var(--shadow-sm);
  border-color: rgba(27, 27, 24, 0.12);
}

.product-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 14px 16px;
}

.product-card-body .mt-auto {
  margin-top: auto;
}

/* P8.3A — bounds title/description to a predictable number of lines so
   cards in the same row stay a uniform height regardless of how long a
   given product's real name/description text is (grid's default
   align-items:stretch already equalizes row height once each card's own
   content height is bounded like this). Same -webkit-line-clamp pattern
   already used elsewhere in this codebase (e.g. css/pages.css's
   .news-list-excerpt/.shop-sidebar-featured-name) rather than a Tailwind
   line-clamp-N utility class, which isn't used anywhere else here.
   Phase 2.5.1 — the clamp MECHANICS (display/-webkit-line-clamp/
   -webkit-box-orient/overflow) moved out to a bare, unscoped
   .product-name/.product-desc rule below (shared by this file's own
   caller - Products/Category/related-products - AND index.php's Featured
   Products, which uses the exact same class names but has no .product-card
   ancestor and its own different, intentionally-kept font-size) - this
   avoids declaring the same 4 clamp properties twice across two pages.
   Only the font-size override stays scoped here, since Shop's own size
   (16px/14px) is genuinely different from Homepage's (17px/15px, kept
   as-is per that task's "giữ nguyên font-size hiện tại" requirement) and
   still needs the .product-card-scoped two-class specificity to reliably
   beat the inline text-[15px]/text-[13px] utility (see the injection-order
   note this comment used to carry in full, condensed here since the root
   cause - Tailwind CDN's stylesheet always injecting last - is unchanged). */
.product-card .product-name {
  font-size: 16px;
}

/* Overrides product-card.php's inline `group-hover:text-forest` Tailwind
   utility (markup is out of scope for this CSS-only task). `.group` is
   the same element as `.product-card` (both classes on one <div>), so
   combining them here reaches (0,3,0) specificity - safely above
   Tailwind's own generated `.group:hover .group-hover\:text-forest`
   rule (0,2,0), for the same injection-order reason noted above. */
.product-card.group:hover .product-name {
  color: #efb73f;
}

.product-card .product-desc {
  font-size: 14px;
}

/* Phase 2.5.1 — shared line-clamp mechanics for every .product-name/
   .product-desc on both Products/Category (includes/partials/
   product-card.php, scoped font-size above) and Homepage Featured
   Products (index.php, its own scoped font-size in css/style.css) -
   deliberately bare/unscoped (not font-size, which differs per page and
   stays separate) so it's declared exactly once for both call sites. */
.product-name {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.product-desc {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.product-card-cta {
  font-size: 14px;
  font-weight: 600;
  color: var(--color-forest);
}

/* P8.3A.5 — .text-forest is a generic Tailwind utility reused sitewide
   (Homepage, site-header, etc. - confirmed via grep), so it is NOT
   overridden globally here. Scoped to product-card price text only
   (includes/partials/product-card.php's `<span class="... text-forest"
   data-product-price>`), rendered by the shared render_product_card()
   component on Product Listing, Category, and Related Products alike -
   intentionally consistent across all three, not a Product-Listing-only
   override. */
.product-card .text-forest {
  color: #f00;
}

/* P4.3 — quick-add-to-cart. .product-card-link carries the flex/height
   behavior .product-card itself used to have back when it was the <a>
   (see product-card.php's header comment for why it's a <div> now). */
.product-card-link {
  display: flex;
  flex-direction: column;
  flex: 1;
  text-decoration: none;
  color: inherit;
}

.product-card-quickadd {
  padding: 0 16px 14px;
}

.product-card-add-btn {
  width: 100%;
  justify-content: center;
}

.shop-state-panel {
  padding: 40px 24px;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  text-align: center;
}

.shop-state-panel h1,
.shop-state-panel h2 {
  margin: 0 0 8px;
}

.shop-state-no-match[hidden] {
  display: none;
}

/* Product Detail (step 2.3) */

.product-detail-grid {
  display: grid;
  /* P8.PRODUCT-R1.3 — grid's own auto minimum-size behavior lets a
     non-wrapping descendant (the gallery thumbs row) push this single
     implicit column wider than the available width below 768px; min-width:0
     is the standard grid/flex fix (same technique already used for the
     breadcrumb overflow, P8.PRODUCT-R1.2) so the column always respects its
     container's width instead of growing to fit content. */
  grid-template-columns: minmax(0, 1fr);
  gap: 32px;
}

@media (min-width: 768px) {
  .product-detail-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: start;
    gap: 48px;
  }
}

.product-gallery-thumbs {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
}

/* P8.PRODUCT-R1.6 — size only, per spec; object-fit (on the img child
   below), active state, click-switch JS (js/shop.js, untouched) all
   unaffected. */
.product-gallery-thumb {
  width: 80px;
  height: 80px;
  padding: 0;
  border-radius: var(--radius-sm);
  border: 2px solid transparent;
  overflow: hidden;
  cursor: pointer;
  background: none;
}

.product-gallery-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.product-gallery-thumb.is-active {
  border-color: var(--color-forest);
}

.product-gallery-thumb:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.product-qty {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--color-line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.product-qty-btn {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-alt);
  border: none;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  color: var(--color-ink);
}

.product-qty-btn:hover {
  background: var(--color-line);
}

.product-qty-btn:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.product-qty-input {
  width: 56px;
  height: 44px;
  border: none;
  border-inline: 1px solid var(--color-line);
  text-align: center;
  /* P8.PRODUCT-R1.6 — per spec. */
  font-size: 16px;
  color: var(--color-ink);
  font-family: inherit;
  -moz-appearance: textfield;
}

.product-qty-input::-webkit-outer-spin-button,
.product-qty-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.product-qty-input:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: -2px;
}

/* P8.3B.2A — price + short description grouped in one soft card
   (product-detail-reference.jpg), same surface-alt card language as
   shop/products.php's sidebar cards (css/shop.css's .shop-sidebar-card). */
.product-price-card {
  background: var(--surface-alt);
  border-radius: var(--radius-md);
  padding: 20px 22px;
}

/* P8.3B.2A-FIX — reference's selling price reads as a deliberate red, not
   the site's forest-green accent; this is the one place that color is
   used (real single price only, still no old-price/discount markup since
   there's no compare-at-price column to back one). */
/* P8.PRODUCT-R1.6 — font-size only, per spec; weight/color/margin
   unchanged. */
.product-price {
  font-size: 32px;
  font-weight: 800;
  color: #C0392B;
  margin: 0 0 8px;
}

/* P8.DATA-2B — effective price + struck regular price + "-N%" badge on one
   wrapping row; .price-was / .price-badge base styles are shared in
   css/style.css (Homepage cards use them too). */
.product-price-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 12px;
  margin: 0 0 8px;
}

.product-price-row .product-price {
  margin: 0;
}

.product-price-was {
  font-size: 16px;
}

.product-short-desc {
  font-size: 14.5px;
  line-height: 1.7;
  color: var(--color-ink-soft);
  margin: 0;
}

/* P8.PRODUCT-R1.6 — "Thêm vào giỏ"/"Mua ngay" CTA typography, scoped to
   .product-info (unique to shop/product.php - confirmed no other page uses
   that class) rather than touching the shared .btn rule other pages/
   surfaces rely on. */
.product-info .btn {
  font-size: 16px;
}

/* P8.PRODUCT-ADMIN-R1.3 — per-axis selector groups, hidden until js/shop.js
   confirms it built a full combination -> variant lookup (.variant-axes-
   mounted, added to the same wrapper the groups live in). A customer with
   JS disabled never sees this - only the always-correct #product-variant-
   fallback below, which stays visible and untouched either way. Same
   convention as .tiptap-editor-mounted + [data-tiptap-fallback] in
   css/admin.css. */
.product-variant-axes {
  display: none;
}

.product-variant-axes.is-mounted {
  display: block;
}

.product-variant-axes.is-mounted + #product-variant-fallback {
  display: none;
}

.product-variant-pill:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.product-variant-pill:disabled:hover {
  border-color: var(--color-line);
}

/* P8.3B.2C-2 — "Thể tích" row, real selectable radio pills once a product
   has active variants (see the PHP-side $productVariants guard in
   shop/product.php). Same outlined-pill visual language as .shop-tag-chip
   / css/pages.css's .pages-quicknav-link, selected state reuses the same
   filled-forest treatment as .shop-tag-chip.is-active. */
.product-variant-row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 0 20px;
}

/* P8.PRODUCT-R1.6 — size/color per spec; layout (flex-shrink) unchanged. */
.product-variant-label {
  font-size: 16px;
  font-weight: 700;
  color: #000;
  flex-shrink: 0;
}

.product-variant-options {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.product-variant-option {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-line);
  /* P8.PRODUCT-R1.6-FIX — font-size only, per spec; pill shape, hover,
     selected/disabled states (below) unchanged. */
  font-size: 16px;
  font-weight: 600;
  color: var(--color-ink);
  cursor: pointer;
  transition: border-color 0.2s ease, background-color 0.2s ease, color 0.2s ease;
}

.product-variant-option:hover {
  border-color: var(--color-forest);
}

.product-variant-option.is-selected {
  background: var(--color-forest);
  border-color: var(--color-forest);
  color: #fff;
}

/* Native radio, kept visible (not visually-hidden) for a plain, always-
   accessible control - just recolored to fit the pill rather than
   removed. */
.product-variant-option input[type="radio"] {
  width: 14px;
  height: 14px;
  accent-color: var(--color-forest);
}

.product-variant-option.is-selected input[type="radio"] {
  accent-color: #fff;
}

/* P8.PRODUCT-R1.6-FIX2 — "Vui lòng chọn đầy đủ tùy chọn sản phẩm.", shown
   instead of the browser's native validation bubble (js/shop.js, form has
   novalidate). R1.6-FIX's version was a bare line of small text - manual
   testing found that too subtle to notice, so this reuses the exact
   background/border-accent language already established for an error
   callout sitewide (.shop-flash-error above: a 16%-tint of --color-clay +
   a clay accent, not a new color), just as a self-contained bordered+
   shadowed box instead of a full-width banner, so it reads with the same
   prominence as the old native bubble without inventing a new visual
   language or using an aggressive solid-red fill. In-flow (not
   position:absolute) on purpose - "compact floating/popup-like" is the
   look, not an actual overlay, so it can never cover another control or
   need overflow/z-index handling at any viewport. js/shop.js moves this
   one element to sit right after the first incomplete axis row before
   revealing it (falls back to right after the whole fallback radio row
   when the axis-pill UI isn't mounted), rather than keeping one copy per
   axis. Hidden by default via the [hidden] attribute (shop/product.php),
   not a class, so no !important is ever needed to win against anything. */
.product-variant-error {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  max-width: 420px;
  margin: 10px 0 0;
  padding: 10px 14px;
  background: rgba(185, 128, 95, 0.16);
  border: 1px solid var(--color-clay);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  font-size: 13.5px;
  line-height: 1.5;
  color: var(--color-ink);
}

.product-variant-error::before {
  content: "⚠";
  flex: none;
  font-size: 15px;
  line-height: 1.4;
  color: var(--color-clay);
}

/* P8.PRODUCT-R1.6-FIX2 — subtle emphasis on whichever axis row the error is
   currently anchored to (task: "subtle visual cue... do not redesign the
   pills... do not use aggressive red backgrounds") - a thin clay-colored
   left border on the row itself, nothing on the pills/options inside it. */
.product-variant-row.has-error {
  border-left: 3px solid var(--color-clay);
  padding-left: 11px;
}

/* P8.3B.2A-FIX — "Số lượng" label to the left of the existing stepper
   (reference's label→control row). */
.product-qty-row {
  display: flex;
  align-items: center;
  gap: 16px;
}

/* P8.PRODUCT-R1.6 — size/color per spec. */
.product-qty-label {
  font-size: 16px;
  font-weight: 700;
  color: #000;
}

/* P8.PRODUCT-R1.6 — font-size only, per spec. */
.product-meta-category {
  margin: 18px 0 0;
  font-size: 16px;
  color: var(--color-ink-soft);
}

.product-meta-category a {
  color: var(--color-forest);
  font-weight: 600;
  text-decoration: none;
}

.product-meta-category a:hover {
  text-decoration: underline;
}

/* Reserved for step 2.7+ (real PDO queries may not resolve synchronously) —
   not rendered today since the mock repository always resolves instantly.
   Kept here so a future latency-aware render doesn't need new CSS. */
.product-card-skeleton {
  background: linear-gradient(90deg, var(--surface-alt) 25%, var(--color-line) 37%, var(--surface-alt) 63%);
  background-size: 400% 100%;
  animation: shop-skeleton-pulse 1.4s ease infinite;
  border-radius: var(--radius-md);
}

@keyframes shop-skeleton-pulse {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .product-card-skeleton { animation: none; }
}

/* Cart (step 2.4) */

.shop-flash {
  padding: 14px 18px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  margin-bottom: 20px;
  /* P15.UI-NOTIFICATION-R1 — subtle fade before js/cart-ui.js's
     initServerFlashes() hides it (display:none, matching the JS timer's
     own 300ms wait) instead of vanishing instantly after its standardized
     4s/5s auto-dismiss window. */
  transition: opacity 0.3s ease;
}
.shop-flash.is-dismissing {
  opacity: 0;
}

.shop-flash-success {
  background: var(--color-sage-light);
  color: var(--color-forest-deep);
}

.shop-flash-error {
  background: rgba(185, 128, 95, 0.16);
  color: var(--color-ink);
  border-left: 3px solid var(--color-clay);
  padding-left: 15px;
}

.shop-cart-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  color: var(--color-ink);
}

.shop-cart-link:hover {
  color: var(--color-forest);
}

.shop-cart-badge {
  position: absolute;
  top: 2px;
  right: 2px;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-full);
  background: var(--color-forest);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
}

.cart-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px;
  padding: 20px 0;
  border-bottom: 1px solid var(--color-line);
}

.cart-line-thumb {
  width: 72px;
  height: 72px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  flex-shrink: 0;
  display: block;
}

.cart-line-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.cart-line-info {
  flex: 1 1 160px;
  min-width: 160px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.cart-line-name {
  font-weight: 600;
  font-size: 14.5px;
  color: var(--color-ink);
  text-decoration: none;
}

.cart-line-name:hover {
  color: var(--color-forest);
}

/* P8.3B.2C-2 — variant label, shown only when the line has one. */
.cart-line-variant {
  font-size: 12.5px;
  color: var(--color-ink-soft);
  margin: 0;
}

.cart-line-unit-price {
  font-size: 13px;
  color: var(--color-ink-soft);
  margin: 0;
}

.cart-line-qty-form {
  display: flex;
  align-items: center;
  gap: 10px;
}

.cart-line-update-btn {
  height: 44px;
  padding: 0 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
  background: var(--surface-raised);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-ink);
  cursor: pointer;
}

.cart-line-update-btn:hover {
  border-color: var(--color-forest);
  color: var(--color-forest);
}

.cart-line-update-btn:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.cart-line-total {
  font-weight: 700;
  font-size: 15px;
  min-width: 90px;
  text-align: right;
  margin: 0;
}

.cart-line-remove-btn {
  height: 44px;
  padding: 0 14px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
  background: none;
  font-size: 13px;
  color: var(--color-ink-soft);
  cursor: pointer;
}

.cart-line-remove-btn:hover {
  border-color: var(--color-clay);
  color: var(--color-clay);
}

.cart-line-remove-btn:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.cart-summary {
  margin-top: 24px;
  padding: 24px;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
  max-width: 380px;
  margin-left: auto;
}

.cart-summary-row {
  display: flex;
  justify-content: space-between;
  font-size: 14px;
  color: var(--color-ink-soft);
  margin-bottom: 10px;
}

.cart-summary-total {
  font-size: 18px;
  font-weight: 700;
  color: var(--color-ink);
  margin-bottom: 20px;
}

/* Checkout (step 2.5) */

.checkout-grid {
  display: grid;
  gap: 32px;
}

@media (min-width: 768px) {
  .checkout-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 380px);
    align-items: start;
  }
}

.checkout-form {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.checkout-field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  border: none;
  padding: 0;
  margin: 0;
}

.checkout-field label,
.checkout-field legend {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-ink);
  padding: 0;
}

.checkout-field input[type="text"],
.checkout-field input[type="tel"],
.checkout-field input[type="email"],
.checkout-field textarea {
  padding: 12px 16px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
  background: var(--surface-raised);
  font-size: 14px;
  font-family: inherit;
  color: var(--color-ink);
  resize: vertical;
}

.checkout-field input:focus-visible,
.checkout-field textarea:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 1px;
}

.checkout-field.has-error input,
.checkout-field.has-error textarea {
  border-color: var(--color-clay);
}

.checkout-field-error {
  font-size: 12.5px;
  /* var(--color-clay) itself measures ~3.18:1 on the page background at
     this size - fails WCAG AA (needs 4.5:1), same class of issue already
     documented for --hover-accent (design tokens tuned for accents/dark
     backgrounds aren't automatically safe as small text on light ones).
     This darkened one-off (~6.5:1) is scoped to this single selector, not
     a new sitewide token. */
  color: #78533E;
  margin: 0;
}

.checkout-radio {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 14px;
  font-weight: 400;
  color: var(--color-ink);
  margin-top: 8px;
}

.checkout-summary {
  padding: 24px;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}

.checkout-summary-line {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-line);
}

.checkout-summary-line-thumb {
  width: 48px;
  height: 48px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  flex-shrink: 0;
}

.checkout-summary-line-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.checkout-summary-line-name {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-ink);
}

.checkout-summary-line-variant {
  font-size: 12px;
  color: var(--color-ink-soft);
}

.checkout-summary-line-qty {
  font-size: 12.5px;
  color: var(--color-ink-soft);
}

.checkout-summary-line-total {
  font-size: 13.5px;
  font-weight: 700;
  color: var(--color-ink);
  white-space: nowrap;
}

/* Ecommerce Upgrade — bank-transfer payment popup (shop/checkout.php,
   shop/order-success.php). .is-open toggled by js/checkout-payment-popup.js,
   same idiom as .mobile-menu/.is-open and openHeaderSearch()/.hidden
   elsewhere in this project - no new show/hide convention invented.
   z-index: 60 matches .site-toast (css/style.css) - the one existing
   fixed-overlay precedent in this codebase, confirmed sitting above the
   header. --shadow-lg is css/style.css's own documented "overlay panels"
   tier, --radius-md the same corner radius every other raised panel here
   already uses - no new token invented either. */
.payment-popup {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 20px;
  background: rgba(27, 27, 24, 0.45);
}

.payment-popup.is-open {
  display: flex;
}

.payment-popup-panel {
  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 40px);
  overflow-y: auto;
  background: var(--surface-raised);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: 28px;
}

.payment-popup-close {
  position: absolute;
  top: 10px;
  right: 10px;
  width: 32px;
  height: 32px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  font-size: 22px;
  line-height: 1;
  color: var(--color-ink-soft);
  cursor: pointer;
}

.payment-popup-title {
  font-size: 18px;
  font-weight: 700;
  color: var(--color-ink);
  margin: 0 0 16px;
  padding-right: 28px;
}

.payment-popup-details {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 20px;
}

.payment-popup-details > div {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: 14px;
}

.payment-popup-details dt {
  color: var(--color-ink-soft);
  flex-shrink: 0;
}

.payment-popup-details dd {
  margin: 0;
  font-weight: 600;
  color: var(--color-ink);
  text-align: right;
}

.payment-popup-note {
  font-size: 12.5px;
  color: var(--color-ink-soft);
  margin: 0 0 16px;
}

.payment-popup-qr {
  text-align: center;
}

.payment-popup-qr img {
  max-width: 220px;
  width: 100%;
  border-radius: var(--radius-sm);
}

.payment-popup-reopen {
  font-size: 13px;
  font-weight: 600;
  color: var(--color-forest);
  background: none;
  border: none;
  padding: 0;
  margin-left: 8px;
  cursor: pointer;
  text-decoration: underline;
}

/* Mobile: bottom-sheet instead of a centered card - full width, rounded
   top corners only, anchored to the bottom of the viewport. */
@media (max-width: 479px) {
  .payment-popup {
    align-items: flex-end;
    padding: 0;
  }

  .payment-popup-panel {
    max-width: 100%;
    max-height: 85vh;
    border-radius: var(--radius-md) var(--radius-md) 0 0;
  }
}

/* Order Success (step 2.6) */

.order-success-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  padding: 20px 24px;
  border-radius: var(--radius-md);
  background: var(--surface-alt);
}

.order-success-meta > div {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.order-success-meta-label {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-ink-soft);
}

.order-success-meta-value {
  font-size: 15px;
  font-weight: 700;
  color: var(--color-ink);
}

.order-success-details > div {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-line);
  margin: 0;
}

.order-success-details dt {
  font-size: 13.5px;
  color: var(--color-ink-soft);
}

.order-success-details dd {
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-ink);
  text-align: right;
  margin: 0;
}

/* P8.3B.2A-FIX — Product Detail tab wrapper (shop/product.php). Two real
   tabs now ("Thông tin sản phẩm" + "Review", js/shop.js's
   [data-product-tabs] switcher); the bottom-border active indicator
   reuses the same visual language as the site header's active nav
   underline (css/style.css), just scoped here instead of touching that
   shared rule. */
.product-tabs {
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--surface-raised);
}

.product-tabs-nav {
  display: flex;
  border-bottom: 1px solid var(--color-line);
  background: var(--surface-alt);
}

.product-tab {
  padding: 15px 26px;
  /* P8.PRODUCT-R1.6 — font-size only, per spec. */
  font-size: 16px;
  font-weight: 700;
  color: var(--color-ink-soft);
  background: none;
  border: none;
  font-family: inherit;
  cursor: pointer;
  transition: color 0.2s ease, background-color 0.2s ease;
}

.product-tab:hover {
  color: var(--color-forest);
}

.product-tab:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: -2px;
}

.product-tab.is-active {
  color: var(--color-forest);
  background: var(--surface-raised);
  box-shadow: inset 0 -2px 0 var(--color-forest);
}

.product-tab-panel {
  padding: 24px 20px;
}

@media (min-width: 768px) {
  .product-tab-panel {
    padding: 32px 36px;
  }
}

.product-review-empty {
  font-size: 14.5px;
  color: var(--color-ink-soft);
  margin: 0;
}

/* P8.DATA-5 — product-wide specifications (shop/product.php). A quiet
   label / value list, no card chrome: it only appears when the admin has
   assigned real attribute values. */
.product-specs-title {
  font-size: 18px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 12px;
}

.product-specs-list {
  margin: 0;
  border-top: 1px solid var(--color-line);
}

.product-specs-row {
  display: grid;
  grid-template-columns: minmax(96px, 30%) 1fr;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--color-line);
  font-size: 14.5px;
}

.product-specs-row dt {
  color: var(--color-ink-soft);
}

.product-specs-row dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Phase 6.4 — Product full description (shop/product.php), rendered via
   render_rich_content(). Same rule shape as css/pages.css's .pages-article
   (not loaded on shop/*.php pages, so duplicated here scoped to this one
   new class rather than adding a cross-stylesheet dependency).
   P8.PRODUCT-R1.4 — .product-full-description is the SAME element as
   .product-tab-panel (shop/product.php's info panel carries both classes on
   one <div>), so the fix must not put a width/margin rule on this class
   itself - that would shrink the panel's own box (background/border/
   padding), not just its text, breaking "keep the existing tab container
   width unchanged". Instead this targets the panel's rendered children
   (the p/h2/h3/ul/ol render_rich_content() actually outputs) so the panel
   box stays full width while its content reads as a centered column inside
   it. The old flat 760px max-width (no centering) is what produced the
   reported narrow, left-heavy look. Below 768px (same breakpoint
   .product-tab-panel's own padding already uses) content stays full-width,
   matching mobile's existing behavior exactly; at and above it, each child
   is ~88% of the panel's available width (capped so it never gets
   uncomfortably wide on very large screens) and centered, so the leftover
   space is split evenly left/right instead of pooling on the right. The
   width/centering rule itself is declared further below (after every
   element-specific rule in this group), not here - h2/h3/p/ul/ol below all
   set their own `margin: <top> 0 <bottom>` shorthand at equal selector
   specificity, which would silently zero out a centering margin declared
   only here, earlier in the cascade; declaring it later, at the same
   specificity, is what lets it actually win for the left/right margin
   while leaving each element's own top/bottom spacing untouched. */
.product-full-description > * {
  max-width: 100%;
}

.product-full-description h2,
.product-full-description h3 {
  font-weight: 700;
  color: var(--color-ink);
  margin: 32px 0 12px;
}

.product-full-description h2:first-child,
.product-full-description h3:first-child {
  margin-top: 0;
}

.product-full-description h2 { font-size: 20px; }
.product-full-description h3 { font-size: 17px; }
/* P8.PRODUCT-ADMIN-R1.5 — H4-H6, same spacing/first-child rule shape as h2/
   h3 above, scaled down from h3's 17px in small steps since these are meant
   for deeper sub-sections, not more emphasis than h3. */
.product-full-description h4,
.product-full-description h5,
.product-full-description h6 {
  font-weight: 700;
  color: var(--color-ink);
  margin: 32px 0 12px;
}
.product-full-description h4:first-child,
.product-full-description h5:first-child,
.product-full-description h6:first-child {
  margin-top: 0;
}
.product-full-description h4 { font-size: 16px; }
.product-full-description h5 { font-size: 15px; }
.product-full-description h6 { font-size: 15px; }

/* P8.PRODUCT-R1.6 — default (no explicit mark) paragraph size/color only;
   an explicit inline font-size/color from the Rich Text Editor
   (P8.PRODUCT-ADMIN-R1.5's <span style="...">, rendered BY the text node
   itself, inside this <p>) always wins over this via plain CSS inline-style
   precedence - no !important needed, and none used. H2-H6/alignment/links/
   lists/sup/sub/tables/images are untouched - none of those rules are
   touched by this change. */
.product-full-description p {
  font-size: 16px;
  line-height: 1.75;
  color: #000;
  margin: 0 0 16px;
}

.product-full-description ul,
.product-full-description ol {
  margin: 0 0 16px;
  padding-left: 20px;
  font-size: 15px;
  line-height: 1.75;
  color: var(--color-ink-soft);
}

.product-full-description li {
  margin-bottom: 6px;
}

/* P8.PRODUCT-ADMIN-R1.5-FIX — underline added so a link is visibly a link
   at rest (task section 7 minimum: "underline or clearly distinct existing
   style"), not just a different-colored word within body-copy-colored
   text. Color unchanged (already the site's existing link color here). */
.product-full-description a {
  color: var(--color-forest);
  text-decoration: underline;
}

/* P8.PRODUCT-ADMIN-R1.5 — table. Deliberately NOT added to the ">h2/h3/.../
   ol" centering selector list in the @media block below (task: "do not
   modify the Product Detail content-width rules") - this wrapper handles
   its own width safety independently: overflow-x:auto lets a wide table
   scroll within itself at any viewport (390px included) instead of ever
   forcing the whole page to overflow, which is all section 6 of this task
   asks for. */
.rich-table-wrap {
  max-width: 100%;
  overflow-x: auto;
  margin: 0 0 16px;
}

.rich-table-wrap table {
  width: 100%;
  border-collapse: collapse;
  font-size: 15px;
  color: var(--color-ink-soft);
}

.rich-table-wrap td,
.rich-table-wrap th {
  border: 1px solid var(--color-line);
  padding: 8px 12px;
  text-align: left;
}

.rich-table-wrap th {
  background: var(--color-bg-alt);
  color: var(--color-ink);
  font-weight: 600;
}

/* P8.PRODUCT-R1.4 — see the comment on .product-full-description > * above:
   deliberately placed after every element-specific rule in this group, at
   the same ".product-full-description > <type>" specificity as their own
   `margin: <top> 0 <bottom>` shorthand, so only the left/right margin here
   wins (centering) while each element's own top/bottom spacing from the
   rules above is untouched. Below 768px (same breakpoint .product-tab-
   panel's own padding already uses) nothing here applies, so content stays
   full-width, matching mobile's existing behavior exactly. */
@media (min-width: 768px) {
  .product-full-description > h2,
  .product-full-description > h3,
  .product-full-description > h4,
  .product-full-description > h5,
  .product-full-description > h6,
  .product-full-description > p,
  .product-full-description > ul,
  .product-full-description > ol {
    /* No extra max-width cap needed: .container (css/style.css) already
       caps the whole page at 1240px, so this 88% can never itself grow
       past ~957px even at very large viewports - adding a second, lower
       cap here (tried during development) just pulled the ratio down
       toward the bottom edge of "roughly 80-90%" on wide screens instead
       of helping. */
    width: 88%;
    margin-left: auto;
    margin-right: auto;
  }
}

/* P8.PRODUCT-R1.6 — "Sản phẩm cùng loại" (Related Products) section title,
   shop/product.php only. Was the Tailwind utility text-[22px]; replaced
   (not layered) with this scoped class so the intended 26px is never a
   specificity gamble against Tailwind's CDN-injected utility CSS, and
   nothing here touches .text-\[26px\]/.text-\[22px\] themselves (neither
   is used anywhere else on this page). */
.product-related-title {
  font-size: 26px;
}

/* P4.2 — All-products listing (shop/products.php). Scoped .shop-* rules
   only; reuses .container/.btn/.trust-icon/.reveal from css/style.css and
   .shop-toolbar/.shop-sort/.shop-filter/.shop-result-count/.shop-breadcrumb
   already above in this file. */

/* P16.PAGE-HERO-MANAGEMENT-R1 — base geometry (position/min-height/flex
   centering/overflow/color/text-align) now lives in the shared
   .hero-section class (css/style.css), added alongside this class in the
   markup (shop/products.php, shop/product.php, shop/category.php);
   .shop-hero-media/-scrim/-content/-breadcrumb below remain fully
   shop-scoped and untouched. */
.shop-hero-media {
  position: absolute;
  inset: 0;
}

.shop-hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.shop-hero-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(22, 36, 28, 0.24), rgba(22, 36, 28, 0.5));
}

.shop-hero-content {
  position: relative;
  z-index: 1;
  padding: 56px 24px 28px;
}

.shop-hero-content h1 {
  font-size: 30px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin-bottom: 16px;
}

/* P16.PAGE-HERO-MANAGEMENT-R1 — old 400px desktop step retired (.hero-
   section is flat 500px everywhere); only the h1 size bump stays here. */
@media (min-width: 768px) {
  .shop-hero-content h1 {
    font-size: 42px;
  }
}

.shop-hero-breadcrumb {
  display: inline-flex;
  background: rgba(255, 255, 255, 0.12);
  border: 1px solid rgba(255, 255, 255, 0.25);
  border-radius: var(--radius-full);
  padding: 8px 18px;
  margin: 0;
}

.shop-hero-breadcrumb ol {
  color: rgba(255, 255, 255, 0.85);
}

.shop-hero-breadcrumb a {
  color: #fff;
}

.shop-hero-breadcrumb a:hover {
  color: var(--hover-accent);
}

.shop-hero-breadcrumb .shop-breadcrumb-sep {
  color: rgba(255, 255, 255, 0.5);
}

/* P8.3B.2A — Product Detail hero carries breadcrumb only, no <h1> inside it
   (the real title renders below in .product-info), and its breadcrumb can
   carry a long product name as its last segment - .shop-hero-breadcrumb-
   long (below) still exists for that overflow handling.
   P16.PAGE-HERO-MANAGEMENT-R1 — .shop-hero-compact (previously 160px
   mobile / 250px desktop, shorter than every other inner-page Hero by
   deliberate prior design choice) is retired: Product Detail now shares
   the same flat 500px .hero-section height as Products Listing/Category,
   per the approved height requirement. The class is removed from
   shop/product.php's markup; nothing else about Product Detail changed. */

/* P8.PRODUCT-R1.2 fix — the truncation must live on the last segment's
   INNER element (the <span>/<a> that actually holds the text), not the
   <li> itself: <li> here is both a flex item (.shop-breadcrumb ol is
   display:flex) and a list-item box, and that combination does not
   reliably clip an inline child's overflow via text-overflow across
   browsers - the <li> got overflow:hidden/ellipsis before, but the long
   product name still visually overflowed because the inline <span> inside
   it was never itself constrained (display:inline + white-space:nowrap +
   overflow:visible), so it rendered at full width regardless of its
   parent's clip box. Splitting the max-width (on <li>, sets the ceiling)
   from the overflow/ellipsis (on the child, does the actual clipping) is
   the standard robust pattern. */
.shop-hero-breadcrumb-long li:last-child {
  max-width: 60vw;
  min-width: 0;
}

.shop-hero-breadcrumb-long li:last-child > * {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

@media (max-width: 640px) {
  .shop-hero-breadcrumb-long li:last-child {
    max-width: 46vw;
  }
}

/* P8.PRODUCT-R1.2 — same fix, scoped to shop/category.php's plain (non-hero)
   breadcrumb via its own modifier class (set only there), so cart.php/
   checkout.php/order-success.php - which share the base .shop-breadcrumb
   class but always render a short, static last segment - are completely
   untouched. */
.shop-breadcrumb-category li:last-child {
  max-width: 70vw;
  min-width: 0;
}

.shop-breadcrumb-category li:last-child > * {
  display: inline-block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: bottom;
}

@media (max-width: 640px) {
  .shop-breadcrumb-category li:last-child {
    max-width: 55vw;
  }
}

/* P17.UI-CSS-REFINEMENT-R1 — Product Listing only (shop/products.php).
   Doubled-class selectors beat the same-specificity Tailwind utilities
   already on these elements (gap-8, pt-10/pb-20 via page-main-open.php's
   optional $pageMainExtraClass, mb-6) - same established trick used
   elsewhere in this project (see css/style.css's .bg-forest-deep.bg-
   forest-deep). Scoped to these 3 classes only, so Product Detail/Cart/
   Checkout/Category (none of which render shop-products-main/-layout/
   -toolbar-products) are structurally unreachable by this rule. */
.shop-products-main.shop-products-main {
  padding-top: 3.5rem;
  padding-bottom: 2rem;
}

.shop-products-layout.shop-products-layout {
  gap: 3.5rem;
}

.shop-toolbar-products.shop-toolbar-products {
  margin-bottom: 2rem;
}

.shop-toolbar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: center;
}

/* 3 columns desktop / 2 tablet / 1 mobile (P8.3A - was 2/2 below 1024,
   which was too cramped at ~165px/card at 390px; single column at the
   base breakpoint per the approved product-page-reference.jpg) —
   deliberately its own scale (not .category-product-grid's 2/3/4) since
   this grid shares its row with a ~25%-width sidebar at desktop, not the
   full container. */
.shop-products-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 20px;
}

@media (min-width: 480px) {
  .shop-products-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
  }
}

@media (min-width: 640px) {
  .shop-products-grid {
    column-gap: 24px;
    row-gap: 32px;
  }
}

@media (min-width: 1024px) {
  /* P8.3A — vertical gap deliberately larger than horizontal (was one
     uniform 20px both ways), per docs/reference/product-page-reference.jpg's
     denser row separation. P17.UI-CSS-REFINEMENT-R1 — column-gap 24->30px
     per spec; row-gap already matched the spec's 36px, unchanged. */
  .shop-products-grid {
    grid-template-columns: repeat(3, 1fr);
    column-gap: 30px;
    row-gap: 36px;
  }
}

.shop-sidebar {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.shop-sidebar-card {
  border: 1px solid var(--color-line);
  border-radius: var(--radius-md);
  padding: 20px;
  background: var(--surface-alt);
}

/* P8.3A.4 — explicit sidebar type-scale bump per approved spec. 24px is
   comfortably readable down to 390px in these short 1-3 word headings
   ("Lọc theo giá", "Danh mục sản phẩm", "Sản phẩm nổi bật"), so no mobile
   override was needed after checking 390 - kept as a documented decision
   here rather than adding an unused media query. */
.shop-sidebar-title {
  font-size: 24px;
  font-weight: 700;
  margin: 0 0 14px;
}

/* P8.3A.1 — search moved here from the toolbar (same #shop-filter-input
   element, just relocated); orange background per the approved
   product-page-reference.jpg, reusing --hover-accent (already an
   established token - the site's existing amber/gold accent, used for
   hover states elsewhere) rather than introducing a new brand color. */
.shop-sidebar-search-card {
  background: var(--hover-accent);
  border-color: var(--hover-accent);
  padding: 16px;
}

.shop-sidebar-search-card .shop-sidebar-title {
  color: var(--color-forest-deep);
}

.shop-sidebar-search-card .shop-filter input {
  width: 100%;
  min-width: 0;
  background: var(--surface-raised);
  border-color: rgba(22, 36, 28, 0.15);
}

/* Price filter - two plain number inputs (real min/max from active
   product prices, computed in shop/products.php), not a fabricated
   slider control. */
.shop-price-filter-row {
  display: flex;
  align-items: center;
  gap: 10px;
}

.shop-price-filter-row input[type="number"] {
  width: 0;
  flex: 1;
  height: 44px;
  padding: 0 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-line);
  background: var(--surface-raised);
  font-size: 13.5px;
  color: var(--color-ink);
  font-family: inherit;
  -moz-appearance: textfield;
}

.shop-price-filter-row input[type="number"]::-webkit-outer-spin-button,
.shop-price-filter-row input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.shop-price-filter-row input[type="number"]:focus-visible {
  outline: 2px solid var(--color-forest);
  outline-offset: 2px;
}

.shop-price-filter-sep {
  color: var(--color-ink-soft);
  flex-shrink: 0;
}

.shop-price-filter-hint {
  margin: 10px 0 0;
  font-size: 12.5px;
  color: var(--color-ink-soft);
}

.shop-sidebar-category-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.shop-sidebar-category-link {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 6px;
  border-radius: var(--radius-sm);
  font-size: 16px;
  color: var(--color-ink);
  text-decoration: none;
  transition: background-color 0.2s ease, color 0.2s ease;
}

.shop-sidebar-category-link:hover {
  background: var(--surface-alt);
  color: #efb73f;
}

/* P8.PRODUCT-R1.2 — current-category state, shop/category.php's sidebar
   list. Same filled-forest/white language as .category-quicknav-link.
   is-active (css/shop.css above) rather than inventing a new treatment. */
.shop-sidebar-category-link.is-active {
  background: var(--color-forest);
  color: #fff;
}

.shop-sidebar-category-link.is-active:hover {
  background: var(--color-forest);
  color: #fff;
}

.shop-sidebar-category-link.is-active .shop-sidebar-category-count {
  color: rgba(255, 255, 255, 0.75);
}

.shop-sidebar-category-count {
  font-size: 12.5px;
  color: var(--color-ink-soft);
}

/* P8.3A.2 — outlined compact chips, same language as
   css/pages.css's .pages-quicknav-link (border+radius-full pill, filled
   forest on active state) rather than the orange search-card treatment. */
.shop-tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.shop-tag-chip {
  display: inline-flex;
  align-items: center;
  padding: 6px 14px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-line);
  font-size: 13px;
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
  transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}

.shop-tag-chip:hover {
  border-color: var(--color-forest);
  color: var(--color-forest);
}

.shop-tag-chip.is-active {
  background: var(--color-forest);
  border-color: var(--color-forest);
  color: #fff;
}

.shop-tag-clear {
  display: inline-block;
  margin-top: 12px;
  font-size: 12.5px;
  color: var(--color-ink-soft);
  text-decoration: underline;
}

.shop-tag-clear:hover {
  color: var(--color-forest);
}

/* P8.3A.4 — Product Listing pagination. Scoped .shop-pagination rather
   than reusing css/pages.css's .pages-pagination (that stylesheet isn't
   loaded on shop/*.php pages - see the file-scoping note at the top of
   this file), same pill language as .shop-tag-chip. */
.shop-pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 32px;
}

.shop-pagination-page,
.shop-pagination-prev,
.shop-pagination-next {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 38px;
  height: 38px;
  padding: 0 12px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-line);
  font-size: 13.5px;
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
  justify-content: center;
  transition: border-color 0.2s ease, color 0.2s ease, background-color 0.2s ease;
}

.shop-pagination-page {
  padding: 0;
}

a.shop-pagination-page:hover,
.shop-pagination-prev:hover,
.shop-pagination-next:hover {
  border-color: var(--color-forest);
  color: var(--color-forest);
}

.shop-pagination-page.is-active {
  background: var(--color-forest);
  border-color: var(--color-forest);
  color: #fff;
}

.shop-sidebar-featured-list {
  display: flex;
  flex-direction: column;
  gap: 30px;
}

.shop-sidebar-featured-item {
  display: flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
}

.shop-sidebar-featured-item img {
  width: 56px;
  height: 56px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  background: var(--surface-alt);
  flex-shrink: 0;
}

.shop-sidebar-featured-text {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.shop-sidebar-featured-name {
  font-size: 16px;
  font-weight: 500;
  color: var(--color-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
}

.shop-sidebar-featured-price {
  font-size: 16px;
  font-weight: 700;
  color: #f00;
}

/* P8.DATA-7 — compact physical specs in the Product Detail info column
   (shop/product.php). A quiet label / value list, only present when the
   product or a variant has physical data; js/shop.js swaps the rows when the
   variant changes. Scoped here so the P8.DATA-5 .product-specs list (a
   different, full-width section) is untouched. */
.product-physical {
  margin: 24px 0 20px;
}

.product-physical-title {
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.01em;
  margin: 0 0 8px;
}

.product-physical-list {
  margin: 0;
  border-top: 1px solid var(--color-line);
}

.product-physical-row {
  display: grid;
  grid-template-columns: minmax(120px, 42%) 1fr;
  gap: 12px;
  padding: 9px 0;
  border-bottom: 1px solid var(--color-line);
  font-size: 14px;
}

.product-physical-row dt {
  color: var(--color-ink-soft);
}

.product-physical-row dd {
  margin: 0;
  min-width: 0;
  overflow-wrap: anywhere;
}
