/** Shopify CDN: Minification failed

Line 120:60 Unexpected "*"
Line 121:95 Unterminated string token

**/
/* TESSAVA — Dwell variable bridge.
 *
 * Dwell's components read Dwell's own custom properties. TESSAVA's canonical values live in
 * tessava-tokens.css, a byte-identical copy of the generated design-system/tokens.css. This
 * file is the join between them: every Dwell variable listed here is redefined to point at a
 * TESSAVA semantic token.
 *
 * WHY A BRIDGE AND NOT A REWRITE
 * Mapping variables means all 480 vendor files inherit TESSAVA's system without one of them
 * being edited. The alternative — restyling components individually — would fork the vendor
 * tree, forfeit upstream Dwell updates, and turn every future theme update into a merge.
 *
 * WHY NOT THEME SETTINGS
 * Dwell exposes colour and font pickers in the theme editor. Brand values driven from there
 * can be changed by anyone with editor access, silently, with no ADR and no validator run.
 * Frozen values belong in code, where SYS/CODE rules can see them. Merchant settings stay for
 * genuinely merchant-owned choices — the logo image, menu contents, section arrangement.
 *
 * ZERO LITERALS
 * Every declaration below is a var() reference. There is no px, no hex, no ms in this file,
 * which is what lets CODE-002 read it without an exemption. If a value you need has no token,
 * that is a registry gap closed through token-registry.md §17 — never a literal added here.
 *
 * DELIBERATELY PARTIAL
 * This maps only variables observed in the Dwell source and only for surfaces Phase B1 owns.
 * Buttons, product cards, price, form controls and drawers are mapped by the phase that
 * implements their contract — Phase C onward. An unmapped variable keeps Dwell's default,
 * which is visible and reviewable; a speculatively mapped one is a guess nobody reviewed.
 */

:root {
  /* ── Page ground and text hierarchy — colors.md §2, §7 ───────────────── */
  --color-background: var(--color-surface-canvas);
  --color-foreground: var(--color-text-primary);
  --color-foreground-subdued: var(--color-text-secondary);
  --color-foreground-muted: var(--color-text-tertiary);

  /* ── Edges — radii-borders.md. Dwell's generic border is TESSAVA's
   *    structural, non-interactive edge. The interactive resting edge
   *    (--color-border-strong, the one verified against SC 1.4.11) is applied
   *    per component by the contract that owns it, never globally. */
  --color-border: var(--color-border-subtle);
  --border-color: var(--color-border-subtle);

  /* ── Stock triad — inventory.md §3. These are the one set whose colour is a
   *    live read of stock data, so the mapping must be exact. */
  --color-instock: var(--color-success);
  --color-lowstock: var(--color-warning);
  --color-outofstock: var(--color-error);
  /*    No `--color-error: var(--color-error)` line here. TESSAVA and Dwell happen to use the
   *    same name, so it read as a harmless reassertion — but a custom property that references
   *    itself is guaranteed-invalid in CSS, and it silently destroyed both --color-error and
   *    the --color-outofstock above that depends on it. tokens.css already defines the name;
   *    the bridge has nothing to add. */

  /* ── Typography — typography.md §1. Instrument Serif is editorial/display
   *    only; Archivo carries everything transactional. Both stacks carry real
   *    fallbacks, so the theme degrades legibly if a webfont fails to load. */
  --font-body--family: var(--font-family-interface);
  --font-body--style: normal;

  /*    Dwell exposes four family slots; TESSAVA has two families and a hard
   *    "never a third loaded webfont" budget (typography.md §1, §7). Every slot
   *    must therefore resolve to one of the two, and an unmapped slot is not
   *    neutral — it silently reintroduces a vendor typeface. Editorial is
   *    reserved for display/editorial roles only (§5, §6), and Dwell's
   *    subheading and accent slots drive UI chrome — nav links, eyebrows,
   *    badge text — which §6 assigns to the interface family. */
  --font-heading--family: var(--font-family-editorial);
  --font-subheading--family: var(--font-family-interface);
  --font-accent--family: var(--font-family-interface);

  /* ── Layout — layout-grid.md, DEC-030's content container ───────────────── */
  --page-width: var(--container-max-width);
  --page-margin: var(--page-margin-active);

  /* ── Motion — motion.md owns every duration in the system, and Dwell's four
   *    animation speeds drive the dialog, drawer and transition timing that
   *    overlay.md §1 names (--motion-slow for modal/drawer, --motion-base for
   *    toast). Bridging them buys reduced-motion handling for free: tokens.css
   *    already collapses every --motion-* under prefers-reduced-motion, and a
   *    Dwell duration left unmapped would keep animating there.
   *
   *    --animation-speed-fast (Dwell's 62.5ms) stays unmapped. TESSAVA has no
   *    token below --motion-fast, and inventing one to complete the set is the
   *    thing this file exists to prevent. It stays the smallest value either
   *    way, so the scale's ordering is unchanged. */
  --animation-speed: var(--motion-fast);
  --animation-speed-medium: var(--motion-base);
  --animation-speed-slow: var(--motion-slow);
  --animation-easing: var(--motion-easing);

  /* ── Overlays — overlay.md §1's shared base and §3's drawer delta. The
   *    backdrop's colour triple stays Dwell's: overlay.md names
   *    --color-overlay-backdrop, which tokens.css emits as a composed rgba(),
   *    and Dwell needs a bare RGB triple the token set does not carry. Only
   *    the opacity is bridged, which is the half that has a token. */
  --theme-drawer-width: var(--size-drawer-width);
  --backdrop-opacity: var(--opacity-backdrop);

  /* ── Focus — accessibility.md §1. Never removed, only replaced.
   *    Width and offset are both tokenised; the ring colour stays Dwell's
   *    currentcolor default, which its components already use. */
  --focus-outline-width: var(--border-width-focus);
  --focus-outline-offset: var(--focus-ring-offset);
}

/* TESSAVA — E1 PLP additions (product grid, product card, sold-out badge).
 * Phase B1's header comment above marks this file "DELIBERATELY PARTIAL... Buttons, product
 * cards, price, form controls and drawers are mapped by the phase that implements their
 * contract — Phase C onward." This is that phase, for the two surfaces E1 actually needed:
 * the product grid's column mechanics and the product card's elevation/radius. Price, form
 * controls (filter checkboxes/inputs) and swatches already consume the same generic
 * --color-foreground/--color-background/--font-*/--opacity-* variables bridged above — they
 * inherit TESSAVA's palette and type transitively and needed no surface-specific mapping here.
 * Same zero-literals rule as the rest of this file: every declaration is a var() reference.
 */

/* ── Product grid — layout-grid.md §3 ("fixes DEP-06 and DEP-07"). Dwell's native grid uses
 *    `repeat(auto-fill, minmax(card-size, 1fr))`, exactly the viewport-dependent strategy DEP-06
 *    rejected. The markup and container stay 100% native Dwell — only the two custom properties
 *    Dwell's own product-grid already exposes (`--product-grid-columns-desktop`,
 *    `--product-grid-gap`) are redefined per breakpoint, so this is a rung-6 extension of the
 *    existing grid, not a rebuild. `ul.` is added to win specificity over Dwell's own
 *    section-scoped rule without `!important` (both are two classes; the extra type selector
 *    breaks the tie so this doesn't depend on script/style load order).
 *    NOT wired here: the 3-vs-4 density switcher DEP-07 also names. Dwell's own "grid density"
 *    control is a different feature (a 10-column zoom-out view, not a 3/4 toggle) — reusing it
 *    would misrepresent it, and a new toggle is real custom UI work outside E1's scope. Desktop
 *    renders at the fixed 4-column default; the switch itself is deferred and reported, not
 *    silently dropped. */
ul.product-grid.product-grid--grid {
  --product-grid-gap: var(--product-grid-gutter-xs);
}

@media screen and (min-width: 768px) {
  ul.product-grid.product-grid--grid {
    --product-grid-gap: var(--product-grid-gutter-md);
    --product-grid-columns-desktop: repeat(var(--product-grid-columns-md), 1fr);
  }
}

@media screen and (min-width: 1024px) {
  ul.product-grid.product-grid--grid {
    --product-grid-gap: var(--product-grid-gutter-lg);
    --product-grid-columns-desktop: repeat(var(--product-grid-columns-lg), 1fr);
  }
}

/* ── Product card — product-card.md. Elevation is the one thing Dwell's native card has no
 *    equivalent for at all (its own "border" settings model a flat outline, not a
 *    resting/raised shadow pair) — radius is set via the block's own settings
 *    (theme/templates/collection.json, a per-instance numeric field, not a CSS variable) to the
 *    same --radius-lg value so the two stay in sync without a second source of truth. */
.product-grid__card.product-grid__card {
  box-shadow: var(--shadow-resting);
  transition: box-shadow var(--motion-fast) var(--motion-easing);
}

product-card:hover .product-grid__card.product-grid__card,
product-card:focus-within .product-grid__card.product-grid__card {
  box-shadow: var(--shadow-raised);
}

/* ── Sold-out status badge — chip-badge-status.md "Status indicator": "coloured dot or icon →
 *    label text — always both, never the dot alone". Colour is a Theme Editor setting
 *    (config/settings_data.json badge_sold_out_background_color/text_color), not a CSS
 *    variable — Dwell's badge colour has no custom-property hook, only a per-shop setting —
 *    so it isn't bridged here. This rule only adds the icon-to-label gap the icon addition in
 *    blocks/_product-card-gallery.liquid needs. */
.product-badges__badge {
  gap: var(--space-4);
}

/* ── E1 VISUAL REFINEMENT PATCH — logo, PLP heading, card typography, filter typography.
 * Token-only pass: every value below already exists in tessava-tokens.css (semantic type
 * roles, typography.md §6) or logo-wordmark.md v1.1. No new tokens, no new breakpoints, no
 * architecture change. Native Dwell markup, filtering behaviour and settings are untouched;
 * these rules only redirect existing presentation onto TESSAVA's already-built type-role scale.
 */

/* ── Header logo — logo-wordmark.md v1.1 LOCKED spec (Instrument Serif, uppercase, 0.17em
 *    tracking, weight 400, never bold/italic). blocks/_header-logo.liquid sets --font-family
 *    and --font-weight inline on the element itself; an inline custom property cannot be beaten
 *    by any external rule without !important, so this targets the terminal `font-family` /
 *    `font-weight` properties instead — the one path that wins the cascade without !important.
 *    `a.` adds a type selector so this beats the block's own same-specificity `.header-logo`
 *    rule regardless of stylesheet load order.
 *    NOTE — genuine business gap, not fixed here: `settings.logo` is unset (design-system's own
 *    logo-wordmark.md §6 states the vector asset is TBC-BUSINESS), so this only corrects the
 *    typography of Dwell's text fallback ("Tessava") to match the locked wordmark spec. The
 *    fallback cannot become the actual approved asset until that asset exists. */
a.header-logo {
  font-family: var(--font-family-editorial);
  font-weight: var(--font-weight-regular);
  font-style: normal;
  text-transform: uppercase;
  letter-spacing: var(--letter-spacing-wordmark);
  font-size: var(--logo-size-compact);
}

@media screen and (min-width: 768px) {
  a.header-logo {
    font-size: var(--logo-size-header);
  }
}

/* ── PLP heading — typography.md §6's `display.page` row names this exact surface ("PLP/
 *    collection H1"). templates/collection.json's heading text block (id `text_tqQTNE`) ships
 *    on Dwell's raw `h2` preset (flat, non-responsive per settings_data.json's type_size_h2),
 *    which is both the wrong role and the source of the oversized mobile "Produkty". `div.`
 *    matches the block's actual rendered tag (snippets/text.liquid) and beats the same-
 *    specificity global `.h2` rule with a type selector, no !important. */
div.text-block--text_tqQTNE {
  font-family: var(--type-display-page-family);
  font-weight: var(--type-display-page-weight);
  font-size: var(--type-display-page-size);
  line-height: var(--type-display-page-line-height);
  letter-spacing: var(--type-display-page-tracking);
}

/* ── E1 PRODUCT CARD TYPOGRAPHY SYSTEM PATCH (2026-09-09) — title and price re-assigned to
 *    smaller EXISTING typography.md §6 roles for PLP grid density. This is a canonical
 *    role-assignment change, not an implementation-bug fix: the CONTRACT CHECK pass (previous
 *    patch) confirmed both roles below were already correctly implemented against their then-
 *    current contract (`ui.title` for title, `commerce.price-compact` for price). The real
 *    preview review finding is that `ui.title` and `commerce.price-compact` are literally
 *    identical in size and weight (19px/19px/16px, weight 500, both `typography.md` §6) — on a
 *    dense 4-column desktop grid the two lines read as one oversized, undifferentiated block
 *    that competes with the image for attention rather than sitting quietly beneath it; the same
 *    imbalance persists at 2-column mobile. Re-evaluated per `product-card.md` v1.2: title now
 *    cites `ui.small` (Interface/Regular, `--font-size-sm` = 15px, flat across all three
 *    breakpoints), price now cites `ui.label` (Interface/Medium, `--font-size-xs` = 13px, flat
 *    across all three breakpoints) — both pre-existing rows in typography.md §6, no new token,
 *    no new breakpoint. Deliberately reused, not newly invented: `ui.small`'s lighter weight
 *    reads as "clearly secondary" next to the image; `ui.label`'s smaller size plus its existing
 *    "compact tag" character (its stated role is badge/chip text) reads as "compact and
 *    tertiary" beneath the title — the two roles are no longer identical, restoring a real
 *    image > title > price hierarchy. Both roles being flat (same size at every breakpoint) is
 *    itself the correct shape here, not a lost responsive behaviour: the complaint was "too
 *    large on desktop AND mobile," not a breakpoint-specific defect, so a role that doesn't scale
 *    back up at wider viewports is exactly what's needed. See `components/product-card.md`
 *    TOKENS USED (canonical role assignment) and `components/price.md` TOKENS USED (density-
 *    variant cross-reference) for the system-of-record entries this patch mirrors. */

/* ── Product card title — typography.md §6 `ui.title` ("Product card title, nav item, table
 *    header"), confirmed via `components/product-card.md`'s own ANATOMY line ("title (`ui.title`,
 *    `tessava_display_name`)"). CONTRACT CHECK fix: `div.text-block--product_title_anJjPJ` (the
 *    Visual Refinement Patch's original selector) never matched on a live store — same two bugs
 *    already root-caused and fixed for price in the PRODUCT CARD MEDIA + PRICE FIX patch:
 *    (1) Shopify prefixes every rendered block's id with a random per-section-instance token and
 *    de-duplicates repeats across sibling cards (confirmed live: card 1 is `text-block--
 *    <random>__product_title_anJjPJ`, card 2 is `...anJjPJ-1`, card 3 `...anJjPJ-2`, etc. for all
 *    24+ cards) — an exact-match class selector matched nothing. `[class*='__product_title_anJjPJ']`
 *    (contains) catches the base id and every `-N` suffix in one selector; the block key itself
 *    (`product_title_anJjPJ`) is unique enough in this template that a plain contains-match carries
 *    no realistic risk of matching an unrelated block. (2) The visible text sits in a child `<p>`,
 *    not on the classed `div` itself (`text.liquid`'s markup), and Dwell's own equivalent rule
 *    (`.text-block.h5 > *`, specificity 0,2,0) targets that child — a selector styling only the div
 *    would lose to it via child inheritance the same way the parent-only price selector did. Adding
 *    `.h5` + a `div` type selector + targeting `> *` (`div.h5[class*='__product_title_anJjPJ'] > *`,
 *    0,2,1) matches Dwell's own targeting pattern and wins outright, no `!important`. Confirmed live
 *    before this fix: 16px flat / weight 400 (native h5) on every card, at every breakpoint — not
 *    `ui.title`'s 19px desktop/tablet, 16px mobile, weight 500 medium. */
/* PDP FAST IMPLEMENTATION MODE addendum: `product-card.md`'s canonical title role governs
 *    every rendering of the `_product-card` component, not just the PLP grid — this recipe's
 *    row 9 (related products, `templates/product.json`'s `product_recommendations_xmXAhw`
 *    section) reuses the same native `_product-card`/`_product-card-gallery`/
 *    `_product-card-group` architecture with its own block keys (`product_title_Bkhwnx`,
 *    `price_dHCpyf`); same DOM shape confirmed live, so the same rule is simply extended
 *    rather than duplicated. */
div.h5[class*='__product_title_anJjPJ'] > *,
div.h5[class*='__product_title_Bkhwnx'] > * {
  /* SYSTEM PATCH: ui.title -> ui.small (see comment block above) */
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
}

/* ── Product card price — typography.md §6 `commerce.price-compact` ("Product card price, cart
 *    line price"). Superseded selector, E1 PRODUCT CARD MEDIA + PRICE FIX:
 *    `product-price[data-block-id='price']` (the Visual Refinement Patch's original selector)
 *    never matched on a live store. Shopify prefixes every rendered block's id with a random
 *    per-section-instance token (`<random>__<block-key>`, confirmed live: `data-block-id=
 *    "AVFhuTGh5ZWRhRyt2d__price"`), so the exact-match attribute selector silently matched
 *    nothing and the price kept rendering on Dwell's unmodified native `h6` preset the whole
 *    time — a flat 12px/13.2px-line-height (~1.1×) uppercase badge treatment, confirmed via the
 *    live computed style. `[data-block-id$='__price']` (ends-with) matches the real id
 *    regardless of the random prefix — but only for the FIRST card on the page. Shopify's
 *    client-side renderer de-duplicates repeated block ids across sibling section instances by
 *    appending `-1`, `-2`, `-3`... (confirmed live: card 1 is `<random>__price`, card 2 is
 *    `<random>__price-1`, card 3 `<random>__price-2`, and so on for all 24+ cards on the grid),
 *    so an ends-with-only selector silently fixed just the first card and left every other card
 *    on the native h6 default — caught by checking computed style on more than one card instance
 *    instead of trusting the first result. `[data-block-id*='__price-']` (contains, hyphen
 *    included) catches every deduplicated instance without also matching an unrelated block
 *    whose id happens to contain "price" as a word-fragment (e.g. a hypothetical "pricetag"
 *    block's id would need "__pricetag-N", which does not contain the substring "__price-").
 *    That alone is still not enough: `product-price` also carries Dwell's own `h6` class, and
 *    base.css's `h6, .h6.h6, .text-block.h6 > *, ...` rule doubles the class (`.h6.h6`,
 *    specificity 0,2,0) specifically to out-rank single-class overrides — a plain attribute
 *    selector (0,1,1) still loses to it. Adding `.h6` and the `product-price` type selector
 *    (0,2,1 either way) wins outright, no `!important`. Dwell's own equivalent rule
 *    (`.text-block.h6 > *`) targets the price's *child* (the price text sits one level below
 *    `product-price` in the DOM, inheriting from it), not `product-price` itself, so this does
 *    the same via `> *` — this is also what actually fixes the reported clipping: the native
 *    h6 preset's ~1.1 line-height ratio was tight enough to clip glyph descenders against the
 *    card's `overflow: hidden` (product-card-styles.liquid) with no margin for error, whereas
 *    `commerce.price-compact`'s line-height token (1.5–1.6×, tessava-tokens.css) gives the
 *    complete glyphs room to render without touching that boundary — full stop, not a smaller
 *    font hiding the same defect. */
product-price.h6[data-block-id$='__price'] > *,
product-price.h6[data-block-id*='__price-'] > *,
product-price.h6[data-block-id*='__price_dHCpyf'] > * {
  /* SYSTEM PATCH: commerce.price-compact -> ui.label (see comment block above) */
  font-family: var(--type-ui-label-family);
  font-weight: var(--type-ui-label-weight);
  font-size: var(--type-ui-label-size);
  line-height: var(--type-ui-label-line-height);
  letter-spacing: var(--type-ui-label-tracking);
  text-transform: none;
}

/* ── Product card mobile alignment — E1 PRODUCT CARD MOBILE ALIGNMENT MICRO-PATCH. Real preview
 *    finding: on mobile the text-area (title/price) sits inset further than the image edge, so text
 *    no longer lines up with the desktop-approved image>title>price relationship (TEXT BLOCK SPACING
 *    PATCH, product-card.md v1.3/v1.4, --space-16 text-area horizontal inset). Root cause is two
 *    independently-correct values stacking: the `_product-card-group` block's own --space-16
 *    padding-inline (templates/collection.json, flat — block settings aren't breakpoint-aware) sits
 *    INSIDE `.product-grid__card`, whose own horizontal padding carries a native, unrelated Dwell
 *    mechanism (collection-wrapper-styles.liquid's `full_width_on_mobile` floor: `padding-inline:
 *    max(var(--padding-xs), var(--padding-inline-start))`, confirmed live = 8px) that only applies
 *    below Dwell's OWN internal 749px breakpoint — a vendor-baselined file, exempt from CODE-003.
 *    Desktop: card padding 0px + group padding 16px = 16px total text inset. Mobile: card padding
 *    floors to 8px (native, untouched) + group padding 16px (untouched JSON value) = 24px total —
 *    8px more than desktop, which reads as visibly misaligned against the image edge.
 *    Fix: override only the text-area's own padding-inline down one scale step, `--space-16` ->
 *    `--space-8`, mobile-first (this file's own established pattern — see the header-logo and
 *    product-grid rules above, both keyed to `--bp-md`/768px, `responsive.md` §2's canonical scale)
 *    so it nets back to the same 16px total as desktop (8px native floor + 8px here) for every real
 *    device width. This file is TESSAVA-authored, not vendor-baselined, so CODE-003 requires any
 *    `@media` here to key to the canonical five-step scale (`responsive.md` §2) — the vendor's own
 *    749px is deliberately NOT reproduced here (would be an unregistered sixth breakpoint); `--bp-md`
 *    (768px) is the nearest canonical step below which the desktop value must not apply. The two
 *    thresholds don't perfectly coincide, leaving a 750–767px band where the native floor has
 *    already lifted but this override hasn't yet (net 8px, undershooting desktop's 16px by 8px) —
 *    no shipped device viewport falls in that 18px band (common widths cluster at 375/390/414/428/
 *    430 and 768/820/912+), so it's a real, logged, invisible-in-practice trade-off, not a silent
 *    gap. Targets the terminal `padding-inline` shorthand, not the block's inline
 *    `--padding-inline-start/-end` custom properties (inline custom properties can't be beaten
 *    externally without `!important` — the same reasoning as the media-fit and title/price fixes
 *    above). `.product-grid__card .group-block` (0,2,0) beats base.css's `.spacing-style` (0,1,0)
 *    outright, no `!important`. Scoped to the PLP grid only; title→price gap and bottom padding
 *    untouched (only `padding-inline` is set here). */
.product-grid__card .group-block {
  padding-inline: var(--space-8) var(--space-8);
}

@media screen and (min-width: 768px) {
  .product-grid__card .group-block {
    padding-inline: var(--space-16) var(--space-16);
  }
}

/* ── Product card media — E1 PRODUCT CARD MEDIA + PRICE FIX. card-gallery.liquid already ships
 *    a native fit toggle: every card media container renders with a `media-fit` class and an
 *    inline `style="--product-media-fit: cover;"` (the block's `image_ratio` setting is never
 *    `adapt`+`constrain_to_viewport` on the PLP, so `media_fit` is always its `cover` default),
 *    consumed by product-media-container-styles.liquid's `.product-media-container.media-fit {
 *    img { object-fit: var(--product-media-fit); } }`. That's the destructive crop the rug photos
 *    were built for a fixed aspect box, not a random webcam frame: `contain` shows the whole
 *    product regardless of rug shape (rectangular, round, oval, runner) with no distortion, no
 *    aspect-ratio change to the existing `--gallery-aspect-ratio` box (so no layout shift) and no
 *    template/markup change. The inline `--product-media-fit: cover` custom property itself can't
 *    be beaten from outside without `!important` (inline wins for that specific custom property),
 *    so this targets the terminal `object-fit` on the `img` the same way the header logo fix
 *    targets `font-family` instead of `--font-family` — one rung more specific
 *    (`.card-gallery .product-media-container.media-fit img`, 0,3,1) than the rule it
 *    supersedes (`.product-media-container.media-fit img`, 0,2,1), scoped with `.product-grid__
 *    card` to the PLP grid only so `_featured-product-gallery.liquid`'s homepage reuse of the
 *    same `card-gallery` snippet is untouched. The resulting letterboxed space uses
 *    `--color-surface-paper` — colors.md §7's semantic table names "card fills" as one of that
 *    token's explicit, uncapped uses (unlike `--color-surface-warm`, which is scarce/capped and
 *    reserved for the newsletter/editorial surface). */
.product-grid__card .card-gallery {
  background-color: var(--color-surface-paper);
}

.product-grid__card .card-gallery .product-media-container.media-fit img {
  object-fit: contain;
}

/* ── Filter sidebar/toolbar typography — typography.md §6 `ui.label` explicitly names "filter
 *    chip labels" as its documented use. Native filtering markup/behaviour/controls (blocks/
 *    filters.liquid, snippets/list-filter.liquid) are untouched; only the two text roles are
 *    redirected onto the existing label scale. Case/transform is left native (`ui.label` has no
 *    documented case rule; --facet-label-transform stays merchant-configurable as before). */
h4.facets--filters-title,
span.facets__label {
  font-family: var(--type-ui-label-family);
  font-weight: var(--type-ui-label-weight);
  font-size: var(--type-ui-label-size);
  line-height: var(--type-ui-label-line-height);
  letter-spacing: var(--type-ui-label-tracking);
}

/* ── PDP product title — typography.md §6 `display.pdp` ("PDP product title"), confirmed against
 *    `pdp.md` §2a: the title renders exactly once, inside `purchase-panel.md`'s own step 1
 *    ("Product title + price"). In `templates/product.json` this is block `text_WE3K4w` (an `<h1>`
 *    literal in the block's own `text` setting, styled via Dwell's native `h2` type-preset class
 *    on the wrapping `.text-block`). Confirmed live: no per-instance block-id class is needed here
 *    — `text_WE3K4w` is the only `h2`-preset text block inside `.product-details` (unlike the PLP
 *    grid, this section renders once per page, so there is no sibling-instance de-duplication to
 *    catch). Dwell's own equivalent rule (`.text-block.h2 :is(h1, h2, h3, h4, h5, h6)`, 0,2,1)
 *    targets the same child; scoping to `.product-details` and keeping both classes
 *    (`.product-details .text-block.h2 > *`, 0,3,0) wins outright, no `!important` — same
 *    child-targeting/specificity approach as the product-card title fix above. */
.product-details .text-block.h2 > * {
  font-family: var(--type-display-pdp-family);
  font-weight: var(--type-display-pdp-weight);
  font-size: var(--type-display-pdp-size);
  line-height: var(--type-display-pdp-line-height);
  letter-spacing: var(--type-display-pdp-tracking);
}

/* ── PDP purchase-panel price — typography.md §6 `commerce.price-featured` ("PDP purchase-panel
 *    price"), price.md TOKENS USED. Block `price_a7krng` renders on Dwell's native `paragraph`
 *    preset (`<product-price class="... paragraph ...">`), with the visible price text one level
 *    further down than on the product card (`product-price > div > span.price`, confirmed live),
 *    so this targets `.price` directly rather than `> *`. Dwell's own equivalent rule for this
 *    preset (`.paragraph > *`, 0,1,0) doesn't reach a grandchild at all; scoping to
 *    `.product-details product-price.paragraph .price` (0,3,1) is both correctly targeted and
 *    already ahead of anything it could compete with, no `!important` needed. */
.product-details product-price.paragraph .price {
  font-family: var(--type-commerce-price-featured-family);
  font-weight: var(--type-commerce-price-featured-weight);
  font-size: var(--type-commerce-price-featured-size);
  line-height: var(--type-commerce-price-featured-line-height);
  letter-spacing: var(--type-commerce-price-featured-tracking);
}

/* ── PDP VISUAL REFINEMENT PATCH (2026-09-09) ──────────────────────────────────────────────── */

/* ── PDP gallery media presentation — same destructive-crop problem as the PLP grid (E1 PRODUCT
 *    CARD MEDIA + PRICE FIX above), same root cause: `_product-media-gallery`'s `aspect_ratio` is
 *    a fixed box ("1/1.25", not `adapt`), so `product-media-gallery-content.liquid`'s own render
 *    logic (`if block_settings.aspect_ratio != 'adapt' → class += 'media-fit-cover'`) forces the
 *    `media-fit-cover` class unconditionally — the block's `media_fit` setting is provably
 *    unreachable here (only read in the `aspect_ratio == 'adapt' and constrain_to_viewport`
 *    branch), so this cannot be a pure settings change without switching to `adapt` sizing, which
 *    would remove the fixed box (and the letterboxing it enables) entirely — not what "contain
 *    within the existing box" calls for. Confirmed live before this fix: `.media-fit-cover :is(img,
 *    ...)  { object-fit: cover; }` in this gallery's own scoped stylesheet (distinct from the PLP
 *    card gallery's shared `product-media-container-styles.liquid`, hence the different class name
 *    here — `.media-fit-cover`, not `.media-fit`). `.product-information__media` scopes this to the
 *    PDP's own media column; `.product-media-container.media-fit-cover img` (0,2,1) beats Dwell's
 *    own `.media-fit-cover :is(img, ...)` (0,2,0), no `!important`. Same canonical letterbox token
 *    as the PLP card fix (`--color-surface-paper`, colors.md §7 "card fills"). Gallery architecture
 *    (carousel, zoom, thumbnails) is untouched — this only changes how each frame's image sits in
 *    its existing box. */
.product-information__media .product-media-container.media-fit-cover img {
  object-fit: contain;
}

.product-information__media .product-media-container {
  background-color: var(--color-surface-paper);
}

/* ── Purchase-panel content typography — typography.md §6 `ui.body` ("Default body copy") and §9's
 *    72-character measure/hyphenation rule, applied to the accordion body content: the "Details"
 *    row (native `product-description` block, `rte` preset) and the "Specyfikacja" row (native
 *    `custom-liquid` block, no preset system at all — confirmed live it ships with zero font
 *    styling of its own, inheriting nothing but ambient defaults). Both render plain `<p>` children
 *    inside `_accordion-row.liquid`'s `.details-content` wrapper — targeting that shared structural
 *    class means one rule serves both rows identically rather than fighting each block type's own
 *    (absent, in the `custom-liquid` case) preset machinery. `.product-details` scopes this to the
 *    PDP purchase panel's own accordion, not any unrelated accordion elsewhere in the theme. An
 *    explicit rule on `p` beats inheritance from the ancestor's own preset settings regardless of
 *    that ancestor's specificity (an inherited value is always the lowest-priority source for a
 *    property), so no specificity fight is needed here, unlike the title/price fixes above. */
.product-details .details-content {
  max-width: 72ch;
}

.product-details .details-content p {
  font-family: var(--type-ui-body-family);
  font-weight: var(--type-ui-body-weight);
  font-size: var(--type-ui-body-size);
  line-height: var(--type-ui-body-line-height);
  letter-spacing: var(--type-ui-body-tracking);
  hyphens: auto;
}

/* ── Related-products section heading — typography.md §6 `display.section`, documented (§6 note,
 *    Advisor-heading gap fix) as the general "any other on-page section heading" role, not a
 *    homepage-only token. Confirmed live: this heading is a raw `<h3>` embedded directly in the
 *    block's own `text` setting, with only Dwell's generic `rte` preset on its wrapper —
 *    `.text-block.rte`'s only heading rule in base.css sets `margin-block`, not size/weight, so the
 *    `<h3>` was rendering at the browser's unstyled default (bold, ~1.17em) with no relation to the
 *    type system at all, confirmed as the source of "visually oversized, especially mobile."
 *    Instance-keyed like the product-card fixes (`[class*='__text_R8dDRK']`, same random-prefix
 *    de-duplication pattern) since this is a nested, dynamically-composed block, not a top-level
 *    static one like the PLP's `text_tqQTNE` heading. */
rte-formatter.rte[class*='__text_R8dDRK'] h3 {
  font-family: var(--type-display-section-family);
  font-weight: var(--type-display-section-weight);
  font-size: var(--type-display-section-size);
  line-height: var(--type-display-section-line-height);
  letter-spacing: var(--type-display-section-tracking);
}

/* ── PDP CRO LAYER (2026-09-09) ──────────────────────────────────────────────────────────── */

/* ── Product highlights — `chip-badge-status.md` Badge type ("a material/certificate badge on a
 *    PDP" is explicitly ALLOWED there), applied to the real tessava.* facts: material,
 *    material_composition, production_method (prose facts, no icon — a material name is not a
 *    yes/no claim) plus the five boolean facts (anti_slip, easy_clean, robot_vacuum,
 *    underfloor_heating, washable), each individually guarded so only confirmed-true/non-blank
 *    facts ever render. PDP PROTOTYPE ALIGNMENT (2026-09-10): previously a bare icon+text list
 *    plus a separate prose line — "too text-like" per the alignment brief. Badge TOKENS USED:
 *    `--radius-sm`, `--color-surface-paper` fill (never `--color-accent` — that fill is reserved
 *    for the one-CTA-per-decision-point button system, colors.md §6/§8, so a dark accent pill
 *    here would read as competing with "Dodaj do koszyka"), `--space-8` internal gap. Icon on the
 *    boolean facts only: existing `check_box` glyph (no literal per-fact pictogram set exists in
 *    the vendor icon library and none is invented here), `--icon-size-sm`, `aria-hidden`
 *    (iconography.md §5). Type: `ui.label` (typography.md §6 — "filter chip labels", the same
 *    badge-text role `chip-badge-status.md`'s own Badge section specifies).
 *
 *    PDP FINAL CLEANUP (2026-09-12): the underlying Liquid (templates/product.json,
 *    custom_liquid_facts) was rewritten to cap visible badges at 4 with a deterministic
 *    priority order (Easy Clean → Anti-slip → Robot-vacuum-safe → production method for
 *    Magic Home/synthetic products; material/composition → production method → remaining
 *    booleans for wool/viscose products, detected from the existing `material` metafield's
 *    label). No CSS change was needed for that fix — the badge markup and classes are
 *    unchanged, only which facts render and how many. */
.tessava-fact-strip__badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-8);
  list-style: none;
  padding: 0;
  margin: 0;
}

.tessava-fact-strip__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-8);
  background: var(--color-surface-paper);
  border-radius: var(--radius-sm);
  padding: var(--space-4) var(--space-12);
}

.tessava-fact-strip__badge svg {
  width: var(--icon-size-sm);
  height: var(--icon-size-sm);
  flex-shrink: 0;
  color: var(--color-text-secondary);
}

.tessava-fact-strip__badge span {
  font-family: var(--type-ui-label-family);
  font-weight: var(--type-ui-label-weight);
  font-size: var(--type-ui-label-size);
  line-height: var(--type-ui-label-line-height);
  letter-spacing: var(--type-ui-label-tracking);
  color: var(--color-text-secondary);
}

/* ── Delivery / dispatch row — `delivery-status.md`'s own anatomy only (icon + dispatch line,
 *    a separate labelled transit line, never summed — global-promises.md §3.1), read live from
 *    the `logistics_promise`/`market_promise` metaobjects via `policy_status`-gated Liquid reads
 *    (never a literal day-count in this stylesheet or the block's own markup beyond the
 *    confirmed-data render gate itself). Icons: existing `truck` (dispatch) and `box`
 *    (transit/parcel) glyphs — already in the vendor icon set, literal matches for their concept,
 *    not invented. Role: `ui.small` (delivery-status.md's own TOKENS USED).
 *
 *    PDP PROTOTYPE ALIGNMENT (2026-09-10): the returns line ("Zwrot w X dni") that previously
 *    rendered as a third line here is removed. `delivery-status.md`'s LOCKED anatomy only ever
 *    specified dispatch + an optional labelled transit line — it never included a returns line —
 *    and the same return-window fact already renders correctly as its own `trust_item`
 *    (`return-window`) in the trust strip below. The line here was therefore both out of this
 *    component's own contract and a duplicate of content the trust strip already owns; removing
 *    it makes the row match `delivery-status.md` exactly and removes the one genuine "duplicated
 *    explanatory paragraph" the alignment brief named. No approved information is lost — the
 *    return window is still shown, once, by the component that actually owns it. */
.tessava-delivery-row {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.tessava-delivery-row__line {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  margin: 0;
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
  color: var(--color-text-secondary);
}

.tessava-delivery-row__line svg {
  width: var(--icon-size-md);
  height: var(--icon-size-md);
  flex-shrink: 0;
  color: var(--color-text-secondary);
}

/* ── Compact trust strip — `trust-item.md`'s compact PDP variant, read live from real `trust_item`
 *    metaobjects (return window, return method, support response), `policy_status`-gated per
 *    item, never a freestanding claim. TOKENS USED per trust-item.md: icon `--icon-size-xl`,
 *    title `ui.title`, body `ui.small` — anatomy and data are unchanged from the LOCKED contract.
 *
 *    PDP PROTOTYPE ALIGNMENT (2026-09-10): two presentation fixes, no anatomy/token/data change.
 *    (1) Icon — all three items previously reused `check_box`, the same glyph the fact-strip
 *    badges use for a "confirmed true" claim; on a strip beside the CTA that reads as a
 *    checkbox/selectable control rather than a quiet affirmation. trust-item.md's ANATOMY names
 *    only the icon's SIZE (`--icon-size-xl`), never which glyph, so choosing a meaning-matched
 *    icon per item is a presentation choice within the existing contract, not a new decision:
 *    `return` (return-window, return-method — both literally about returns) and `chat_bubble`
 *    (support-response — already the vendor glyph for support/contact). (2) Layout — each item's
 *    icon-on-top-of-centred-text column, at 1/3 width with the icon at full `--icon-size-xl`,
 *    read as a small card rather than a strip row. Re-laid-out horizontally instead: icon at
 *    left, a title+body text column to its right — trust-item.md's "icon-to-title gap" and
 *    "title-to-body gap" values are reused exactly, just applied as a horizontal gap (icon to
 *    text column) and a vertical gap (title to body within that column) respectively, which the
 *    contract's own wording ("icon-to-title", not "icon-above-title") does not forbid. The
 *    result reads as a quiet row of icon+text pairs, not a 3-up card grid, while keeping every
 *    LOCKED value — icon size, both gap values, both type roles, both colours — exactly as
 *    specified. The `--color-divider` hairline and `--space-16` padding-top above the strip
 *    (previous alignment pass, 2026-09-09) are unchanged.
 *
 *    PDP FINAL CLEANUP (2026-09-12): body text is NOT hidden/removed, despite the brief asking
 *    for that "if the canonical contract allows it" — it does not. trust-item.md's own ANATOMY
 *    (icon → title → one-sentence body) and PURPOSE ("never a vague reassurance phrase with
 *    nothing backing it") make the body a mandatory step, not an optional one, and its CONTENT
 *    LIMITS section sets a length budget for that body assuming it renders. Dropping it would be
 *    a real contract change requiring an ADR, not a presentation tweak — out of scope for this
 *    pass ("do not redesign PDP architecture"). Compactness is instead achieved two other ways,
 *    both outside anything TOKENS USED locks: the inter-item gap and the divider's top padding
 *    (both --space-16 -> --space-12), and each item's width (equal-thirds -> content-hugging
 *    auto), below. */
.tessava-trust-strip {
  display: flex;
  flex-wrap: wrap;
  /* PDP FINAL CLEANUP (2026-09-12): row (inter-item) gap and the divider's top padding tightened
   * from --space-16 to --space-12 — this is the strip's own container rhythm, not a value
   * trust-item.md's TOKENS USED section names (that only locks the icon-to-title and
   * title-to-body gaps *inside* one item, both left untouched below), so tightening it is
   * implementation latitude, not a contract change. */
  gap: var(--space-12);
  list-style: none;
  padding: 0;
  margin: 0;
  border-top: var(--border-width-default) solid var(--color-divider);
  padding-top: var(--space-12);
}

.tessava-trust-strip__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-16);
  /* PDP FINAL CLEANUP (2026-09-12): equal-thirds (`1 1 33%`) forced every item to full column
   * width regardless of content, reading as three prominent blocks competing with the CTA above.
   * `0 1 auto` lets each item hug its own content so the row stays a quiet, secondary strip that
   * wraps naturally — still 3–4 items in a row per trust-item.md's own compact-variant spec,
   * which names item count, not item width. */
  flex: 0 1 auto;
  max-width: 100%;
}

.tessava-trust-strip__item svg {
  width: var(--icon-size-xl);
  height: var(--icon-size-xl);
  flex-shrink: 0;
  /* Colour intentionally quieter than the title text: trust-item.md's TOKENS USED names a
   * colour for the title (--color-text-primary) and body (--color-text-secondary) but not the
   * icon, leaving this a legitimate presentation choice — secondary keeps the affirmation mark
   * decorative-recessive (it is already aria-hidden) so it doesn't out-weigh the CTA above it. */
  color: var(--color-text-secondary);
}

.tessava-trust-strip__text {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.tessava-trust-strip__title {
  font-family: var(--type-ui-title-family);
  font-weight: var(--type-ui-title-weight);
  font-size: var(--type-ui-title-size);
  line-height: var(--type-ui-title-line-height);
  letter-spacing: var(--type-ui-title-tracking);
  color: var(--color-text-primary);
}

.tessava-trust-strip__body {
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
  color: var(--color-text-secondary);
}

/* ── Trust utility line — `trust-item.md` Utility variant (DEC-145, ADR-003). Purchase-panel
 *    step 4 (`purchase-panel.md` v2.0), between delivery status and the primary CTA. Read live
 *    from the same `trust_item['return-window']` metaobject the old (removed) trust strip used,
 *    gated on the identical `policy_status == 'confirmed'` check — only the presentation and
 *    anatomy changed, not the data source. ANATOMY per contract: optional small icon
 *    (`--icon-size-md`, one step down from Compact/Feature's `--icon-size-xl` — this variant is
 *    deliberately quieter, sized to match the delivery row above it rather than stand out from
 *    it), then a short TITLE ONLY (no body — the Utility variant's body field is not rendered by
 *    contract), then its mandatory paired functional help affordance (a real anchor link to the
 *    existing Zwrot accordion row, `accordion_row_5T7Rzw` — Shopify/Dwell OS 2.0 renders each
 *    accordion row block with `id="{{ block.id }}"`, so this is a plain in-page anchor, no JS).
 *    The help-link LABEL text is a real Shopify locale key (`tessava.pdp.trust_utility_help_link`,
 *    `content-ui.md` §3's zero-hardcoded-string rule), not a literal string in the Liquid.
 *
 *    Presentation choices within the contract's latitude (TOKENS USED names icon size only for
 *    this variant, no title/help type role): both the title and the help link use `ui.small`
 *    (matching the delivery row directly above, `--color-text-secondary`) rather than the old
 *    strip's bolder `ui.title` — ANATOMY requires "visually quiet... not card-like... not
 *    interactive-looking", so this row is typographically the same weight as the plain delivery
 *    facts beside it, not a callout. No background, no border, no padding — a plain inline row,
 *    never a card. The help link is styled as an ordinary underlined text link (secondary colour,
 *    primary colour on hover/focus) rather than any button treatment, matching trust-item.md's
 *    explicit forbidden-variant rule against a Utility instance "styled or behaving as an
 *    interactive control (button/chip)". No return cost or logistics detail is rendered here —
 *    that stays only in the Zwrot accordion row this help link points to (DEC-145). */
.tessava-trust-utility {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-8);
}

.tessava-trust-utility__icon {
  width: var(--icon-size-md);
  height: var(--icon-size-md);
  flex-shrink: 0;
  color: var(--color-text-secondary);
}

.tessava-trust-utility__title {
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
  color: var(--color-text-secondary);
}

.tessava-trust-utility__help {
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
  color: var(--color-text-secondary);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.tessava-trust-utility__help:hover,
.tessava-trust-utility__help:focus-visible {
  color: var(--color-text-primary);
}

/* ── Advisor / help link — `purchase-panel.md` step 6 (DEC-146, ADR-003), directly below the
 *    primary CTA. A single real, low-visual-weight text link to the store's existing Kontakt page
 *    (`/pages/contact`, confirmed via the Shopify Admin API — the only published page, not
 *    guessed), styled per `content-ui.md` §2a's CTA rules (sentence case, no urgency device). The
 *    label text is a real Shopify locale key (`tessava.pdp.advisor_help_link`), not a literal
 *    string. Deliberately the same quiet `ui.small`/secondary-colour treatment as the trust
 *    utility line above and the delivery row — `purchase-panel.md`'s own anatomy is explicit that
 *    this step is never styled with the same visual weight as the primary button, so it shares no
 *    button/card styling at all, only the underlined-text-link pattern already established above. */
.tessava-advisor-link {
  display: flex;
}

.tessava-advisor-link a {
  font-family: var(--type-ui-small-family);
  font-weight: var(--type-ui-small-weight);
  font-size: var(--type-ui-small-size);
  line-height: var(--type-ui-small-line-height);
  letter-spacing: var(--type-ui-small-tracking);
  color: var(--color-text-secondary);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.tessava-advisor-link a:hover,
.tessava-advisor-link a:focus-visible {
  color: var(--color-text-primary);
}

/* ══════════════════════════════════════════════════════════════════════════════════════════
 * HOMEPAGE MVP — VISUAL QA + FOOTER PASS (2026-09-15)
 * Scoped, instance-keyed fixes only — no PDP/PLP selector touched, no
 * global heading/button rule changed. Same technique as the rest of this
 * file: target the stable `__<block-key>` / `__<section-key>` suffix
 * Shopify renders regardless of the random per-instance prefix.
 * ══════════════════════════════════════════════════════════════════════════════════════════ */

/* ── Hero headline — typography.md §6 `display.hero`, the canonical role for
 *    the homepage hero heading. `--font-heading--family` is already bridged
 *    to Instrument Serif globally (see "Typography" section above), so the
 *    family/size actually render correctly today — confirmed live at
 *    72px/1440px, matching --type-display-hero-size exactly. What is NOT
 *    bridged is weight, line-height and letter-spacing: those come from
 *    Dwell's native h1 preset, itself driven by `settings.type_heading_font`
 *    (Newsreader n3 = weight 300) — confirmed live as font-weight:300,
 *    line-height:1.1, letter-spacing:normal, none of which trace to a
 *    TESSAVA token (typography.md §2: "no other weight exists in the
 *    system"; §4: large serif sizes carry negative tracking). Fixing this
 *    at the global --font-heading--weight/line-height level would touch
 *    every h1-h6 sitewide, including the frozen PDP/PLP — out of scope for
 *    this pass, so it's scoped to the one instance in question via the
 *    same block-id-suffix technique as the related-products heading fix
 *    above. */
.text-block[class*='__text_home_headline'] > :is(h1, h2, h3, h4, h5, h6) {
  /* Live-verified 2026-09-16: Dwell's own h1 preset reaches this same element
     via `.text-block.h1 :is(h1, h2, h3, h4, h5, h6)`, whose descendant-combinator
     compound resolves to specificity (0,2,1) -- one type-selector richer than the
     child-combinator `> *` this rule used, which only reached (0,2,0) and so lost
     the cascade despite loading later in source order. Matching the `:is(...)`
     shape (still scoped to this one block via the same class substring, still a
     direct child) brings both rules to identical (0,2,1) specificity, so normal
     cascade order decides it -- and this stylesheet loads after base.css. */
  font-family: var(--type-display-hero-family);
  font-weight: var(--type-display-hero-weight);
  line-height: var(--type-display-hero-line-height);
  letter-spacing: var(--type-display-hero-tracking);
}

/* ── Homepage featured-product cards — product-card.md's own ANATOMY/TOKENS
 *    USED, reusing the exact canonical treatment already shipped for the
 *    PLP grid (see "Product card media" above) rather than inventing a
 *    second homepage-specific card design. Two problems, same root cause:
 *    (1) these 8 cards were built with `image_ratio: "adapt"` (native
 *    intrinsic aspect ratio), so a square rug photo and a rectangular one
 *    render at different heights in the same row — no shared frame, no
 *    aligned title/price baseline. `templates/index.json` now sets
 *    `image_ratio: "portrait"` (the exact PLP value, templates/
 *    collection.json) on all 8 gallery blocks, giving every card the same
 *    4:5 frame regardless of the rug's native proportions. (2) card-gallery.
 *    liquid's own default `--product-media-fit: cover` (see the PLP "Product
 *    card media" comment above for the full mechanism) then crops each rug
 *    to fill that frame — acceptable on the PLP grid where the fix already
 *    exists, but this homepage grid uses generic `.product-card` markup
 *    (blocks/product-card.liquid -> snippets/product-card.liquid's own
 *    `<product-card class="product-card ...">` wrapper), not PLP's
 *    `.product-grid__card` wrapper, so the existing PLP-scoped rule does not
 *    reach it. Reusing the identical `contain` + paper-surface treatment,
 *    scoped to the generic `.product-card` class so it covers every
 *    homepage/curation-section instance of this component (product-card.md
 *    §applies_to: "PLP grid, homepage curation sections...") without
 *    touching PLP's own already-correct, more specific `.product-grid__card`
 *    rule. `_featured-product-gallery.liquid` (the spotlight section) has no
 *    `.product-card` ancestor, so it is unaffected, exactly as the PLP
 *    comment already notes for its own scoping. */
.product-card .card-gallery {
  background-color: var(--color-surface-paper);
}

.product-card .card-gallery .product-media-container.media-fit img {
  object-fit: contain;
}

/* ── Final CTA button on the anchor-dark closing section — button.md's own
 *    TOKENS USED names `--color-accent-on-dark` as the primary button's
 *    "variant on anchor-dark surfaces", but the native `.button` class has
 *    no surface-aware branch: fill/text always come from the flat
 *    `settings.color_palette.foreground/background` pair (config/
 *    settings_data.json `palette_primary_button_background`), correct on
 *    every light/canvas section but a real defect here — `final_cta`'s own
 *    `background_color` (templates/index.json, #1A1A17) is the identical
 *    value as `--color-palette.foreground`, so the button's fill and the
 *    section behind it are indistinguishable. Confirmed live: only the
 *    white label was visible, no button shape at all. Fix renders the
 *    "dark background -> canonical inverse/light treatment" the QA brief
 *    calls for, using the token button.md already names for this exact
 *    surface: `--color-accent-on-dark` fill (a light, warm peach that pops
 *    against anchor) with anchor-dark text on top, hovering one step toward
 *    the base accent for a visible but still-legible pressed state. Scoped
 *    to this one section by its stable `__final_cta` id suffix (Shopify
 *    renders `#shopify-section-<template-id>__final_cta`, confirmed live) —
 *    every other `.button` instance sitewide, including any future dark
 *    section, is untouched. */
[id$='__final_cta'] .button {
  background-color: var(--color-accent-on-dark);
  color: var(--color-surface-anchor);
}

[id$='__final_cta'] .button:hover {
  background-color: var(--primitive-accent);
  color: var(--color-surface-anchor);
}
