/* bk-marketplace.css — Bookku cross-tenant marketplace (Phase 1 UAT)
   Frontend lane, mra-marketplace-phase1.

   DESIGN.md sections implemented:
     §1 / §2.2  — cool-tinted white canvas (--bk-surface-0 ≈ #F7F9FC), NOT pure #FFF / warm-white
     §2.1       — trust-blue brand axis via --brand-primary; all accents cascade
     §2.3 / §2.7— ink hierarchy + contrast floors (price/name on ink, muted meta ≥4.5:1)
     §2.4       — semantic soft fills for the trust chip (success-soft / neutral)
     §3.1 / §3.2— editorial pairing (Fraunces display on hero title, Plus Jakarta body)
     §3.4 / §3.6— tabular price digits, ฿ prefix (via --price-font, mirrors storefront)
     §4.5       — button states (hover / focus-visible / active), 44px touch
     §6.1       — three-tier brand-mixed elevation (--bk-elev-*)
     §6.4       — semantic z-index ladder (--z-sticky for the sticky search bar)
     §8.5       — intrinsic responsive grid repeat(auto-fill, minmax()) — mobile-first
     §1 ban     — no side-stripe borders, no 1px-border+heavy-shadow pairing, radius ≤18px
     §4.4       — auth-modal dialog motion (zoom-in-95 + fade, spring easing; transform/opacity only)
     §6.4       — z-index ladder for the modal (--z-modal-backdrop 1200 / --z-modal 1300)
     §4.14      — modal is fixed-position (never absolute-in-overflow) — overlay clipping ban
     §8.2       — 44×44 touch targets on modal close + logout + OTP controls
     §8.3       — reduced-motion block (collapses the modal zoom + all page motion)
     §11.2 note — APEX chrome stays Bookku-blue (the §11.2 tenant-cascade rule governs
                  CUSTOMER auth inside a tenant shop.html, NOT this cross-tenant apex)

   This is a STANDALONE page (backend serves it at GET /marketplace). It re-uses
   the storefront card visual language (.prod-card: full-bleed rounded .img +
   .meta with .name + .prod-price-row pill-left/price-right) so it reads native,
   but ships its own scoped tokens rather than pulling the 8k-line template-coupled
   bk-shop.css. Tokens are copied verbatim from bk-shop.css :root so hues match. */

/* ── Marketplace v2 (Wave B, 2026-07-02) — cream/cognac scoped palette ──────
   This file is now the SOURCE OF TRUTH for marketplace hues (it no longer
   mirrors bk-shop.css trust-blue). Ported VERBATIM from the pk-approved mock
   (docs/active/mocks/grid.html:22-69). Marketplace-scoped divergence from
   DESIGN.md — a deliberate warm-luxury atmosphere (MARKETPLACE_V2_DESIGN
   §PK DECISIONS / §invariants). Back-compat aliases below keep the 900 legacy
   downstream lines working while sections are re-skinned. */
:root {
  /* ── canvas + surfaces — §2.2 inversion preserved: surface-1/2 LIGHTER than canvas ── */
  --mkt-canvas:        #F5F0E6;
  /* neutral mixes in SRGB — oklch drifts near-neutrals pink in Chrome/Safari<16.4.
     saturated derivations (cognac/green/amber) stay oklch. */
  --mkt-surface-1:     color-mix(in srgb, #FFFFFF 45%, var(--mkt-canvas));
  --mkt-surface-2:     color-mix(in srgb, #FFFFFF 72%, var(--mkt-canvas));
  --mkt-surface-inset: color-mix(in srgb, var(--mkt-ink) 5%, var(--mkt-canvas));
  /* ── ink — espresso ladder ── */
  --mkt-ink:           #221A14;
  --mkt-ink-secondary: #3B2B20;
  --mkt-ink-muted:     #6E5A49;
  --mkt-line:          color-mix(in srgb, var(--mkt-ink) 14%, var(--mkt-canvas));
  --mkt-line-soft:     color-mix(in srgb, var(--mkt-ink) 8%,  var(--mkt-canvas));
  /* ── single action accent — cognac (blue retired on this surface) ── */
  --mkt-accent:        #8A5A2B;
  --mkt-accent-ink:    #FFFFFF;
  --mkt-accent-hover:  color-mix(in oklch, var(--mkt-accent) 88%, black);
  --mkt-accent-active: color-mix(in oklch, var(--mkt-accent) 78%, black);
  --mkt-accent-soft:   color-mix(in srgb, var(--mkt-accent) 10%, var(--mkt-canvas));
  --mkt-accent-ring:   color-mix(in oklch, var(--mkt-accent) 45%, transparent);
  /* ── verified — mapped 1:1 from DESIGN.md §2.4, NEVER re-hued ── */
  --bk-success:        #15803D;
  --mkt-success-soft:  color-mix(in srgb, var(--bk-success) 8%, var(--mkt-canvas));
  --mkt-success-deep:  color-mix(in oklch, var(--bk-success) 72%, var(--mkt-ink));
  /* ── featured (paid placement) — amber, tuned to cream; ≠ verified green ── */
  --mkt-featured:      #8F6400;
  --mkt-featured-soft: color-mix(in srgb, var(--mkt-featured) 12%, var(--mkt-canvas));
  /* ── sale/promo (Wave A 2026-07-04) — a restrained red tuned to the cream
       canvas, distinct from cognac accent + amber featured; ≥4.5:1 on canvas.
       Used ONLY for a discounted card's promo price (struck original stays in
       muted ink). Never applied to a masked/contact figure. ── */
  --mkt-sale:          #B42318;
  /* ── radius — mapped 1:1 §4.0 ── */
  --bk-radius:12px; --bk-radius-sm:6px; --bk-radius-md:10px;
  --bk-radius-lg:12px; --bk-radius-xl:18px; --bk-radius-pill:999px;
  /* ── elevation — §6.1 pattern, tinted with the surface accent (cognac) ── */
  --mkt-elev-card:
    0 1px 2px 0  color-mix(in oklch, var(--mkt-accent) 7%, transparent),
    0 4px 14px 0 color-mix(in oklch, var(--mkt-accent) 9%, transparent);
  --mkt-elev-float:
    0 2px 6px 0   color-mix(in oklch, var(--mkt-accent) 9%,  transparent),
    0 8px 24px 0  color-mix(in oklch, var(--mkt-accent) 12%, transparent),
    0 24px 48px 0 color-mix(in oklch, var(--mkt-accent) 7%,  transparent);
  /* ── motion — mapped 1:1 §4.4 ── */
  --bk-ease-spring:cubic-bezier(.34,1.56,.64,1);
  --bk-ease-standard:cubic-bezier(.2,0,0,1);
  --bk-dur-enter:200ms; --bk-dur-exit:140ms;
  /* ── z ladder — §6.4. KEEP --z-dropdown 1200 for the filter listbox (custom mkDD). ── */
  --z-sticky:1100; --z-dropdown:1200; --z-float:1300;
  /* ── type — §3.1 luxury template. Cormorant Garamond display face. ── */
  --font-display:'Cormorant Garamond','Sarabun',Georgia,serif;
  /* Thai display face — §3.1 CRITICAL: Cormorant has NO Thai glyphs, so Thai
     display text (e.g. the Wave C1 hero headline) MUST NOT be set in Cormorant.
     Sarabun/Noto Sans Thai carry the Thai display type instead. */
  --font-thai-display:'Sarabun','Noto Sans Thai',system-ui,-apple-system,sans-serif;
  --font-body:'Plus Jakarta Sans','Noto Sans Thai',system-ui,-apple-system,sans-serif;
  --price-font: var(--font-body);   /* price uses tabular Plus Jakarta, NOT Bebas (see §3) */
  --container-w:1360px;

  /* ── back-compat aliases (legacy downstream selectors still reference these;
       renamed opportunistically as each section is re-skinned) ── */
  --brand-primary:       var(--mkt-accent);
  --brand-primary-ink:   var(--mkt-accent-ink);
  --brand-primary-hover: var(--mkt-accent-hover);
  --brand-primary-active:var(--mkt-accent-active);
  --brand-primary-soft:  var(--mkt-accent-soft);
  --brand-primary-tint:  var(--mkt-accent-soft);
  --brand-primary-ring:  var(--mkt-accent-ring);
  --accent:              var(--mkt-accent);
  --bk-surface-0:        var(--mkt-canvas);
  --bk-surface-1:        var(--mkt-surface-1);
  --bk-surface-2:        var(--mkt-surface-2);
  --bk-surface-inset:    var(--mkt-surface-inset);
  --bk-ink:              var(--mkt-ink);
  --bk-ink-muted:        var(--mkt-ink-muted);
  --bk-ink-subtle:       var(--mkt-ink-muted);
  --text:                var(--mkt-ink);
  --text-mute:           var(--mkt-ink-muted);
  --border:              var(--mkt-line);
  --bk-success-soft:     var(--mkt-success-soft);
  --bk-elev-base:        0 1px 0 0 color-mix(in oklch, var(--mkt-accent) 4%, transparent);
  /* Legacy elevation names → the cognac --mkt-elev-* values above. Five
     selectors (.mk-dd-list / .mk-shop-strip / .mk-modal / .mk-pdp-main /
     .mk-pdp-shopcard) still consume these; without the alias their box-shadow
     computed to `none` (Wave B regression fix). */
  --bk-elev-card:        var(--mkt-elev-card);
  --bk-elev-float:       var(--mkt-elev-float);
  --display-font:        var(--font-display);
  --body-font:           var(--font-body);
  --card-radius:         var(--bk-radius-md);
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bk-surface-0);
  color: var(--text);
  font-family: var(--body-font);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { font-family: var(--display-font); }
:lang(th) { letter-spacing: 0; }  /* §3.3 — Thai never gets negative tracking */

.mk-wrap { max-width: var(--container-w); margin: 0 auto; padding: 0 16px; }

/* ── Marketplace v2 (Wave B) — global grammar ported from the mock ────────── */
.mk-ic { width: 20px; height: 20px; stroke-width: 2; flex: none; }
/* display type — Cormorant; Thai display falls to Sarabun (§3.3 no negative
   tracking on Thai). English display gets tight tracking via [lang="en"]. */
.mk-display { font-family: var(--font-display); font-weight: 500; letter-spacing: 0; text-wrap: balance; overflow-wrap: anywhere; }
[lang="en"].mk-display, .mk-display [lang="en"] { letter-spacing: -0.02em; }
.price { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }
:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 2px; border-radius: var(--bk-radius-sm); }

/* ── Topbar ─────────────────────────────────────────────────────── */
/* ── Topbar (v2.1 NAV RESTRUCTURE) — slim + sticky on both platforms ──
   Wordmark left · loupe / เปิดร้าน / profile right. Sticky above the (also
   sticky) filter bar; both share the --z-sticky ladder (§6.4) with the topbar
   winning by source order. Solid surface + hairline base shadow (§6.1). */
.mk-topbar {
  position: sticky; top: 0; z-index: calc(var(--z-sticky) + 1);
  background: var(--bk-surface-2);
  box-shadow: var(--bk-elev-base);
  border-bottom: 1px solid var(--mkt-line-soft);
}
.mk-topbar-inner {
  max-width: var(--container-w); margin: 0 auto; padding: 8px 16px;
  display: flex; align-items: center; gap: 12px;
}
.mk-topbar-spacer { flex: 1 1 auto; }
.mk-brand {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-family: var(--display-font); font-weight: 600;
  font-size: 20px; letter-spacing: -0.01em; color: var(--text);
  text-decoration: none;
}
.mk-brand .mk-brand-dot { color: var(--brand-primary); }

/* topbar nav cluster (v2.1) — loupe · เปิดร้าน · profile slot */
.mk-topnav { display: inline-flex; align-items: center; gap: 4px; }
/* §8.2 — every affordance ≥44px */
.mk-topnav-ic {
  display: grid; place-items: center; width: 44px; height: 44px; flex: none;
  border: 0; background: transparent; border-radius: var(--bk-radius-md);
  color: var(--mkt-ink-secondary); cursor: pointer;
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              color var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-topnav-ic .mk-ic { width: 21px; height: 21px; }
.mk-topnav-ic:hover { background: var(--mkt-surface-inset); color: var(--mkt-ink); }
.mk-topnav-ic:active { transform: translateY(1px); }
.mk-topnav-ic:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: -2px; }
/* relocated seller CTA — restrained text link (RealReal "Sell With Us"). */
.mk-topnav-sell {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0 12px; border-radius: var(--bk-radius-md);
  font-family: var(--body-font); font-size: 14px; font-weight: 600;
  color: var(--mkt-ink-secondary); text-decoration: none; white-space: nowrap;
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              color var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-topnav-sell .mk-ic { width: 18px; height: 18px; color: var(--mkt-accent); flex: none; }
.mk-topnav-sell:hover { background: var(--mkt-accent-soft); color: var(--mkt-accent); }
.mk-topnav-sell:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: -2px; }
/* the label text hides on very narrow phones — the store glyph carries it. */
@media (max-width: 460px) {
  .mk-topnav-sell span { display: none; }
  .mk-topnav-sell { padding: 0; width: 44px; justify-content: center; }
}

/* ── Hero (Wave C1 — "Editorial depth", Option B pk-chosen 2026-07-04) ──────
   Generous asymmetric cream whitespace (more top than bottom); ONE focal
   element — the Thai headline. The eyebrow is REMOVED (DESIGN.md §3.5 eyebrow
   discipline). Editorial luxury comes from measure + whitespace + a single
   cognac accent keyword — NOT from setting Thai in Cormorant (§3.1 critical:
   Cormorant has no Thai glyphs). An optional Latin italic kicker (Cormorant is
   Latin-safe) sits quietly above. */
.mk-hero {
  padding: 40px 0 8px;
  /* Wave C1 (DESIGN.md §1): softer, OFF-CENTRE single-color cognac wash at ~4%
     (was a centred 7% bleed). One low-opacity single-stop radial, no multi-stop
     SaaS gradient — the wash reads as depth, not paint. */
  background:
    radial-gradient(120% 100% at 20% 0%,
      color-mix(in srgb, var(--mkt-accent) 4%, transparent),
      transparent 62%);
}
.mk-hero-typeled {
  /* Wave C1 minimal (2026-07-04): COMPACT so "Special for you" begins quickly
     below (pk wants to reach the shelf fast). Editorial breathing room, not a
     tall band — asymmetric, a touch more top than bottom (DESIGN.md §3.2). */
  padding: clamp(40px, 8vh, 72px) 0 clamp(24px, 4.5vh, 40px);
  text-align: center;
}
/* Wave C1 minimal — the hero is now a SINGLE Latin editorial line (the page h1).
   Cormorant Garamond ITALIC is Latin-only, so it renders correctly (DESIGN.md
   §3.1 critical Thai-glyph rule — no Thai here, italic Latin serif is safe).
   §3.2 tasteful display scale; §3.5 sentence-case + trailing period. */
.mk-hero-line {
  margin: 0 auto; max-width: 22ch;
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: clamp(24px, 3.6vw, 40px);
  line-height: 1.18; letter-spacing: .005em;
  color: var(--mkt-ink);
  text-wrap: balance;   /* Latin only — safe to balance (§3.7) */
}
/* The ONE cognac accent — "luxury" — is the single restrained emphasis (§3.1). */
.mk-hero-word { color: var(--mkt-accent); }
.mk-hero .mk-sub {
  margin: 0 0 20px;
  color: var(--text-mute);
  font-size: clamp(14px, 3.6vw, 17px);
  max-width: 62ch; text-wrap: pretty;
}

/* ── Search overlay (v2.1) — opened by the topbar loupe. Slim field in a
   lightweight top sheet (NOT a hero bar). Drives the same #mk-search-input. */
.mk-search-overlay {
  position: fixed; inset: 0; z-index: var(--z-float);
  background: color-mix(in srgb, var(--mkt-ink) 32%, transparent);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  display: flex; justify-content: center; align-items: flex-start;
  padding: clamp(16px, 8vh, 96px) 16px 16px;
  animation: mk-overlay-in var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-search-overlay[hidden] { display: none; }
@keyframes mk-overlay-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .mk-search-overlay { animation: none; } }
.mk-search-sheet {
  width: 100%; max-width: 640px;
  background: var(--mkt-surface-2);
  border: 1px solid var(--mkt-line-soft);
  border-radius: var(--bk-radius-xl);
  box-shadow: var(--mkt-elev-float);
  padding: 14px;
}

/* ── Unverified-shop gating (2026-07-03) — on-marketplace contact sheet ──────
   The buy/contact CTA of an UNVERIFIED (storefront-less) shop opens this bottom
   sheet on the marketplace surface (no storefront deep-link → no 404). Cream/
   cognac tokens; §4.4 bottom-sheet slide-up; §8.2 44px tap targets; §11.0 sprite
   icons. Mirrors the storefront openContactSheet chrome. */
.mk-contact-overlay {
  position: fixed; inset: 0; z-index: var(--z-float, 9000);
  background: color-mix(in srgb, var(--mkt-ink) 42%, transparent);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  display: flex; justify-content: center; align-items: flex-end;
  animation: mk-overlay-in var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-contact-sheet {
  width: 100%; max-width: 480px;
  background: var(--mkt-surface-2);
  border: 1px solid var(--mkt-line-soft);
  border-radius: var(--bk-radius-xl) var(--bk-radius-xl) 0 0;
  box-shadow: var(--mkt-elev-float);
  padding: 18px 18px calc(18px + env(safe-area-inset-bottom, 0px));
  max-height: 84vh; overflow-y: auto;
  animation: mk-sheet-up var(--bk-dur-enter) var(--bk-ease-spring);
}
@keyframes mk-sheet-up { from { transform: translateY(100%); } to { transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) {
  .mk-contact-overlay, .mk-contact-sheet { animation: none; }
}
.mk-contact-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 12px; margin-bottom: 14px;
}
.mk-contact-title {
  font-family: var(--display-font, var(--body-font));
  font-size: 17px; font-weight: 700; color: var(--mkt-ink); letter-spacing: -0.01em;
}
.mk-contact-sub { font-size: 12.5px; color: var(--mkt-ink-muted); margin-top: 3px; }
.mk-contact-x {
  flex: 0 0 auto; width: 44px; height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--mkt-ink-muted);
  border-radius: var(--bk-radius-pill); cursor: pointer;
}
.mk-contact-x:hover { background: var(--mkt-surface-inset); color: var(--mkt-ink); }
.mk-contact-x svg { width: 18px; height: 18px; }
.mk-contact-body { display: flex; flex-direction: column; gap: 8px; }
.mk-contact-row {
  display: flex; align-items: center; gap: 12px;
  min-height: 52px; padding: 12px 14px;
  border: 1px solid var(--mkt-line); border-radius: var(--bk-radius-md);
  background: var(--mkt-surface-1);
  color: var(--mkt-ink); font-size: 14px; font-weight: 600;
  text-decoration: none; transition: background 140ms ease, border-color 140ms ease;
}
.mk-contact-row:hover { background: var(--mkt-accent-soft); border-color: var(--mkt-accent-ring); }
.mk-contact-ic { width: 22px; height: 22px; color: var(--mkt-accent); flex: 0 0 auto; }
.mk-contact-empty {
  padding: 20px; text-align: center; font-size: 13px; color: var(--mkt-ink-muted);
}
.mk-contact-copy {
  width: 100%; margin-top: 14px; min-height: 44px;
  background: var(--mkt-surface-inset); color: var(--mkt-ink);
  border: 1px solid var(--mkt-line);
}
.mk-contact-copy:hover { background: var(--mkt-accent-soft); }
/* Seller-header contact button reuses .mk-shop-visit chrome but as a <button>. */
.mk-shop-contact { cursor: pointer; font: inherit; }
/* ── Search box (drives ?q) ─────────────────────────────────────── */
.mk-search {
  display: flex; gap: 8px; align-items: stretch;
  width: 100%;
}
.mk-search-field {
  position: relative; flex: 1 1 auto; min-width: 0;
}
.mk-search-field svg {
  position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  width: 18px; height: 18px; color: var(--text-mute); pointer-events: none;
}
.mk-search input {
  width: 100%; min-height: 50px;
  padding: 10px 14px 10px 44px;
  font-family: var(--body-font); font-size: 16px; color: var(--text);
  background: var(--mkt-surface-inset);
  border: 1.5px solid var(--border);
  border-radius: 999px;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.mk-search input::placeholder { color: var(--bk-ink-subtle); }
.mk-search input:hover { border-color: var(--text-mute); }
.mk-search input:focus-visible {
  outline: none; border-color: var(--mkt-accent);
  box-shadow: 0 0 0 3px var(--mkt-accent-ring);
}
.mk-search-close {
  display: grid; place-items: center; width: 50px; height: 50px; flex: none;
  border: 1px solid var(--mkt-line); background: var(--mkt-surface-1);
  border-radius: 50%; color: var(--mkt-ink-secondary); cursor: pointer;
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-search-close .mk-ic { width: 18px; height: 18px; }
.mk-search-close:hover { background: var(--mkt-surface-inset); }
.mk-search-close:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 2px; }

.mk-footer-seller { margin: 0 0 10px; font-size: 13px; color: var(--text-mute); }
.mk-footer-seller a {
  color: var(--brand-primary); font-weight: 600; text-decoration: none;
}
.mk-footer-seller a:hover { text-decoration: underline; }

/* ── Buttons (§4.5) ─────────────────────────────────────────────── */
.mk-btn {
  min-height: 44px; padding: 10px 18px;
  border-radius: 999px; border: 1.5px solid transparent;
  font-family: var(--body-font); font-weight: 600; font-size: 15px;
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  transition: transform 160ms ease, background-color 160ms ease,
              border-color 160ms ease, box-shadow 160ms ease;
}
.mk-btn-primary { background: var(--brand-primary); color: var(--brand-primary-ink); }
.mk-btn-primary:hover  { background: var(--brand-primary-hover); }
.mk-btn-primary:active { background: var(--brand-primary-active); transform: translateY(1px); }
.mk-btn-ghost {
  background: var(--bk-surface-2); color: var(--text);
  border-color: var(--border);
}
.mk-btn-ghost:hover { border-color: var(--text-mute); }
.mk-btn:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }

/* ── Filter / sort bar (sticky) — Wave B cream skin (mock grid.html:219-243) ──
   v2.1: the topbar is NOW sticky, so the filter bar sticks BELOW it (offset by
   --mk-topbar-h, ~60px) instead of at top:0 — otherwise the two would overlap.
   Solid cream (no blur) with a soft hairline; the shop select + (disabled)
   verified toggle join sort/brand/cat. */
:root { --mk-topbar-h: 61px; }
.mk-filters {
  position: sticky; top: var(--mk-topbar-h); z-index: var(--z-sticky);
  background: var(--mkt-canvas);
  border-bottom: 1px solid var(--mkt-line-soft);
  margin-top: 12px;
}
.mk-filters-inner {
  max-width: var(--container-w); margin: 0 auto; padding: 12px 24px;
  display: flex; flex-wrap: wrap; gap: 10px; align-items: center;
}
@media (max-width: 760px) { .mk-filters-inner { padding: 10px 16px; } }
.mk-select-wrap { position: relative; display: inline-flex; }
/* FIX 2 — the display:inline-flex above overrides the UA [hidden] rule, so the
   shop-filter wrap (grid-only) needs an explicit hide on the shop/pdp view. */
.mk-select-wrap[hidden] { display: none; }
.mk-select {
  -webkit-appearance: none; appearance: none;
  min-height: 44px; padding: 0 34px 0 16px;
  display: inline-flex; align-items: center;
  font-family: var(--body-font); font-size: 14px; font-weight: 500; color: var(--mkt-ink-secondary);
  background: var(--mkt-surface-2);
  border: 1px solid var(--mkt-line); border-radius: var(--bk-radius-md);
  cursor: pointer;
  background-image:
    linear-gradient(45deg, transparent 50%, var(--mkt-ink-muted) 50%),
    linear-gradient(135deg, var(--mkt-ink-muted) 50%, transparent 50%);
  background-position: calc(100% - 18px) center, calc(100% - 13px) center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              border-color var(--bk-dur-exit) var(--bk-ease-standard),
              transform var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-select:hover { background: var(--mkt-surface-1); border-color: var(--mkt-ink-muted); }
.mk-select:active { transform: translateY(1px); }
.mk-select:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 2px; }
/* ── Wave B fix 1 (2026-07-02) — custom filter dropdown ─────────────────
   The native <select> option popup rendered over the pill text (pk report).
   bk-marketplace.js enhances each pill into a button+listbox; the native
   select stays hidden as the state holder. Styling contract:
   §4.5 pill button (the button REUSES .mk-select so the pill look is
   byte-identical) · §6.4 --z-dropdown layer · §8.2 ≥44px option rows ·
   §8.3 reduced-motion-safe reveal · §4.14 panel overlays, nothing clips it
   (.mk-filters has no overflow) · §2.3 ink hierarchy / §2.5 brand accent. */
.mk-select-wrap.mk-dd-on > select { display: none; }   /* custom control took over */
.mk-dd-btn { text-align: left; }
.mk-dd-btn[aria-expanded="true"] { border-color: var(--brand-primary); }
.mk-dd-btn.is-filtering {                 /* an applied filter is visible at a glance */
  border-color: var(--brand-primary);
  background-color: color-mix(in oklch, var(--brand-primary) 8%, var(--bk-surface-2));
}
.mk-dd-list {
  position: absolute; top: calc(100% + 6px); left: 0;
  z-index: var(--z-dropdown);
  min-width: 100%; width: max-content; max-width: min(320px, 88vw);
  max-height: min(340px, 60vh); overflow-y: auto;
  margin: 0; padding: 6px; list-style: none;
  background: var(--bk-surface-2);
  border: 1.5px solid var(--border); border-radius: 14px;
  box-shadow: var(--bk-elev-float);
  animation: mk-dd-in 140ms ease;
}
@keyframes mk-dd-in {
  from { opacity: 0; transform: translateY(-4px); }
  to   { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: reduce) {           /* §8.3 */
  .mk-dd-list { animation: none; }
}
.mk-dd-list:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }
.mk-dd-option {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 8px 14px;               /* §8.2 touch target */
  border-radius: 9px;
  font-size: 14px; font-weight: 500; color: var(--text);
  cursor: pointer; white-space: nowrap;
}
.mk-dd-option.is-active {                            /* keyboard/hover highlight */
  background: color-mix(in oklch, var(--brand-primary) 8%, transparent);
}
.mk-dd-option[aria-selected="true"] {
  font-weight: 600; color: var(--brand-primary);
}
.mk-dd-option[aria-selected="true"]::before {
  content: "✓"; font-weight: 700; margin-right: 2px;
}
.mk-dd-option[aria-selected="false"]::before {
  content: ""; width: 12px;                          /* align labels under the check */
}

.mk-price-fields { display: inline-flex; align-items: center; gap: 6px; }
.mk-price-fields input {
  width: 96px; min-height: 44px;
  padding: 8px 12px; font-family: var(--body-font); font-size: 14px; color: var(--mkt-ink);
  background: var(--mkt-surface-inset);
  border: 1px solid var(--mkt-line); border-radius: var(--bk-radius-md);
  font-variant-numeric: tabular-nums;
}
.mk-price-fields input:focus-visible {
  outline: none; border-color: var(--mkt-accent);
  box-shadow: 0 0 0 3px var(--mkt-accent-ring);
}
.mk-price-fields .mk-dash { color: var(--mkt-ink-muted); }
.mk-filters .mk-spacer { flex: 1 1 auto; }
.mk-result-count { font-size: 13px; color: var(--mkt-ink-muted); font-variant-numeric: tabular-nums; }

/* ── Verified-only toggle (Wave C Piece 1, 2026-07-03) — mock .mk-ftoggle
   (grid.html:244-268). NOW LIVE: the backend threads verified_only through the
   shared _catalog_filter_clauses (grid + all facet counts), so a click filters
   server-side to green-badge (marketplace_badge='green') shops. Through Wave B
   this shipped disabled + data-wave="c" + aria-disabled + a "เร็วๆ นี้" chip
   (no backend param); those are gone now. The [disabled]/[aria-disabled]/.mk-soon
   rules below are kept but inert (no element carries them anymore). */
.mk-ftoggle {
  display: inline-flex; align-items: center; gap: 10px; flex: none;
  min-height: 44px; padding: 0 16px 0 12px; margin-left: auto; cursor: pointer;
  border: 1px solid var(--mkt-line); border-radius: var(--bk-radius-md);
  background: var(--mkt-surface-2);
  font-size: 14px; font-weight: 500; color: var(--mkt-ink-secondary);
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              border-color var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-ftoggle .mk-ic { width: 18px; height: 18px; color: var(--mkt-ink-muted); transition: color var(--bk-dur-exit) var(--bk-ease-standard); }
.mk-ftoggle[disabled], .mk-ftoggle[aria-disabled="true"] { cursor: not-allowed; opacity: .72; }
.mk-ftoggle .mk-soon {
  font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  color: var(--mkt-ink-muted); background: var(--mkt-surface-inset);
  border: 1px solid var(--mkt-line-soft); border-radius: var(--bk-radius-sm);
  padding: 1px 7px;
}
.mk-switch {
  width: 40px; height: 22px; border-radius: var(--bk-radius-pill);
  background: color-mix(in oklch, var(--mkt-ink) 22%, var(--mkt-canvas));
  position: relative; flex: none;
  transition: background-color var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-switch::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 18px; height: 18px; border-radius: 50%;
  background: #fff; box-shadow: 0 1px 3px rgb(34 26 20/.25);
  transition: transform var(--bk-dur-enter) var(--bk-ease-spring);
}
/* aria-pressed="true" green-on styles carried from the mock (LIVE, Wave C Piece 1). */
.mk-ftoggle[aria-pressed="true"] { border-color: color-mix(in oklch, var(--bk-success) 40%, var(--mkt-line)); background: var(--mkt-success-soft); }
.mk-ftoggle[aria-pressed="true"] .mk-switch { background: var(--bk-success); }
.mk-ftoggle[aria-pressed="true"] .mk-switch::after { transform: translateX(18px); }
.mk-ftoggle[aria-pressed="true"] .mk-ic { color: var(--bk-success); }
.mk-ftoggle[aria-pressed="true"] span { color: var(--mkt-success-deep); }
/* ── Mobile filter bar (v2.1, issue #3) — compact horizontal-scroll pill row ──
   The old layout stacked every control full-width vertically (clunky). v2.1
   collapses them into ONE horizontally-scrolling row of pills (sort · brand ·
   category · ร้านค้า · price · verified) that overflows off-screen-right instead
   of pushing the grid down. All wiring is untouched — this is layout-only; the
   mkDD-enhanced selects, the shop filter (onFilterChanged), and the disabled
   verified toggle all keep their exact DOM + handlers. Desktop bar unchanged. */
/* v2.1 FIX (2026-07-03): the earlier horizontal-scroll approach put
   `overflow-x:auto` + `mask-image` on .mk-filters-inner — but that element is
   the positioning ANCESTOR of the mkDD `.mk-dd-list` panels (position:absolute
   inside a .mk-select-wrap child), so the overflow/mask established a clip
   context that clipped every dropdown to the ~64px pill row (only the first
   option showed = filtering broken). The bk-marketplace.css contract literally
   requires ".mk-filters has no overflow". Fix: NO overflow/clip on the ancestor
   — the compact pills simply WRAP to two rows. Dropdowns open unclipped; all
   wiring untouched. This is still far more compact than the old full-width
   vertical stack (pills size to content). Desktop bar unchanged. */
@media (max-width: 760px) {
  .mk-filters-inner {
    flex-wrap: wrap; align-items: center; gap: 8px;
    /* explicitly NO overflow / NO mask here — see comment above. */
  }
  /* compact the pills so two rows fit cleanly under the sticky bar. */
  .mk-select { min-height: 40px; padding: 0 30px 0 12px; font-size: 13px; }
  .mk-price-fields { gap: 4px; }
  .mk-price-fields input { min-height: 40px; width: 84px; font-size: 13px; }
  /* the flexible spacer collapses so the pills pack tight; the result count
     drops to its own full-width line at the end of the wrap. */
  .mk-filters .mk-spacer { display: none; }
  .mk-result-count { flex-basis: 100%; order: 99; margin-top: 2px; }
  .mk-ftoggle { min-height: 40px; margin-left: 0; gap: 8px; }
}

/* ════ Featured shelf (Wave B, FORK B) — คัดพิเศษประจำวัน 2×2 infinite carousel.
   Static 16-item shelf (4 pages × 2×2) hand-authored in marketplace.html,
   driven by the ported JS carousel + per-card rotator. Ported VERBATIM from
   the mock (grid.html:137-216). The amber "แนะนำ" tag (--mkt-featured) is
   visually + textually distinct from the green verified shield (invariant). ══ */
.mk-shelf {
  margin: 26px auto 8px; max-width: var(--container-w);
  background: var(--mkt-surface-1);
  border: 1px solid var(--mkt-line-soft);
  border-radius: var(--bk-radius-xl);
  box-shadow: var(--mkt-elev-card);
  padding: 24px 24px 22px;
}
.mk-shelf[hidden] { display: none; }
/* v2.1 (issue #5): the head is now ONLY the eyebrow "Special for you." (italic
   Cormorant) + a ">" affordance — the Thai h2 is gone.
   v2.1 FIX (2026-07-03): .mk-shelf-head is a flex ROW containing the head LINK
   (.mk-shelf-title, wrapping just the eyebrow + ">") and, as a SIBLING (not a
   child of the <a>), the .mk-shelf-ctrl arrow group — so an arrow click pages
   the carousel instead of following the head link. */
.mk-shelf-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 10px;
}
.mk-shelf-title {
  display: inline-flex; align-items: center; gap: 8px;
  text-decoration: none; color: inherit; border-radius: var(--bk-radius-sm);
}
.mk-shelf-title:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 4px; }
.mk-shelf-head h2 { font-size: clamp(23px, 2.5vw, 30px); line-height: 1.2; margin: 0; }
.mk-shelf-eyebrow {
  font-family: var(--font-display); font-style: italic; font-weight: 500;
  font-size: clamp(22px, 2.6vw, 30px); line-height: 1.15;
  color: var(--mkt-ink); letter-spacing: .01em;
}
/* the ">" affordance next to the eyebrow (mock cue that the shelf links onward) */
.mk-shelf-more {
  display: inline-grid; place-items: center; width: 26px; height: 26px; flex: none;
  color: var(--mkt-accent);
  transition: transform var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-shelf-more .mk-ic { width: 18px; height: 18px; }
.mk-shelf-title:hover .mk-shelf-more { transform: translateX(3px); }
.mk-shelf-ctrl { margin-left: auto; display: flex; align-items: center; gap: 6px; }
.mk-shelf-arrow {
  width: 38px; height: 38px; border-radius: 50%; flex: none;
  display: grid; place-items: center; color: var(--mkt-ink-secondary);
  background: var(--mkt-surface-2); border: 1px solid var(--mkt-line);
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              border-color var(--bk-dur-exit) var(--bk-ease-standard),
              transform var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-shelf-arrow:hover { background: var(--mkt-surface-1); border-color: var(--mkt-ink-muted); }
.mk-shelf-arrow:active { transform: translateY(1px); }
.mk-shelf-arrow .mk-ic { width: 18px; height: 18px; }
/* viewport clips the sliding track; each "page" = one 2×2 block. FULL shelf
   width (Wave B defect-2 fix): the 2×2 page spans the whole shelf so each
   .mk-feat is ~half the shelf (large 2-up editorial cards, no dead right-hand
   gutter) — matches the approved grid-desktop-top.png intent. */
.mk-carousel { position: relative; overflow: hidden; margin-top: 14px; }
.mk-track { display: flex; will-change: transform; transition: transform 480ms var(--bk-ease-standard); touch-action: pan-y; }
.mk-track.no-anim { transition: none; }
.mk-page {
  flex: 0 0 100%; min-width: 100%;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 22px 20px;
  padding: 2px 2px 4px;
}
.mk-carousel-dots { display: flex; justify-content: center; gap: 7px; margin-top: 14px; }
.mk-cdot {
  width: 7px; height: 7px; border-radius: 50%; flex: none; padding: 0; border: 0;
  background: color-mix(in oklch, var(--mkt-ink) 22%, var(--mkt-canvas));
  transition: background-color 260ms var(--bk-ease-standard), transform 260ms var(--bk-ease-standard);
}
.mk-cdot.is-on { background: var(--mkt-accent); transform: scale(1.15); }
/* featured card = SAME grammar/size as a grid card, plus amber แนะนำ tag + crossfade. */
.mk-feat {
  position: relative;
  background: var(--mkt-surface-1);
  border: 1px solid var(--mkt-line-soft);
  border-radius: var(--bk-radius-lg);
  /* FIX 3 (2026-07-04) — clip any residual overshoot so a long 7-digit price row
     can never spill past the rounded card edge (the media already rounds via its
     own overflow:hidden; the body content stays inside this box). */
  overflow: hidden;
  box-shadow: var(--mkt-elev-card);
  transition: transform var(--bk-dur-enter) var(--bk-ease-standard),
              box-shadow var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-feat:hover { transform: translateY(-3px); box-shadow: var(--mkt-elev-float); }
.mk-feat-media {
  /* Wave B defect-2: full-width shelf makes each card ~half the shelf (~560px);
     a 5/4 landscape media keeps the large 2-up cards from becoming absurdly
     tall (a 4:5 portrait at 560px would be ~700px tall). */
  position: relative; display: block; aspect-ratio: 5/4; overflow: hidden;
  border-radius: var(--bk-radius-lg) var(--bk-radius-lg) 0 0;
  background: var(--mkt-surface-inset);
}
.mk-feat-media img { width: 100%; height: 100%; object-fit: cover; }
/* crossfade stack — swaps SKU/image every 6s. */
.rot-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 700ms var(--bk-ease-standard); }
.rot-img.is-on { opacity: 1; }
.mk-media-tone { position: absolute; inset: 0; pointer-events: none; background: var(--mkt-canvas); mix-blend-mode: multiply; opacity: .16; }
.mk-media-shade { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(to top, rgb(34 26 20/.28), transparent 30%); }
.mk-feat-tag {
  position: absolute; top: 9px; left: 9px; z-index: 2;
  display: inline-flex; align-items: center; gap: 5px;
  background: var(--mkt-featured); color: #fff;
  font-size: 11px; font-weight: 600; letter-spacing: .03em;
  border-radius: var(--bk-radius-sm); padding: 3px 9px;
  box-shadow: 0 1px 3px rgb(34 26 20/.22);
}
.mk-feat-tag .mk-ic { width: 11px; height: 11px; fill: currentColor; stroke: none; }
.rot-dots { position: absolute; right: 9px; bottom: 9px; display: flex; gap: 5px; z-index: 2; }
.rot-dot { width: 5px; height: 5px; border-radius: 50%; background: #F5F0E6; opacity: .5; transition: opacity 300ms var(--bk-ease-standard); box-shadow: 0 0 2px rgb(34 26 20/.4); }
.rot-dot.is-on { opacity: 1; }
/* Wave B defect-2 — larger 2-up cards: scale the body type up on desktop
   (mobile overrides below keep it compact). */
.mk-feat-body { padding: 15px 18px 18px; display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.mk-feat-brand { font-family: var(--font-display); font-size: 26px; font-weight: 600; line-height: 1.15; letter-spacing: -0.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-feat-name { font-size: 13.5px; color: var(--mkt-ink-secondary); line-height: 1.4; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.mk-shop-row { display: flex; align-items: center; gap: 5px; font-size: 12.5px; color: var(--mkt-ink-muted); margin-top: 6px; min-width: 0; }
.mk-shop-row .mk-ic { width: 17px; height: 17px; color: var(--bk-success); flex: none; }
.mk-shop-row .mk-shop-glyph { width: 15px; height: 15px; color: var(--mkt-ink-muted); opacity: .75; flex: none; }
.mk-shop-row b { font-weight: 600; color: var(--mkt-ink-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Wave A (2026-07-04): featured foot mirrors the grid card — left group (contact
   pill or spacer) + price cluster pinned bottom-right via space-between. */
.mk-feat-foot { display: flex; align-items: flex-end; justify-content: space-between; gap: 6px; margin-top: 8px; }
.mk-feat-foot .mk-foot-left { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 4px; min-width: 0; flex: 0 1 auto; }
/* FIX 3 (2026-07-04) — the price cluster (struck original + promo) must be allowed
   to SHRINK and, if it still cannot fit on one line, WRAP the struck original above
   the promo — rather than the old `flex:0 0 auto` nowrap that clipped the original's
   leading digits on a 7-digit ฿1,450,000 at 375px. */
.mk-feat-price-cluster { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: flex-end; gap: 4px 6px; min-width: 0; flex: 0 1 auto; }
.mk-feat-price { flex: 0 1 auto; min-width: 0; }
.mk-feat-price-cluster .mk-price-original { font-size: 13px; }
@media (max-width: 760px) { .mk-feat-price-cluster .mk-price-original { font-size: 11px; } }
.mk-feat-price {
  display: inline-flex; align-items: baseline; gap: 8px;
  font-size: 19px; font-weight: 600; text-align: right;
  font-variant-numeric: tabular-nums;
}
.mk-feat-price.mk-price-ask { font-weight: 600; color: var(--mkt-ink-secondary); font-size: 15px; }
/* v2.1 — masked featured price carries the SAME quiet "ติดต่อร้าน" pill as the
   grid card (Piece M), so '฿Nxx,xxx' + hint never run together.
   FIX 2 (2026-07-04) — Wave A moved this pill out of .mk-feat-price and into
   .mk-feat-foot > .mk-foot-left, so the old `.mk-feat-price .mk-contact-hint`
   scope stopped matching → the pill rendered as 16px dark body text wrapping to
   3 lines. Match it in BOTH the legacy and the new location. */
.mk-feat-price .mk-contact-hint,
.mk-feat-foot .mk-contact-hint {
  flex: 0 1 auto; min-width: 0;
  font-size: 11px; font-weight: 500; letter-spacing: 0.01em; line-height: 1.5;
  padding: 2px 8px; border-radius: var(--bk-radius-sm);
  color: var(--mkt-ink-muted);
  background: var(--mkt-surface-inset);
  border: 1px solid var(--mkt-line-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 760px) {
  .mk-shelf { padding: 16px 14px 16px; border-radius: var(--bk-radius-lg); margin-left: 16px; margin-right: 16px; }
  .mk-shelf-head h2 { font-size: 22px; }
  /* mobile keeps a tighter 2×2; a 4/5 portrait media reads better in the
     narrow half-columns than the desktop 5/4 landscape. */
  .mk-page { gap: 12px 10px; }
  .mk-feat-media { aspect-ratio: 4/5; }
  .mk-feat-body { padding: 9px 10px 11px; gap: 2px; }
  .mk-feat-brand { font-size: 15px; }
  .mk-feat-name { font-size: 11px; }
  .mk-shop-row { font-size: 11px; margin-top: 4px; }
  .mk-shop-row .mk-ic { width: 15px; height: 15px; }
  .mk-shop-row .mk-shop-glyph { width: 14px; height: 14px; }
  .mk-feat-price { font-size: 14px; }
  .mk-feat-price.mk-price-ask { font-size: 12.5px; }
  /* FIX 3 (2026-07-04) — 7-digit luxury prices (฿1,290,000 struck ฿1,450,000)
     overflow the narrow 2-up featured card. Mirror the grid .mk-card-foot: stack
     the foot to a column so the price cluster gets the FULL card width instead of
     competing with the left pill for the same row. Price stays right-aligned. */
  .mk-feat-foot { flex-direction: column; align-items: stretch; gap: 4px; }
  .mk-feat-foot .mk-foot-left { flex: 0 0 auto; }
  .mk-feat-price-cluster { align-self: flex-end; flex-wrap: wrap; justify-content: flex-end; }
}

/* ════ Float stack — seller-invite popup + docked pill + (hidden) AI FAB ══════
   Wave C1 (2026-07-04): the seller-invite popup + docked pill are REVIVED here
   (recovered from Wave B #155, removed in v2.1 #159). The AI FAB stays hidden.
   The stack floats bottom-RIGHT on desktop; on mobile (<1024) it docks ABOVE
   the floating tab bar so it never overlaps the 4-tab bar. DESIGN.md §6.4
   z-ladder (--z-float), §4.14 no overlay clipping (fixed, not clipped). */
.mk-float { position: fixed; right: 24px; bottom: 24px; z-index: calc(var(--z-float) + 1); display: flex; flex-direction: column; align-items: flex-end; gap: 12px; }
/* FIX 1 (2026-07-04, revised Wave C) — the `.mk-float:empty` guard was DEAD: the
   container always ships three element children (seller-invite, seller-pill, fab),
   so :empty never matches. The transparent-but-laid-out fixed box then intercepts
   taps over the grid beneath (esp. the full-width bar on mobile ≤1023px). Make the
   container pointer-transparent so taps pass THROUGH to the grid, and re-enable
   hit-testing only on the actually-visible interactive children. */
.mk-float { pointer-events: none; }
.mk-seller-invite.is-on,
.mk-seller-pill.is-on,
.mk-fab:not([hidden]) { pointer-events: auto; }
/* FIX 1 (2026-07-04) — the `.mk-fab { display: grid }` author rule below beats the
   UA `[hidden]{display:none}`, so the `hidden` button still paints. Restore the
   hide explicitly (mirrors .mk-select-wrap[hidden] / .mk-search-overlay[hidden] /
   .mk-shelf[hidden]). `.mk-fab[hidden]` (0,1,1) outranks `.mk-fab` (0,1,0) on
   specificity, so it wins regardless of source order. */
.mk-fab[hidden] { display: none; }
.mk-fab {
  display: grid; place-items: center;
  width: 56px; height: 56px; border-radius: 50%;
  background: var(--mkt-accent); color: var(--mkt-accent-ink);
  box-shadow: var(--mkt-elev-float);
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              transform var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-fab:hover { background: var(--mkt-accent-hover); }
.mk-fab:active { transform: translateY(1px); }
.mk-fab:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 3px; }
.mk-fab .mk-ic { width: 24px; height: 24px; fill: currentColor; stroke: none; }

/* ── Seller-invite popup (Wave C1, recovered from #155) ──────────────────────
   A cognac-elevated promo card; the Thai headline stays in the body/Thai font
   (no Cormorant on Thai, §3.1). Hidden at rest; .is-on reveals it (the JS adds
   it after the buyer scrolls past the hero — §2.6 buyer-search-primary). */
.mk-seller-invite {
  width: 280px; background: var(--mkt-surface-2);
  border-radius: var(--bk-radius-lg); box-shadow: var(--mkt-elev-float);
  border: 1px solid var(--mkt-line-soft);
  padding: 16px 18px; position: relative;
  /* a11y (FIX 5, preserved): visibility:hidden at rest removes the × + CTA from
     the tab order until the scroll trigger fires (opacity:0 alone left them
     keyboard-focusable = phantom tab stops); delayed to the end of the fade-out. */
  opacity: 0; transform: translateY(8px); pointer-events: none; visibility: hidden;
  transition: opacity var(--bk-dur-enter) var(--bk-ease-standard),
              transform var(--bk-dur-enter) var(--bk-ease-spring),
              visibility 0s linear var(--bk-dur-enter);
}
.mk-seller-invite.is-on {
  opacity: 1; transform: none; pointer-events: auto; visibility: visible;
  transition: opacity var(--bk-dur-enter) var(--bk-ease-standard),
              transform var(--bk-dur-enter) var(--bk-ease-spring),
              visibility 0s;
}
/* Thai display headline — Sarabun/Noto, NOT Cormorant (§3.1 Thai-glyph rule). */
.mk-seller-invite h3 {
  font-family: var(--font-thai-display); font-weight: 700;
  font-size: 17px; line-height: 1.3; margin: 0 0 5px; padding-right: 22px;
  color: var(--mkt-ink); letter-spacing: 0;
}
.mk-seller-invite p { font-size: 13px; color: var(--mkt-ink-muted); line-height: 1.6; margin: 0; }
.mk-seller-invite-cta { font-weight: 700; color: var(--mkt-accent); text-decoration: none; border-bottom: 1.5px solid color-mix(in oklch, var(--mkt-accent) 45%, transparent); transition: border-color var(--bk-dur-exit) var(--bk-ease-standard); }
.mk-seller-invite-cta:hover { border-bottom-color: var(--mkt-accent); }
.mk-seller-invite-cta:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 2px; }
.mk-seller-x { position: absolute; top: 4px; right: 4px; width: 44px; height: 44px; display: grid; place-items: center; color: var(--mkt-ink-muted); border: 0; background: transparent; cursor: pointer; border-radius: var(--bk-radius-md); }
.mk-seller-x:hover { background: var(--mkt-surface-inset); }
.mk-seller-x:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: -2px; }
.mk-seller-x .mk-ic { width: 15px; height: 15px; stroke: currentColor; fill: none; }
/* Docked pill — the on-demand affordance once the popup is dismissed. */
.mk-seller-pill {
  display: none; align-items: center; gap: 7px;
  min-height: 44px; padding: 0 16px; border: 0; cursor: pointer;
  border-radius: var(--bk-radius-pill); font-family: inherit;
  background: var(--mkt-surface-2); color: var(--mkt-ink-secondary);
  font-size: 13.5px; font-weight: 600; box-shadow: var(--mkt-elev-card);
  border: 1px solid var(--mkt-line-soft);
}
.mk-seller-pill.is-on { display: inline-flex; }
.mk-seller-pill:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: 2px; }
.mk-seller-pill .mk-ic { width: 16px; height: 16px; color: var(--mkt-accent); stroke: currentColor; fill: none; }
@media (prefers-reduced-motion: reduce) {   /* This is a promotional / dismissible affordance (NOT primary buyer content):
     it is intentionally JS-gated — opacity:0 + visibility:hidden until the
     .is-on class is added. It does NOT claim strict §8.6 reveal-safety for
     primary content; here we simply zero the transition so the reveal is
     instant under reduced-motion. */
  .mk-seller-invite { transition: visibility 0s linear var(--bk-dur-enter); }
  .mk-seller-invite.is-on { transition: visibility 0s; }
}

/* ════ Mobile floating-pill tab bar (<1024) — espresso frosted glass.
   v2.1 (issue #4): 4 tabs, and collapse-to-icon on scroll DOWN (labels fade,
   pill shortens; icons + ≥44px targets survive) — expand on scroll UP. The
   bk-floating-nav shrink-on-scroll pattern (DESIGN.md §4.15 / §1290), adapted
   to the marketplace's espresso glass. bk-marketplace.js toggles .is-compact. */
.mk-tabbar {
  --tab-espresso: color-mix(in oklch, var(--mkt-ink) 92%, black);
  --tab-gold:     #C89A5B;   /* cognac-gold, legible on espresso */
  display: none; position: fixed;
  left: clamp(12px, 4vw, 16px); right: clamp(12px, 4vw, 16px);
  bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
  z-index: var(--z-float);
  max-width: 520px; margin: 0 auto;
  align-items: stretch; gap: 2px; padding: 6px;
  border-radius: var(--bk-radius-pill);
  background: color-mix(in oklch, var(--tab-espresso) 58%, transparent);
  -webkit-backdrop-filter: blur(20px) saturate(180%);
  backdrop-filter: blur(20px) saturate(180%);
  box-shadow:
    inset 0 0 0 1px rgb(245 240 230/.14),
    0 1px 2px 0  rgb(20 14 9/.24),
    0 10px 28px 0 rgb(20 14 9/.26),
    0 20px 44px 0 rgb(20 14 9/.16);
  /* animate only padding + transform for the compact shrink (§8.3-safe). */
  transition: padding 180ms var(--bk-ease-standard), transform 180ms var(--bk-ease-standard);
  will-change: transform;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .mk-tabbar { background: color-mix(in oklch, var(--tab-espresso) 90%, transparent); }
}
.mk-tab {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
  min-height: 52px; padding: 6px 4px; border-radius: calc(var(--bk-radius-md) + 2px);
  border: 0; background: transparent; font-family: inherit; cursor: pointer;
  font-size: 10.5px; font-weight: 600; letter-spacing: .01em; line-height: 1.1;
  color: color-mix(in oklch, #F7F2E9 74%, transparent); text-decoration: none;
  text-shadow: 0 1px 2px rgb(20 14 9/.35);
  -webkit-tap-highlight-color: transparent;
  transition: color 160ms var(--bk-ease-standard), background-color 160ms var(--bk-ease-standard);
}
.mk-tab .mk-ic { width: 22px; height: 22px; flex: none; }
/* label collapses on scroll-down (opacity/max-height only — no layout thrash). */
.mk-tab span {
  display: block; overflow: hidden; opacity: 1; max-height: 1.4em;
  transition: opacity 140ms var(--bk-ease-standard), max-height 180ms var(--bk-ease-standard);
}
.mk-tab.is-active { color: var(--tab-gold); }
.mk-tab.is-active span { font-weight: 700; }
.mk-tab:active { background: color-mix(in oklch, #F5F0E6 12%, transparent); }
.mk-tab:focus-visible { outline: 2px solid var(--tab-gold); outline-offset: -2px; border-radius: var(--bk-radius-pill); }

/* compact (scroll-down) — labels hide, pill shortens; icons + ≥44px stay (§8.2). */
.mk-tabbar.is-compact { padding: 4px; transform: translateY(2px); }
.mk-tabbar.is-compact .mk-tab { min-height: 44px; padding: 4px; }
.mk-tabbar.is-compact .mk-tab span { opacity: 0; max-height: 0; pointer-events: none; }

@media (prefers-reduced-motion: reduce) {   /* §8.3 — no shrink glide */
  .mk-tabbar, .mk-tab, .mk-tab span { transition: none; }
  .mk-tabbar.is-compact { transform: none; }
}

/* desktop ≥1024: NO bottom bar (top bar covers it); <1024 shows it. */
@media (max-width: 1023px) {
  .mk-tabbar { display: flex; }
  /* Wave C1: dock the float stack (seller card / pill) ABOVE the 4-tab bar so it
     NEVER overlaps it. The bar sits at bottom 12px + safe-area and is ~64px tall
     → lift the stack to ~88px above the safe-area bottom. Stretch it to the
     screen gutter and let the card fill the width for a compact mobile card. */
  .mk-float {
    bottom: calc(env(safe-area-inset-bottom, 0px) + 88px);
    left: clamp(12px, 4vw, 16px); right: clamp(12px, 4vw, 16px);
    align-items: stretch; max-width: 520px; margin: 0 auto;
  }
  /* compact mobile card — full-width within the gutter, above the tab bar. */
  .mk-seller-invite { width: auto; }
  .mk-seller-pill.is-on { display: inline-flex; align-self: flex-end; }
  body { padding-bottom: calc(88px + env(safe-area-inset-bottom, 0px)); }  /* clear the pill */
}

/* ── Toast (Wave A 2026-07-04) — non-blocking snackbar for the "saved items"
   coming-soon tap. Bottom-centered, above the mobile tab bar, auto-dismisses. ── */
.mk-toast {
  position: fixed; left: 50%; bottom: 32px; transform: translate(-50%, 12px);
  z-index: calc(var(--z-float) + 2);
  max-width: min(88vw, 360px);
  padding: 11px 18px; border-radius: var(--bk-radius-pill);
  background: var(--mkt-ink); color: var(--mkt-canvas);
  font-family: var(--body-font); font-size: 14px; font-weight: 500; line-height: 1.4;
  text-align: center; box-shadow: var(--mkt-elev-float);
  opacity: 0; pointer-events: none;
  transition: opacity var(--bk-dur-enter) var(--bk-ease-standard),
              transform var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-toast.is-on { opacity: 1; transform: translate(-50%, 0); }
@media (max-width: 1023px) { .mk-toast { bottom: calc(env(safe-area-inset-bottom, 0px) + 92px); } }
@media (prefers-reduced-motion: reduce) { .mk-toast { transition: opacity var(--bk-dur-enter) linear; } }

/* ── Grid (Wave B) — 4-up desktop / 3-up mobile (mock grid.html:275-278,440) ──
   density = repttown compactness in a warm-luxury skin. Fixed column count
   (not intrinsic auto-fill) so the card rhythm matches the approved mock. */
.prod-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 20px 16px;
  padding: 8px 0 64px;
}
@media (max-width: 760px) {
  .prod-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px 8px; }
}

/* ── Product card (Wave B) — re-skinned to the mock's .mk-card visual grammar
   (grid.html:280-324), re-targeted onto the REAL .prod-card two-anchor
   structure. The structural hooks (.prod-card / .prod-card-link / .mk-shop-link
   / .prod-price-row / .price / .mk-contact-hint) are KEPT — the SPA click
   router, _reqGen fence + Return Rail depend on them.
   .mk-card-media→.img · .mk-card-body→.meta. Card is a bordered cream tile
   with brand-tinted (cognac) elevation, image zoom on hover, brand-first
   display face, single-line name + shop, condition chip + price in the foot. */
.prod-card {
  position: relative;
  display: flex; flex-direction: column;
  background: var(--mkt-surface-1);
  border: 1px solid var(--mkt-line-soft);        /* hairline border on cream */
  border-radius: var(--bk-radius-md);
  box-shadow: var(--mkt-elev-card);
  overflow: hidden;
  color: inherit; text-decoration: none;
  container-type: inline-size;   /* meta text scales with card width (cqi) */
  transition: transform var(--bk-dur-enter) var(--bk-ease-standard),
              box-shadow var(--bk-dur-enter) var(--bk-ease-standard),
              border-color var(--bk-dur-enter) var(--bk-ease-standard);
}
.prod-card:hover {
  transform: translateY(-3px);
  box-shadow: var(--mkt-elev-float);
  border-color: var(--mkt-line);
}
.prod-card-link {
  display: block; color: inherit; text-decoration: none;
}
.prod-card-link::after {          /* stretched product hit-area (whole card) */
  content: ""; position: absolute; inset: 0; z-index: 1;
}
.prod-card-link:focus-visible::after {
  outline: 3px solid var(--mkt-accent-ring); outline-offset: -2px;
  border-radius: var(--bk-radius-md);
}
.prod-card .img {
  position: relative; display: block;
  aspect-ratio: 1 / 1;            /* square-ish, fills width (mock .mk-card-media) */
  overflow: hidden;
  background: var(--mkt-surface-inset);
}
.prod-card .img img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform 600ms var(--bk-ease-standard);
}
.prod-card:hover .img img { transform: scale(1.035); }
.prod-card .img .mk-noimg {
  width: 100%; height: 100%;
  display: flex; align-items: center; justify-content: center;
  color: var(--mkt-ink-muted);
}
.prod-card .img .mk-noimg svg { width: 34%; height: 34%; opacity: 0.5; }
/* image tone/shade overlays (mock .mk-media-tone/.mk-media-shade), optional. */
.prod-card .mk-media-tone { position: absolute; inset: 0; pointer-events: none; background: var(--mkt-canvas); mix-blend-mode: multiply; opacity: .16; }
.prod-card .mk-media-shade { position: absolute; inset: 0; pointer-events: none; background: linear-gradient(to top, rgb(34 26 20/.28), transparent 30%); }

/* Trust badge (legacy overlay chip) — retained ONLY for the seller-page
   header inline chip (.mk-badge-inline); on cards the verified mark is now the
   green SHIELD beside the shop name (badgeHtml renders .mk-shop-verified). */
.mk-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-weight: 600; letter-spacing: 0.01em;
  padding: 4px 9px; border-radius: var(--bk-radius-sm);
  line-height: 1.3;
}
.mk-badge svg { width: 12px; height: 12px; flex: 0 0 auto; }
.mk-badge-green { background: var(--mkt-success-soft); color: var(--mkt-success-deep); }
.mk-badge-gray  { background: var(--mkt-surface-inset); color: var(--mkt-ink-muted); }
/* Journey Phase 2 — the SAME trust chip in normal flow (seller-page header /
   compact bar). One renderer (badgeHtml), two placements. */
.mk-badge.mk-badge-inline {
  position: static; pointer-events: auto;
  box-shadow: none;
  font-size: 12px; padding: 5px 10px;
  max-width: none;
}

/* ── Seller page (Journey Phase 2, 2026-07-02) — DESIGN.md §2.1 apex chrome,
   §4.2-adjacent header strip, §8.2 targets, §6.4 z-ladder ─────────────────
   Header strip above the standard grid: parent-only crumb, monogram avatar,
   shop name (display face), trust chip + product count, and a small
   "เยี่ยมชมหน้าร้านเต็ม ↗" storefront exit. Collapses to the fixed compact
   bar on mobile scroll (body.mk-compact-on, IO-toggled). */
.mk-shop-header {
  padding: 18px 0 4px;
  background:
    radial-gradient(120% 90% at 50% -10%,
      color-mix(in oklch, var(--brand-primary) 8%, transparent),
      transparent 60%);
}
.mk-shop-header[hidden] { display: none; }
.mk-shop-crumb { font-size: 14px; margin-bottom: 10px; }
.mk-shop-crumb a {
  display: inline-flex; align-items: center;
  min-height: 44px; padding: 4px 10px 4px 0;   /* §8.2 tap row */
  color: var(--text-mute); font-weight: 600; text-decoration: none;
  transition: color 140ms ease;
}
.mk-shop-crumb a:hover { color: var(--brand-primary); }
.mk-shop-crumb a:focus-visible {
  outline: 3px solid var(--brand-primary-ring); outline-offset: 2px;
  border-radius: 8px;
}
.mk-shop-strip {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 16px;
  background: var(--bk-surface-1);
  border: 1px solid var(--border);
  border-radius: 18px;                 /* §4.0 xl ceiling */
  box-shadow: var(--bk-elev-card);
}
.mk-shop-avatar {
  width: 52px; height: 52px; flex: 0 0 auto;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand-primary-soft); color: var(--brand-primary);
  font-family: var(--display-font); font-weight: 600; font-size: 22px;
}
.mk-shop-id { flex: 1 1 auto; min-width: 0; }
.mk-shop-name {
  margin: 0 0 4px; font-weight: 500;
  font-size: clamp(20px, 5vw, 28px); line-height: 1.15;
  letter-spacing: -0.01em; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.mk-shop-meta {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px;
}
.mk-shop-count { font-size: 13px; color: var(--text-mute); font-variant-numeric: tabular-nums; }
.mk-shop-visit {
  flex: 0 0 auto;
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 44px; padding: 8px 14px;          /* §8.2 */
  border-radius: 999px; border: 1.5px solid var(--border);
  background: var(--bk-surface-2);
  font-size: 13px; font-weight: 600; color: var(--text-mute);
  text-decoration: none; white-space: nowrap;
  transition: color 140ms ease, border-color 140ms ease;
}
.mk-shop-visit:hover { color: var(--brand-primary); border-color: var(--brand-primary); }
.mk-shop-visit:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }

/* Mobile sticky compact bar — fixed at top while body.mk-compact-on (the full
   strip has scrolled out); the sticky filter bar shifts down beneath it so
   both stay usable. Mobile-only; desktop keeps the full header (it's short).
   transform/opacity only (§4.4 motion rule), reduced-motion covered by the
   global §8.3 block below. */
.mk-shop-compact {
  position: fixed; top: 0; left: 0; right: 0;
  z-index: calc(var(--z-sticky) + 1);
  display: none;
  align-items: center; gap: 8px;
  min-height: 48px; padding: 6px 12px;
  background: var(--bk-surface-2);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--bk-elev-base);
  transform: translateY(-100%);
  transition: transform 200ms cubic-bezier(0.2, 0, 0, 1);
}
.mk-shop-compact[hidden] { display: none; }
.mk-compact-back {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px; flex: 0 0 auto;   /* §8.2 */
  border-radius: 999px;
  color: var(--text); font-size: 22px; font-weight: 600;
  text-decoration: none;
  transition: background-color 140ms ease;
}
.mk-compact-back:hover { background: var(--bk-surface-inset); }
.mk-compact-back:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }
.mk-compact-name {
  flex: 1 1 auto; min-width: 0;
  font-weight: 600; font-size: 15px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (max-width: 767px) {
  .mk-shop-compact:not([hidden]) { display: flex; }
  body.mk-compact-on .mk-shop-compact:not([hidden]) { transform: translateY(0); }
  body.mk-compact-on .mk-filters { top: 56px; }   /* slide under the compact bar */
}
@media (max-width: 639px) {
  /* Narrow phones: let the visit link wrap under the identity row. */
  .mk-shop-strip { flex-wrap: wrap; padding: 12px 14px; gap: 10px 12px; }
}

/* Empty/missing-state CTA can be a link (seller-page "ไปหน้าตลาด Bookku"). */
.mk-state a.mk-btn { text-decoration: none; }

/* card body (mock .mk-card-body) — flex column, brand-first display face. */
.prod-card .meta {
  padding: 9px 11px 11px;
  display: flex; flex-direction: column; gap: 2px; flex: 1;
}
/* brand — Cormorant display line, single line ellipsis (mock .mk-card-brand). */
.prod-card .mk-card-brand {
  font-family: var(--font-display);
  font-size: clamp(13.5px, 8cqi, 17px); font-weight: 600;
  line-height: 1.15; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* name — single line, ellipsis (mock .mk-card-name; was 2-line clamp before). */
.prod-card .name {
  font-size: clamp(10.5px, 6cqi, 12px); color: var(--mkt-ink-secondary);
  line-height: 1.4; font-weight: 400; margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* shop line (mock .mk-card-shop) — verified shield OR store glyph + shop name.
   v2.1 (issue #1): the shop is a QUIET attribution line, clearly subordinate to
   the product identity (brand + name). Smaller + muted ink (≥4.5:1 on cream),
   regular weight, a hair more separation from the name above. The green verified
   shield stays inline when badge==='green'. */
.prod-card .mk-shop {
  display: flex; align-items: center; gap: 4px;
  font-size: clamp(9px, 4.4cqi, 10.5px); font-weight: 400;
  color: var(--mkt-ink-muted);
  margin-top: 5px; min-width: 0;
}
.prod-card .mk-shop .mk-shop-name {
  /* issue #1 ROOT CAUSE: the seller-page title rule `.mk-shop-name`
     (font-size: clamp(20px,5vw,28px), display face) was leaking onto the CARD
     shop span (same class), so the shop rendered LARGE and competed with the
     product. Force it back to the quiet attribution size + body font here. */
  font-family: var(--font-body);
  font-size: inherit; font-weight: 400; letter-spacing: 0; line-height: 1.3;
  margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  color: var(--mkt-ink-muted);
}
.prod-card .mk-shop .mk-shop-glyph { width: 12px; height: 12px; flex: none; color: var(--mkt-ink-muted); opacity: .7; }
.prod-card .mk-shop .mk-ic-verified { width: 13px; height: 13px; flex: none; color: var(--bk-success); }
@media (max-width: 760px) {
  .prod-card .mk-shop .mk-shop-glyph,
  .prod-card .mk-shop .mk-ic-verified { width: 11px; height: 11px; }
}
/* Journey Phase 2 (2c) — the shop line stays a DISTINCT seller-page tap target
   (≥44px via padding + compensating negative margins), lifted above the
   stretched product link, reading as a link on hover/focus (cognac ink). */
a.mk-shop-link {
  position: relative; z-index: 2;
  min-height: 44px; padding: 12px 6px 12px 0;
  margin: -10px 0 -12px;
  text-decoration: none;
  transition: color 140ms ease;
}
a.mk-shop-link:hover, a.mk-shop-link:focus-visible {
  color: var(--mkt-accent); text-decoration: none;
}
a.mk-shop-link:hover .mk-shop-name,
a.mk-shop-link:focus-visible .mk-shop-name { text-decoration: underline; }
a.mk-shop-link:focus-visible {
  outline: 3px solid var(--mkt-accent-ring);
  outline-offset: -2px; border-radius: var(--bk-radius-sm);
}
/* bottom row (mock .mk-card-foot) — Wave A (2026-07-04): the foot is a fixed
   two-slot row — a LEFT group (condition chip + "ติดต่อร้าน" pill, or an empty
   spacer) and the price cluster pinned bottom-RIGHT. justify-content:space-between
   with the left group ALWAYS present (spacer when the pill is absent) keeps the
   price's x-position identical on every card. */
.prod-card .mk-card-foot {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 6px; margin-top: auto; padding-top: 8px;
}
/* the left group — cond chip + contact pill stacked/inline; shrinkable so the
   price is never pushed off the right edge on a narrow card. */
.prod-card .mk-foot-left {
  display: flex; align-items: flex-end; flex-wrap: wrap; gap: 4px;
  min-width: 0; flex: 0 1 auto;
}
/* empty spacer that reserves the left slot when there is no pill — keeps the
   space-between geometry (price stays bottom-right) with zero visual weight. */
.mk-foot-spacer { display: block; width: 0; height: 0; }
@media (max-width: 760px) {
  /* narrow 3-up cards: stack the left group above the price so neither clips,
     but keep the price aligned to the right edge (align-self on .prod-price-row). */
  .prod-card .mk-card-foot { flex-direction: column; align-items: stretch; gap: 4px; padding-top: 6px; }
}
/* condition-grade chip (mock .mk-chip-cond) — quiet frosted micro-pill. */
.mk-cond {
  flex: none;
  font-size: clamp(9px, 4.4cqi, 10.5px); font-weight: 500; letter-spacing: 0.01em;
  line-height: 1.5;
  padding: 2px 7px; border-radius: var(--bk-radius-sm);
  color: var(--mkt-ink-secondary);
  background: var(--mkt-surface-inset);
  border: 1px solid var(--mkt-line-soft);
}
@media (max-width: 760px) { .mk-cond { font-size: 9px; padding: 1px 5px; } }
.prod-card .prod-price-row {
  display: flex; align-items: baseline; justify-content: flex-end;
  gap: 4px 6px; min-width: 0; flex: 0 1 auto;
}
/* FIX 3 (2026-07-04) — on a narrow 3-up grid card at 375px a 7-digit promo pair
   (฿1,450,000 struck + ฿1,290,000) cannot fit on ONE line; allow the row to wrap
   so the struck original drops onto its own line ABOVE the promo price instead of
   clipping its leading digits. Individual figures keep white-space:nowrap so no
   single number ever breaks mid-digit. */
@media (max-width: 760px) { .prod-card .prod-price-row { align-self: flex-end; flex-wrap: wrap; } }
.prod-card .prod-price-row .price {
  margin: 0; font-weight: 600;
  font-family: var(--price-font);
  font-size: clamp(11px, 7cqi, 14.5px);
  color: var(--mkt-ink);
  font-variant-numeric: tabular-nums;   /* §3.4 tabular */
  text-align: right; white-space: nowrap; flex: 0 0 auto;
}
/* Promo (Wave A 2026-07-04): struck ORIGINAL list price in muted gray, shown
   BEFORE the (red) promo price. Never rendered for masked/contact cards. */
.mk-price-original {
  font-family: var(--price-font);
  font-size: clamp(10px, 6cqi, 12px);
  color: var(--mkt-ink-muted);
  text-decoration: line-through;
  font-variant-numeric: tabular-nums;
  white-space: nowrap; flex: 0 0 auto;
}
/* the discounted (effective) price rendered in the sale red. */
.price.mk-price-promo, .mk-feat-price.mk-price-promo { color: var(--mkt-sale); }
/* "สอบถามราคา" (contact-only, price-less) — the mock's .mk-price-ask look. */
.prod-card .prod-price-row .price.mk-price-ask {
  font-weight: 600; color: var(--mkt-ink-secondary);
  font-size: clamp(11px, 6cqi, 12.5px);
}
/* Piece M — masked-price cards carry a quiet "ติดต่อร้าน" hint beside the
   '฿52x,xxx' figure (muted meta ink ≥4.5:1). NEVER echoes an exact figure. */
.prod-card .mk-contact-hint {
  flex: 0 1 auto; min-width: 0;
  font-size: clamp(9px, 4.4cqi, 10.5px); font-weight: 500;
  letter-spacing: 0.01em; line-height: 1.5;
  padding: 2px 7px; border-radius: var(--bk-radius-sm);
  color: var(--mkt-ink-muted);
  background: var(--mkt-surface-inset);
  border: 1px solid var(--mkt-line-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ── States: loading skeleton / empty / error ──────────────────── */
.bk-skel {
  border-radius: 18px;
  background:
    linear-gradient(100deg,
      color-mix(in oklch, var(--brand-primary) 5%, var(--bk-surface-inset)) 30%,
      color-mix(in oklch, var(--brand-primary) 10%, var(--bk-surface-inset)) 50%,
      color-mix(in oklch, var(--brand-primary) 5%, var(--bk-surface-inset)) 70%);
  background-size: 200% 100%;
  animation: mk-shimmer 1.3s linear infinite;
}
.bk-skel-prod { aspect-ratio: 1 / 1; }
@keyframes mk-shimmer { to { background-position: -200% 0; } }
@media (prefers-reduced-motion: reduce) { .bk-skel { animation: none; } }

.mk-state {
  grid-column: 1 / -1;
  text-align: center; padding: 48px 16px;
  color: var(--text-mute);
}
.mk-state svg { width: 64px; height: 64px; color: var(--bk-ink-subtle); margin-bottom: 12px; }
.mk-state h2 {
  margin: 0 0 6px; font-family: var(--display-font);
  font-weight: 500; font-size: 22px; color: var(--text);
}
.mk-state p { margin: 0 0 16px; font-size: 14px; }

/* Busy-retry spinner for the 429/503 auto-retry state (bk-marketplace.js
   renderBusy). Brand-token colours only — no raw hex (§2.5 / §9.4). Decorative
   (aria-hidden); the .mk-state text carries the message, so under reduced motion
   the ring simply stops — content stays fully readable (§8.3 / §8.6). */
.mk-spin {
  display: inline-block;
  width: 40px; height: 40px;
  margin-bottom: 12px;
  border-radius: 50%;
  border: 3px solid var(--brand-primary-soft);
  border-top-color: var(--brand-primary);
  animation: mk-spin 0.8s linear infinite;
}
@keyframes mk-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .mk-spin { animation: none; } }

/* ── Pagination ────────────────────────────────────────────────── */
.mk-more { text-align: center; padding: 16px 0 40px; }
.mk-more[hidden] { display: none; }

/* ── Footer ────────────────────────────────────────────────────── */
.mk-footer {
  margin-top: 24px; padding: 28px 16px 40px;
  border-top: 1px solid var(--border);
  color: var(--text-mute); font-size: 13px; text-align: center;
}
.mk-footer .mk-disclaimer { max-width: 60ch; margin: 8px auto 0; text-wrap: pretty; }
.mk-footer-links {
  margin: 12px auto 0; display: flex; gap: 8px 14px;
  align-items: center; justify-content: center; flex-wrap: wrap;
}
.mk-footer-links a {
  color: var(--text-mute); text-decoration: none;
  border-bottom: 1px solid transparent; transition: color 160ms ease, border-color 160ms ease;
}
.mk-footer-links a:hover { color: var(--brand-primary); border-bottom-color: var(--brand-primary-ring); }
.mk-footer-links .mk-dot { color: var(--bk-ink-subtle); }

/* ── Account chip in the topbar (logged-out button / logged-in identity) ──
   Bookku-blue chrome is intentional: the marketplace is the APEX cross-tenant
   surface (--brand-primary = trust-blue, see :root note above / DESIGN.md §2.1).
   §11.2's tenant-cascade "no Bookku-blue" rule governs CUSTOMER auth INSIDE a
   tenant shop.html — it does NOT apply to this apex chrome. */
.mk-account { display: inline-flex; align-items: center; gap: 8px; }
.mk-account-btn {                       /* logged-OUT: "เข้าสู่ระบบ / สมัคร" */
  min-height: 40px; padding: 8px 16px;
  border-radius: 999px; border: 1.5px solid transparent;
  background: var(--brand-primary); color: var(--brand-primary-ink);
  font-family: var(--body-font); font-weight: 600; font-size: 14px;
  cursor: pointer; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 6px;
  transition: transform 160ms ease, background-color 160ms ease;
}
.mk-account-btn svg { width: 16px; height: 16px; }
.mk-account-btn:hover  { background: var(--brand-primary-hover); }
.mk-account-btn:active { background: var(--brand-primary-active); transform: translateY(1px); }
.mk-account-btn:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }
/* v2.1 NAV — logged-out profile is a QUIET icon-only button in the slim top nav
   (not the loud filled brand pill). §8.2 44px, §11.0 SVG glyph. */
.mk-account-btn.mk-account-icon {
  width: 44px; height: 44px; min-height: 0; padding: 0;
  justify-content: center; gap: 0;
  background: transparent; color: var(--mkt-ink-secondary);
  border-radius: var(--bk-radius-md);
  transition: background-color var(--bk-dur-exit) var(--bk-ease-standard),
              color var(--bk-dur-exit) var(--bk-ease-standard);
}
.mk-account-btn.mk-account-icon .mk-ic { width: 22px; height: 22px; }
.mk-account-btn.mk-account-icon:hover { background: var(--mkt-surface-inset); color: var(--mkt-ink); }
.mk-account-btn.mk-account-icon:active { background: var(--mkt-surface-inset); transform: translateY(1px); }
.mk-account-btn.mk-account-icon:focus-visible { outline: 3px solid var(--mkt-accent-ring); outline-offset: -2px; }

.mk-account-id {                        /* logged-IN identity pill */
  display: inline-flex; align-items: center; gap: 8px;
  min-height: 40px; padding: 6px 8px 6px 12px;
  border-radius: 999px;
  background: var(--brand-primary-tint);
  border: 1.5px solid var(--border);
  max-width: min(60vw, 320px);
}
.mk-account-avatar {
  width: 26px; height: 26px; flex: 0 0 auto;
  border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--brand-primary); color: var(--brand-primary-ink);
  font-weight: 700; font-size: 12px;
}
.mk-account-name {
  font-size: 14px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mk-logout-btn {
  min-height: 32px; min-width: 44px; padding: 6px 12px;
  border-radius: 999px; border: 1.5px solid var(--border);
  background: var(--bk-surface-2); color: var(--text-mute);
  font-family: var(--body-font); font-weight: 600; font-size: 13px;
  cursor: pointer; white-space: nowrap;
  transition: color 160ms ease, border-color 160ms ease;
}
.mk-logout-btn:hover { color: var(--text); border-color: var(--text-mute); }
.mk-logout-btn:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }

/* ── Auth modal (§4.4 dialog motion · §6.4 z-index ladder · §4.14 overlay) ──
   Native <dialog>-style centered modal (fixed-position, NOT absolute-in-overflow
   — §4.14 clipping ban). Backdrop = --z-modal-backdrop, card = --z-modal. */
.mk-modal-backdrop {
  position: fixed; inset: 0; z-index: 1200;      /* --z-modal-backdrop (§6.4) */
  background: color-mix(in oklch, var(--bk-ink) 55%, transparent);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center;
  padding: 16px;
  animation: mk-fade-in var(--mk-dur-enter, 200ms) linear;
}
.mk-modal-backdrop[hidden] { display: none; }
.mk-modal {
  position: relative; z-index: 1300;              /* --z-modal (§6.4) */
  width: 100%; max-width: 420px;
  max-height: calc(100dvh - 32px); overflow-y: auto;
  background: var(--bk-surface-2);
  border-radius: 18px;                            /* §5 dialog radius ceiling */
  box-shadow: var(--bk-elev-float);               /* §6.1 brand-mixed float */
  padding: 24px 24px 22px;
  /* §4.4 zoom-in + fade, spring easing; transform/opacity only. */
  animation: mk-zoom-in var(--mk-dur-enter, 200ms) cubic-bezier(0.34, 1.56, 0.64, 1),
             mk-fade-in var(--mk-dur-enter, 200ms) linear;
}
@keyframes mk-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes mk-zoom-in { from { transform: scale(0.95); } to { transform: scale(1); } }

.mk-modal-close {
  position: absolute; top: 12px; right: 12px;
  width: 44px; height: 44px;                      /* §8.2 touch target */
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: transparent; color: var(--text-mute);
  border-radius: 999px; cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease;
}
.mk-modal-close:hover { background: var(--bk-surface-inset); color: var(--text); }
.mk-modal-close:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }
.mk-modal-close svg { width: 20px; height: 20px; }

.mk-modal h2 {
  margin: 0 4px 4px 0; padding-right: 28px;
  font-family: var(--display-font); font-weight: 500;
  font-size: 24px; letter-spacing: -0.01em; color: var(--text);
}
.mk-modal-sub { margin: 0 0 18px; font-size: 14px; color: var(--text-mute); }

/* Social auth buttons (Google / LINE) — full-width provider buttons */
.mk-oauth { display: flex; flex-direction: column; gap: 10px; }
.mk-oauth-btn {
  min-height: 48px; padding: 10px 16px;
  border-radius: 12px; border: 1.5px solid var(--border);
  background: var(--bk-surface-2); color: var(--text);
  font-family: var(--body-font); font-weight: 600; font-size: 15px;
  cursor: pointer; text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; gap: 10px;
  transition: border-color 160ms ease, background-color 160ms ease, transform 160ms ease;
}
/* FIX 9 (2026-07-04 Wave C) — the `.mk-oauth-btn { display: inline-flex }` author
   rule beats the UA `[hidden]{display:none}`, so a hidden OAuth button (e.g. the
   pk-requested "hide Google" #mk-auth-google[hidden]) still paints. Restore the
   hide explicitly (mirrors .mk-fab[hidden] above). `.mk-oauth-btn[hidden]` (0,1,1)
   outranks `.mk-oauth-btn` (0,1,0) on specificity, so it wins regardless of order. */
.mk-oauth-btn[hidden] { display: none; }
.mk-oauth-btn:hover  { border-color: var(--text-mute); }
.mk-oauth-btn:active { transform: translateY(1px); }
.mk-oauth-btn:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }
.mk-oauth-btn svg, .mk-oauth-btn img { width: 20px; height: 20px; flex: 0 0 auto; }
.mk-oauth-line { background: #06C755; color: #FFF; border-color: #06C755; }
.mk-oauth-line:hover { background: #05b34c; border-color: #05b34c; }

/* Divider between social + phone */
.mk-or {
  display: flex; align-items: center; gap: 12px;
  margin: 16px 0; color: var(--bk-ink-subtle); font-size: 12px;
}
.mk-or::before, .mk-or::after {
  content: ""; flex: 1 1 auto; height: 1px; background: var(--border);
}

/* Phone-OTP form */
.mk-field { margin: 0 0 12px; }
.mk-label { display: block; margin: 0 0 6px; font-size: 13px; font-weight: 600; color: var(--text); }
.mk-input {
  width: 100%; min-height: 48px;
  padding: 10px 14px;
  font-family: var(--body-font); font-size: 16px; color: var(--text);
  background: var(--bk-surface-2);
  border: 1.5px solid var(--border); border-radius: 12px;
  transition: border-color 160ms ease, box-shadow 160ms ease;
}
.mk-input::placeholder { color: var(--bk-ink-subtle); }
.mk-input:hover { border-color: var(--text-mute); }
.mk-input:focus-visible {
  outline: none; border-color: var(--brand-primary);
  box-shadow: 0 0 0 3px var(--brand-primary-ring);
}
.mk-input-code { letter-spacing: 0.35em; font-variant-numeric: tabular-nums; text-align: center; }

.mk-btn-block { width: 100%; margin-top: 4px; }
.mk-btn[disabled], .mk-oauth-btn[aria-disabled="true"] { opacity: 0.55; cursor: not-allowed; }

/* Inline hint / error under the OTP form */
.mk-hint { margin: 8px 0 0; font-size: 13px; color: var(--text-mute); }
.mk-error { margin: 8px 0 0; font-size: 13px; color: #B91C1C; }
.mk-error[hidden], .mk-hint[hidden] { display: none; }

/* "back to phone / resend" secondary link */
.mk-linkbtn {
  background: none; border: none; padding: 4px 2px; margin: 0;
  font-family: var(--body-font); font-size: 13px; font-weight: 600;
  color: var(--brand-primary); cursor: pointer; text-decoration: underline;
}
.mk-linkbtn:disabled { color: var(--text-mute); cursor: default; text-decoration: none; }
.mk-linkbtn:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; border-radius: 4px; }

/* Consent line (PDPA §1) — inline disclosure with a checkbox (not pre-ticked) */
.mk-consent {
  display: flex; gap: 10px; align-items: flex-start;
  margin: 16px 0 4px; padding: 12px;
  background: var(--brand-primary-tint);
  border: 1px solid var(--border); border-radius: 12px;
  font-size: 12.5px; line-height: 1.55; color: var(--text-mute);
}
.mk-consent input[type="checkbox"] {
  flex: 0 0 auto; width: 20px; height: 20px; margin: 1px 0 0;
  accent-color: var(--brand-primary); cursor: pointer;
}
.mk-consent label { cursor: pointer; }
.mk-consent a { color: var(--brand-primary); font-weight: 600; text-decoration: underline; }

.mk-stage[hidden] { display: none; }

/* ── PDPA / legal pages (marketplace-privacy.html · marketplace-terms.html) ── */
.mk-legal {
  max-width: 760px; margin: 0 auto; padding: 32px 20px 64px;
}
.mk-legal .mk-legal-back {
  display: inline-flex; align-items: center; gap: 6px;
  margin-bottom: 20px; font-size: 14px; font-weight: 600;
  color: var(--brand-primary); text-decoration: none;
}
.mk-legal .mk-legal-back:hover { text-decoration: underline; }
.mk-legal .mk-legal-back svg { width: 16px; height: 16px; }
.mk-legal h1 {
  margin: 0 0 4px; font-weight: 500;
  font-size: clamp(26px, 6vw, 34px); line-height: 1.2; letter-spacing: -0.01em;
  color: var(--text);
}
.mk-legal .mk-legal-meta { margin: 0 0 28px; font-size: 13px; color: var(--text-mute); }
.mk-legal h2 {
  margin: 30px 0 8px; font-weight: 500;
  font-size: clamp(18px, 4.4vw, 22px); letter-spacing: -0.01em; color: var(--text);
}
.mk-legal p { margin: 0 0 12px; font-size: 15.5px; line-height: 1.7; color: var(--text); text-wrap: pretty; }
.mk-legal ul { margin: 0 0 12px; padding-left: 22px; }
.mk-legal li { margin: 0 0 8px; font-size: 15.5px; line-height: 1.7; color: var(--text); }
.mk-legal a { color: var(--brand-primary); font-weight: 600; }
.mk-legal .mk-legal-lead {
  padding: 14px 16px; margin: 0 0 24px;
  background: var(--brand-primary-tint); border: 1px solid var(--border);
  border-radius: 14px; font-size: 15px; color: var(--text-mute);
}
/* Visible placeholder token (e.g. {dpo_email}) — pk fills before launch (§ PDPA notes) */
.mk-token {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.92em; padding: 1px 6px; border-radius: 6px;
  background: color-mix(in oklch, var(--brand-primary) 10%, white);
  color: var(--brand-primary-active); white-space: nowrap;
}

/* ══ Journey Phase 3 (2026-07-02) — marketplace-native PDP ═══════════════
   /marketplace/p/<slug>?shop=<tid> inside the Bookku-blue apex shell
   (§2.1 brand axis · §2.2 cool-white canvas). Mobile-first single column
   (gallery → name/condition → price → CTA above the fold → merchant-of-
   record → shop card → description); two columns from 860px (§8.1). While
   the pdp owns the surface, body.mk-pdp-on hides the grid + filter bar —
   the footer (trust disclaimer) stays on every view. */
body.mk-pdp-on .mk-filters,
body.mk-pdp-on #mk-main { display: none; }

.mk-pdp-wrap { padding: 12px 0 40px; }
.mk-pdp-crumb { margin: 4px 0 10px; }        /* reuses .mk-shop-crumb type */

.mk-pdp-grid { display: grid; grid-template-columns: 1fr; gap: 18px; }
@media (min-width: 860px) {                   /* §8.1 desktop split */
  .mk-pdp-grid { grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr); gap: 32px; }
}

/* Gallery — full-bleed 1:1 cover like the cards (§4.2 media slot); resting
   card surface + soft elevation (§6.1 tier 1, radius ≤18px per the §1 ban). */
.mk-pdp-main {
  aspect-ratio: 1 / 1; overflow: hidden;
  border-radius: 18px; border: 1px solid var(--border);
  background: var(--bk-surface-1);
  box-shadow: var(--bk-elev-card);
  display: flex; align-items: center; justify-content: center;
}
.mk-pdp-main img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-pdp-main .mk-noimg { width: 44%; height: 44%; color: var(--bk-ink-subtle); }
.mk-pdp-main .mk-noimg svg { width: 100%; height: 100%; }
.mk-pdp-thumbs { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.mk-pdp-thumb {
  width: 64px; height: 64px; padding: 0;      /* ≥44px tap target (§8.2) */
  border-radius: 12px; overflow: hidden; cursor: pointer;
  border: 2px solid var(--border); background: var(--bk-surface-1);
}
.mk-pdp-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mk-pdp-thumb.is-active { border-color: var(--brand-primary); }
.mk-pdp-thumb:focus-visible { outline: 3px solid var(--brand-primary-ring); outline-offset: 2px; }

/* Identity block — ink hierarchy per §2.3; taxonomy line stays muted meta. */
.mk-pdp-name {
  margin: 0 0 6px; font-weight: 700;
  font-size: clamp(20px, 5vw, 26px); line-height: 1.25;
  letter-spacing: -0.01em; color: var(--text);
}
.mk-pdp-meta { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 10px; }
.mk-pdp-meta .mk-cond { margin: 0; }
.mk-pdp-tax { font-size: 12.5px; color: var(--text-mute); }

/* Price — tabular ฿ digits (§3.4/§3.6, --price-font mirrors the cards);
   masked items reuse the card's contact-hint chip grammar (Piece M). */
.mk-pdp-price-row { display: flex; align-items: center; gap: 10px; margin: 12px 0 0; }
.mk-pdp-price {
  font-family: var(--price-font); font-weight: 700;
  font-size: clamp(24px, 6vw, 32px); color: var(--text);
  font-variant-numeric: tabular-nums;
}
.mk-pdp-price-row .mk-contact-hint {          /* pdp twin of the card chip */
  font-size: 11px; font-weight: 600; letter-spacing: 0.02em;
  padding: 3px 10px; border-radius: 999px;
  color: var(--bk-ink-muted);                  /* §2.3 muted ink ≥4.5:1 (§2.7) */
  background: color-mix(in oklch, var(--bk-ink-muted) 8%, white);
  border: 1px solid var(--border);
}

/* THE CTA (amendment #1) — the page's ONE primary action (§4.5 button
   states via .mk-btn-primary; §8.2 comfortable 52px target). Above the fold
   on the 375px reference viewport (§8.4). */
.mk-pdp-cta {
  display: flex; width: 100%; min-height: 52px;
  margin: 14px 0 0; font-size: 16.5px;
  text-decoration: none;
}
/* Expectation microcopy + merchant-of-record disclosure (amendment #9) —
   small but visible muted meta (§2.3; ≥4.5:1 per §2.7), never footer-tucked. */
.mk-pdp-micro { margin: 8px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--text-mute); }
.mk-pdp-mor {
  margin: 10px 0 0; font-size: 12.5px; font-weight: 600; color: var(--text-mute);
  padding: 6px 10px; border-radius: 10px;
  background: var(--brand-primary-tint); border: 1px solid var(--border);
  width: fit-content;
}

/* Shop card — seller identity BEFORE any click-out (§4.2 card grammar,
   §6.1 tier 1; monogram avatar + trust chip reuse the seller-page pieces). */
.mk-pdp-shopcard {
  display: flex; align-items: center; gap: 12px;
  margin: 18px 0 0; padding: 12px 14px;
  background: var(--bk-surface-1); border: 1px solid var(--border);
  border-radius: 14px; box-shadow: var(--bk-elev-card);
}
.mk-pdp-shopcard .mk-shop-avatar { width: 44px; height: 44px; font-size: 18px; }
.mk-pdp-shopcard-id { flex: 1 1 auto; min-width: 0; }
.mk-pdp-shopname {
  font-weight: 600; font-size: 15px; color: var(--text);
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
}
.mk-pdp-shoplinks { display: flex; flex-wrap: wrap; gap: 2px 16px; margin-top: 2px; }
.mk-pdp-shoplinks a {
  display: inline-flex; align-items: center; gap: 3px;
  min-height: 44px; padding: 6px 0;            /* §8.2 row-height targets */
  margin: -8px 0;
  font-size: 13px; font-weight: 600; color: var(--brand-primary);
  text-decoration: none;
}
.mk-pdp-shoplinks a:hover, .mk-pdp-shoplinks a:focus-visible { text-decoration: underline; }
.mk-pdp-shoplinks a:focus-visible {
  outline: 3px solid var(--brand-primary-ring); outline-offset: -2px; border-radius: 8px;
}

/* Description — readable measure (§3.7) + preserved line breaks. */
.mk-pdp-desc { margin: 22px 0 0; }
.mk-pdp-desc h2 { margin: 0 0 8px; font-size: 15px; font-weight: 700; color: var(--text); }
.mk-pdp-desc p {
  margin: 0; max-width: 65ch;
  font-size: 14.5px; line-height: 1.75; color: var(--text);
  white-space: pre-line; overflow-wrap: anywhere;
}

/* Loading skeleton — reuses the §8.3-safe .bk-skel shimmer. */
.mk-pdp-skel-img { aspect-ratio: 1 / 1; }
.mk-pdp-skel-line { height: 22px; margin: 0 0 12px; border-radius: 8px; }
.mk-pdp-skel-cta { height: 52px; margin-top: 18px; border-radius: 14px; }

/* ── Operator-owned promo banners (Wave C3, 2026-07-04) ──────────────────
   Marketplace-wide TOP (below hero) + BOTTOM (before footer) banners the
   operator manages. "Not too big" (pk) — a slim editorial strip, NOT a hero.
   Cream canvas (§2.2 --mkt-canvas), dimensional card shadow (§6 / --mkt-elev-card),
   cognac hairline (§2.2 --mkt-line). Constrained to the .mk-wrap measure so it
   lines up with the hero + shelf + grid. The image scales responsively; a mobile
   crop (image_url_mobile) swaps in via <picture>. Fail-open: JS leaves the
   [hidden] section empty when there are no banners, so nothing paints. */
.mk-promo { padding: 0; }
.mk-promo[hidden] { display: none; }
.mk-promo .mk-wrap { padding-top: 0; padding-bottom: 0; }
.mk-promo-top    { margin: clamp(8px, 2vh, 20px) 0 clamp(4px, 1.5vh, 12px); }
.mk-promo-bottom { margin: clamp(20px, 4vh, 40px) 0 clamp(8px, 2vh, 16px); }
/* One or more stacked banners inside a placement (sort_order asc). */
.mk-promo__list { display: flex; flex-direction: column; gap: 12px; }
/* The banner card — a link (or a bare figure when no link_url). Clipped rounded
   card so the image corners follow the radius; §4.14 no absolute-in-overflow. */
.mk-promo__item {
  display: block;
  position: relative;
  border-radius: var(--bk-radius-lg, 16px);
  overflow: hidden;
  background: var(--mkt-surface-1);
  border: 1px solid var(--mkt-line-soft);
  box-shadow: var(--mkt-elev-card);
  text-decoration: none;
  line-height: 0;                 /* kill inline-image descender gap */
}
a.mk-promo__item { cursor: pointer; transition: box-shadow var(--bk-dur-enter, .18s) var(--bk-ease-standard, ease), transform var(--bk-dur-enter, .18s) var(--bk-ease-standard, ease); }
a.mk-promo__item:hover { box-shadow: var(--mkt-elev-float); transform: translateY(-1px); }
a.mk-promo__item:focus-visible { outline: 2px solid var(--mkt-accent); outline-offset: 2px; }
.mk-promo__img {
  display: block; width: 100%; height: auto;
  object-fit: cover;
}
/* "Not too big" height caps (§3.2). Top a touch taller than bottom; images with
   an intrinsic ratio are capped so a tall asset can't blow the strip up. */
.mk-promo-top    .mk-promo__img { max-height: 120px; }
.mk-promo-bottom .mk-promo__img { max-height: 100px; }
@media (max-width: 640px) {
  .mk-promo-top    .mk-promo__img { max-height: 96px; }
  .mk-promo-bottom .mk-promo__img { max-height: 84px; }
}
/* Optional caption overlay when a headline/subtext is supplied — a soft
   bottom-anchored ink scrim so text stays legible over any image (§2.7 contrast).
   Kept OUT of overflow-clipped absolute traps by living inside the clipped card. */
.mk-promo__cap {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 10px 16px;
  line-height: 1.25;
  color: #fff;
  background: linear-gradient(to top,
    color-mix(in srgb, var(--mkt-ink) 62%, transparent), transparent);
}
.mk-promo__head { font-family: var(--font-display); font-style: italic; font-weight: 500; font-size: clamp(15px, 2.4vw, 20px); }
.mk-promo__sub  { font-size: clamp(12px, 1.8vw, 14px); opacity: .92; }

/* ── §8.3 reduced-motion — collapse all animation/motion (paste block) ── */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ── Wave C2 (2026-07-04) — SAVE / HEART control on product cards ─────────────
   A subtle, cream/cognac save button pinned top-right of the card image. It is a
   REAL <button> (keyboard reachable, aria-pressed reflects state). Sits ABOVE the
   stretched .prod-card-link::after hit-area (z-index) so the tap toggles save and
   never triggers the card navigation. Empty = hollow cognac outline heart; saved =
   filled cognac. §8.2 44px effective tap target (36px visual + generous padding). */
.mk-heart {
  position: absolute; top: 8px; right: 8px; z-index: 3;
  width: 36px; height: 36px; padding: 0; margin: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: var(--bk-radius-pill);
  border: 1px solid var(--mkt-line-soft);
  background: color-mix(in srgb, var(--mkt-surface-2) 82%, transparent);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  color: var(--mkt-accent); cursor: pointer;
  box-shadow: 0 1px 3px 0 color-mix(in oklch, var(--mkt-accent) 12%, transparent);
  transition: transform var(--bk-dur-enter) var(--bk-ease-standard),
              background var(--bk-dur-enter) var(--bk-ease-standard),
              border-color var(--bk-dur-enter) var(--bk-ease-standard);
}
.mk-heart:hover { border-color: var(--mkt-accent); transform: translateY(-1px); }
.mk-heart:focus-visible {
  outline: 2px solid var(--mkt-accent-ring); outline-offset: 2px;
}
.mk-heart-ic {
  width: 19px; height: 19px; display: block;
  fill: none; stroke: currentColor; stroke-width: 1.8;
  stroke-linejoin: round; stroke-linecap: round;
  transition: fill var(--bk-dur-enter) var(--bk-ease-standard);
}
/* Saved: filled cognac heart + a warm tinted chip. */
.mk-heart.is-saved { color: var(--mkt-accent); border-color: var(--mkt-accent); }
.mk-heart.is-saved .mk-heart-ic { fill: currentColor; }
/* A tiny pop when toggled on (reduced-motion users skip it via the global block). */
@media (prefers-reduced-motion: no-preference) {
  .mk-heart.is-saved .mk-heart-ic { animation: mk-heart-pop 260ms var(--bk-ease-standard); }
}
@keyframes mk-heart-pop {
  0% { transform: scale(0.7); } 55% { transform: scale(1.18); } 100% { transform: scale(1); }
}
/* Featured card: same control, nudged in from the larger media's rounded corner. */
.mk-feat .mk-heart { top: 10px; right: 10px; z-index: 3; }
