/*
 * Storefront page.
 *
 * Mobile-first, and namespaced under .dakkeh- throughout. WoodMart keeps the
 * site shell; this styles only the content it renders inside it (§27).
 *
 * Theme variables used here were verified against WoodMart 8.3.9. Each has a
 * literal fallback so the page is still coherent under another theme.
 */

.dakkeh-storefront {
    font-family: var(--wd-text-font, inherit);
    color: var(--wd-text-color, #333);
    padding: 16px 0 48px;
}

/* Theme reset -------------------------------------------------------------
 *
 * WoodMart styles the bare element, not a class:
 *
 *   :is(.btn,.button,button,[type=submit],[type=button]) {
 *       display: inline-flex; justify-content: center;
 *       min-height: var(--btn-height, 42px); padding: 5px 20px; ... }
 *
 * — base.min.css, verified in 8.3.9. Every round control on this page is a
 * bare <button type="button">, so all of that applied to them, and
 * `min-height` is the half that cannot be argued with by declaring a size:
 * it clamps `height` whatever the specificity, so a 36px circle rendered
 * 36 x 42 — an egg with a "+" in it — and the row arrows came out 40 x 42
 * with 20px of side padding they never asked for.
 *
 * So what these controls do *not* want has to be said, not just what they do.
 * `button.dakkeh-…` rather than a lone class, because the theme's `:is(...)`
 * carries class specificity (0,1,0) and a single class would only tie with it,
 * leaving the outcome to stylesheet order, which is not ours to guarantee.
 * Same argument, same shape, as the reset at the top of address.css.
 *
 * Sizes stay where each control defines them; this only lifts the floor the
 * theme put under them. `.dakkeh-card__add` is listed under `--market`
 * deliberately: the theme-drawn card variant (ADR-037) is a full-width pill
 * that wants both the 42px floor and its own padding.
 */

.dakkeh-card--market button.dakkeh-card__add,
.dakkeh-stepper button.dakkeh-stepper__step,
.dakkeh-sheet button.dakkeh-sheet__close,
.dakkeh-store-header__links button.dakkeh-store-header__link,
button.dakkeh-row__arrow {
    min-width: 0;
    min-height: 0;
    padding: 0;
}

/* Preview bar -------------------------------------------------------------
   Only ever rendered for the vendor or an administrator (ADR-041). Deliberately
   unlike anything else on the page — this has to read as scaffolding around the
   store rather than as part of it. */

.dakkeh-preview-bar {
    display: flex;
    flex-wrap: wrap;
    gap: 4px 10px;
    align-items: baseline;
    margin: 0 0 12px;
    padding: 10px 14px;
    border: 1px dashed rgba(0, 0, 0, 0.25);
    border-radius: 10px;
    background: #fff8e1;
    color: #6b4f00;
    font-size: 13px;
}

.dakkeh-preview-bar span {
    opacity: 0.8;
}

/* Store header ------------------------------------------------------------ */

.dakkeh-store-header {
    display: flex;
    gap: 14px;
    align-items: flex-start;
    justify-content: space-between;
    padding: 4px 0 10px;
}

.dakkeh-store-header__identity {
    display: flex;
    gap: 14px;
    align-items: center;
    min-width: 0;
}

.dakkeh-store-header__logo {
    width: 72px;
    height: 72px;
    border-radius: var(--wd-brd-radius, 12px);
    object-fit: cover;
    flex: 0 0 auto;
    background: #fff;
    /* Vendor logos are often transparent PNGs on white; the border keeps one
       from dissolving into the page. */
    border: 1px solid rgba(0, 0, 0, 0.07);
}

.dakkeh-store-header__body {
    min-width: 0;
}

.dakkeh-store-header__name {
    margin: 0 0 6px;
    font-family: var(--wd-title-font, inherit);
    font-size: 22px;
    line-height: 1.2;
    color: var(--wd-title-color, #111);
}

/* One line of facts, each separated by a dot drawn in CSS rather than typed
   into the markup — a separator in the text would be read out by a screen
   reader between every pair. */
.dakkeh-store-header__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 10px;
    font-size: 13px;
    color: var(--wd-text-color, #555);
}

.dakkeh-store-header__meta > * + *::before {
    content: "";
    display: inline-block;
    width: 3px;
    height: 3px;
    margin-inline-end: 8px;
    border-radius: 50%;
    background: currentColor;
    opacity: 0.4;
    vertical-align: middle;
}

.dakkeh-store-header__status {
    font-weight: 600;
}

/* Availability drives colour, so the state reads at a glance rather than
   needing the text to be parsed. */
.dakkeh-store-header__status[data-dakkeh-availability="AVAILABLE"] {
    color: #2f7d4f;
}

.dakkeh-store-header__status[data-dakkeh-availability="UNAVAILABLE_NOW"],
.dakkeh-store-header__status[data-dakkeh-availability="NOT_SERVICEABLE"] {
    color: #c05746;
}

.dakkeh-store-header__rating {
    font-weight: 600;
    white-space: nowrap;
}

.dakkeh-store-header__star {
    color: #f5a623;
    margin-inline-end: 2px;
}

.dakkeh-store-header__eta,
.dakkeh-store-header__delivery {
    white-space: nowrap;
    opacity: 0.9;
}

.dakkeh-store-header__links {
    display: flex;
    gap: 8px;
    flex: 0 0 auto;
}

/* Round, quiet, and 40px square: these sit next to a shop name on a phone and
   have to be tappable without competing with it. */
.dakkeh-store-header__link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    padding: 0;
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.14));
    border-radius: 50%;
    background: transparent;
    color: inherit;
    font-size: 16px;
    line-height: 1;
    text-decoration: none;
    cursor: pointer;
}

.dakkeh-store-header__link:hover {
    background: rgba(0, 0, 0, 0.04);
}

/* Badges ------------------------------------------------------------------ */

.dakkeh-store-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin: 0 0 10px;
    padding: 0;
    list-style: none;
}

.dakkeh-store-badge {
    padding: 4px 10px;
    border-radius: 999px;
    background: rgba(0, 0, 0, 0.05);
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
}

.dakkeh-store-about {
    margin: 0 0 12px;
    padding: 12px 14px;
    border-radius: var(--wd-brd-radius, 10px);
    background: rgba(0, 0, 0, 0.03);
    font-size: 13px;
    line-height: 1.6;
    white-space: pre-line;
}

.dakkeh-store-about[hidden] {
    display: none;
}

/* Delivery address --------------------------------------------------------
   The control itself is styled by address.css, which is loaded with it. This
   only gives it a place to stand on this page: full width and boxed, so it
   reads as the question it is rather than as a stray button (ADR-042). */

.dakkeh-store-address {
    margin: 0 0 10px;
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.14));
    border-radius: var(--wd-brd-radius, 10px);
}

.dakkeh-store-address .dakkeh-address {
    width: 100%;
    padding: 10px 12px;
    border: 0;
    /* The theme centres button content. Left-aligned here: this reads as a
       field showing a value, not as a call to action. */
    justify-content: flex-start;
    text-align: left;
}

/* Delivery terms strip ----------------------------------------------------
   Filled in client-side, so it stays out of the flow until it has something to
   say rather than reserving an empty band. */

.dakkeh-store-terms {
    margin: 0 0 14px;
    padding: 10px 14px;
    border-radius: var(--wd-brd-radius, 10px);
    background: rgba(47, 125, 79, 0.09);
    color: #2f7d4f;
    font-size: 13px;
    font-weight: 600;
}

.dakkeh-store-terms[hidden] {
    display: none;
}

/* Out-of-range notice -----------------------------------------------------
   Said when this store does not deliver to the address the shopper gave. Sits
   where the terms strip would, because it answers the same question, and is
   coloured as a refusal rather than an error: the shopper did nothing wrong,
   the shop simply does not come this far (ADR-048). */

.dakkeh-store-notice {
    margin: 0 0 14px;
    padding: 12px 14px;
    border-radius: var(--wd-brd-radius, 10px);
    background: rgba(192, 87, 70, 0.1);
    color: #a2402f;
    font-size: 14px;
    font-weight: 600;
}

.dakkeh-store-notice[hidden] {
    display: none;
}

/* Every card at once, rather than each one marked separately: the catalogue
   is not the problem, the address is, and a shop dimmed as a whole says that
   where three hundred identical badges would not. */

.dakkeh-storefront--unserviceable .dakkeh-card,
.dakkeh-storefront--unserviceable .dakkeh-category-tile,
.dakkeh-storefront--unserviceable .product {
    opacity: 0.45;
    transition: opacity 0.15s ease;
}

/* Pointer events go with the dimming, so a tap cannot land on something the
   basket would refuse. The controls are separately disabled in script — this
   is the visual half, and neither is trusted on its own. */
.dakkeh-storefront--unserviceable [data-dakkeh-add],
.dakkeh-storefront--unserviceable .add_to_cart_button,
.dakkeh-storefront--unserviceable [data-dakkeh-card-stepper] {
    pointer-events: none;
    filter: grayscale(1);
}

/* Search ------------------------------------------------------------------ */

.dakkeh-store-search {
    margin: 0 0 14px;
}

.dakkeh-store-search__input {
    width: 100%;
    padding: 11px 14px;
    /* 16px keeps iOS Safari from zooming the viewport on focus. */
    font-size: 16px;
    font-family: inherit;
    color: inherit;
    background: #fff;
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.18));
    border-radius: var(--wd-form-brd-radius, 10px);
}

/* Category tiles ----------------------------------------------------------
   The browse view: pictures a shopper recognises faster than they read. Auto-
   fill rather than a fixed count, so the same rule covers a store with four
   aisles and one with forty. */

.dakkeh-category-tiles {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
    gap: 12px 8px;
    margin: 4px 0 18px;
}

.dakkeh-category-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    font-family: inherit;
    cursor: pointer;
}

.dakkeh-category-tile__media {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    aspect-ratio: 1 / 1;
    border-radius: var(--wd-brd-radius, 14px);
    background: rgba(0, 0, 0, 0.04);
    overflow: hidden;
}

.dakkeh-category-tile__image {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

.dakkeh-category-tile__initial {
    font-size: 24px;
    font-weight: 700;
    opacity: 0.35;
    text-transform: uppercase;
}

.dakkeh-category-tile__name {
    font-size: 12px;
    line-height: 1.3;
    text-align: center;
    /* Two lines, then clipped: a long aisle name must not push the tile below
       it out of alignment. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.dakkeh-category-tile:hover .dakkeh-category-tile__media,
.dakkeh-category-tile:focus-visible .dakkeh-category-tile__media {
    outline: 2px solid var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    outline-offset: 2px;
}

/* The tile is the filter's state as well as its control, so the chosen aisle
   has to be obvious from across the page — a ring the same weight as the hover
   one would not be. */
.dakkeh-category-tile.is-active .dakkeh-category-tile__media {
    outline: 3px solid var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    outline-offset: 2px;
}

.dakkeh-category-tile.is-active .dakkeh-category-tile__name {
    color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    font-weight: 700;
}

/* Filtered view heading ---------------------------------------------------
   What is being shown, and the way back out of it. The tiles scroll away; this
   does not, because it sits directly above the results. */

.dakkeh-results-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    margin: 0 0 14px;
}

.dakkeh-results-head[hidden] {
    display: none;
}

.dakkeh-results-head__title {
    margin: 0;
    font-family: var(--wd-title-font, inherit);
    font-size: 17px;
    line-height: 1.3;
    color: var(--wd-title-color, #111);
}

.dakkeh-results-head__clear {
    flex: 0 0 auto;
    padding: 6px 12px;
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.15));
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.dakkeh-results-head__clear:hover {
    border-color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
}

/* The grid is excluded from scroll anchoring, and the skeletons with it.

   Chrome picks a node in the viewport and keeps *it* still when content around
   it resizes. The skeletons are in view when they are replaced by the cards
   they were holding space for, so the browser was chasing its own anchor and
   scrolling the shopper 445px past the aisle heading they had just been taken
   to. Measured on a store page, not guessed. Appending page two is below the
   fold and never moved anything either way. */
[data-dakkeh-results],
.dakkeh-skeletons,
.dakkeh-results-more {
    overflow-anchor: none;
}

/* The rest of the aisle ---------------------------------------------------
   Built by storefront.js under the results grid, so an aisle longer than one
   listing request is still one listing. It is the fallback as much as the
   control: the script fetches the next page on its own as the shopper reaches
   this, and the button is what a browser without IntersectionObserver — or a
   page of results too short to scroll — is left with. */

.dakkeh-results-more {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
    margin: 22px 0 6px;
}

.dakkeh-results-more[hidden] {
    display: none;
}

/* Scoped to the storefront rather than left on one class, for the reason the
   checkout flow's buttons are: WoodMart styles a bare `button` through
   selectors more specific than a single class of ours — a grey fill, uppercase,
   42px tall — and this is a control a shopper taps repeatedly to get down a
   long aisle. Two classes win the argument without `!important`. */
.dakkeh-storefront .dakkeh-results-more__button,
.dakkeh-storefront .dakkeh-results-more__button:hover {
    min-height: 44px;
    padding: 10px 26px;
    border: 1px solid var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    border-radius: 999px;
    box-shadow: none;
    background: transparent;
    color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    text-transform: none;
    letter-spacing: normal;
    line-height: 1.3;
    cursor: pointer;
}

.dakkeh-storefront .dakkeh-results-more__button:hover {
    background: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    color: #fff;
}

.dakkeh-storefront .dakkeh-results-more__button[disabled],
.dakkeh-storefront .dakkeh-results-more__button[disabled]:hover {
    background: transparent;
    color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    opacity: 0.6;
    cursor: default;
}

.dakkeh-results-more__count {
    margin: 0;
    color: var(--color-gray-500, #767676);
    font-size: 13px;
}

/* Rows --------------------------------------------------------------------
   A row is a heading, its tools, and a track. The accent colour a vendor picks
   rides in --dakkeh-accent, which is set on the section element; everything
   below falls back to the theme's primary colour when they picked none. */

.dakkeh-row {
    margin: 0 0 26px;
}

.dakkeh-row__head {
    display: flex;
    align-items: center;
    gap: 10px;
    margin: 0 0 12px;
    padding: 10px 14px;
    border-radius: var(--wd-brd-radius, 12px);
    /* The band is the accent at low opacity rather than the accent itself:
       vendors pick saturated colours, and white-on-orange for a whole heading
       band is where storefronts stop being readable.

       The flat grey is declared first for browsers without color-mix; they get
       a neutral band and the accent still shows on the edge and the controls. */
    background: rgba(0, 0, 0, 0.04);
    background: color-mix(in srgb, var(--dakkeh-accent, var(--wd-primary-color, #7e9b83)) 12%, transparent);
    border-inline-start: 4px solid var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
}

.dakkeh-row__title {
    margin: 0;
    font-family: var(--wd-title-font, inherit);
    font-size: 17px;
    line-height: 1.3;
    color: var(--wd-title-color, #111);
}

.dakkeh-row__tools {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-inline-start: auto;
}

.dakkeh-row__more {
    padding: 6px 10px;
    border: 0;
    border-radius: 999px;
    background: transparent;
    color: var(--dakkeh-accent, var(--wd-primary-color, #7e9b83));
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}

.dakkeh-row__arrow {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: #fff;
    color: inherit;
    font-size: 18px;
    line-height: 1;
    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.16);
    cursor: pointer;
}

.dakkeh-row__arrow[hidden] {
    display: none;
}

.dakkeh-row__arrow:disabled {
    opacity: 0.35;
    cursor: default;
}

/* The track scrolls by touch, wheel and keyboard on its own; the arrows are an
   addition for pointer users, not the mechanism. Scroll snapping stops a swipe
   leaving half a card at the edge. */
.dakkeh-row__track {
    display: flex;
    gap: 12px;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    scroll-behavior: smooth;
    /* Room for the focus ring and the card shadow, which a hidden overflow
       would otherwise clip. */
    padding: 2px 2px 6px;
    margin: 0 -2px;
    scrollbar-width: none;
}

.dakkeh-row__track::-webkit-scrollbar {
    display: none;
}

.dakkeh-row__track > * {
    flex: 0 0 auto;
    width: 140px;
    scroll-snap-align: start;
}

@media (min-width: 600px) {
    .dakkeh-row__track > * {
        width: 165px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .dakkeh-row__track {
        scroll-behavior: auto;
    }
}

/* Vendor banners ---------------------------------------------------------- */

.dakkeh-banner {
    margin: 0 0 26px;
}

.dakkeh-banner__image {
    width: 100%;
    height: auto;
    display: block;
    border-radius: var(--wd-brd-radius, 12px);
}

/* Sections and grid ------------------------------------------------------- */

.dakkeh-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px;
}

/* An author `display: grid` outranks the browser's own rule for [hidden], so
   the results grid and the skeletons — both of which ship hidden — need this
   said explicitly. Without it the skeleton row shimmers on every page load. */
.dakkeh-grid[hidden] {
    display: none;
}

@media (min-width: 600px) {
    .dakkeh-grid {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }
}

@media (min-width: 900px) {
    .dakkeh-grid {
        grid-template-columns: repeat(5, minmax(0, 1fr));
    }
}

/* Product card ------------------------------------------------------------ */

.dakkeh-card {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.dakkeh-card__media {
    position: relative;
    margin-bottom: 8px;
}

.dakkeh-card__image {
    width: 100%;
    /* Reserved aspect ratio, so loading images do not shift the grid. */
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: var(--wd-thumb-brd-radius, 10px);
    background: rgba(0, 0, 0, 0.04);
    display: block;
}

.dakkeh-card__image--placeholder {
    display: block;
}

.dakkeh-card__badge {
    position: absolute;
    top: 8px;
    left: 8px;
    padding: 3px 8px;
    border-radius: 999px;
    background: var(--wd-primary-color, #7e9b83);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
}

.dakkeh-card__name {
    margin: 0 0 4px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.35;
    color: var(--wd-text-color, #333);
    /* Two lines maximum keeps rows aligned across the grid. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.dakkeh-card__price {
    margin: auto 0 0;
    font-size: 14px;
}

.dakkeh-card__price-now {
    font-weight: 700;
    color: var(--wd-title-color, #111);
}

.dakkeh-card__price-was {
    margin-left: 6px;
    opacity: 0.55;
    font-size: 12px;
}

.dakkeh-card__note {
    margin: 4px 0 0;
    font-size: 12px;
    color: #c05746;
}

/* A product that cannot be delivered to the shopper's address is dimmed but
   still shown, so they can see the store stocks it (§26). */
.dakkeh-card.is-unavailable .dakkeh-card__media,
.dakkeh-card.is-unavailable .dakkeh-card__name,
.dakkeh-card.is-unavailable .dakkeh-card__price {
    opacity: 0.45;
}

/* Market card -------------------------------------------------------------
   The grocery tile (ADR-037), on the token set the rest of the shell moved to.

   Small, dense, and addable without leaving the row — a shopper buys twenty of
   these in a session and opens none of them. What ADR-053 changes is what
   happens when they do open one: the tap goes to a sheet rather than a page, so
   the whole tile is now a target and has to look like one.

   Three rules hold this together and are easy to undo by accident:

   1. **The tile never reflows.** Everything that changes state — the add
      control becoming a stepper, the veil, the note — is absolutely positioned
      or reserves its space. A row of eight that shuffles as one is added is
      the shopper losing their place mid-tap.
   2. **Every control is styled at two classes deep.** WoodMart styles bare
      `button` through `:is(.btn,.button,button,…)` — one class of specificity —
      in a stylesheet enqueued after this one, so a single-class rule here
      loses the tie and the steppers come back as the theme's grey rectangles.
   3. **Motion is opt-out, not decoration.** Every transition here is listed in
      the reduced-motion block at the end of the file. */

.dakkeh-grid--market {
    display: grid;
    /* 150px rather than 140: the tile gained padding and a 36px control, and
       at 140 the two-line name and the price started to collide on a 360px
       phone. Still four across a tablet and six on a wide desktop. */
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: clamp(10px, 1.6vw, 16px);
}

.dakkeh-card--market {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: 10px 10px 12px;
    border: 1px solid var(--dakkeh-line, rgba(0, 0, 0, 0.08));
    border-radius: calc(var(--dakkeh-radius, 14px) + 4px);
    background: var(--dakkeh-surface, #fff);
    /* Two shadows doing different jobs: a tight one that seats the tile on the
       page, and a wide soft one that gives it somewhere to lift to. Both are
       nearly invisible at rest, which is the point — 449 products means a grid
       of these, and a grid of drop shadows is a grid of noise. */
    box-shadow:
        0 1px 2px rgba(16, 24, 32, 0.04),
        0 8px 24px -18px rgba(16, 24, 32, 0.5);
    transition:
        transform 0.28s cubic-bezier(0.22, 1, 0.36, 1),
        box-shadow 0.28s cubic-bezier(0.22, 1, 0.36, 1),
        border-color 0.28s ease;
}

/* Hover only where hovering means something. On a touch screen this state is
   what a tap leaves behind, stuck until the shopper taps elsewhere. */
@media (hover: hover) {
    .dakkeh-card--market:hover {
        transform: translateY(-3px);
        border-color: color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 30%, transparent);
        box-shadow:
            0 2px 4px rgba(16, 24, 32, 0.05),
            0 18px 34px -20px rgba(16, 24, 32, 0.55);
    }

    /* Only an image inside the link, which is the element doing the clipping.
       A product with no permalink is not wrapped in one, and scaling it would
       push it out over the tile's own edge. */
    .dakkeh-card--market:hover .dakkeh-card__link .dakkeh-card__image {
        transform: scale(1.045);
    }
}

/* The tile is one target, so the ring goes round the tile. Drawn from the
   focused link rather than on it: a 2px outline on an inline `<a>` traces the
   text, which tells a keyboard shopper the words are focused rather than the
   card. */
.dakkeh-card--market:focus-within {
    border-color: var(--dakkeh-accent, #7e9b83);
    box-shadow:
        0 0 0 3px color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 22%, transparent),
        0 8px 24px -18px rgba(16, 24, 32, 0.5);
}

.dakkeh-card--market :focus-visible {
    outline: 2px solid var(--dakkeh-accent, #7e9b83);
    outline-offset: 2px;
    border-radius: 6px;
}

/* The press. 60ms of nothing-else-changed is what makes a tap feel received on
   a connection that will take another 300 to answer it (§14). */
.dakkeh-card--market:active {
    transform: translateY(-1px) scale(0.985);
    transition-duration: 0.06s;
}

.dakkeh-card--market .dakkeh-card__media {
    position: relative;
    margin-bottom: 10px;
    /* One step inside the tile's radius, which is what keeps the two curves
       concentric rather than looking like a sticker on a card. */
    border-radius: var(--dakkeh-radius, 14px);
    /* A tint, not white. Most of this catalogue is a packet photographed on
       white, and on a white tile the product floats with no edge at all. */
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 3.5%, transparent);
}

/* The clip is on the link rather than the media, and the difference matters.
   The link holds only the image, so its scale on hover is contained — while
   the badge, the veil and the add button are siblings outside it, and their
   shadows are not sheared off at the media's edge. */
.dakkeh-card__link {
    display: block;
    border-radius: inherit;
    overflow: hidden;
}

.dakkeh-card--market .dakkeh-card__image {
    /* Contain, not cover: a tin cropped to fill its frame loses the label,
       which is the only thing distinguishing it from the tin beside it. */
    object-fit: contain;
    background: transparent;
    border-radius: 0;
    padding: 8px;
    transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1), filter 0.3s ease;
}

/* Badges over the media ---------------------------------------------------- */

.dakkeh-card__discount {
    position: absolute;
    top: 8px;
    inset-inline-start: 8px;
    z-index: 2;
    padding: 4px 8px;
    border-radius: 999px;
    /* Warm red on its own rather than the theme accent: this is the one thing
       on the tile that is not the shop talking about itself, and it has to
       read against a photograph of any colour. */
    background: linear-gradient(180deg, #e4574f, #cf3f37);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.01em;
    line-height: 1.35;
    white-space: nowrap;
    box-shadow: 0 2px 8px -2px rgba(207, 63, 55, 0.65);
}

/* Out of stock, said on the picture. A veil rather than a line of text under
   the price: the state of the goods belongs on the goods, and a card whose
   image is behind frosted glass reads as unavailable before a word of it is
   parsed. */
.dakkeh-card__veil {
    position: absolute;
    inset: 0;
    z-index: 3;
    border-radius: inherit;
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--dakkeh-surface, #fff) 72%, transparent);
    /* Progressive: browsers without it get the wash above, which is already
       enough to read the state. */
    backdrop-filter: saturate(0.35) blur(1px);
    -webkit-backdrop-filter: saturate(0.35) blur(1px);
}

.dakkeh-card__veil[hidden] {
    display: none;
}

.dakkeh-card__veil-text {
    padding: 5px 10px;
    border-radius: 999px;
    background: var(--dakkeh-ink, #14181b);
    color: var(--dakkeh-surface, #fff);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.02em;
    text-transform: uppercase;
}

/* Body --------------------------------------------------------------------- */

.dakkeh-card--market .dakkeh-card__name {
    margin: 0 0 6px;
    font-size: 13px;
    font-weight: 500;
    line-height: 1.35;
    /* Two lines, and the space for two lines whether or not the name needs
       them, so prices sit on one baseline across the row. */
    min-height: 2.7em;
    color: var(--dakkeh-ink, #14181b);
}

.dakkeh-card--market .dakkeh-card__name a {
    color: inherit;
    text-decoration: none;
}

/* Price last, and on one line. `auto` on the top margin pins it to the bottom
   of a tile whose name ran to one line rather than two. */
.dakkeh-card--market .dakkeh-card__price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px 6px;
    margin: auto 0 0;
}

.dakkeh-card--market .dakkeh-card__price-now {
    font-size: 16px;
    font-weight: 700;
    letter-spacing: -0.01em;
    /* Prices in a column that do not agree about digit width read as a ragged
       list of different numbers. */
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-ink, #14181b);
}

/* The top of a price range, on the tile of a product sold in several sizes
   (ADR-071). Lighter than the price beside it and allowed to wrap: at 16px a
   grocery tile is roughly eight characters wide, and "$9.78 – $23.45" is not.
   It wraps to a second line rather than shrinking both numbers, because the
   first one is what the shopper compares between tiles. */
.dakkeh-card--market .dakkeh-card__price-to {
    font-size: 13px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.55));
    white-space: nowrap;
}

.dakkeh-card--market .dakkeh-card__price--range .dakkeh-card__price-now {
    /* The two numbers are one phrase, so they wrap together as one. */
    display: inline-flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0 5px;
}

.dakkeh-card--market .dakkeh-card__price-was {
    margin: 0;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.5));
    text-decoration-thickness: 1px;
}

.dakkeh-card--market .dakkeh-card__note {
    margin: 6px 0 0;
    font-size: 11px;
    line-height: 1.35;
    font-weight: 600;
    color: #c05746;
}

.dakkeh-card--market .dakkeh-card__note[hidden] {
    display: none;
}

/* A product that cannot be delivered to the shopper's address is dimmed but
   still shown, so they can see the store stocks it (§26). The image is
   desaturated as well as dimmed: opacity alone on a bright packet still reads
   as available. */
.dakkeh-card--market.is-unavailable .dakkeh-card__media,
.dakkeh-card--market.dakkeh-unavailable .dakkeh-card__media {
    opacity: 0.55;
}

.dakkeh-card--market.is-unavailable .dakkeh-card__image,
.dakkeh-card--market.dakkeh-unavailable .dakkeh-card__image {
    filter: grayscale(0.75);
}

.dakkeh-card--market.is-unavailable .dakkeh-card__name,
.dakkeh-card--market.dakkeh-unavailable .dakkeh-card__name,
.dakkeh-card--market.is-unavailable .dakkeh-card__price,
.dakkeh-card--market.dakkeh-unavailable .dakkeh-card__price {
    opacity: 0.55;
}

/* Add control and stepper -------------------------------------------------
   One 36px circle that becomes a three-part pill in place. The pill is wider
   than the circle and grows towards the start of the line, so the tile does
   not reflow when it appears and a row of cards does not jump as items are
   added.

   36px rather than the 34 it was: the WCAG 2.2 target-size minimum is 24 and
   the practical floor for a thumb on a moving bus is not. */

.dakkeh-card__actions {
    position: absolute;
    z-index: 4;
    bottom: 8px;
    inset-inline-end: 8px;
}

.dakkeh-card--market .dakkeh-card__add {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    margin: 0;
    padding: 0;
    border-radius: 50%;
    /* Filled, not outlined. The outlined circle it replaces was invisible over
       a pale packet, which is most of this catalogue. */
    background: var(--dakkeh-accent, #7e9b83);
    border: 1px solid color-mix(in srgb, var(--dakkeh-ink, #14181b) 8%, transparent);
    color: #fff;
    cursor: pointer;
    box-shadow:
        0 1px 2px rgba(16, 24, 32, 0.16),
        0 6px 14px -8px color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 90%, transparent);
    transition:
        transform 0.22s cubic-bezier(0.34, 1.56, 0.64, 1),
        box-shadow 0.22s ease,
        opacity 0.18s ease;
}

@media (hover: hover) {
    .dakkeh-card--market .dakkeh-card__add:hover:not(:disabled) {
        transform: scale(1.08);
        box-shadow:
            0 2px 4px rgba(16, 24, 32, 0.18),
            0 10px 20px -8px color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 90%, transparent);
    }
}

.dakkeh-card--market .dakkeh-card__add:active:not(:disabled) {
    transform: scale(0.9);
    transition-duration: 0.06s;
}

.dakkeh-card--market .dakkeh-card__add:disabled {
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 12%, transparent);
    color: color-mix(in srgb, var(--dakkeh-ink, #14181b) 45%, transparent);
    box-shadow: none;
    cursor: not-allowed;
}

.dakkeh-card--market .dakkeh-card__add[hidden] {
    display: none;
}

/* The glyph. Drawn rather than typed: "+" is a different width, weight and
   vertical position in every font the theme might be set to, and these
   buttons are circles whose whole job is to look like targets. */
.dakkeh-icon {
    width: 18px;
    height: 18px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.25;
    stroke-linecap: round;
    display: block;
    pointer-events: none;
}

.dakkeh-stepper {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 3px;
    border-radius: 999px;
    background: var(--dakkeh-accent, #7e9b83);
    box-shadow:
        0 1px 2px rgba(16, 24, 32, 0.16),
        0 6px 16px -8px color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 90%, transparent);
    /* Opens out of the circle it replaced rather than appearing beside it. */
    transform-origin: 100% 50%;
    animation: dakkeh-stepper-in 0.26s cubic-bezier(0.22, 1, 0.36, 1);
}

[dir="rtl"] .dakkeh-stepper {
    transform-origin: 0 50%;
}

@keyframes dakkeh-stepper-in {
    from { transform: scaleX(0.55) scaleY(0.9); opacity: 0; }
    to   { transform: none; opacity: 1; }
}

.dakkeh-stepper[hidden] {
    display: none;
}

.dakkeh-stepper .dakkeh-stepper__step {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: #fff;
    cursor: pointer;
    transition: background-color 0.16s ease, transform 0.16s ease;
}

@media (hover: hover) {
    .dakkeh-stepper .dakkeh-stepper__step:hover:not(:disabled) {
        background: rgba(255, 255, 255, 0.22);
    }
}

.dakkeh-stepper .dakkeh-stepper__step:active:not(:disabled) {
    transform: scale(0.88);
}

.dakkeh-stepper .dakkeh-stepper__step:disabled {
    opacity: 0.45;
    cursor: default;
}

.dakkeh-stepper .dakkeh-stepper__count {
    min-width: 22px;
    text-align: center;
    color: #fff;
    font-size: 13px;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* A quantity the server has not confirmed yet is shown, because the tap must
   feel answered — but it is marked, because it is not yet true (§14). */
.dakkeh-card--market.is-pending .dakkeh-stepper {
    opacity: 0.7;
}

.dakkeh-empty {
    padding: 28px 0;
    text-align: center;
    opacity: 0.7;
}

.dakkeh-storefront.is-loading {
    opacity: 0.55;
    transition: opacity 0.12s ease;
}

/* Vendor dashboard: delivery settings ------------------------------------- */

.dakkeh-vendor-settings__intro {
    margin: 0 0 18px;
    opacity: 0.8;
}

.dakkeh-zone-card {
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.14));
    border-radius: var(--wd-brd-radius, 10px);
    padding: 14px 16px 16px;
    margin: 0 0 14px;
}

.dakkeh-zone-card__legend {
    padding: 0 6px;
    font-size: 15px;
}

.dakkeh-zone-card__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 12px;
    margin-top: 10px;
}

.dakkeh-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    font-size: 13px;
}

.dakkeh-field input {
    padding: 8px 10px;
    font-size: 15px;
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.18));
    border-radius: var(--wd-form-brd-radius, 8px);
}

/* Add to cart ------------------------------------------------------------- */

.dakkeh-card__add {
    margin-top: 8px;
    padding: 7px 12px;
    width: 100%;
    border: 0;
    border-radius: var(--wd-brd-radius, 10px);
    background: var(--wd-primary-color, #7e9b83);
    color: #fff;
    font-size: 13px;
    font-weight: 600;
    cursor: pointer;
}

.dakkeh-card__add:hover:not(:disabled) {
    filter: brightness(0.93);
}

.dakkeh-card__add:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Cart bar ----------------------------------------------------------------
   Fixed to the viewport bottom: on a phone the basket must stay reachable
   without scrolling back up through a long aisle.

   Drawn as a floating rounded panel rather than a full-width dock, so the
   aisle stays visible behind it and the bar reads as something resting over
   the page rather than cutting it off.

   Every control inside is styled at two classes deep. WoodMart styles bare
   `button` through `:is(.btn,.button,button,…)` — one class of specificity —
   in a stylesheet enqueued after this one, so a single-class rule here loses
   the tie and the steppers come back as the theme's grey rectangles. */

.dakkeh-cart-bar {
    position: fixed;
    left: 50%;
    /* Above the home indicator on a phone, not under it. */
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    transform: translateX(-50%);
    z-index: 90;
    display: flex;
    flex-direction: column;
    gap: 10px;
    width: calc(100% - 24px);
    max-width: 560px;
    /* Wider sides than top and bottom: the ends are semicircles, and text set
       flush to 12px would run into the curve on the nudge row. */
    padding: 12px 18px;
    /* A true capsule. The contents are two short rows, so the ends read as
       round rather than as a stretched stadium. */
    border-radius: 999px;
    /* The fill inside is clipped to this shape, which is what makes the pill
       itself the progress track. */
    overflow: hidden;
    /* The hairline is what separates the panel from a dark page behind it once
       the background goes translucent. */
    border: 1px solid rgba(255, 255, 255, 0.12);
    background: var(--wd-title-color, #111);
    color: #fff;
    /* Three stacked shadows rather than one: a contact edge, a short shadow
       for shape and a wide soft one for lift. A single large blur reads as a
       grey smudge under the bar. */
    box-shadow:
        0 1px 2px rgba(0, 0, 0, 0.18),
        0 10px 24px -8px rgba(0, 0, 0, 0.32),
        0 28px 56px -20px rgba(0, 0, 0, 0.45),
        inset 0 1px 0 rgba(255, 255, 255, 0.10);
    animation: dakkeh-cart-bar-in 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Frosted only where the browser can actually blur what is behind it. Without
   the guard the alpha would just wash the bar out over the page. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .dakkeh-cart-bar {
        background: color-mix(in srgb, var(--wd-title-color, #111) 86%, transparent);
        -webkit-backdrop-filter: blur(20px) saturate(1.6);
        backdrop-filter: blur(20px) saturate(1.6);
    }
}

/* It arrives when the first item is added, which is a change worth seeing. */
@keyframes dakkeh-cart-bar-in {
    from {
        opacity: 0;
        transform: translateX(-50%) translateY(12px) scale(0.98);
    }

    to {
        opacity: 1;
        transform: translateX(-50%) translateY(0) scale(1);
    }
}

/* Opened, it is a panel of basket lines and can no longer be a capsule — a
   999px radius on something that tall bows the sides in past the contents. */
.dakkeh-cart-bar.is-expanded {
    border-radius: 28px;
}

.dakkeh-cart-bar__row {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 12px;
}

/* Announced, never shown. */
.dakkeh-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* display:flex above outranks the browser's own [hidden] rule, so an empty
   basket left a bar with nothing in it pinned to the viewport. */
/* Clear of WoodMart's bottom toolbar -------------------------------------
   The theme's mobile toolbar is `position: fixed; bottom: 0; height: 55px;
   z-index: 350` and is shown below 1025px. The basket bar sits at z-index 90
   from inside the page wrapper, so it was drawn *under* it: 41px of a 77px bar
   — the whole checkout button on a 375px screen — was behind Shop / Wishlist /
   Cart / My account.

   Raising the z-index is not the fix. The toolbar is painted from a stacking
   context this bar cannot climb out of, which is the same wall the quick-view
   sheet hit (ADR-053) and the reason that one is a top-layer dialog. A bar that
   merely sat on top of the toolbar would also cover the navigation, which is
   not an improvement — the bar has to move.

   So it moves, on the theme's own terms. WoodMart signals the toolbar with a
   `sticky-toolbar-on` body class and lifts its own floating furniture by the
   same 55px (`.wd-sticky-btn` to 55px, `.scrollToTop` to 67px). This follows
   that convention rather than inventing a second one, so a shop that turns the
   toolbar off gets the original offset back with no JavaScript involved.

   The literal 55px is the theme's own, verified in
   `css/parts/opt-bottom-toolbar.min.css` — it publishes no custom property for
   the toolbar's height, only `--wd-sticky-btn-height` for a different element.
   The breakpoint is the theme's too: the toolbar is `display: none` above
   1024px, so above that the bar must not be moved for a toolbar that is not
   there.

   `env(safe-area-inset-bottom)` is deliberately dropped here. The toolbar is
   anchored at `bottom: 0` and already covers the home indicator, so its top
   edge is a flat 55px from the viewport bottom on every device; adding the
   inset again would push the bar a further 34px up an iPhone for no reason.
   That is why the theme's own `.scrollToTop` uses a bare `67px`. */

@media (max-width: 1024px) {
    .sticky-toolbar-on .dakkeh-cart-bar {
        bottom: calc(14px + 55px);
    }
}

.dakkeh-cart-bar[hidden] {
    display: none;
}

.dakkeh-cart-bar .dakkeh-cart-bar__summary {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 1px;
    min-height: 0;
    padding: 6px 30px 6px 14px;
    border: 1px solid rgba(255, 255, 255, 0.10);
    border-radius: 18px;
    background: rgba(255, 255, 255, 0.06);
    box-shadow: none;
    color: inherit;
    font: inherit;
    text-transform: none;
    line-height: 1.3;
    text-align: left;
    cursor: pointer;
    transition: background 0.18s ease, border-color 0.18s ease;
}

.dakkeh-cart-bar .dakkeh-cart-bar__summary:hover,
.dakkeh-cart-bar .dakkeh-cart-bar__summary:focus-visible {
    background: rgba(255, 255, 255, 0.12);
    border-color: rgba(255, 255, 255, 0.20);
}

/* The affordance that the summary opens something. Points up once it has. */
.dakkeh-cart-bar__summary::after {
    content: '';
    position: absolute;
    top: 50%;
    right: 13px;
    width: 7px;
    height: 7px;
    border-right: 2px solid currentColor;
    border-bottom: 2px solid currentColor;
    border-radius: 1px;
    opacity: 0.6;
    transform: translateY(-75%) rotate(45deg);
    transition: transform 0.24s cubic-bezier(0.22, 1, 0.36, 1);
}

.dakkeh-cart-bar.is-expanded .dakkeh-cart-bar__summary::after {
    transform: translateY(-25%) rotate(225deg);
}

.dakkeh-cart-bar__count {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    opacity: 0.7;
}

.dakkeh-cart-bar__total {
    font-size: 17px;
    font-weight: 700;
    /* So the width does not jitter as digits change under a tapped stepper. */
    font-variant-numeric: tabular-nums;
}

/* Free-delivery progress --------------------------------------------------
   Drawn as the pill's own fill rather than as a separate track: the bar the
   shopper is already looking at becomes the measure, so the basket total and
   how close it is to the vendor's threshold are one object, not two.

   Shown only when the vendor has terms to make progress towards: a minimum
   they have set, or an offer threshold they have configured. Both figures and
   the remainder come from the server (§5); the width is the only thing
   derived here, and it is derived from those. */

.dakkeh-cart-bar__fill {
    position: absolute;
    /* Anchored to the left edge and stretched to the full height of the pill,
       so the whole bar tints rather than a strip inside it. */
    top: 0;
    bottom: 0;
    left: 0;
    z-index: 0;
    display: block;
    width: 0;
    background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--wd-primary-color, #7e9b83) 22%, transparent) 0%,
        color-mix(in srgb, var(--wd-primary-color, #7e9b83) 42%, transparent) 100%
    );
    /* Slower than a tap, so the fill is visibly travelling rather than
       jumping — the movement is the whole point of showing it. */
    transition: width 0.55s cubic-bezier(0.22, 1, 0.36, 1), background 0.3s ease;
}

/* Opened, the pill is a panel of basket lines, and a wash running the full
   height of it would sit behind the product names rather than behind a summary
   row. It drops to a line along the bottom edge: same figure, out of the way of
   the reading the shopper opened the panel to do. */
.dakkeh-cart-bar.is-expanded .dakkeh-cart-bar__fill {
    top: auto;
    height: 5px;
    border-radius: 0 999px 999px 0;
}

/* Where a background wash is vague, this is exact: a bright edge standing at
   the leading end of the fill, so the shopper can read their position off the
   pill rather than guess it from a gradient. */
.dakkeh-cart-bar__fill::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    width: 2px;
    background: var(--wd-primary-color, #7e9b83);
    box-shadow: 0 0 10px 1px color-mix(in srgb, var(--wd-primary-color, #7e9b83) 70%, transparent);
}

/* A single sweep of light across the filled part, not a looping animation: a
   permanently shimmering bar in the corner of the eye is hard to read past. */
.dakkeh-cart-bar__fill::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        90deg,
        transparent 0%,
        rgba(255, 255, 255, 0.22) 50%,
        transparent 100%
    );
    transform: translateX(-100%);
}

.dakkeh-cart-bar.is-advancing .dakkeh-cart-bar__fill::after {
    animation: dakkeh-cart-sweep 0.9s ease-out;
}

@keyframes dakkeh-cart-sweep {
    to {
        transform: translateX(100%);
    }
}

/* Reached: the pill is full and green, which says it before the words do. */
.dakkeh-cart-bar.is-unlocked .dakkeh-cart-bar__fill {
    background: linear-gradient(90deg, rgba(58, 168, 96, 0.30) 0%, rgba(88, 192, 122, 0.48) 100%);
}

/* No leading edge once there is nowhere further to go. */
.dakkeh-cart-bar.is-unlocked .dakkeh-cart-bar__fill::before {
    display: none;
}

.dakkeh-cart-bar__progress {
    position: relative;
    z-index: 1;
    padding: 1px 2px 0;
}

.dakkeh-cart-bar__progress[hidden] {
    display: none;
}

.dakkeh-cart-bar__nudge {
    margin: 0;
    font-size: 12px;
    line-height: 1.35;
    opacity: 0.85;
}

.dakkeh-cart-bar__nudge[hidden] {
    display: none;
}

.dakkeh-cart-bar.is-unlocked .dakkeh-cart-bar__nudge {
    color: #9ce4b2;
    font-weight: 600;
    opacity: 1;
}

.dakkeh-cart-bar .dakkeh-cart-bar__checkout {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-left: auto;
    min-height: 0;
    padding: 12px 24px;
    border: 0;
    /* Fully round: the pill is what makes the bar read as current rather than
       as the square dock it replaced. */
    border-radius: 999px;
    background: var(--wd-primary-color, #7e9b83);
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    text-transform: none;
    text-decoration: none;
    white-space: nowrap;
    box-shadow: 0 4px 14px -4px color-mix(in srgb, var(--wd-primary-color, #7e9b83) 70%, transparent);
    transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1), filter 0.15s ease;
}

.dakkeh-cart-bar .dakkeh-cart-bar__checkout:hover,
.dakkeh-cart-bar .dakkeh-cart-bar__checkout:focus-visible {
    filter: brightness(1.06);
}

.dakkeh-cart-bar .dakkeh-cart-bar__checkout:active {
    transform: scale(0.97);
}

/* Editable lines ----------------------------------------------------------
   The panel opens upward out of a bar pinned to the bottom of the viewport.
   It is first in the markup so the summary keeps its place on screen instead
   of being pushed down as the panel grows. */

.dakkeh-cart-bar__lines {
    position: relative;
    z-index: 1;
    max-height: min(46vh, 320px);
    padding: 2px 4px;
    overflow-y: auto;
    /* Momentum scrolling inside a fixed element, which iOS otherwise gives to
       the page behind it. */
    -webkit-overflow-scrolling: touch;
}

.dakkeh-cart-bar__lines[hidden] {
    display: none;
}

.dakkeh-cart-lines {
    margin: 0;
    padding: 0;
    list-style: none;
}

.dakkeh-cart-line {
    display: flex;
    align-items: center;
    /* So the error below can claim a row of its own instead of squeezing the
       product name out of the one above it. */
    flex-wrap: wrap;
    gap: 4px 10px;
    padding: 9px 0;
    border-bottom: 1px solid rgba(255, 255, 255, 0.12);
    font-size: 13px;
}

.dakkeh-cart-line:last-child {
    border-bottom: 0;
}

.dakkeh-cart-line__name {
    /* Zero basis, not auto: with the row wrapping for the error line, a long
       name sized to its content would take the whole row and push the stepper
       onto the next one. */
    flex: 1 1 0;
    min-width: 0;
    /* Two lines of a long grocery name, then ellipsis: the quantity control
       must not be pushed off the row by the product it belongs to. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.dakkeh-cart-line__qty {
    display: flex;
    align-items: center;
    gap: 2px;
    flex: 0 0 auto;
    padding: 2px;
    border: 1px solid rgba(255, 255, 255, 0.16);
    border-radius: 999px;
    background: rgba(255, 255, 255, 0.06);
}

.dakkeh-cart-bar .dakkeh-cart-line__step {
    /* 32px round: the smallest comfortable tap target for a control the
       shopper presses repeatedly. min-height is reset because the theme sets
       42px on every button and would stretch the pill. */
    width: 32px;
    height: 32px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    box-shadow: none;
    color: inherit;
    font-size: 17px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease;
}

.dakkeh-cart-bar .dakkeh-cart-line__step:hover:not(:disabled),
.dakkeh-cart-bar .dakkeh-cart-line__step:focus-visible {
    background: rgba(255, 255, 255, 0.16);
}

.dakkeh-cart-bar .dakkeh-cart-line__step:disabled {
    background: transparent;
    opacity: 0.35;
    cursor: not-allowed;
}

.dakkeh-cart-line__count {
    min-width: 18px;
    font-size: 13px;
    font-variant-numeric: tabular-nums;
    text-align: center;
}

.dakkeh-cart-line__total {
    flex: 0 0 auto;
    min-width: 56px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    text-align: right;
}

.dakkeh-cart-bar .dakkeh-cart-line__remove {
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: transparent;
    box-shadow: none;
    color: inherit;
    opacity: 0.55;
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease, opacity 0.15s ease;
}

.dakkeh-cart-bar .dakkeh-cart-line__remove:hover,
.dakkeh-cart-bar .dakkeh-cart-line__remove:focus-visible {
    background: rgba(255, 255, 255, 0.14);
    opacity: 1;
}

/* Full row: a refusal explains itself under the line it refused. */
.dakkeh-cart-line__error {
    flex-basis: 100%;
    margin: 0;
    font-size: 12px;
    color: var(--wd-red, #e05c4b);
}

.dakkeh-cart-line__error[hidden] {
    display: none;
}

/* While a change is in flight the figures on screen are the previous ones.
   They are dimmed rather than recomputed here: the server owns every number
   in this bar, so the honest thing to show is "not settled yet" (§14). */
.dakkeh-cart-bar.is-pending .dakkeh-cart-bar__total,
.dakkeh-cart-bar.is-pending .dakkeh-cart-line__total,
.dakkeh-cart-bar.is-pending .dakkeh-cart-bar__nudge,
/* The fill is dimmed with them: the length it is showing is the previous
   answer, and a pill left bright would look like it had already updated. */
.dakkeh-cart-bar.is-pending .dakkeh-cart-bar__fill {
    opacity: 0.45;
}

.dakkeh-cart-bar.is-pending .dakkeh-cart-bar__checkout {
    pointer-events: none;
    opacity: 0.5;
}

/* Checkout preflight ------------------------------------------------------ */

/* Only the button is stood down: the figures beside it are current, unlike
   during a quantity change, so dimming them would say something untrue. */
.dakkeh-cart-bar.is-checking .dakkeh-cart-bar__checkout {
    pointer-events: none;
    opacity: 0.6;
}

/* Why checkout was refused. Sits above the row, in the same place the nudge
   does, because it replaces that sentence's job the moment it appears. */
.dakkeh-cart-bar__alert {
    position: relative;
    z-index: 1;
    margin: 0 0 8px;
    padding: 8px 12px;
    border-radius: 12px;
    background: rgba(224, 92, 75, 0.16);
    color: #ffc9c1;
    font-size: 12px;
    line-height: 1.4;
}

.dakkeh-cart-bar__alert[hidden] {
    display: none;
}

/* The line the shopper has to act on, marked as well as explained: the
   sentence under it is small, and a basket of ten lines needs the one that is
   holding up checkout to be findable without reading all ten. */
.dakkeh-cart-line.is-blocked {
    /* Negative margin against the padding, so the tint has room to breathe
       without the row's contents shifting sideways as it appears. */
    margin: 0 -10px;
    padding-right: 10px;
    padding-left: 10px;
    border-radius: 12px;
    background: rgba(224, 92, 75, 0.12);
    box-shadow: inset 2px 0 0 var(--wd-red, #e05c4b);
}

/* Store conflict dialog --------------------------------------------------- */

.dakkeh-dialog {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(0, 0, 0, 0.45);
}

.dakkeh-dialog[hidden] {
    display: none;
}

.dakkeh-dialog__panel {
    width: 100%;
    max-width: 420px;
    padding: 22px;
    border-radius: var(--wd-brd-radius, 12px);
    background: #fff;
}

.dakkeh-dialog__title {
    margin: 0 0 10px;
    font-size: 18px;
    color: var(--wd-title-color, #111);
}

.dakkeh-dialog__body {
    margin: 0 0 18px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--wd-text-color, #444);
}

/* The actions name their stores, so their width is a vendor's name and not a
   fixed phrase. They wrap rather than overflow, and once wrapped each takes
   the row. */
.dakkeh-dialog__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    justify-content: flex-end;
}

.dakkeh-dialog__cancel,
.dakkeh-dialog__confirm {
    flex: 0 1 auto;
    padding: 9px 16px;
    border-radius: var(--wd-brd-radius, 10px);
    font-size: 14px;
    font-weight: 600;
    line-height: 1.3;
    text-align: center;
    cursor: pointer;
}

/* The cancel action is visually dominant: switching stores parks a basket the
   shopper has already filled, so it should never be the easy accident. */
.dakkeh-dialog__cancel {
    border: 1px solid var(--wd-form-brd-color, rgba(0, 0, 0, 0.18));
    background: #fff;
    color: var(--wd-title-color, #111);
}

.dakkeh-dialog__confirm {
    border: 0;
    background: var(--wd-primary-color, #7e9b83);
    color: #fff;
}

/* Vendor header slider --------------------------------------------------
   A scroll-snap strip, not a scripted carousel. Native scrolling keeps the
   keyboard, touch and screen-reader behaviour the browser already gets right,
   and costs no JavaScript on a page whose load time is the point. */

.dakkeh-store-slider {
    display: flex;
    gap: 8px;
    margin-bottom: 16px;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    border-radius: var(--wd-brd-radius, 12px);
}

/* A single image is not a slider; let it fill the width rather than sit in a
   scroller that cannot scroll. */
.dakkeh-store-slider__image:only-child {
    width: 100%;
}

.dakkeh-store-slider__image {
    flex: 0 0 100%;
    scroll-snap-align: start;
    /* Fixed ratio so the page does not jump as each image loads — the images
       are vendor-supplied and arrive at whatever dimensions they like. */
    aspect-ratio: 3 / 1;
    width: 100%;
    object-fit: cover;
    border-radius: var(--wd-brd-radius, 12px);
}

@media (max-width: 600px) {
    .dakkeh-store-slider__image {
        aspect-ratio: 2 / 1;
    }
}

/* Vendor dashboard picker */
.dakkeh-header-images {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 12px;
}

.dakkeh-header-image {
    position: relative;
    width: 180px;
    margin: 0;
}

.dakkeh-header-image img {
    width: 100%;
    height: 110px;
    object-fit: cover;
    border-radius: 6px;
    display: block;
}

.dakkeh-vendor-hint {
    opacity: 0.8;
}

/* =========================================================================
   Storefront shell — 2026 refresh
   =========================================================================
   Product cards are the theme's now (ADR-033), so nothing here styles a card.
   This is the frame around them: header, search, aisles, section rhythm and
   loading states. Everything keys off WoodMart variables so a palette change
   in the theme panel moves this too. */

.dakkeh-storefront {
    /* Fluid rather than stepped: one scale that works from a phone to a wide
       desktop without a breakpoint for each. */
    --dakkeh-gap: clamp(12px, 2vw, 24px);
    --dakkeh-radius: var(--wd-brd-radius, 14px);
    --dakkeh-surface: var(--wd-main-bgcolor, #fff);
    --dakkeh-ink: var(--wd-title-color, #14181b);
    --dakkeh-muted: color-mix(in srgb, var(--wd-text-color, #5b6670) 70%, transparent);
    --dakkeh-line: color-mix(in srgb, var(--wd-title-color, #14181b) 10%, transparent);
    --dakkeh-accent: var(--wd-primary-color, #7e9b83);
}

/* Store header ------------------------------------------------------------ */

.dakkeh-store-header {
    display: flex;
    gap: clamp(12px, 2vw, 20px);
    align-items: center;
    padding: clamp(12px, 2vw, 22px) 0 clamp(14px, 2.4vw, 26px);
}

.dakkeh-store-header__logo {
    width: clamp(56px, 8vw, 76px);
    height: clamp(56px, 8vw, 76px);
    border-radius: var(--dakkeh-radius);
    /* A hairline instead of a shadow: vendor logos are often already on a
       white tile, and a shadow doubles up on that. */
    box-shadow: 0 0 0 1px var(--dakkeh-line);
}

.dakkeh-store-header__name {
    font-size: clamp(20px, 3.4vw, 30px);
    letter-spacing: -0.02em;
    line-height: 1.15;
}

.dakkeh-store-header__status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

/* A dot carries the state as well as the colour, so it survives being read in
   greyscale or by someone who cannot separate red from green. */
.dakkeh-store-header__status::before {
    content: '';
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex: 0 0 auto;
}

/* Sticky utility bar ------------------------------------------------------ */

.dakkeh-store-search {
    position: relative;
    margin: 0 0 var(--dakkeh-gap);
}

.dakkeh-store-search__input {
    padding: 13px 16px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--dakkeh-ink) 4%, var(--dakkeh-surface));
    border: 1px solid transparent;
    transition: border-color 0.15s ease, background 0.15s ease;
}

.dakkeh-store-search__input:focus {
    outline: none;
    background: var(--dakkeh-surface);
    border-color: var(--dakkeh-accent);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--dakkeh-accent) 18%, transparent);
}

.dakkeh-category-tile:focus-visible,
.dakkeh-row__track:focus-visible,
.dakkeh-row__more:focus-visible {
    outline: 2px solid var(--dakkeh-accent);
    outline-offset: 2px;
}

/* Rows --------------------------------------------------------------------
   Named .dakkeh-section until the rows became vendor-arranged (ADR-037). */

.dakkeh-row {
    /* No `content-visibility: auto` here, however tempting the below-the-fold
       saving is.
     *
     * It implies `contain: layout paint style`, and **paint containment clips
     * descendants to this element's padding box**. WoodMart's product hover is
     * `.fade-in-block { position: absolute; top: 100% }`
     * (`css/parts/woo-product-loop-base.min.css`) — the panel is painted
     * entirely *below* the card it belongs to. For any card in a section's last
     * row that lands outside the section, so hovering cut the card in half.
     *
     * There is no width of `padding-bottom` that fixes it either: the panel's
     * height is whatever the shop owner's WoodMart panel puts in it — button,
     * swatches, quantity — and is not knowable from here.
     *
     * The card is the theme's (ADR-033). Paint-containing it is fighting it. */
    margin-bottom: clamp(26px, 4vw, 44px);
}

.dakkeh-row__title {
    font-size: clamp(16px, 2.2vw, 21px);
    letter-spacing: -0.01em;
}

/* Availability over a theme-rendered card --------------------------------- */

/* The card is the theme's, so this only dims it and never restyles it. The
   item stays visible: a shopper who cannot find something they expected
   assumes the shop is broken (§26). */
.dakkeh-unavailable .product-image-link,
.dakkeh-unavailable .wd-entities-title,
.dakkeh-unavailable .price,
.dakkeh-unavailable .dakkeh-card__media,
.dakkeh-unavailable .dakkeh-card__name,
.dakkeh-unavailable .dakkeh-card__price {
    opacity: 0.4;
    transition: opacity 0.15s ease;
}

/* Deliberately not scoped to .dakkeh-unavailable, because not every note comes
   with one. A refusal given for a reason that is not about the product — a
   dropped connection, an internal error — is written onto the card as well, and
   has to be readable without the card being dimmed as though the item were off
   the shelf. */
.dakkeh-card__note {
    margin: 6px 0 0;
    font-size: 12px;
    font-weight: 600;
    color: var(--wd-red, #c0392b);
}

/* Loading ----------------------------------------------------------------- */

.dakkeh-skeleton {
    border-radius: var(--dakkeh-radius);
    background: linear-gradient(
        100deg,
        color-mix(in srgb, var(--dakkeh-ink) 6%, transparent) 30%,
        color-mix(in srgb, var(--dakkeh-ink) 11%, transparent) 50%,
        color-mix(in srgb, var(--dakkeh-ink) 6%, transparent) 70%
    );
    background-size: 220% 100%;
    animation: dakkeh-shimmer 1.3s ease-in-out infinite;
    aspect-ratio: 3 / 4;
}

@keyframes dakkeh-shimmer {
    from { background-position: 160% 0; }
    to   { background-position: -60% 0; }
}

/* The old opacity dip is gone: skeletons already say "loading", and fading
   the results the shopper is reading is worse than leaving them alone. */
.dakkeh-storefront.is-loading {
    opacity: 1;
}

.dakkeh-empty {
    padding: clamp(32px, 6vw, 60px) 16px;
    text-align: center;
    color: var(--dakkeh-muted);
    font-size: 15px;
}

/* Quick-view sheet --------------------------------------------------------
   What opens when a card is tapped, instead of the product page (ADR-053).

   A sheet from the bottom rather than a centred dialog, because the thing it
   replaces was a page load on a phone, and the thumb that tapped the card is
   already at the bottom of the screen. On a wide screen it becomes a centred
   panel: a 1600px-wide strip of content 200px tall is not a sheet, it is a
   bar.

   Everything in here ships empty. The panel is in the cached page; its
   contents arrive per shopper from a route that is not shared-cached
   (ADR-007). */

/* A `<dialog>`, so every UA default has to be undone before anything is built
   on top of it: the browser gives one `position: fixed`, `inset: 0`, `margin:
   auto`, a border, padding, a white background and `width: fit-content`. */
dialog.dakkeh-sheet {
    position: fixed;
    inset: 0;
    width: 100%;
    max-width: none;
    height: 100%;
    max-height: none;
    margin: 0;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    overflow: visible;
}

/* Closed is `display: none` from the UA sheet, which is what `[open]` is for.
   No `z-index` anywhere here: a modal dialog is in the browser's top layer,
   above every stacking context on the page — which is the whole reason this is
   a dialog. WoodMart's toolbar is z-index 350 and its side panels 500, from
   contexts a descendant of the page wrapper cannot climb out of. */
dialog.dakkeh-sheet[open] {
    display: flex;
    align-items: flex-end;
    justify-content: center;
}

/* The scrim is the dialog's own backdrop pseudo-element rather than a button
   over the page — it cannot be mispositioned, and it is in the top layer with
   the panel. Dismissing on a backdrop click is wired in script, because a
   click there reports the dialog itself as its target. */
dialog.dakkeh-sheet::backdrop {
    background: rgba(12, 17, 22, 0.44);
    /* Cheap on the compositor and it does the one job a scrim has: separating
       the panel from a grid of packets in every colour. */
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
    animation: dakkeh-sheet-fade 0.22s ease-out;
}

@keyframes dakkeh-sheet-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}

.dakkeh-sheet .dakkeh-sheet__panel {
    position: relative;
    width: 100%;
    max-width: 560px;
    /* Never taller than the screen it is on, and shorter than a full takeover
       so the aisle behind stays visible — the sheet is a look at one product,
       not a new page, and it should not pretend otherwise. */
    max-height: min(88vh, 880px);
    display: flex;
    flex-direction: column;
    /* No horizontal padding here: it belongs to the scrolling half below, so
       that a scrollbar sits at the panel's edge rather than on top of the
       right-aligned spec values. */
    padding: 0 0 calc(16px + env(safe-area-inset-bottom, 0px));
    border-radius: 26px 26px 0 0;
    background: var(--dakkeh-surface, #fff);
    box-shadow: 0 -12px 44px -16px rgba(12, 17, 22, 0.5);
    overflow: hidden;
    animation: dakkeh-sheet-rise 0.34s cubic-bezier(0.22, 1, 0.36, 1);
    /* Set by the drag handler while a finger is on the panel, and cleared on
       release. Zero the rest of the time, so the rule below is inert. */
    translate: 0 var(--dakkeh-sheet-drag, 0px);
}

@keyframes dakkeh-sheet-rise {
    from { transform: translateY(14%); opacity: 0.4; }
    to   { transform: none; opacity: 1; }
}

/* Leaving. Driven by a class rather than by removing the element, so the sheet
   is still on screen while it goes. */
.dakkeh-sheet.is-closing .dakkeh-sheet__panel {
    animation: dakkeh-sheet-sink 0.2s ease-in forwards;
}

.dakkeh-sheet.is-closing::backdrop {
    animation: dakkeh-sheet-fade 0.2s ease-in reverse forwards;
}

@keyframes dakkeh-sheet-sink {
    from { transform: none; opacity: 1; }
    to   { transform: translateY(18%); opacity: 0; }
}

@media (min-width: 720px) {
    dialog.dakkeh-sheet[open] {
        align-items: center;
        padding: 24px;
    }

    .dakkeh-sheet .dakkeh-sheet__panel {
        border-radius: 24px;
        max-height: min(86vh, 820px);
        padding-bottom: 16px;
        animation-name: dakkeh-sheet-pop;
    }

    @keyframes dakkeh-sheet-pop {
        from { transform: translateY(12px) scale(0.97); opacity: 0; }
        to   { transform: none; opacity: 1; }
    }
}

/* The grab handle. A real button, because it is the close control on a phone
   where the × is a long reach away — and because a decorative bar that
   dismisses on drag is a gesture with nothing to announce it. */
.dakkeh-sheet .dakkeh-sheet__grab {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 100%;
    padding: 12px 0 8px;
    border: 0;
    background: none;
    cursor: grab;
    touch-action: none;
}

.dakkeh-sheet .dakkeh-sheet__grab::before {
    content: "";
    width: 42px;
    height: 4px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 18%, transparent);
}

/* One close affordance per input model, rather than both at once in a small
   panel: the handle is the drag target and only exists where dragging does,
   and the corner × is where a pointer looks for it. */
@media (min-width: 720px) {
    .dakkeh-sheet .dakkeh-sheet__grab {
        display: none;
    }
}

.dakkeh-sheet .dakkeh-sheet__close {
    /* Shown from 720px up, where the handle is not. */
    display: none;
    position: absolute;
    top: 10px;
    inset-inline-end: 10px;
    z-index: 3;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 7%, transparent);
    color: var(--dakkeh-ink, #14181b);
    cursor: pointer;
}

.dakkeh-sheet .dakkeh-sheet__close:hover {
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 13%, transparent);
}

@media (min-width: 720px) {
    .dakkeh-sheet .dakkeh-sheet__close {
        display: grid;
    }

    /* The handle's padding was the panel's top spacing on a phone. Without it
       the hero would sit against the panel's edge. */
    .dakkeh-sheet .dakkeh-sheet__panel {
        padding-top: 20px;
    }
}

/* The scrolling half. Only this scrolls: the actions stay on the panel, so the
   Add button never leaves the screen on a long description. */
.dakkeh-sheet__body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 0 clamp(16px, 4vw, 24px) 4px;
    /* Reserved whether or not the scrollbar is an overlay one, so the layout
       does not shift when a short sheet grows past the fold. */
    scrollbar-gutter: stable;
}

.dakkeh-sheet__product {
    display: flex;
    flex-direction: column;
}

.dakkeh-sheet__hero {
    position: relative;
    align-self: center;
    width: min(100%, 320px);
    aspect-ratio: 1 / 1;
    border-radius: 20px;
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 3.5%, transparent);
    overflow: hidden;
}

.dakkeh-sheet__image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 14px;
    display: block;
    /* Swapped by the thumbnail strip; the fade is what stops the swap reading
       as a flicker. */
    animation: dakkeh-sheet-fade 0.2s ease-out;
}

.dakkeh-sheet__image--placeholder {
    padding: 0;
}

.dakkeh-sheet__discount {
    top: 12px;
    inset-inline-start: 12px;
}

.dakkeh-sheet__thumbs {
    display: flex;
    gap: 8px;
    margin: 12px 0 0;
    padding: 0;
    list-style: none;
    overflow-x: auto;
    scrollbar-width: none;
    justify-content: center;
}

.dakkeh-sheet__thumbs::-webkit-scrollbar {
    display: none;
}

.dakkeh-sheet .dakkeh-sheet__thumb {
    width: 52px;
    height: 52px;
    padding: 4px;
    border: 1px solid var(--dakkeh-line, rgba(0, 0, 0, 0.1));
    border-radius: 12px;
    background: var(--dakkeh-surface, #fff);
    cursor: pointer;
    transition: border-color 0.18s ease, transform 0.18s ease;
}

.dakkeh-sheet .dakkeh-sheet__thumb img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    display: block;
}

.dakkeh-sheet .dakkeh-sheet__thumb.is-current {
    border-color: var(--dakkeh-accent, #7e9b83);
    transform: translateY(-1px);
}

.dakkeh-sheet__head {
    margin: 18px 0 0;
}

.dakkeh-sheet__eyebrow {
    margin: 0 0 4px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--dakkeh-accent, #7e9b83);
}

.dakkeh-sheet__name {
    margin: 0 0 8px;
    font-family: var(--wd-title-font, inherit);
    font-size: clamp(19px, 2.4vw, 23px);
    line-height: 1.25;
    color: var(--dakkeh-ink, #14181b);
}

.dakkeh-sheet__price {
    display: flex;
    align-items: baseline;
    gap: 8px;
    margin: 0;
}

.dakkeh-sheet__price-now {
    font-size: 24px;
    font-weight: 800;
    letter-spacing: -0.02em;
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-ink, #14181b);
}

.dakkeh-sheet__price-was {
    font-size: 15px;
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.5));
}

/* One line of state, colour-coded so it reads before it is read. */
.dakkeh-sheet__state {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 10px 0 0;
    font-size: 13px;
    font-weight: 600;
}

.dakkeh-sheet__state::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
}

.dakkeh-sheet__state--ok {
    color: #2f7d4f;
}

.dakkeh-sheet__state--low {
    color: #b06d1c;
}

.dakkeh-sheet__state--blocked {
    color: #c05746;
}

/* ---------------------------------------------------------------------------
   The option picker (ADR-071)

   Chips rather than a select: four options on the median product here, labels
   as short as "250g", and a native select on a phone opens a system sheet on
   top of the one this already is.

   Every chip carries its own price, which is the reason the picker exists —
   a shopper reading $9.78 on a tile and choosing 1kg has to see $23.45 here
   rather than at checkout.
   --------------------------------------------------------------------------- */
.dakkeh-options {
    margin: 16px 0 0;
}

.dakkeh-options__label {
    margin: 0 0 8px;
    font-size: 13px;
    font-weight: 700;
    color: var(--dakkeh-ink, #14181b);
}

.dakkeh-options__list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/*
 * Every chip starts from nothing.
 *
 * WoodMart styles bare `button` elements through selectors more specific than a
 * single class of ours — a grey fill, no border, no radius, `inline-flex`,
 * 5px/20px padding, uppercase, centre-aligned — and the first version of these
 * rules quietly lost every one of those arguments. The chips rendered as the
 * theme's own grey slabs, and with `border-width: 0` winning, the chosen state
 * had nothing left to draw itself with.
 *
 * Prefixing with the container doubles the specificity and wins without
 * `!important`, which would then have to be argued with in turn by anyone
 * restyling this. Same fix, and the same reason, as the checkout indicator
 * (checkout-flow.css).
 */
.dakkeh-options .dakkeh-option,
.dakkeh-options .dakkeh-option:hover,
.dakkeh-options .dakkeh-option:focus {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 1px;
    width: auto;
    min-width: 0;
    min-height: 48px;
    padding: 7px 14px;
    border: 1.5px solid var(--dakkeh-line, rgba(0, 0, 0, 0.14));
    border-radius: 14px;
    background: transparent;
    background-color: transparent;
    box-shadow: none;
    color: var(--dakkeh-ink, #14181b);
    font: inherit;
    line-height: 1.3;
    text-align: left;
    text-transform: none;
    letter-spacing: normal;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.dakkeh-options .dakkeh-option:hover:not(:disabled) {
    border-color: var(--dakkeh-accent, #7e9b83);
}

/* The chosen one is stated twice — a filled border and a tinted ground —
   because a border alone at this weight is the only difference between
   "chosen" and "hovered" on a device that has no hover. */
.dakkeh-options .dakkeh-option.is-chosen {
    border-color: var(--dakkeh-accent, #7e9b83);
    background-color: color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 12%, transparent);
}

.dakkeh-option__label {
    font-size: 14px;
    font-weight: 700;
    line-height: 1.2;
}

.dakkeh-option__price {
    font-size: 12px;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.55));
}

.dakkeh-options .dakkeh-option.is-chosen .dakkeh-option__price {
    color: var(--dakkeh-ink, #14181b);
}

/* Dimmed and struck rather than removed. A size that is normally stocked and
   is not today is worth knowing about — a shopper who cannot find the 250g bag
   assumes the shop stopped selling it (§26). */
.dakkeh-options .dakkeh-option:disabled {
    cursor: not-allowed;
    border-style: dashed;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.45));
}

.dakkeh-options .dakkeh-option:disabled .dakkeh-option__label {
    text-decoration: line-through;
    text-decoration-thickness: 1px;
}

.dakkeh-option__state {
    font-size: 11px;
    font-weight: 700;
    color: #c05746;
}

.dakkeh-options__elsewhere {
    margin: 16px 0 0;
    padding: 10px 12px;
    border-radius: 12px;
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 5%, transparent);
    font-size: 13px;
    line-height: 1.5;
    color: var(--wd-text-color, #47525c);
}

.dakkeh-sheet__summary {
    margin: 14px 0 0;
    font-size: 14px;
    line-height: 1.6;
    color: var(--wd-text-color, #47525c);
}

/* The facts table. A definition list rather than a table element: these are
   label-and-value pairs, not a grid with meaningful rows and columns, and a
   screen reader announces the pairing without the caller having to build a
   header row that says nothing. */
.dakkeh-specs {
    margin: 16px 0 0;
    padding: 4px 0 0;
    border-top: 1px solid var(--dakkeh-line, rgba(0, 0, 0, 0.09));
}

.dakkeh-specs__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    padding: 9px 0;
    border-bottom: 1px solid var(--dakkeh-line, rgba(0, 0, 0, 0.06));
    font-size: 13px;
}

.dakkeh-specs__row:last-child {
    border-bottom: 0;
}

.dakkeh-specs dt {
    flex: 0 0 auto;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.55));
}

.dakkeh-specs dd {
    margin: 0;
    text-align: end;
    font-weight: 600;
    color: var(--dakkeh-ink, #14181b);
}

.dakkeh-sheet .dakkeh-sheet__more {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    /* A row in the pinned bar rather than a line of body text, so it gets a
       tappable height of its own. */
    min-height: 34px;
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    color: var(--dakkeh-accent, #7e9b83);
    text-decoration: none;
    transition: gap 0.18s ease;
}

.dakkeh-sheet .dakkeh-sheet__more:hover {
    gap: 10px;
    text-decoration: underline;
}

/* Actions ------------------------------------------------------------------
   Pinned to the panel rather than the scroller, and lifted off it by a fade so
   a description scrolling underneath does not run into the button. */

.dakkeh-sheet__actions {
    position: sticky;
    bottom: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 10px;
    margin-top: 14px;
    padding: 14px 0 2px;
    /* Fades the scrolling content out from under the bar rather than cutting
       it off at a hard line. */
    background: linear-gradient(
        to bottom,
        color-mix(in srgb, var(--dakkeh-surface, #fff) 0%, transparent),
        var(--dakkeh-surface, #fff) 22%
    );
}

.dakkeh-sheet__buy {
    display: flex;
    align-items: center;
    gap: 10px;
}

.dakkeh-sheet .dakkeh-sheet__add {
    flex: 1 1 auto;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 50px;
    padding: 0 18px;
    border: 0;
    border-radius: 999px;
    background: var(--dakkeh-accent, #7e9b83);
    color: #fff;
    font-size: 15px;
    font-weight: 700;
    cursor: pointer;
    box-shadow: 0 8px 20px -10px color-mix(in srgb, var(--dakkeh-accent, #7e9b83) 95%, transparent);
    transition: transform 0.18s cubic-bezier(0.34, 1.56, 0.64, 1), filter 0.18s ease;
}

.dakkeh-sheet .dakkeh-sheet__add:hover:not(:disabled) {
    filter: brightness(1.06);
}

.dakkeh-sheet .dakkeh-sheet__add:active:not(:disabled) {
    transform: scale(0.98);
}

.dakkeh-sheet .dakkeh-sheet__add:disabled {
    background: color-mix(in srgb, var(--dakkeh-ink, #14181b) 12%, transparent);
    color: color-mix(in srgb, var(--dakkeh-ink, #14181b) 45%, transparent);
    box-shadow: none;
    cursor: not-allowed;
}

.dakkeh-sheet .dakkeh-sheet__add[hidden] {
    display: none;
}

.dakkeh-sheet__add-price {
    font-variant-numeric: tabular-nums;
}

/* The stepper here is the card's, at the size a sheet's own controls are. */
.dakkeh-sheet .dakkeh-stepper--wide {
    flex: 1 1 auto;
    justify-content: space-between;
    min-height: 50px;
    padding: 4px 6px;
}

.dakkeh-sheet .dakkeh-stepper--wide .dakkeh-stepper__step {
    width: 42px;
    height: 42px;
}

.dakkeh-sheet .dakkeh-stepper--wide .dakkeh-stepper__count {
    font-size: 16px;
}

/* A quantity the server has not confirmed yet, in the sheet's own stepper.
   Says the same thing the tile's does, because it is the same line (§14). */
.dakkeh-sheet__product.is-pending .dakkeh-stepper {
    opacity: 0.7;
}

/* The page behind the sheet does not scroll. `overscroll-behavior` on the
   panel keeps a touch drag inside it; this is the other half, for a wheel or a
   trackpad over the scrim. */
.dakkeh-sheet-open {
    overflow: hidden;
}

/* Loading and failure ------------------------------------------------------ */

.dakkeh-sheet__loading {
    display: grid;
    gap: 14px;
    padding: 8px 0 20px;
    justify-items: center;
}

.dakkeh-sheet__loading span {
    width: 100%;
    height: 16px;
    border-radius: 8px;
    background: linear-gradient(
        90deg,
        color-mix(in srgb, var(--dakkeh-ink, #14181b) 6%, transparent) 25%,
        color-mix(in srgb, var(--dakkeh-ink, #14181b) 11%, transparent) 37%,
        color-mix(in srgb, var(--dakkeh-ink, #14181b) 6%, transparent) 63%
    );
    background-size: 220% 100%;
    animation: dakkeh-shimmer 1.3s ease-in-out infinite;
}

.dakkeh-sheet__loading span:first-child {
    width: min(100%, 320px);
    height: auto;
    aspect-ratio: 1 / 1;
    border-radius: 20px;
}

.dakkeh-sheet__loading span:last-child {
    width: 55%;
    justify-self: start;
}

/* A sheet that cannot be filled says so and offers the page it was standing
   in for. Refusing to open at all would leave the shopper tapping a card that
   does nothing. */
.dakkeh-sheet__failed {
    padding: 20px 0 28px;
    text-align: center;
    color: var(--dakkeh-muted, rgba(0, 0, 0, 0.55));
    font-size: 14px;
}

@media (prefers-reduced-motion: reduce) {
    .dakkeh-skeleton {
        animation: none;
    }

    /* The sheet still arrives and still leaves; it stops sliding to do it.
       Matched at the same depth as the rules they override — the sheet's own
       controls are written two classes deep to outrank WoodMart's `button`
       styling, and a one-class rule here would lose to *them*. */
    .dakkeh-sheet .dakkeh-sheet__panel,
    dialog.dakkeh-sheet::backdrop,
    .dakkeh-sheet .dakkeh-sheet__image,
    .dakkeh-card--market .dakkeh-stepper,
    .dakkeh-sheet .dakkeh-stepper {
        animation: none;
    }

    .dakkeh-card--market,
    .dakkeh-card--market .dakkeh-card__image,
    .dakkeh-card--market .dakkeh-card__add,
    .dakkeh-stepper .dakkeh-stepper__step,
    .dakkeh-sheet .dakkeh-sheet__add,
    .dakkeh-sheet .dakkeh-sheet__more,
    .dakkeh-sheet .dakkeh-sheet__thumb {
        transition: none;
    }

    /* A tile that lifts and scales on press is the one piece of motion here
       that is purely feedback, so it goes entirely rather than being sped up. */
    .dakkeh-card--market:hover,
    .dakkeh-card--market:active,
    .dakkeh-card--market:hover .dakkeh-card__image {
        transform: none;
    }

    .dakkeh-cart-bar,
    .dakkeh-cart-bar.is-advancing .dakkeh-cart-bar__fill::after {
        animation: none;
    }

    .dakkeh-category-tile__media,
    .dakkeh-store-search__input,
    .dakkeh-unavailable .product-image-link,
    .dakkeh-storefront--unserviceable .dakkeh-card,
    /* The bar still reaches the right length, it just gets there at once. */
    .dakkeh-cart-bar__fill,
    .dakkeh-cart-bar__summary::after,
    .dakkeh-cart-bar .dakkeh-cart-bar__checkout {
        transition: none;
    }
}
