/* ==========================================================================
   ShopInnKart - Component & theme layer
   Loaded after tailwind.css (the utility layer).

   Every colour, radius and width here reads from a CSS custom property that
   /includes/header.php prints from Admin > Settings > Theme. Nothing visual
   is hard-coded in PHP.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. Webfont
   --------------------------------------------------------------------------
   --sik-font has always led with "Inter" and nothing ever loaded it: there was
   no @font-face, no <link> to a font host, and no assets/fonts directory. Every
   visitor fell straight through to `ui-sans-serif, system-ui` — Segoe UI on
   Windows, SF on a Mac. Admin > Settings > Theme offers Inter, Roboto and
   Poppins too, and picking any of them changed nothing for the same reason.
   The store has never rendered in the typeface it claims.

   Plus Jakarta Sans, self-hosted. Self-hosted rather than linked to a font CDN
   because a third-party stylesheet in <head> is a render-blocking request to a
   host we do not control, and it hands every visitor's IP to that host.

   ONE variable file per subset covers 400-800, so the whole weight range costs
   52 KB — less than a single static weight of most families.

   latin-ext is NOT optional here: its range U+20AD-20C0 is what carries U+20B9,
   the rupee sign. Shipping only `latin` would have left every price falling
   back to the system font for its first glyph — "₹399" set in two typefaces.

   ---- font-display: optional, and why it is not swap ---------------------

   swap paints the fallback immediately and re-lays the text out when the file
   lands. The metric overrides below make that re-layout very nearly free, but
   NOT free: size-adjust equalises the AVERAGE advance of a sample string, and
   any line that happens to sit within a few tenths of a percent of its wrap
   point still re-wraps when the per-glyph advances change. That is not a
   tuning error, it is the shape of the problem — two different typefaces
   cannot break every line in the same place.

   Measured with both woff2 responses held back 1200ms (CDP Fetch domain) so
   the swap is FORCED to land after first paint, which is what a real network
   does and what localhost hides; headless Edge, a PerformanceObserver on
   layout-shift installed through Page.addScriptToEvaluateOnNewDocument — i.e.
   genuinely before the first byte of the document is parsed — three cold runs
   each, cache disabled:

       shop.php    1366   0.0092   .sik-listing__sub goes from two lines to
                                   one, the listing body lifts 23.8px
       shop.php     390   0.0009   a card title un-wraps, .sik-card__foot
                                   drops 21.5px
       product.php 1366   0.0007   the announce promo re-measures
       index.php   1366   0.0006   the same announce promo
       search.php   390   0.0002   the "Clear all" chip changes width

   optional gives the face a ~100ms block period and NO swap period: the file
   is either in hand in time for first paint and used for that whole page
   view, or it is not used on that view at all (and is cached for the next
   one). Nothing re-wraps after paint, so the shift cannot occur — every
   figure above is 0.0000 under the same forced 1200ms delay, and the natural
   localhost load, where the preload wins the race, renders in Plus Jakarta
   Sans and also measures 0.0000. (The one entry left on index.php at 1366
   afterwards, 0.00015, is the hero carousel advancing between slides of
   different heights, not the font — section 8's .sik-hero__slide.)

   The cost is a first view, on a slow enough connection, that reads in the
   fallback. Which is exactly what the overrides below are for.

   ---- Why the faces below carry metric overrides ------------------------

   Under `optional` the page renders in ONE of the two fonts for its whole
   life, so the two have to lay out the same — otherwise the store is a
   different shape depending on how the visitor's connection went. Each face
   therefore declares the metrics of the font it stands in for, measured
   rather than guessed: rendered at 1000px in a headless page, advance width
   compared against the fallback stack and the baseline read off a zero-size
   inline box (scratchpad/left2-storefront/metrics.html re-runs it):

       Segoe UI (= what `ui-sans-serif`/`system-ui` resolve to on Windows,
       and the first installed name in the stack elsewhere):
           ascent 107.9%   descent 25.1%   line-gap 0%   line box 133%
           x-height 50.0%  cap-height 70.0%
       Plus Jakarta Sans as shipped:
           ascent 103.8%   descent 23.7%   line-gap 0%   line box 127.5%
           x-height 53.6%  cap-height 74.5%

   ascent-override / descent-override / line-gap-override are divided by that
   face's size-adjust on purpose: Blink re-scales an explicit override by
   size-adjust, so 108% written literally lands as 102%. The values below are
   the ones that COME OUT at 107.9 / 25.1 / 0 once size-adjust has been
   applied — verified by measuring the adjusted faces back, not by reading the
   spec.

   FIVE faces per subset instead of one 400-800 face, because the width ratio
   is not constant across the weight axis: Segoe UI only ships Regular,
   Semibold, Bold and Black, so 500 and 600 render identically in the fallback
   while Plus Jakarta Sans interpolates them, and a single size-adjust that
   fixed 400 would leave 800 out by 7%. The bands are tiled rather than pinned
   to the five design weights so nothing SNAPS: admin.css asks for 650, which
   lands in the 551-650 band and still renders at 650, with the size-adjust of
   the neighbouring design weight. All five bands point at the same two files,
   so this is still two downloads.

   The size-adjust does mean the store renders Plus Jakarta Sans a few percent
   off its nominal size. That is the point of the descriptor and not a
   compromise: Plus Jakarta Sans has a much larger x-height than Segoe UI, so
   at 94.9% its x-height is 50.9% of the font size against Segoe UI's 50.0% —
   the two LOOK the same size, which is what keeps a fallback view and a
   webfont view the same page. It is also how a probe can tell which one is on
   screen: `height: 100ex` at `font-size: 1000px` measures 508.6px when Plus
   Jakarta Sans is being painted and exactly 500px when it is not, where
   document.fonts.check() cannot tell them apart (under `optional` a face that
   missed its deadline is loaded but unused).

   The preload that goes with this lives in includes/header.php, in <head>
   ahead of the stylesheets, and deliberately links the same unversioned URL
   these rules request. It is not decoration under `optional`: it is what wins
   the 100ms block period, and therefore what decides whether the store
   renders in its own typeface at all.
   -------------------------------------------------------------------------- */

/* Band 1 of 5 — the 400 end. size-adjust 94.883% is Segoe UI's advance width
   for a 151-character sample divided by Plus Jakarta Sans's, at weight 400. */
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 400 450;
    font-display: optional;
    size-adjust: 94.883%;
    ascent-override: 113.719%;
    descent-override: 26.454%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 400 450;
    font-display: optional;
    size-adjust: 94.883%;
    ascent-override: 113.719%;
    descent-override: 26.454%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Band 2 of 5 — --fw-medium (500). Segoe UI renders 500 as Semibold. */
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 451 550;
    font-display: optional;
    size-adjust: 96.471%;
    ascent-override: 111.847%;
    descent-override: 26.018%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 451 550;
    font-display: optional;
    size-adjust: 96.471%;
    ascent-override: 111.847%;
    descent-override: 26.018%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Band 3 of 5 — --fw-semibold (600), the most used weight in the whole
   stylesheet, and the band admin.css's 650 lands in. */
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 551 650;
    font-display: optional;
    size-adjust: 95.559%;
    ascent-override: 112.914%;
    descent-override: 26.266%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 551 650;
    font-display: optional;
    size-adjust: 95.559%;
    ascent-override: 112.914%;
    descent-override: 26.266%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Band 4 of 5 — --fw-bold (700), and where `font-weight: bold` resolves. */
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 651 750;
    font-display: optional;
    size-adjust: 98.508%;
    ascent-override: 109.534%;
    descent-override: 25.480%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 651 750;
    font-display: optional;
    size-adjust: 98.508%;
    ascent-override: 109.534%;
    descent-override: 25.480%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* Band 5 of 5 — --fw-black (800). The one band where Plus Jakarta Sans is
   NARROWER than the fallback, because Segoe UI answers 800 with Black. */
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 751 800;
    font-display: optional;
    size-adjust: 101.919%;
    ascent-override: 105.869%;
    descent-override: 24.628%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Plus Jakarta Sans";
    font-style: normal;
    font-weight: 751 800;
    font-display: optional;
    size-adjust: 101.919%;
    ascent-override: 105.869%;
    descent-override: 24.628%;
    line-gap-override: 0%;
    src: url("../fonts/plus-jakarta-sans-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                   U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                   U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* The display serif. One style only - italic - because that is the only way
   it is used: the festive flourish above a heading. Two subsets, the same
   split as the sans, so a Latin-only page downloads 38 KB and not 62.
   Self-hosted for the same reasons as the sans above.

   Its fallback is Georgia italic, and the two do NOT get the size-adjust
   treatment the sans gets: .sik-flourish asks for 600, Georgia has no 600, so
   the browser answers with Georgia Bold Italic — which is 18.7% wider than
   Playfair at 600 (measured, not quoted: size-adjust: 117.863% is what it
   would take). Matching that would blow the flourish up by a fifth, and the
   flourish is one or two words on a line of its own, so a width difference
   re-wraps nothing. What IS matched is the vertical metric, which is what
   moved the hero: Georgia italic is 91.7% / 21.9% against Playfair's 108.2% /
   25.1%, so the line box went from 113.6% to 133.3% of the font size when the
   file landed and every block under the flourish dropped. The overrides below
   put Playfair's line box exactly on Georgia's, with no size-adjust, so the
   glyphs keep their designed size and the box around them stops moving.

   font-display: optional for the same reason as the sans, and it matters more
   here: Playfair is deliberately NOT preloaded (38 KB for one decorative line
   is not worth it on every page), so it is discovered late and a `swap` would
   land well after first paint on every cold view. Under `optional` a cold
   view sets the flourish in Georgia italic — same line box, same position —
   and the next view, with the file cached, sets it in Playfair. */
@font-face {
    font-family: "Playfair Display";
    font-style: italic;
    font-weight: 500 700;
    font-display: optional;
    ascent-override: 91.7%;
    descent-override: 21.9%;
    line-gap-override: 0%;
    src: url("../fonts/playfair-display-italic-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                   U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                   U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
    font-family: "Playfair Display";
    font-style: italic;
    font-weight: 500 700;
    font-display: optional;
    ascent-override: 91.7%;
    descent-override: 21.9%;
    line-gap-override: 0%;
    src: url("../fonts/playfair-display-italic-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329,
                   U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   1. Design tokens

   Three layers, and components only ever read the second and third:

     1. INPUTS   Brand and neutral colours. Admin > Settings > Theme prints
                 these inline in includes/header.php, so the values below are
                 only what a store with no theme settings renders.
     2. ROLES    ink, surface, line, inverse, cta, tone. A component asks for
                 "the surface a menu sits on", never for #fff — which is what
                 lets the dark theme re-point a role without touching a single
                 component rule.
     3. SCALES   type, space, radius, shadow, motion, layers.

   Dark theme: `:root[data-theme="dark"]`, stamped before first paint by the
   inline script at the top of includes/header.php. Its specificity (0,2,0)
   beats the inline `:root` block the theme settings print (0,1,0), so the
   dark palette wins without depending on source order.
   -------------------------------------------------------------------------- */
:root {
    color-scheme: light;

    /* --- 1. Inputs: brand (admin-owned) --------------------------------- */
    --sik-primary:       #F4511E;
    --sik-accent:        #FF8A3D;
    --sik-navy:          #0F2143;

    /* --- 1. Inputs: neutrals (admin-owned) ------------------------------ */
    --sik-text:          #111827;
    --sik-muted:         #6B7280;
    --sik-border:        #E5E7EB;
    --sik-surface:       #FFFFFF;
    --sik-soft:          #F8F7F4;

    /* --- Derived brand shades -------------------------------------------
       Declared here and nowhere else. Custom properties resolve at computed
       time, so these follow whatever primary the theme settings print inline.
       -dark carries white button text (≥4.5:1 for the shipped primaries);
       -ink is the type-sized shade for links and prices on a light surface. */
    --sik-primary-dark:  color-mix(in srgb, var(--sik-primary) 84%, #000);
    --sik-primary-ink:   color-mix(in srgb, var(--sik-primary) 76%, #000);
    --sik-primary-soft:  color-mix(in srgb, var(--sik-primary) 8%, var(--sik-surface));
    --sik-navy-light:    color-mix(in srgb, var(--sik-navy) 84%, #fff);
    --sik-navy-soft:     color-mix(in srgb, var(--sik-navy) 6%, var(--sik-surface));

    /* --- 2. Roles: ink --------------------------------------------------- */
    --sik-ink:           var(--sik-text);                                        /* headings, prices, icons */
    --sik-ink-2:         color-mix(in srgb, var(--sik-text) 82%, var(--sik-surface)); /* long-form body copy */
    --sik-placeholder:   color-mix(in srgb, var(--sik-muted) 78%, var(--sik-surface));

    /* --- 2. Roles: surfaces ---------------------------------------------- */
    --sik-bg:            var(--sik-surface);   /* the page itself */
    --sik-surface-2:     color-mix(in srgb, var(--sik-soft) 60%, var(--sik-surface)); /* wells, table heads */
    --sik-elevated:      var(--sik-surface);   /* menus, drawers, modals, toasts */
    --sik-media:         var(--sik-soft);      /* the well behind a product photo */
    --sik-hover:         color-mix(in srgb, var(--sik-text) 5%, transparent);
    --sik-press:         color-mix(in srgb, var(--sik-text) 9%, transparent);
    --sik-scrim:         rgba(9, 11, 16, .48);

    /* --- 2. Roles: lines ------------------------------------------------- */
    --sik-line:          var(--sik-border);
    --sik-line-strong:   color-mix(in srgb, var(--sik-text) 22%, var(--sik-surface));
    --sik-input-line:    color-mix(in srgb, var(--sik-text) 24%, var(--sik-surface));
    --sik-input-bg:      var(--sik-surface);

    /* --- 2. Roles: inverse (dark chrome — footer, dark bands) ------------- */
    --sik-inverse:       var(--sik-navy);
    --sik-on-inverse:    #FFFFFF;
    --sik-on-inverse-2:  rgba(255, 255, 255, .72);
    --sik-inverse-line:  rgba(255, 255, 255, .12);
    --sik-inverse-hover: rgba(255, 255, 255, .08);

    /* --- 2. Roles: call to action ---------------------------------------- */
    --sik-cta:           var(--sik-primary-dark);
    --sik-cta-hover:     color-mix(in srgb, var(--sik-primary) 70%, #000);
    --sik-on-cta:        #FFFFFF;

    /* --- 2. Roles: status ------------------------------------------------
       One hue per meaning. Chips and alerts derive their tint and text from
       the hue with color-mix against the CURRENT surface and ink, so the same
       rule yields a dark-on-pastel chip in light mode and a light-on-deep chip
       in dark mode. Measured ≥4.5:1 for every tone in both themes. */
    --sik-success:       #16A34A;
    --sik-warning:       #D97706;
    --sik-danger:        #DC2626;
    --sik-info:          #2563EB;
    --sik-star:          #F5A524;

    --sik-success-ink:   color-mix(in srgb, var(--sik-success) 62%, var(--sik-ink));
    --sik-danger-ink:    color-mix(in srgb, var(--sik-danger) 70%, var(--sik-ink));
    --sik-warning-ink:   color-mix(in srgb, var(--sik-warning) 60%, var(--sik-ink));
    --sik-info-ink:      color-mix(in srgb, var(--sik-info) 70%, var(--sik-ink));

    --sik-success-soft:  color-mix(in srgb, var(--sik-success) 11%, var(--sik-surface));
    --sik-danger-soft:   color-mix(in srgb, var(--sik-danger) 9%, var(--sik-surface));
    --sik-warning-soft:  color-mix(in srgb, var(--sik-warning) 12%, var(--sik-surface));
    --sik-info-soft:     color-mix(in srgb, var(--sik-info) 9%, var(--sik-surface));

    /* --- 3. Shape ---------------------------------------------------------
       --sik-radius* are printed from Settings > Theme > Corner radius.
       --sik-radius-btn and the --sik-card-* pair are printed from Button
       shape and Card style; the values here are the no-settings default. */
    --sik-radius:        12px;
    --sik-radius-sm:     8px;
    --sik-radius-lg:     18px;
    --sik-radius-xs:     6px;
    --sik-radius-pill:   999px;
    --sik-radius-btn:    var(--sik-radius-sm);
    --sik-card-border:   transparent;
    --sik-card-shadow:   none;
    --sik-container:     1280px;

    /* --sik-header-base is the authored row height and never changes.
       --sik-header-h is the *measured* header height that header.js writes on
       load and resize; anchors and sticky offsets read it. Keeping them
       separate is what stops the measure-then-restyle feedback loop. */
    --sik-header-base:   64px;
    --sik-header-h:      65px;
    /* Height of the fixed phone bottom bar, plus the iOS home-indicator inset.
       Everything that floats above it offsets from this one pair of values. */
    --sik-bottomnav-h:   60px;
    --sik-comparebar-h:  92px;
    --sik-bottomnav-space: calc(var(--sik-bottomnav-h) + env(safe-area-inset-bottom, 0px));
    /* Focus ring, derived so a re-themed primary keeps its halo. */
    --sik-focus-ring:    0 0 0 3px color-mix(in srgb, var(--sik-primary) 28%, transparent);
    --sik-font:          "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    /* The festive flourish. Italic serif, used for one or two words above a
       heading - never for a paragraph, a button or anything a shopper reads
       at length. Georgia is the fallback, so the shape survives a font that
       has not arrived yet. */
    --sik-font-display:  "Playfair Display", Georgia, "Times New Roman", serif;

    /* --- 3. Elevation -----------------------------------------------------
       Restrained on purpose: a hairline plus a short, soft falloff. Depth is
       reserved for things that genuinely float (menus, drawers, modals). */
    --sik-shadow-xs:     0 1px 2px rgba(16, 24, 40, .05);
    --sik-shadow-soft:   0 1px 3px rgba(16, 24, 40, .06), 0 1px 2px rgba(16, 24, 40, .04);
    --sik-shadow-card:   0 6px 16px -4px rgba(16, 24, 40, .08), 0 2px 4px -2px rgba(16, 24, 40, .04);
    --sik-shadow-lift:   0 16px 32px -12px rgba(16, 24, 40, .14), 0 4px 8px -4px rgba(16, 24, 40, .05);
    --sik-shadow-pop:    0 24px 48px -16px rgba(16, 24, 40, .22), 0 0 0 1px rgba(16, 24, 40, .05);

    /* =====================================================================
       SCALES
       One scale for the whole storefront. Anything that needs a size, a gap
       or a duration reads it from here rather than inventing a value.
       ===================================================================== */

    /* --- Type scale (1.200 minor third, 15px body) --------------------- */
    --fs-2xs:   11px;
    --fs-xs:    12px;
    --fs-sm:    13px;
    --fs-base:  14px;
    --fs-md:    15px;
    --fs-lg:    17px;
    --fs-xl:    20px;
    --fs-2xl:   24px;
    --fs-3xl:   30px;
    --fs-4xl:   36px;
    --fs-5xl:   clamp(30px, 4.4vw, 44px);
    --fs-hero:  clamp(34px, 5.4vw, 60px);

    /* Weights: only these five, so nothing drifts to a random 550. */
    --fw-normal:    400;
    --fw-medium:    500;
    --fw-semibold:  600;
    --fw-bold:      700;
    --fw-black:     800;

    /* Line heights, named by intent rather than number. */
    --lh-tight:   1.1;    /* display headings */
    --lh-snug:    1.3;    /* card + section headings */
    --lh-normal:  1.5;    /* UI text */
    --lh-relaxed: 1.7;    /* body copy, policy pages */

    /* Letter spacing. Tight for display, wide only for small uppercase labels. */
    --ls-tighter: -0.03em;
    --ls-tight:   -0.018em;
    --ls-normal:  0;
    --ls-wide:    0.02em;
    --ls-wider:   0.06em;
    --ls-widest:  0.1em;

    /* --- Spacing scale (4px base) --------------------------------------- */
    --sp-1:  4px;
    --sp-2:  8px;
    --sp-3:  12px;
    --sp-4:  16px;
    --sp-5:  20px;
    --sp-6:  24px;
    --sp-7:  32px;
    --sp-8:  40px;
    --sp-9:  48px;
    --sp-10: 56px;
    --sp-11: 64px;
    --sp-12: 80px;

    /* Section rhythm — the vertical gap between storefront blocks. Fluid so
       a phone does not inherit desktop-sized whitespace. Generous: space is
       the cheapest premium signal there is. */
    /* Tightened from 44/84 after seeing the full page: with twelve bands on
       the homepage the old rhythm put up to 168px between two sections, and
       a festive storefront reads as abandoned when it is mostly gaps. */
    --section-y:    clamp(34px, 4.2vw, 62px);
    --section-y-sm: clamp(22px, 3vw, 38px);
    --section-y-lg: clamp(46px, 6vw, 88px);
    --gutter:       clamp(16px, 3vw, 28px);

    /* --- Icon sizes ------------------------------------------------------ */
    --icon-xs: 14px;
    --icon-sm: 16px;
    --icon-md: 18px;
    --icon-lg: 20px;
    --icon-xl: 24px;
    --icon-2xl: 28px;
    --icon-stroke: 1.6;

    /* Minimum comfortable tap target (WCAG 2.5.5 / platform guidance). */
    --tap: 44px;

    /* --- Motion ---------------------------------------------------------- */
    --dur-fast:   .14s;
    --dur-base:   .2s;
    --dur-slow:   .32s;
    --dur-slower: .5s;
    --ease-out:   cubic-bezier(.22, 1, .36, 1);
    --ease-in-out: cubic-bezier(.4, 0, .2, 1);
    --ease-spring: cubic-bezier(.34, 1.4, .64, 1);

    /* --- Layering -------------------------------------------------------- */
    --z-sticky:  50;
    --z-header:  60;
    --z-overlay: 90;
    --z-drawer:  95;
    --z-modal:   120;
    --z-toast:   200;
}

/* --------------------------------------------------------------------------
   1a. Dark theme

   Designed, not inverted. The page is a near-black with a trace of blue so it
   never reads as a dead #000; surfaces step UP in lightness as they come
   forward (page → section → card → menu), which is the dark-mode equivalent of
   a shadow. Brand colours keep their hue but their text shades move toward
   white, because a shade tuned to be dark enough on white is illegible here.
   -------------------------------------------------------------------------- */
:root[data-theme="dark"] {
    color-scheme: dark;

    --sik-text:          #ECEDEF;
    --sik-muted:         #A1A6AE;
    --sik-border:        #26282E;
    --sik-surface:       #0E0F12;
    --sik-soft:          #15161A;
    --sik-navy:          #1A1C21;

    --sik-surface-2:     #1A1B20;
    --sik-elevated:      #17181C;
    --sik-media:         #1B1C21;
    --sik-hover:         rgba(255, 255, 255, .06);
    --sik-press:         rgba(255, 255, 255, .1);
    --sik-scrim:         rgba(0, 0, 0, .64);
    --sik-line-strong:   #3A3D45;
    --sik-input-line:    #34373F;
    --sik-input-bg:      #15161A;

    --sik-inverse:       #15161A;
    --sik-on-inverse:    #ECEDEF;
    --sik-on-inverse-2:  rgba(236, 237, 239, .68);
    --sik-inverse-line:  rgba(255, 255, 255, .08);
    --sik-inverse-hover: rgba(255, 255, 255, .06);

    --sik-primary-ink:   color-mix(in srgb, var(--sik-primary) 58%, #fff);
    --sik-primary-soft:  color-mix(in srgb, var(--sik-primary) 16%, var(--sik-surface));
    --sik-navy-light:    #22242A;
    --sik-navy-soft:     #1A1B20;

    --sik-success-ink:   color-mix(in srgb, var(--sik-success) 60%, #fff);
    --sik-danger-ink:    color-mix(in srgb, var(--sik-danger) 55%, #fff);
    --sik-warning-ink:   color-mix(in srgb, var(--sik-warning) 55%, #fff);
    --sik-info-ink:      color-mix(in srgb, var(--sik-info) 55%, #fff);
    --sik-success-soft:  color-mix(in srgb, var(--sik-success) 16%, var(--sik-surface));
    --sik-danger-soft:   color-mix(in srgb, var(--sik-danger) 16%, var(--sik-surface));
    --sik-warning-soft:  color-mix(in srgb, var(--sik-warning) 16%, var(--sik-surface));
    --sik-info-soft:     color-mix(in srgb, var(--sik-info) 16%, var(--sik-surface));

    --sik-shadow-xs:     0 1px 2px rgba(0, 0, 0, .4);
    --sik-shadow-soft:   0 1px 3px rgba(0, 0, 0, .45), 0 0 0 1px rgba(255, 255, 255, .03);
    --sik-shadow-card:   0 8px 20px -6px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .04);
    --sik-shadow-lift:   0 18px 36px -12px rgba(0, 0, 0, .65), 0 0 0 1px rgba(255, 255, 255, .05);
    --sik-shadow-pop:    0 28px 56px -16px rgba(0, 0, 0, .75), 0 0 0 1px rgba(255, 255, 255, .06);
}

/* A theme switch should be one clean cut, not three hundred colour
   transitions running at once. app.js holds this class for a frame. */
.sik-theme-switching,
.sik-theme-switching *,
.sik-theme-switching *::before,
.sik-theme-switching *::after { transition: none !important; }

/* Paper is white. Printing an order or invoice from dark mode must not burn
   a page of toner, so print always resolves to the light palette. */
@media print {
    :root[data-theme="dark"] {
        color-scheme: light;
        --sik-text: #111827; --sik-muted: #4B5563; --sik-border: #D1D5DB;
        --sik-surface: #FFFFFF; --sik-soft: #F8F7F4; --sik-navy: #0F2143;
        --sik-surface-2: #FAFAF9; --sik-elevated: #FFFFFF; --sik-media: #F8F7F4;
        --sik-primary-ink: color-mix(in srgb, var(--sik-primary) 76%, #000);
        --sik-primary-soft: color-mix(in srgb, var(--sik-primary) 8%, #fff);
    }
}

/* Every animated transition in the storefront is opt-in through these
   tokens, so `prefers-reduced-motion` has exactly one place to switch off. */
@media (prefers-reduced-motion: reduce) {
    :root {
        --dur-fast: .01ms;
        --dur-base: .01ms;
        --dur-slow: .01ms;
        --dur-slower: .01ms;
    }
}

/* The bottom bar only exists on phones, so above it the offset collapses to
   zero and every calc() that reads it resolves to plain 0. */
@media (min-width: 1024px) {
    :root { --sik-bottomnav-h: 0px; }
}

/* --------------------------------------------------------------------------
   1b. Utility-layer guard

   tailwind.css is loaded *before* this file, so any component rule here that
   sets `display` silently defeats `.hidden` at equal specificity — that is how
   the "desktop only" header icons ended up rendering on phones. Restoring the
   utility at higher specificity keeps markup honest.
   -------------------------------------------------------------------------- */
[hidden] { display: none; }
.sik-action.hidden,
.sik-btn.hidden,
.sik-atc.hidden,
.sik-iconbtn.hidden { display: none; }
/* Same trap for the `hidden` attribute: [hidden] is declared here, near the top,
   so any later component rule that sets `display` beats it at equal specificity.
   Components that can legitimately be hidden re-state it. */
.sik-atc[hidden] { display: none; }
/* .sik-btn sets display:inline-flex further down, so `hidden` did nothing to a
   button. Three places set it and all three meant it: the notification
   centre's "Load older notifications" (rendered hidden so the no-JS pager
   shows instead, then hidden again once the feed runs out) and both
   "Mark all read" buttons with nothing left unread - each stayed on screen,
   and in the tab order, saying the opposite of what the page meant. */
.sik-btn[hidden] { display: none; }

/* --------------------------------------------------------------------------
   1c. Brand expression — gradient, glass, depth, motion

   The palette above says WHICH colours the store uses. This block says how the
   brand is *expressed*: the pink-to-crimson sweep taken from the logo, the
   glass used on floating chrome, the coloured depth under a call to action,
   and the handful of keyframes everything animates with.

   Every value derives from --sik-primary / --sik-accent, so a store that
   re-themes in Admin > Settings > Theme gets its own gradient rather than this
   one. Nothing here is a raw hex except the neutral white/black sheens, which
   are light itself and not brand colour.

   Contrast: --sik-grad-cta starts at the primary darkened 8% and ends at the
   accent, so every stop along it clears 4.5:1 against white for the shipped
   brand (5.0:1 at the light end, 9.4:1 at the dark end). Gradient *text*
   (.sik-gradient-text) is deliberately limited to display-sized headings,
   where 3:1 applies.
   -------------------------------------------------------------------------- */
:root {
    /* --- The third colour ------------------------------------------------
       Pink and wine carry the brand; gold is what makes it festive. It is
       never a background for dark text and never a button - it is the warm
       highlight on a dark panel: a flourish, a price, a rule, a star.
       --sik-gold-ink is the shade that clears 4.5:1 on a light surface, for
       the rare case gold has to be read on white. */
    --sik-gold:        #F59E0B;
    --sik-gold-2:      #FCD34D;
    --sik-gold-soft:   #FEF3C7;
    --sik-gold-ink:    #92610A;

    /* --- Brand gradients -------------------------------------------------- */
    --sik-grad-from:   var(--sik-primary);
    --sik-grad-to:     var(--sik-accent);

    /* The identity sweep: hero washes, headings, badges, decorative rules. */
    --sik-grad-brand:  linear-gradient(135deg, var(--sik-grad-from) 0%, var(--sik-grad-to) 100%);
    /* A wider, three-stop version for large areas, so the middle does not go flat. */
    --sik-grad-brand-wide: linear-gradient(120deg,
                            color-mix(in srgb, var(--sik-grad-from) 88%, #fff) 0%,
                            var(--sik-grad-from) 38%,
                            var(--sik-grad-to) 100%);
    /* Buttons and other white-on-brand surfaces. Contrast-bounded, see above. */
    --sik-grad-cta:    linear-gradient(135deg,
                            color-mix(in srgb, var(--sik-primary) 92%, #000) 0%,
                            var(--sik-accent) 100%);
    /* The barely-there tint for section backgrounds and card washes. */
    --sik-grad-soft:   linear-gradient(135deg,
                            color-mix(in srgb, var(--sik-primary) 9%, var(--sik-surface)) 0%,
                            color-mix(in srgb, var(--sik-accent) 7%, var(--sik-surface)) 100%);
    /* Dark brand band: near-black warmed toward the brand, for inverse sections. */
    --sik-grad-dark:   linear-gradient(135deg,
                            var(--sik-inverse) 0%,
                            color-mix(in srgb, var(--sik-accent) 34%, var(--sik-inverse)) 100%);
    /* The wine panel: hero, promo cards, the announcement strip. Deep at the
       edges, lifted through the middle so a wide panel does not go flat. */
    --sik-grad-wine:   linear-gradient(100deg,
                            var(--sik-inverse) 0%,
                            color-mix(in srgb, var(--sik-accent) 62%, var(--sik-inverse)) 48%,
                            color-mix(in srgb, var(--sik-inverse) 88%, #000) 100%);
    /* The urgency sweep: flash sale headers and countdown bars. */
    --sik-grad-flash:  linear-gradient(90deg,
                            color-mix(in srgb, var(--sik-primary) 78%, var(--sik-accent)) 0%,
                            var(--sik-primary) 52%,
                            color-mix(in srgb, var(--sik-primary) 84%, #fff) 100%);
    /* Gold, as a sweep for rules and small flourishes on dark panels. */
    --sik-grad-gold:   linear-gradient(120deg, var(--sik-gold) 0%, var(--sik-gold-2) 100%);

    /* Light passing over a surface: the top-edge lip on a 3D button, the
       sweep that crosses a card on hover, the shimmer on a skeleton. */
    --sik-sheen:       linear-gradient(100deg, transparent 20%, rgba(255, 255, 255, .28) 50%, transparent 80%);
    --sik-lip:         inset 0 1px 0 rgba(255, 255, 255, .26), inset 0 -1px 0 rgba(0, 0, 0, .14);

    /* --- Glass ------------------------------------------------------------
       Used only where something genuinely floats over content: the stuck
       header, the sticky buy bar, an overlay control on a photo. A glass
       panel over a plain background is just a tinted box with a GPU cost. */
    --sik-glass-bg:    color-mix(in srgb, var(--sik-surface) 76%, transparent);
    --sik-glass-2:     color-mix(in srgb, var(--sik-surface) 62%, transparent);
    --sik-glass-line:  color-mix(in srgb, var(--sik-ink) 9%, transparent);
    --sik-glass-blur:  saturate(1.7) blur(14px);
    --sik-glass-shadow: 0 8px 28px -10px rgba(16, 24, 40, .18);

    /* --- Coloured depth ----------------------------------------------------
       A brand-tinted shadow reads as the object glowing with its own colour
       rather than casting a grey one. Reserved for brand-coloured surfaces:
       a neutral card with a pink shadow looks like a mistake. */
    --sik-shadow-brand:    0 8px 20px -8px color-mix(in srgb, var(--sik-primary) 52%, transparent),
                           0 2px 6px -2px color-mix(in srgb, var(--sik-primary) 30%, transparent);
    --sik-shadow-brand-lg: 0 18px 34px -12px color-mix(in srgb, var(--sik-primary) 56%, transparent),
                           0 4px 10px -4px color-mix(in srgb, var(--sik-primary) 28%, transparent);
    --sik-shadow-brand-sm: 0 4px 12px -4px color-mix(in srgb, var(--sik-primary) 42%, transparent);
    --sik-ring-brand:      0 0 0 1px color-mix(in srgb, var(--sik-primary) 30%, transparent);

    /* --- Ambient light ------------------------------------------------------
       Two soft brand-coloured pools painted behind the page. They give the
       long scroll of a storefront some depth without putting a gradient on
       every single section. */
    --sik-aura: radial-gradient(60% 50% at 8% -6%, color-mix(in srgb, var(--sik-primary) 11%, transparent) 0%, transparent 62%),
                radial-gradient(52% 46% at 104% 2%, color-mix(in srgb, var(--sik-accent) 10%, transparent) 0%, transparent 60%);

    /* --- Motion ------------------------------------------------------------
       Distances and durations for the shared keyframes below, so "how far a
       thing rises" is one decision rather than forty. */
    --rise:        14px;
    --lift:        -3px;
    --lift-sm:     -2px;
    --dur-reveal:  .55s;
}

:root[data-theme="dark"] {
    /* Glass over a dark page tints down, not up, or it turns into fog. */
    --sik-glass-bg:    color-mix(in srgb, var(--sik-elevated) 78%, transparent);
    --sik-glass-2:     color-mix(in srgb, var(--sik-elevated) 64%, transparent);
    --sik-glass-line:  rgba(255, 255, 255, .1);
    --sik-glass-shadow: 0 10px 30px -12px rgba(0, 0, 0, .7);

    /* On a near-black page a coloured shadow reads as a glow, so it is
       carried further and the grey component is dropped entirely. */
    --sik-shadow-brand:    0 10px 26px -10px color-mix(in srgb, var(--sik-primary) 62%, transparent),
                           0 0 0 1px color-mix(in srgb, var(--sik-primary) 22%, transparent);
    --sik-shadow-brand-lg: 0 20px 40px -14px color-mix(in srgb, var(--sik-primary) 70%, transparent),
                           0 0 0 1px color-mix(in srgb, var(--sik-primary) 26%, transparent);
    --sik-shadow-brand-sm: 0 6px 16px -6px color-mix(in srgb, var(--sik-primary) 52%, transparent);

    --sik-grad-soft:   linear-gradient(135deg,
                            color-mix(in srgb, var(--sik-primary) 14%, var(--sik-surface)) 0%,
                            color-mix(in srgb, var(--sik-accent) 12%, var(--sik-surface)) 100%);
    --sik-aura: radial-gradient(60% 50% at 8% -6%, color-mix(in srgb, var(--sik-primary) 14%, transparent) 0%, transparent 62%),
                radial-gradient(52% 46% at 104% 2%, color-mix(in srgb, var(--sik-accent) 13%, transparent) 0%, transparent 60%);
    --sik-lip:         inset 0 1px 0 rgba(255, 255, 255, .16), inset 0 -1px 0 rgba(0, 0, 0, .3);
}

/* --------------------------------------------------------------------------
   1d. Shared keyframes + reveal

   Six animations for the whole storefront. Each one moves opacity and
   transform only — the two properties a browser can animate on the compositor
   — so none of them cause layout or paint work while running.
   -------------------------------------------------------------------------- */
@keyframes sik-rise      { from { opacity: 0; transform: translate3d(0, var(--rise), 0); } to { opacity: 1; transform: none; } }
@keyframes sik-fade      { from { opacity: 0; } to { opacity: 1; } }
@keyframes sik-pop       { from { opacity: 0; transform: scale(.94); } to { opacity: 1; transform: none; } }
@keyframes sik-shimmer   { to { background-position-x: -200%; } }
@keyframes sik-float     { 50% { transform: translate3d(0, -9px, 0); } }
@keyframes sik-grad-pan  { to { background-position: 200% 50%; } }

/* Scroll reveal. Elements start hidden ONLY when the script that reveals them
   is running (html.js), so with JavaScript off or broken the page is fully
   readable — the single most common way a reveal effect takes a site down. */
.js .sik-reveal {
    opacity: 0;
    transform: translate3d(0, var(--rise), 0);
    transition: opacity var(--dur-reveal) var(--ease-out), transform var(--dur-reveal) var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
}
.js .sik-reveal.is-in { opacity: 1; transform: none; will-change: auto; }

/* Stagger: children of a revealed row come in one after another. Capped at
   six, after which everything shares the last delay — a twelve-card grid
   should not take a second and a half to finish arriving. */
.js .sik-reveal--stagger > *              { transition-delay: 0ms; }
.js .sik-reveal--stagger > :nth-child(2)  { transition-delay: 60ms; }
.js .sik-reveal--stagger > :nth-child(3)  { transition-delay: 120ms; }
.js .sik-reveal--stagger > :nth-child(4)  { transition-delay: 180ms; }
.js .sik-reveal--stagger > :nth-child(5)  { transition-delay: 240ms; }
.js .sik-reveal--stagger > :nth-child(n+6){ transition-delay: 300ms; }

/* --------------------------------------------------------------------------
   1e. Brand expression utilities

   Small, named, reusable. A component reaches for one of these instead of
   re-declaring a gradient, so changing the brand sweep changes it everywhere.
   -------------------------------------------------------------------------- */

/* Gradient type. Display sizes only — at body size a gradient fill loses the
   contrast the flat ink colour guarantees. */
.sik-gradient-text {
    background: var(--sik-grad-brand);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* Some engines drop the clip mid-paint; the brand ink underneath keeps
       the text readable rather than invisible if that happens. */
    -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .sik-gradient-text { color: var(--sik-primary-ink); -webkit-text-fill-color: currentColor; }
}

/* The festive flourish: an italic serif line above a heading. Gold on a dark
   panel, wine on a light one - the gold is not readable on white at this
   size, so it is not offered there. */
.sik-flourish {
    display: block;
    font-family: var(--sik-font-display);
    font-style: italic;
    font-weight: 600;
    line-height: 1.15;
    letter-spacing: -0.01em;
    color: var(--sik-accent);
}
.sik-section--navy .sik-flourish,
.sik-onwine .sik-flourish,
.sik-flash .sik-flourish { color: var(--sik-gold-2); text-shadow: 0 0 18px color-mix(in srgb, var(--sik-gold) 55%, transparent); }
[data-theme="dark"] .sik-flourish { color: var(--sik-gold-2); }

/* A gradient hairline: section dividers, card tops, the footer's edge. */
.sik-grad-rule {
    height: 2px;
    border: 0;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-brand);
    opacity: .9;
}

/* A brand-gradient chip: "Flash sale", "Limited stock", a discount tag. */
.sik-gchip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    padding: 3px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-cta);
    color: #fff;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    line-height: 1.6;
    box-shadow: var(--sik-shadow-brand-sm);
    white-space: nowrap;
}
.sik-gchip svg { width: var(--icon-xs); height: var(--icon-xs); }

/* The glass treatment, as one class. Falls back to an opaque surface where
   backdrop-filter is unsupported, so nothing ever renders as unreadable
   text over a photograph. */
.sik-glass {
    background: var(--sik-surface);
    border: 1px solid var(--sik-glass-line);
    box-shadow: var(--sik-glass-shadow);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-glass {
        background: var(--sik-glass-bg);
        -webkit-backdrop-filter: var(--sik-glass-blur);
        backdrop-filter: var(--sik-glass-blur);
    }
}

/* Hover lift, for anything that should feel pickable. */
.sik-lift { transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out); }
@media (hover: hover) {
    .sik-lift:hover { transform: translate3d(0, var(--lift), 0); box-shadow: var(--sik-shadow-lift); }
}
.sik-lift:active { transform: translate3d(0, 0, 0); }

/* Shimmer, for skeletons and "loading" placeholders. */
.sik-shimmer {
    background-image: linear-gradient(90deg,
        var(--sik-media) 0%, color-mix(in srgb, var(--sik-ink) 7%, var(--sik-media)) 40%, var(--sik-media) 80%);
    background-size: 200% 100%;
    animation: sik-shimmer 1.4s linear infinite;
}

/* A slow drift across a brand gradient. Used on one or two hero-scale
   surfaces only; it repaints forever, so it is not free. */
.sik-grad-live {
    background: linear-gradient(100deg, var(--sik-grad-from), var(--sik-grad-to), var(--sik-grad-from));
    background-size: 200% 100%;
    animation: sik-grad-pan 9s linear infinite;
}

.sik-float { animation: sik-float 6s var(--ease-in-out) infinite; }

/* Reduced motion, and the admin's global "no animation" switch, stop all of
   it — including the infinite ones, which are the ones that actually hurt. */
@media (prefers-reduced-motion: reduce) {
    .sik-shimmer, .sik-grad-live, .sik-float { animation: none; }
    .js .sik-reveal { opacity: 1; transform: none; transition: none; }
}
.sik-noanim .sik-shimmer,
.sik-noanim .sik-grad-live,
.sik-noanim .sik-float { animation: none; }
.sik-noanim .sik-reveal { opacity: 1; transform: none; transition: none; }

/* --------------------------------------------------------------------------
   2. Base
   -------------------------------------------------------------------------- */
html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--sik-header-h) + var(--sp-4));
    background: var(--sik-bg);
}

body {
    font-family: var(--sik-font);
    color: var(--sik-text);
    background: var(--sik-bg);
    font-size: var(--fs-md);
    line-height: var(--lh-normal);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;

    /* Nothing may push the page sideways on a phone. `clip` is preferred over
       `hidden` because it does not turn the body into a scroll container,
       which would break `position: sticky` on the header. */
    overflow-x: hidden;
    overflow-x: clip;
}

body.sik-no-scroll { overflow: hidden; }

/* The page's ambient light: two soft brand-coloured pools painted behind
   everything. Fixed, so a long storefront scroll keeps its depth instead of
   running out of gradient; z-index -1 puts it under all content but over the
   canvas. It is a static paint - no animation, no scroll listener. */
body::before {
    content: "";
    position: fixed;
    inset: 0;
    z-index: -1;
    background: var(--sik-aura);
    pointer-events: none;
}
/* A page that sets its own full-bleed background (checkout, invoice print)
   opts out rather than fighting it. */
body.sik-flat::before { display: none; }

/* --------------------------------------------------------------------------
   Heading hierarchy. Sizes come from the type scale, so an <h2> has the same
   weight and rhythm in the footer, on a policy page and inside a product tab.
   -------------------------------------------------------------------------- */
h1, h2, h3, h4, h5, h6 {
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
    margin: 0;
    text-wrap: balance;
}

h1 { font-size: var(--fs-3xl); line-height: var(--lh-tight); letter-spacing: var(--ls-tighter); }
h2 { font-size: var(--fs-2xl); line-height: var(--lh-tight); }
h3 { font-size: var(--fs-xl); }
h4 { font-size: var(--fs-lg); }
h5 { font-size: var(--fs-md); }
h6 { font-size: var(--fs-base); font-weight: var(--fw-semibold); }

@media (min-width: 768px) {
    h1 { font-size: var(--fs-4xl); }
    h2 { font-size: var(--fs-3xl); }
}

/* --------------------------------------------------------------------------
   Shared type roles. Use these instead of styling text ad hoc.
   -------------------------------------------------------------------------- */

/* The small label above a heading. Uppercase is reserved for this role and
   the tiny meta labels — never for headings, buttons or navigation. */
.sik-eyebrow {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
    line-height: var(--lh-normal);
}

.sik-lead    { font-size: var(--fs-lg); line-height: var(--lh-relaxed); color: var(--sik-muted); }
.sik-body    { font-size: var(--fs-base); line-height: var(--lh-relaxed); }
.sik-caption { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-meta    { font-size: var(--fs-2xs); line-height: var(--lh-normal); color: var(--sik-muted); }

/* Prices are tabular so columns of them line up digit for digit. */
.sik-num { font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1; }

/* --------------------------------------------------------------------------
   Display heading roles — three sizes, chosen by the role a heading plays.
   Each component keeps its own colour and spacing; only the size lives here.
   -------------------------------------------------------------------------- */

/* Role 1 — section heading: "Shop by category", newsletter, product name. */
.sik-heading__title,
.sik-flash__title,
.sik-news__title,
.sik-pdp__title { font-size: var(--fs-2xl); }

/* Role 2 — feature panel heading: the deal and promo panels. */
.sik-deal__title,
.sik-promo__title { font-size: var(--fs-2xl); }

/* Role 3 — the hero. The one display size, already fluid at token level. */
.sik-hero__title { font-size: var(--fs-hero); }

@media (min-width: 768px) {
    .sik-heading__title,
    .sik-flash__title,
    .sik-news__title { font-size: var(--fs-3xl); }
    .sik-pdp__title  { font-size: var(--fs-3xl); }
    .sik-deal__title,
    .sik-promo__title { font-size: var(--fs-4xl); }
}

a { color: inherit; text-decoration: none; }
/* The global link hover uses the ink shade of the brand: the raw primary is
   a fill colour and fails AA at the 13-14px most links are set in. */
a:hover { color: var(--sik-primary-ink); }

img { max-width: 100%; height: auto; }

::selection { background: color-mix(in srgb, var(--sik-primary) 26%, transparent); color: var(--sik-ink); }

:focus-visible {
    outline: 2px solid var(--sik-primary);
    outline-offset: 2px;
    border-radius: 4px;
}

.sik-container {
    width: 100%;
    max-width: var(--sik-container);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

/* Section rhythm. Every storefront block breathes from the same three tokens,
   so a phone never inherits desktop whitespace. */
.sik-section { padding-block: var(--section-y); }
.sik-section--sm { padding-block: var(--section-y-sm); }
.sik-section--lg { padding-block: var(--section-y-lg); }
.sik-section--none { padding-block: 0; }
.sik-section--soft { background: var(--sik-soft); }
.sik-section--navy { background: var(--sik-inverse); color: var(--sik-on-inverse); }
.sik-section--navy h2, .sik-section--navy h3 { color: var(--sik-on-inverse); }

/* Homepage builder colours (widget_section_attrs() prints them as --w-bg and
   --w-fg). Light mode only: a colour picked against a white page cannot be
   trusted on the dark palette, so dark mode keeps the theme's own surfaces. */
:root:not([data-theme="dark"]) .sik-section[style*="--w-bg"] { background: var(--w-bg); }
:root:not([data-theme="dark"]) .sik-section[style*="--w-fg"],
:root:not([data-theme="dark"]) .sik-section[style*="--w-fg"] :is(h2, h3, .sik-heading__title, .sik-heading__sub) { color: var(--w-fg); }
/* Same reasoning for a promo banner's own background colour. */
[data-theme="dark"] .sik-promo[style*="background"] { background: var(--sik-inverse) !important; }

/* The "ink" shades are tuned for the page surface. On a dark band the
   relationship inverts, so dark bands lighten them toward white instead. */
.sik-section--navy,
.sik-flash,
body:not(.sik-footer-light) .sik-footer {
    --sik-primary-ink: color-mix(in srgb, var(--sik-primary) 58%, #fff);
    --sik-success-ink: color-mix(in srgb, var(--sik-success) 58%, #fff);
}

/* --------------------------------------------------------------------------
   2b. Icon system

   includes/icons.php is the only place an SVG is authored, and every glyph it
   returns carries `.sik-ico`. Size and weight are therefore decided here, once,
   from the icon scale — a template can no longer invent an off-scale glyph.

   The w-N and h-N utilities in tailwind.css already resolved to the same pixel
   values (w-4=16, w-5=20, w-6=24, w-7=28), so re-pointing them at the tokens
   is a zero-visual-change refactor that makes --icon-* the single authority:
   retuning a token now moves every icon instead of none of them. Width and
   height are both set from the one class because tailwind.css never defined
   `.h-3\.5`, which left the 30 `w-3.5 h-3.5` icons sizing off `height:auto`.

   Declared here, early in the base layer, so a component that deliberately
   sizes its own glyph later in the file (.sik-empty > svg, .sik-suggest svg,
   .sik-atc__glyph svg) still wins at equal specificity.
   -------------------------------------------------------------------------- */
.sik-ico {
    /* One stroke weight for the whole set. The 1.7 baked into the SVG markup
       is only the no-CSS fallback; this is what actually renders, so retuning
       --icon-stroke retunes every glyph. */
    stroke-width: var(--icon-stroke);
    stroke-linecap: round;
    stroke-linejoin: round;
    /* An icon is never the thing that gives way. Two separate mechanisms were
       squashing them at 320 — flex-shrink on the flex items, and the reset's
       `svg { max-width: 100% }`, which clamps the width while the height stays
       authored. Measured 6.2x16 inside .sik-btn and 12.2x14 in .sik-star: a
       glyph out of square reads as a rendering fault, not a tight layout. The
       label wraps instead. */
    flex: none;
    max-width: none;
}
svg.w-3\.5 { width: var(--icon-xs);  height: var(--icon-xs); }
svg.w-4    { width: var(--icon-sm);  height: var(--icon-sm); }
svg.w-5    { width: var(--icon-lg);  height: var(--icon-lg); }
svg.w-6    { width: var(--icon-xl);  height: var(--icon-xl); }
svg.w-7    { width: var(--icon-2xl); height: var(--icon-2xl); }

/* Tap-area extender. An icon-only control may look like a 34-40px chip, but a
   finger needs --tap. One rule owns that everywhere instead of each component
   re-inventing the same centred pseudo-element (.sik-iconbtn and .sik-hero__dot
   each had their own copy with a hard-coded 44px). The pseudo is out of flow,
   so it never changes the visible size or the layout around it. */
.sik-iconbtn::after,
.sik-hero__dot::after,
.sik-hero__arrow::after,
.sik-rail__nav::after,
.sik-rail__dot::after,
.sik-search__submit::after,
.sik-search__voice::after,
/* 26x26 visible chip. The field is 56.5px tall and the flex gap either side is
   --sp-2, so a centred 44px circle grows into the gap and overhangs the input
   and the voice button by ~1px each — it does not reach the input's text area,
   and .sik-search__voice is a later positioned sibling so it still wins its own
   clicks. Growing the button itself instead would cost 18px of a search input
   measured at only 102px wide at 320. */
.sik-search__clear::after,
.sik-toast__close::after,
.sik-chip button::after,
a.sik-social__link::after {
    content: "";
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: var(--tap);
    height: var(--tap);
    min-width: var(--tap);
    min-height: var(--tap);
    border-radius: 50%;
}

/* --------------------------------------------------------------------------
   3. Section heading

   An optional gradient eyebrow, a display title whose accent word carries the
   brand sweep, one line of subtitle, and the section's single link on the
   right. The hierarchy does the shouting, not colour on every word.

   `title` and `title_accent` remain two fields in the homepage builder; the
   accent span is what picks up the gradient.
   -------------------------------------------------------------------------- */
.sik-heading {
    display: grid;
    gap: var(--sp-2);
    margin-bottom: var(--sp-6);
}
@media (min-width: 768px) { .sik-heading { margin-bottom: var(--sp-7); } }

/* Small caps label above the title: "New in", "Ends tonight". */
.sik-heading__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-primary-ink);
}
.sik-heading__eyebrow::before {
    content: "";
    width: 22px; height: 2px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-brand);
}
.sik-section--navy .sik-heading__eyebrow,
.sik-flash .sik-heading__eyebrow { color: var(--sik-on-inverse); }

.sik-heading__title {
    /* Size comes from the display heading roles block in section 2. */
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-tighter);
    line-height: var(--lh-tight);
    color: var(--sik-ink);
    max-width: 100%;
    text-wrap: balance;
}

/* The accent word carries the brand gradient. Display sizes only, which is
   exactly where this span appears. */
.sik-heading__accent {
    background: var(--sik-grad-brand);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .sik-heading__accent { color: var(--sik-primary-ink); -webkit-text-fill-color: currentColor; }
}
/* On a dark band the gradient loses too much against the background, so the
   accent goes to the light end of the brand instead. */
.sik-section--navy .sik-heading__accent,
.sik-flash .sik-heading__accent {
    background: none;
    color: color-mix(in srgb, var(--sik-primary) 46%, #fff);
    -webkit-text-fill-color: currentColor;
}

.sik-heading__sub {
    color: var(--sik-muted);
    font-size: var(--fs-md);
    line-height: var(--lh-normal);
    max-width: 62ch;
    text-wrap: pretty;
}
.sik-section--navy .sik-heading__sub,
.sik-flash .sik-heading__sub { color: var(--sik-on-inverse-2); }

.sik-heading--center { justify-items: center; text-align: center; }
.sik-heading--center .sik-heading__sub { margin-inline: auto; }

/* With a "View all" link on the right. */
.sik-heading--row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sp-3) var(--sp-5);
    flex-wrap: wrap;
}
.sik-heading--row > :first-child { min-width: 0; flex: 1 1 260px; display: grid; gap: var(--sp-2); }

.sik-viewall {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding-inline: var(--sp-4);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--sik-ink);
    white-space: nowrap;
    border-radius: var(--sik-radius-pill);
    border: 1px solid var(--sik-line);
    background: var(--sik-surface);
    transition: color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.sik-viewall svg { transition: transform var(--dur-fast) var(--ease-out); }
.sik-viewall:hover {
    color: var(--sik-primary-ink);
    border-color: color-mix(in srgb, var(--sik-primary) 40%, var(--sik-line));
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-viewall:hover svg { transform: translateX(3px); }
.sik-section--navy .sik-viewall,
.sik-flash .sik-viewall {
    color: var(--sik-on-inverse);
    background: transparent;
    border-color: var(--sik-inverse-line);
}
.sik-section--navy .sik-viewall:hover,
.sik-flash .sik-viewall:hover { color: var(--sik-on-inverse); border-color: var(--sik-on-inverse); box-shadow: none; }

/* --------------------------------------------------------------------------
   4. Buttons

   One shape for every button, from Settings > Theme > Button shape
   (--sik-radius-btn). Sentence case, one weight, no tracking. Tones:

     primary   the brand call to action — gradient, coloured depth, one per view
     navy      the strong neutral: ink on the page, inverted in dark mode
     outline   the quiet secondary
     ghost     a tinted tertiary for toolbars and utility rows
     danger    destructive only

   The primary's depth is three things stacked: a coloured shadow beneath, a
   1px light lip along the top edge and a dark one along the bottom (--sik-lip),
   and a sheen that sweeps across on hover. It presses IN rather than merely
   shrinking — the shadow collapses and the lip inverts, which is what makes a
   tap feel like it landed.
   -------------------------------------------------------------------------- */
.sik-btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    font-family: inherit;
    font-weight: var(--fw-semibold);
    font-size: var(--fs-base);
    line-height: 1;
    letter-spacing: -0.005em;
    padding: 0 var(--sp-5);
    min-height: var(--tap);
    border-radius: var(--sik-radius-btn);
    border: 1px solid transparent;
    cursor: pointer;
    white-space: nowrap;
    text-align: center;
    text-decoration: none;
    overflow: hidden;
    isolation: isolate;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-base) var(--ease-out),
                color var(--dur-base) var(--ease-out),
                border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                filter var(--dur-base) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}
.sik-btn:active { transform: scale(.98); }
.sik-btn:disabled, .sik-btn.is-loading { opacity: .6; cursor: not-allowed; pointer-events: none; }
/* Content sits above the sheen layer. */
.sik-btn > * { position: relative; z-index: 1; }

.sik-btn--primary {
    background-color: var(--sik-cta);
    background-image: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand), var(--sik-lip);
}
/* The sweep. A single skewed band moved with transform only. */
.sik-btn--primary::after {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 140%;
    background: var(--sik-sheen);
    transform: translate3d(-120%, 0, 0) skewX(-18deg);
    transition: transform var(--dur-slow) var(--ease-out);
    pointer-events: none;
    z-index: 0;
}
@media (hover: hover) {
    .sik-btn--primary:hover {
        color: var(--sik-on-cta);
        transform: translate3d(0, var(--lift-sm), 0);
        box-shadow: var(--sik-shadow-brand-lg), var(--sik-lip);
        /* Darkens rather than brightens: contrast with white text goes up. */
        filter: saturate(1.05) brightness(.97);
    }
    .sik-btn--primary:hover::after { transform: translate3d(120%, 0, 0) skewX(-18deg); }
}
.sik-btn--primary:active {
    transform: translate3d(0, 1px, 0) scale(.99);
    box-shadow: var(--sik-shadow-brand-sm), inset 0 2px 5px rgba(0, 0, 0, .22);
}

.sik-btn--navy {
    background: var(--sik-ink);
    color: var(--sik-bg);
    box-shadow: var(--sik-shadow-soft), var(--sik-lip);
}
.sik-btn--navy:hover { background: color-mix(in srgb, var(--sik-ink) 84%, var(--sik-bg)); color: var(--sik-bg); box-shadow: var(--sik-shadow-card), var(--sik-lip); }
@media (hover: hover) { .sik-btn--navy:hover { transform: translate3d(0, var(--lift-sm), 0); } }
.sik-btn--navy:active { transform: translate3d(0, 1px, 0) scale(.99); box-shadow: inset 0 2px 5px rgba(0, 0, 0, .25); }

.sik-btn--outline { background: transparent; color: var(--sik-ink); border-color: var(--sik-line-strong); }
.sik-btn--outline:hover {
    background: var(--sik-hover);
    border-color: color-mix(in srgb, var(--sik-primary) 55%, var(--sik-line-strong));
    color: var(--sik-primary-ink);
}

.sik-btn--outline-white {
    background: transparent;
    color: var(--sik-on-inverse);
    border-color: color-mix(in srgb, var(--sik-on-inverse) 45%, transparent);
}
.sik-btn--outline-white:hover { background: var(--sik-on-inverse); color: var(--sik-inverse); border-color: var(--sik-on-inverse); }

.sik-btn--ghost { background: var(--sik-surface-2); color: var(--sik-ink); }
.sik-btn--ghost:hover { background: var(--sik-press); color: var(--sik-ink); }

/* Tinted brand tertiary: reads as brand without competing with the primary. */
.sik-btn--soft {
    background: color-mix(in srgb, var(--sik-primary) 11%, var(--sik-surface));
    color: var(--sik-primary-ink);
    border-color: color-mix(in srgb, var(--sik-primary) 20%, transparent);
}
.sik-btn--soft:hover {
    background: color-mix(in srgb, var(--sik-primary) 17%, var(--sik-surface));
    color: var(--sik-primary-ink);
    border-color: color-mix(in srgb, var(--sik-primary) 34%, transparent);
}

.sik-btn--danger { background: var(--sik-danger); color: #fff; box-shadow: var(--sik-lip); }
.sik-btn--danger:hover { background: color-mix(in srgb, var(--sik-danger) 85%, #000); color: #fff; }

.sik-btn--sm { padding: 0 var(--sp-4); min-height: 36px; font-size: var(--fs-sm); }
.sik-btn--lg { padding: 0 var(--sp-7); min-height: 52px; font-size: var(--fs-md); }
/* A finger still gets the full target. */
@media (pointer: coarse) { .sik-btn--sm { min-height: var(--tap); } }
.sik-btn--block { width: 100%; }
.sik-btn--pill { border-radius: var(--sik-radius-pill); }

/* Inline spinner shown while an AJAX action is in flight. */
.sik-btn .sik-spinner { display: none; }
.sik-btn.is-loading .sik-spinner { display: inline-block; }
.sik-btn.is-loading .sik-btn__label { opacity: .7; }

.sik-spinner {
    width: 15px; height: 15px;
    border: 2px solid currentColor;
    border-right-color: transparent;
    border-radius: 50%;
    animation: sik-spin .6s linear infinite;
    flex: none;
}
@keyframes sik-spin { to { transform: rotate(360deg); } }

/* Small circular icon button (wishlist / compare / quick view / close). */
.sik-iconbtn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px; height: 38px;
    border-radius: 50%;
    background: var(--sik-elevated);
    color: var(--sik-ink);
    border: 1px solid var(--sik-line);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
    flex: none;
}
/* The --tap target comes from the shared extender in section 2b. */
.sik-iconbtn:hover {
    background: var(--sik-surface-2);
    border-color: color-mix(in srgb, var(--sik-primary) 34%, var(--sik-line-strong));
    color: var(--sik-primary-ink);
}
@media (hover: hover) { .sik-iconbtn:hover { transform: translate3d(0, var(--lift-sm), 0); box-shadow: var(--sik-shadow-brand-sm); } }
.sik-iconbtn:active { transform: scale(.94); box-shadow: none; }
.sik-iconbtn.is-active { color: var(--sik-ink); border-color: var(--sik-line-strong); }
.sik-iconbtn.is-active svg { fill: currentColor; }
/* A saved heart is red everywhere it appears — the one colour a shopper
   already knows the meaning of. A compared product inverts instead. */
.sik-iconbtn[data-wishlist].is-active { color: var(--sik-danger); border-color: color-mix(in srgb, var(--sik-danger) 40%, var(--sik-line)); }
.sik-iconbtn[data-compare].is-active { background: var(--sik-ink); border-color: var(--sik-ink); color: var(--sik-bg); }
.sik-iconbtn[data-compare].is-active svg { fill: none; }

/* The heart's one flourish: a quick pop when it is saved, fired by app.js. */
@keyframes sik-heartbeat {
    30% { transform: scale(1.22); }
    55% { transform: scale(.94); }
   100% { transform: none; }
}
.sik-iconbtn.is-bump svg { animation: sik-heartbeat .42s var(--ease-spring); }
.sik-noanim .sik-iconbtn.is-bump svg { animation: none; }

/* --------------------------------------------------------------------------
   5. Announcement bar + ticker

   A slim wine strip above the header carrying two independent things: the
   store's messages on the left, and the one running offer — its code and the
   time left on it — on the right. Either can be absent; the strip lays out the
   same whichever is there.

   One message at a time on desktop (rotated by header.js), a paused-on-touch
   ticker on phones. Dismissal is remembered per browser by app.js.
   -------------------------------------------------------------------------- */
.sik-announce {
    /* The brand sweep, bounded for contrast: every stop clears 4.5:1 against
       the white text it carries. An operator colour set in Admin > Appearance
       replaces it wholesale (see --sik-hd-ann-bg in 6b). */
    background: var(--sik-grad-wine);
    color: #fff;
    font-size: var(--fs-sm);
    position: relative;
    /* A step under --z-header, so the pinned header always paints over it. */
    z-index: var(--z-sticky);
}
/* A gold hairline along the bottom: the one place gold touches the chrome. */
.sik-announce::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 1px;
    background: var(--sik-grad-gold);
    opacity: .5;
}
.sik-announce[hidden] { display: none; }
.sik-announce a { color: inherit; }
.sik-announce a:hover { color: inherit; text-decoration: underline; text-underline-offset: 3px; }

/* messages | promo | dismiss */
.sik-announce__bar {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 38px;
}
.sik-announce__msgs { flex: 1 1 auto; min-width: 0; }

.sik-announce__row {
    display: flex;
    align-items: center;
    justify-content: flex-start;
    gap: var(--sp-7);
    min-height: 38px;
    padding-block: 0;
}
a.sik-announce__item { min-height: 38px; }
@media (pointer: coarse) {
    .sik-announce__row,
    a.sik-announce__item { min-height: var(--tap); }
}
.sik-announce__item {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    white-space: nowrap;
    min-width: 0;
}
.sik-announce__item > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
}
.sik-announce__item svg { color: var(--sik-gold-2); opacity: .95; flex: none; width: var(--icon-sm); height: var(--icon-sm); }
/* The headline half of a message is gold; the detail after it stays white. */
.sik-announce__item strong { font-weight: var(--fw-bold); color: var(--sik-gold-2); }

/* Desktop shows one message and rotates. Without JavaScript the first stays. */
.sik-announce__row--static .sik-announce__item { display: none; }
.sik-announce__row--static .sik-announce__item:first-child { display: inline-flex; }
.sik-announce__row--static.is-rotating .sik-announce__item { display: none; }
.sik-announce__row--static.is-rotating .sik-announce__item.is-active {
    display: inline-flex;
    animation: sik-announce-in var(--dur-slow) var(--ease-out) both;
}
@keyframes sik-announce-in {
    from { opacity: 0; transform: translateY(4px); }
    to   { opacity: 1; transform: none; }
}
.sik-announce__row--static .sik-announce__item > span { overflow: visible; text-overflow: clip; }

/* --- the running offer ---------------------------------------------------- */
.sik-announce__promo {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex: none;
    font-size: var(--fs-xs);
}

/* The code chip. A button, because it copies the code — the one thing a
   shopper actually needs out of this strip. */
.sik-announce__code {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 3px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: rgba(255, 255, 255, .12);
    border: 1px solid rgba(255, 255, 255, .22);
    color: inherit;
    font: inherit;
    font-size: var(--fs-xs);
    cursor: pointer;
    white-space: nowrap;
    transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.sik-announce__code:hover { background: rgba(255, 255, 255, .2); border-color: rgba(255, 255, 255, .4); }
.sik-announce__code-label { opacity: .78; font-weight: var(--fw-normal); }
.sik-announce__code-value {
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    color: var(--sik-gold-2);
}
.sik-announce__promo-text { white-space: nowrap; font-weight: var(--fw-medium); }

/* The clock. Tabular digits in their own dark cells so the strip does not
   twitch sideways once a second. */
.sik-announce__cd {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    font-variant-numeric: tabular-nums;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
}
.sik-announce__cd b {
    min-width: 21px;
    padding: 2px 4px;
    text-align: center;
    border-radius: var(--sik-radius-xs);
    background: rgba(0, 0, 0, .38);
    border: 1px solid rgba(255, 255, 255, .15);
    color: var(--sik-gold-2);
}
.sik-announce__cd i { font-style: normal; opacity: .7; }

.sik-announce__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px; height: 26px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: rgba(255, 255, 255, .72);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-announce__close:hover { background: rgba(255, 255, 255, .16); color: #fff; }
/* The 44px hit area comes from the shared extender in section 2b. */

/* Below the search breakpoint the strip keeps the code and the clock — the two
   things worth the space — and drops the sentence between them. */
@media (max-width: 767.98px) {
    .sik-announce__promo-text { display: none; }
    .sik-announce__code-label { display: none; }
}
/* One slim line, and below ~560px it cannot hold both halves: the message
   ticker gets squeezed to a single word while the offer beside it keeps its
   full width. When an offer IS running, its code and clock are the
   actionable half, so the ticker stands down; with no offer the ticker has
   the strip to itself and nothing is hidden. */
@media (max-width: 560px) {
    .sik-announce__bar { gap: var(--sp-2); }
    .sik-announce__bar:has(.sik-announce__promo) .sik-announce__msgs { display: none; }
    .sik-announce__promo { flex: 1 1 auto; justify-content: center; min-width: 0; }
}
/* .sik-announce__cd is also a .sik-countdown, and section 13 declares that
   class later at the same specificity - a bare .sik-announce__cd rule here
   loses to it. Qualifying by the strip is what makes this stick. */
@media (max-width: 379px) {
    .sik-announce .sik-announce__cd { display: none; }
}

/* Marquee ticker used on small screens. */
.sik-ticker { overflow: hidden; }
.sik-ticker__track {
    display: flex;
    gap: var(--sp-9);
    width: max-content;
    animation: sik-marquee var(--sik-ticker-speed, 40s) linear infinite;
    will-change: transform;
}
.sik-ticker:hover .sik-ticker__track,
.sik-ticker:focus-within .sik-ticker__track { animation-play-state: paused; }
@keyframes sik-marquee {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

/* Reduced motion: a stopped marquee inside overflow:hidden would only ever
   show its first message, so it becomes a plain horizontal scroller. */
@media (prefers-reduced-motion: reduce) {
    .sik-ticker { overflow-x: auto; scrollbar-width: none; }
    .sik-ticker::-webkit-scrollbar { display: none; }
    .sik-ticker__track { gap: var(--sp-7); }
    .sik-ticker__track > [aria-hidden="true"] { display: none; }
}
.sik-noanim .sik-ticker { overflow-x: auto; scrollbar-width: none; }
.sik-noanim .sik-ticker__track { animation: none; }
@media (max-width: 767px) {
    .sik-announce__row--static { display: none; }
}
@media (min-width: 768px) {
    .sik-announce__row--ticker { display: none; }
}

/* --------------------------------------------------------------------------
   6. Header

   One row from 1200px: wordmark | navigation | search | actions.
   1024-1199: the navigation takes a second row inside the same <header>, so
   mega panels still hang from the header and header.js measures one element.
   Below 1024 the drawer is the navigation and the bottom bar carries the
   destinations a thumb reaches for.

   Once it is stuck the header turns into an app bar: the page colour at 86%
   over a backdrop blur, so content scrolling underneath reads through faintly.
   -------------------------------------------------------------------------- */
.sik-header {
    /* Authored row height. header.js measures the rendered header into
       --sik-header-h; the two must stay separate or the measurement feeds
       back into the layout. Admin > Appearance > Header overrides both steps. */
    --sik-header-base: 60px;
    position: relative;
    z-index: var(--z-sticky);
    background: var(--sik-bg);
    border-bottom: 1px solid var(--sik-line);
    transition: box-shadow var(--dur-base) var(--ease-out),
                background-color var(--dur-base) var(--ease-out);
}
@media (min-width: 768px) { .sik-header { --sik-header-base: 72px; } }

.sik-header--sticky { position: sticky; top: 0; z-index: var(--z-header); }
.sik-header.is-stuck {
    box-shadow: var(--sik-shadow-card);
    border-bottom-color: color-mix(in srgb, var(--sik-primary) 22%, var(--sik-line));
}
/* A hairline of brand along the bottom edge once the header is pinned, so the
   app bar reads as the store's rather than as a generic white strip. */
.sik-header.is-stuck::after {
    content: "";
    position: absolute;
    inset: auto 0 -1px 0;
    height: 2px;
    background: var(--sik-grad-brand);
    opacity: .85;
    pointer-events: none;
}

@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    /* (0,2,0) beats the (0,1,1) `body .sik-header` rule in 6b, and it mixes the
       operator's own header colour when one is set rather than replacing it. */
    .sik-header--sticky.is-stuck {
        background: color-mix(in srgb, var(--sik-hd-bg, var(--sik-hd-bg-auto, var(--sik-bg))) 86%, transparent);
        -webkit-backdrop-filter: saturate(1.6) blur(16px);
        backdrop-filter: saturate(1.6) blur(16px);
    }
}

.sik-header__row {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--sik-header-base);
}

/* Burger and wordmark travel together, so the row holds the same children at
   every width. min-width:0 lets the wordmark scale down instead of pushing the
   actions out of the container on a 320px phone. */
.sik-header__lead {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    flex: 0 1 auto;
    min-width: 0;
}

@media (min-width: 768px) {
    .sik-header__row {
        display: grid;
        grid-template-columns: auto minmax(0, 1fr) auto;
        gap: var(--sp-5);
    }
    .sik-header__row > .sik-search { display: block; width: 100%; justify-self: center; }
    .sik-actions { justify-self: end; }
}

@media (min-width: 1200px) {
    .sik-header__row { grid-template-columns: auto auto minmax(0, 1fr) auto; gap: var(--sp-6); }
    .sik-header__row > .sik-search { justify-self: end; }
}

@media (max-width: 479px) {
    .sik-header__row { gap: var(--sp-1); }
}

/* --- wordmark ------------------------------------------------------------- */
.sik-logo { display: inline-flex; align-items: center; flex: 0 1 auto; min-width: 0; }
.sik-logo img {
    height: 30px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
    object-position: left center;
}
@media (min-width: 768px) { .sik-logo img { height: 36px; } }

/* Settings > Store has a second logo for dark backgrounds. When one exists the
   header carries both and the theme decides which is shown. */
.sik-logo__dark { display: none; }
[data-theme="dark"] .sik-logo__light { display: none; }
[data-theme="dark"] .sik-logo__dark { display: block; }

/* --- primary navigation --------------------------------------------------- */
.sik-header__nav { display: none; min-width: 0; }
@media (min-width: 1024px) { .sik-header__nav { display: block; } }

@media (min-width: 1024px) and (max-width: 1199.98px) {
    /* An explicit row, so the auto-placed wordmark, search and actions keep
       row one to themselves. */
    .sik-header__nav {
        grid-row: 2;
        grid-column: 1 / -1;
        margin-inline: calc(var(--gutter) * -1);
        padding: 0 var(--gutter) var(--sp-1);
    }
}
@media (min-width: 1200px) {
    /* Inline in the row there is no band to draw. (0,2,1) beats 6b's
       `body .sik-header__nav`, which still styles the two-row layout. */
    body .sik-header .sik-header__nav { background: transparent; border-top: 0; }
}

.sik-nav { display: none; min-width: 0; }
@media (min-width: 1024px) { .sik-nav { display: block; width: 100%; } }

.sik-nav__list {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    /* NOT overflow-x:auto: a scroll container would clip every mega panel.
       NAV_INLINE_LIMIT in navbar.php keeps the row from running long. */
    flex-wrap: nowrap;
    min-width: 0;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* Every panel hangs off its own item. */
.sik-nav__item { position: relative; list-style: none; flex: 0 0 auto; }

.sik-nav__link {
    --sik-nav-pad: var(--sp-3);
    position: relative;
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    gap: var(--sp-1);
    padding: var(--sp-2) var(--sik-nav-pad);
    min-height: 40px;
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    letter-spacing: var(--ls-normal);
    white-space: nowrap;
    color: var(--sik-ink-2);
    background: none;
    border: 0;
    cursor: pointer;
    border-radius: var(--sik-radius-pill);
    transition: color var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out);
}

.sik-nav__link:hover,
.sik-nav__item.is-open > .sik-nav__link {
    color: var(--sik-primary-ink);
    background: color-mix(in srgb, var(--sik-primary) 8%, transparent);
}
.sik-nav__link.is-current { color: var(--sik-ink); font-weight: var(--fw-semibold); }

/* One underline for every link, wiped in from the left on hover and held open
   for the current page. Transform only, so it costs nothing to animate. */
.sik-nav__link::after {
    content: "";
    position: absolute;
    left: var(--sik-nav-pad);
    right: var(--sik-nav-pad);
    bottom: 3px;
    height: 2px;
    border-radius: 2px;
    background: var(--sik-grad-brand);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-base) var(--ease-out);
}
.sik-nav__link:hover::after,
.sik-nav__item.is-open > .sik-nav__link::after,
.sik-nav__link.is-current::after { transform: scaleX(1); }
.sik-noanim .sik-nav__link::after { transition: none; }
.sik-nav__icon { width: var(--icon-sm); height: var(--icon-sm); flex: none; }
/* A backstop against an essay typed into the Menu Builder, not a layout tool. */
.sik-nav__text { white-space: nowrap; max-width: 24ch; overflow: hidden; text-overflow: ellipsis; }
.sik-nav__caret { display: inline-flex; opacity: .6; transition: transform var(--dur-base) var(--ease-out); }
.sik-nav__caret-svg { width: var(--icon-xs); height: var(--icon-xs); }
.sik-nav__item.is-open > .sik-nav__link .sik-nav__caret { transform: rotate(180deg); }

.sik-nav__badge {
    font-size: 10px;
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    line-height: 1;
    padding: 3px 5px;
    border-radius: var(--sik-radius-xs);
    background: var(--sik-cta);
    color: var(--sik-on-cta);
    position: relative;
    top: -6px;
    flex: none;
}

/* --- menu panels ---------------------------------------------------------- */
/* One panel shape for the whole navigation: a floating card aligned to the
   trigger that opened it, set 8px below the bar. */
.sik-mega {
    position: absolute;
    left: 0; right: auto;
    top: calc(100% + var(--sp-2));
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-pop);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px) scale(.99);
    transform-origin: top left;
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                visibility var(--dur-base);
    z-index: var(--z-sticky);
}
/* An invisible bridge over the 8px gap: it belongs to the panel, which is a
   child of the item, so a pointer crossing it never leaves the item. */
.sik-mega::before {
    content: "";
    position: absolute;
    left: 0; right: 0;
    bottom: 100%;
    height: calc(var(--sp-2) + 2px);
}
.sik-nav__item.is-open > .sik-mega { opacity: 1; visibility: visible; transform: none; }

.sik-mega--drop {
    min-width: 248px;
    max-width: min(320px, 92vw);
    padding: var(--sp-2);
    max-height: min(70vh, 520px);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.sik-mega--end { left: auto; right: 0; transform-origin: top right; }

.sik-mega--stack {
    --sik-mega-w: 340px;
    display: flex;
    flex-direction: column;
    width: min(var(--sik-mega-w), calc(100vw - var(--gutter) * 2));
    max-height: min(calc(100vh - var(--sik-header-h, 120px) - var(--sp-6)), 560px);
    overflow: hidden;
}

.sik-mega__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex: none;
    min-height: var(--tap);
    margin: var(--sp-2) var(--sp-2) 0;
    padding: var(--sp-3);
    border-radius: var(--sik-radius);
    background: var(--sik-surface-2);
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-mega__head:hover { background: var(--sik-press); }
.sik-mega__cover {
    width: 36px;
    height: 36px;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    object-fit: cover;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-media);
    color: var(--sik-ink);
}
.sik-mega__cover-svg { width: var(--icon-lg); height: var(--icon-lg); }
.sik-mega__head-body { flex: 1; min-width: 0; display: block; }
.sik-mega__eyebrow {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    line-height: var(--lh-normal);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-mega__head-text {
    display: block;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-mega__head-go {
    display: inline-flex;
    flex: none;
    color: var(--sik-muted);
    transition: transform var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-mega__head:hover .sik-mega__head-go { transform: translateX(3px); color: var(--sik-ink); }

.sik-mega__scroll {
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    padding: var(--sp-2);
    scrollbar-width: thin;
}

.sik-mega__list { display: grid; gap: 2px; list-style: none; margin: 0; padding: 0; }
.sik-mega__list li { list-style: none; }
.sik-mega__group + .sik-mega__group {
    margin-top: var(--sp-1);
    padding-top: var(--sp-2);
    border-top: 1px solid var(--sik-line);
}
.sik-mega__sub {
    position: relative;
    display: grid;
    gap: 2px;
    list-style: none;
    margin: 2px 0 0;
    padding: 0 0 0 var(--sp-5);
}

.sik-mega__link {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 40px;
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    line-height: var(--lh-snug);
    color: var(--sik-ink-2);
    border-radius: var(--sik-radius-sm);
    transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-mega__link { min-height: var(--tap); } }
.sik-mega__link:hover,
.sik-mega__link:focus-visible { color: var(--sik-ink); background: var(--sik-hover); }

.sik-mega__link--parent { font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-mega__link--sub { font-size: var(--fs-sm); font-weight: var(--fw-normal); color: var(--sik-muted); }
.sik-mega__link-text {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-mega__link .sik-badge { flex: none; }
.sik-mega__glyph {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: var(--icon-sm);
    height: var(--icon-sm);
    color: var(--sik-muted);
    transition: color var(--dur-fast) var(--ease-out);
}
.sik-mega__link:hover .sik-mega__glyph,
.sik-mega__link:focus-visible .sik-mega__glyph { color: var(--sik-ink); }
.sik-mega__glyph-svg { width: var(--icon-sm); height: var(--icon-sm); }
.sik-mega__link--sub .sik-mega__glyph,
.sik-mega__link--sub .sik-mega__glyph-svg { width: var(--icon-xs); height: var(--icon-xs); }

/* An uploaded glyph is an <img>: it paints itself and cannot be recoloured. */
.sik-ico--file { object-fit: contain; }

.sik-mega__promo {
    display: block;
    flex: none;
    margin: 0 var(--sp-2) var(--sp-2);
    border-radius: var(--sik-radius);
    overflow: hidden;
    background: var(--sik-media);
    aspect-ratio: 28 / 15;
}
.sik-mega__promo-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slow) var(--ease-out);
}
.sik-mega__promo:hover .sik-mega__promo-img { transform: scale(1.03); }

/* --- header actions ------------------------------------------------------- */
.sik-actions {
    display: flex;
    align-items: center;
    gap: 2px;
    margin-left: auto;
    flex: none;
    position: relative;
}
.sik-action {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap); height: var(--tap);
    border-radius: 50%;
    color: var(--sik-ink);
    background: transparent;
    border: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
    /* Without this the burger is the one flex item willing to shrink, and it
       collapsed to 0px on phones. */
    flex: none;
}
.sik-action__icon { width: var(--icon-lg); height: var(--icon-lg); }
.sik-action:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-action:active { background: var(--sik-press); transform: scale(.95); }
.sik-menu.is-open > .sik-action { background: var(--sik-press); color: var(--sik-ink); }

/* Light / dark toggle: desktop only; the drawer carries the phone control.
   The glyph shown is the theme a click would switch TO. */
.sik-action--theme { display: none; }
@media (min-width: 1024px) { .sik-action--theme { display: inline-flex; } }
.sik-theme-glyph--sun { display: none; }
[data-theme="dark"] .sik-theme-glyph--sun { display: block; }
[data-theme="dark"] .sik-theme-glyph--moon { display: none; }

/* Compare is a desktop affordance; the drawer carries it on phones. */
.sik-action--compare { display: none; }
@media (min-width: 1024px) { .sik-action--compare { display: inline-flex; } }

/* The bottom bar carries the wishlist on phones and tablets. */
@media (max-width: 1023px) {
    body.sik-has-bottomnav .sik-action--wishlist { display: none; }
}
@media (max-width: 479px) {
    .sik-action--wishlist { display: none; }
    .sik-actions { gap: 0; }
}

/* Signed-in identity: initials in a disc, inside the same --tap target. */
.sik-action__avatar {
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--sik-ink);
    color: var(--sik-bg);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    line-height: 1;
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-action:hover .sik-action__avatar,
.sik-menu.is-open > .sik-action .sik-action__avatar { background: var(--sik-cta); color: var(--sik-on-cta); }

.sik-action__count {
    position: absolute;
    top: 4px;
    right: 3px;
    min-width: 18px; height: 18px;
    padding-inline: 5px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-cta);
    color: var(--sik-on-cta);
    font-size: 10.5px;
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 2px solid var(--sik-bg);
    line-height: 1;
    pointer-events: none;
}
.sik-action__count:empty,
.sik-action__count[data-count="0"] { display: none; }
/* A count that just changed gives one small nod, so an add from a card reads
   as having landed in the header. cart.js / wishlist.js toggle .is-bumped. */
.sik-action__count.is-bumped { animation: sik-count-bump var(--dur-slow) var(--ease-spring); }
@keyframes sik-count-bump { 0% { transform: scale(1); } 40% { transform: scale(1.35); } 100% { transform: scale(1); } }

/* --- the inline search field in the header -------------------------------- */
/* Compact and quiet at rest; it lifts onto the elevated surface on focus. The
   submit button is dropped here — Enter submits and the leading glyph already
   says what the field is. The page and dialog variants keep theirs. */
.sik-search--header .sik-search__field {
    min-height: 42px;
    background: var(--sik-surface-2);
    border-color: transparent;
    border-radius: var(--sik-radius-pill);
    padding-inline: var(--sp-4) var(--sp-2);
}
.sik-search--header .sik-search__input { padding-block: var(--sp-2); font-size: var(--fs-base); }
.sik-search--header .sik-search__submit { display: none; }
.sik-search--header .sik-search__voice { width: 32px; height: 32px; }

/* ==========================================================================
   6b. HEADER CUSTOMIZATION  -  Admin > Appearance > Header
   ==========================================================================

   Two independent systems, and keeping them independent is the whole design:

     SCALE   custom properties (--sik-hd-*) printed into the <style> block in
             includes/header.php by header_css_vars(). Heights, sizes, spacing,
             type and colour.
     SHAPE   body classes (.sik-hstyle--*, .sik-hhover--*, .sik-hnav-off, …)
             written by header_body_class(). Which bars exist, where the search
             lives, how the header is separated from the page.

   A menu style never touches a scale property the operator owns, and a scale
   property never decides whether a bar exists. That is why an operator can
   pick "Category-Focused" AND a 15px menu font and get both.

   THREE RULES FOR EDITING THIS BLOCK

   1. Every selector starts with `body `. The shipped rules above are single
      class selectors, so the extra element bumps specificity by one and this
      block wins WITHOUT depending on where it sits in the file. A sibling
      agent moving code around cannot silently disable it.

   2. Every `var()` carries the shipped value as its final fallback:
          var(--sik-hd-navbg, var(--sik-hd-navbg-auto, var(--sik-surface)))
                 operator          menu style            what shipped
      header_css_vars() only prints the first one when the operator actually
      chose something, so a store that changes nothing renders byte-identically.
      Deleting a fallback breaks the "defaults reproduce the shipped header"
      guarantee that the whole feature rests on.

   3. A menu style sets `--sik-hd-*-auto` on `.sik-header`, never `--sik-hd-*`.
      Custom properties resolve against the element they are USED on, so a
      value a style declares on .sik-header is visible to the rules below it
      but is still beaten by the operator's :root declaration. Setting the
      un-suffixed name in a style would make the operator's field inert.
   -------------------------------------------------------------------------- */

/* --- scale: the row, the wordmark, the bars ------------------------------- */
/* Re-points the header's own authored height variable rather than overriding
   min-height on .sik-header__row, so header.js keeps measuring one number and
   nothing downstream has to learn a second one. */
body .sik-header { --sik-header-base: var(--sik-hd-h-sm, 60px); }
@media (min-width: 768px) {
    body .sik-header { --sik-header-base: var(--sik-hd-h, 72px); }
}

body .sik-logo img { height: var(--sik-hd-logo-sm, calc(var(--tap) - var(--sp-3))); }
@media (min-width: 768px) {
    body .sik-logo img { height: var(--sik-hd-logo, calc(var(--tap) - var(--sp-1))); }
}

/* --- scale: header chrome ------------------------------------------------- */
body .sik-header {
    background: var(--sik-hd-bg, var(--sik-hd-bg-auto, var(--sik-surface)));
    border-bottom: var(--sik-hd-bw, 1px) solid var(--sik-hd-bc, var(--sik-hd-bc-auto, var(--sik-border)));
    box-shadow: var(--sik-hd-shadow, var(--sik-hd-shadow-auto, none));
}
body .sik-header.is-stuck {
    box-shadow: var(--sik-hd-shadow-stuck, var(--sik-hd-shadow-stuck-auto, var(--sik-shadow-soft)));
}
body .sik-header__nav {
    background: var(--sik-hd-navbg, var(--sik-hd-navbg-auto, var(--sik-surface)));
    border-top: var(--sik-hd-bw, 1px) solid var(--sik-hd-bc, var(--sik-hd-bc-auto, var(--sik-border)));
}

/* --- scale: announcement bar ---------------------------------------------- */
/* `inherit` on the link rather than a second colour field: the bar is one
   surface, and a linked message that ignored the bar's own text colour is the
   bug this replaces. */
body .sik-announce {
    background: var(--sik-hd-ann-bg, var(--sik-grad-wine));
    color: var(--sik-hd-ann-fg, #fff);
}
body .sik-announce a { color: inherit; }

/* --- scale: navigation typography ----------------------------------------- */
body .sik-header .sik-nav__list {
    gap: var(--sik-hd-navgap, var(--sp-1));
    justify-content: var(--sik-hd-navjust, var(--sik-hd-navjust-auto, flex-start));
}
body .sik-nav__link {
    --sik-nav-pad: var(--sik-hd-navpad, var(--sp-2));
    min-height: 40px;
    font-size: var(--sik-hd-navfs, var(--fs-base));
    font-weight: var(--sik-hd-navfw, var(--fw-medium));
    letter-spacing: var(--sik-hd-navls, var(--ls-normal));
    text-transform: var(--sik-hd-navtt, none);
    color: var(--sik-hd-navfg, var(--sik-ink-2));
}
/* The shipped bar widens its item padding one step at 1440. Deriving that step
   from the admin's own number (+4px) rather than freezing it at 12px is what
   keeps a stock store pixel-identical on a wide monitor, not just at 1280. */
@media (min-width: 1440px) {
    body .sik-nav__link { --sik-nav-pad: var(--sik-hd-navpad-lg, var(--sp-3)); }
}
body .sik-nav__link:hover,
body .sik-nav__item.is-open > .sik-nav__link {
    color: var(--sik-hd-navhover-fg, var(--sik-ink));
    background: var(--sik-hd-navhover-bg, var(--sik-hover));
}
body .sik-nav__link.is-current { color: var(--sik-hd-navactive-fg, var(--sik-ink)); }
body .sik-nav__link.is-current::after { background: var(--sik-hd-navactive, var(--sik-primary)); }

/* Hover treatments. `pill` is the shipped one and adds nothing; the other two
   cancel the tint and put the emphasis somewhere else. inset box-shadow rather
   than a pseudo element, because ::after is already spoken for by the
   "current page" rule directly above and an element has only one of each. */
body.sik-hhover--underline .sik-nav__link:hover,
body.sik-hhover--underline .sik-nav__item.is-open > .sik-nav__link {
    background: transparent;
    box-shadow: inset 0 -2px 0 0 var(--sik-hd-navhover-fg, var(--sik-ink));
}
body.sik-hhover--color .sik-nav__link:hover,
body.sik-hhover--color .sik-nav__item.is-open > .sik-nav__link { background: transparent; }

/* --- scale: action cluster ------------------------------------------------ */
body .sik-action {
    width: var(--sik-hd-tap, var(--tap));
    height: var(--sik-hd-tap, var(--tap));
    color: var(--sik-hd-actionfg, var(--sik-ink));
}
body .sik-action__icon {
    width: var(--sik-hd-icon, var(--icon-lg));
    height: var(--sik-hd-icon, var(--icon-lg));
}
/* Restating the colour on hover as well: the shipped hover rule pins it back
   to navy at a higher specificity than the base rule above, so without this a
   custom action colour would snap back the moment you pointed at it. */
body .sik-action:hover {
    background: var(--sik-hd-actionbg, var(--sik-hover));
    color: var(--sik-hd-actionfg, var(--sik-ink));
}
body .sik-action__avatar {
    width: calc(var(--sik-hd-tap, var(--tap)) - var(--sp-4));
    height: calc(var(--sik-hd-tap, var(--tap)) - var(--sp-4));
}
/* The badge is centred on the GLYPH's corner, not the button's, so both terms
   have to follow the admin's sizes or a 56px control leaves the count floating
   in the padding. Same derivation as the shipped rule, one token deeper. */
body .sik-action__count {
    top: calc((var(--sik-hd-tap, var(--tap)) - var(--sik-hd-icon, var(--icon-lg))) / 2 - var(--sp-2));
    right: calc((var(--sik-hd-tap, var(--tap)) - var(--sik-hd-icon, var(--icon-lg))) / 2 - var(--sp-2));
}

/* --- scale: the inline search field --------------------------------------- */
body .sik-header__row > .sik-search { max-width: var(--sik-hd-searchw, 560px); }
@media (min-width: 1536px) {
    body .sik-header__row > .sik-search { max-width: var(--sik-hd-searchw-lg, 640px); }
}

/* --- shape: optional pieces ----------------------------------------------- */
/* The category bar switched off. display:none rather than skipping the render,
   because this element is already the one whose visibility is a breakpoint
   decision - and it lets the admin preview toggle it inside the live iframe. */
body.sik-hnav-off .sik-header__nav { display: none; }
/* ...and hand the burger back, or the store has no primary navigation at all
   above 1024px. .sik-burger is display:none from 1024 up because the category
   bar is meant to have taken over by then; switch the bar off and both are
   gone, leaving only the wordmark and the action icons. Measured with the
   class applied at 1280: nav display none, burger display none, 0 nav links
   reachable.
   The `minimal` menu style hits the same wall and already does exactly this
   (see body.sik-hstyle--minimal .sik-burger below) — this branch just never
   got the matching line. The drawer it opens already carries every nav link,
   so nothing new is needed. */
body.sik-hnav-off .sik-burger { display: inline-flex; }

/* The store tagline beside the wordmark. Desktop only: on a phone the lead
   slot is already competing with the burger and the action cluster. */
.sik-header__tagline {
    display: none;
    padding-left: var(--sp-3);
    margin-left: var(--sp-1);
    border-left: 1px solid var(--sik-hd-bc, var(--sik-hd-bc-auto, var(--sik-border)));
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    line-height: var(--lh-normal);
    text-transform: uppercase;
    /* 11px type, so the muted ink rather than a lighter grey. */
    color: var(--sik-muted);
    white-space: nowrap;
}
@media (min-width: 1024px) {
    body.sik-htagline .sik-header__tagline { display: inline-block; }
}

/* Where the inline field gives way to the search overlay. The shipped answer
   is 768; these two move it later or drop it entirely, and each one hands the
   magnifier back at exactly the widths the field is no longer there. */
@media (max-width: 1023px) {
    body.sik-hsearch--1024 .sik-header__row > .sik-search { display: none; }
    body.sik-hsearch--1024 .sik-search-toggle { display: inline-flex; }
}
body.sik-hsearch--never .sik-header__row > .sik-search { display: none; }
body.sik-hsearch--never .sik-search-toggle { display: inline-flex; }

/* ==========================================================================
   6c. MENU STYLES
   Five real layouts, not five names for one. Each one is a different answer to
   the same four questions: does the category bar exist, what surface is it on,
   where does search live, and how is the header separated from the page.
   The single-column mega panel is unchanged in all five - that is settled.
   ========================================================================== */

/* --- Classic Navigation --------------------------------------------------- */
/* Intentionally empty. `classic` IS the shipped header, so it adds no rule at
   all; every fallback above already describes it. Do not "fill this in" - the
   fact that this style is zero CSS is what makes "Reset to default" provably
   restore the shipped look. */

/* --- Modern Mega Menu ----------------------------------------------------- */
/* The rule between the header and the page is replaced by a shadow, and the
   category bar moves onto the soft surface so the two rows read as chrome and
   content rather than one undifferentiated white block. */
body.sik-hstyle--mega .sik-header {
    --sik-hd-shadow-auto: var(--sik-shadow-soft);
    --sik-hd-navbg-auto: var(--sik-soft);
    --sik-hd-navjust-auto: center;
    border-bottom-width: 0;
}
body.sik-hstyle--mega .sik-header__nav { border-top-color: transparent; }
/* Roomier hit areas to match the softer surface. Derived from the operator's
   own padding so their number is respected, not replaced. */
body.sik-hstyle--mega .sik-nav__link {
    --sik-nav-pad: calc(var(--sik-hd-navpad, var(--sp-2)) + var(--sp-2));
    border-radius: 999px;
}
@media (min-width: 1440px) {
    body.sik-hstyle--mega .sik-nav__link { --sik-nav-pad: calc(var(--sik-hd-navpad-lg, var(--sp-3)) + var(--sp-2)); }
}
/* A pill hover wants a pill underline, so the current-page marker lifts off the
   bar's edge and sits under the label instead. */
body.sik-hstyle--mega .sik-nav__link.is-current::after { bottom: var(--sp-2); height: 2px; border-radius: 2px; }

/* --- Minimal Premium ------------------------------------------------------ */
/* No category bar at any width and no inline field: the drawer carries the
   navigation and the overlay carries the search, which is the whole point of
   the style. Both surfaces already exist and already work at every width, so
   this is a re-routing rather than a new component. */
body.sik-hstyle--minimal .sik-header__nav { display: none; }
body.sik-hstyle--minimal .sik-burger { display: inline-flex; }
body.sik-hstyle--minimal .sik-header__row > .sik-search { display: none; }
body.sik-hstyle--minimal .sik-search-toggle { display: inline-flex; }
/* One row and nothing else, so it can afford the height the two-row layouts
   spend on a second bar. */
body.sik-hstyle--minimal .sik-header__row { min-height: calc(var(--sik-header-base) + var(--sp-4)); }
/* `-bc-auto`, never `-bc`: a custom property declared on .sik-header is nearer
   than the operator's :root declaration and would win, which is exactly what
   rule 3 at the top of 6b forbids. Setting the suffixed name instead keeps
   "Bottom border colour" live under this style too. */
body.sik-hstyle--minimal .sik-header {
    --sik-hd-bc-auto: color-mix(in srgb, var(--sik-border) 55%, transparent);
}
/* With the search gone the row has one flexible slot left; letting the actions
   sit hard right keeps the wordmark and the controls at the two edges instead
   of drifting together in the middle. */
@media (min-width: 768px) {
    body.sik-hstyle--minimal .sik-header__row { grid-template-columns: auto minmax(0, 1fr) auto; }
}

/* --- Category-Focused Navigation ------------------------------------------ */
/* The departments become the loudest thing in the header: a solid strip, light
   labels, and a hairline between each one so the bar reads as a directory. The
   top row gives back a spacing step to pay for it. */
body.sik-hstyle--category .sik-header {
    --sik-hd-navbg-auto: var(--sik-inverse);
}
body.sik-hstyle--category .sik-header__nav { border-top-color: transparent; }
body.sik-hstyle--category .sik-header__row { min-height: calc(var(--sik-header-base) - var(--sp-3)); }
/* The strip supplies its own contrast pair, because a dark label on a navy band
   is unreadable - but it supplies it as the FALLBACK, not as an override. An
   operator who deliberately picks a category link colour still gets it; one who
   leaves the field blank gets white, which is the only readable answer here.
   (These rules used to hard-code #fff and silently made four colour fields
   inert under this style.) */
body.sik-hstyle--category .sik-nav__link { color: var(--sik-hd-navfg, var(--sik-on-inverse)); }
body.sik-hstyle--category .sik-nav__item + .sik-nav__item { border-left: 1px solid rgba(255, 255, 255, .16); }
body.sik-hstyle--category .sik-nav__link:hover,
body.sik-hstyle--category .sik-nav__item.is-open > .sik-nav__link {
    color: var(--sik-hd-navhover-fg, var(--sik-on-inverse));
    background: var(--sik-hd-navhover-bg, rgba(255, 255, 255, .12));
}
body.sik-hstyle--category .sik-nav__link.is-current { color: var(--sik-hd-navactive-fg, var(--sik-on-inverse)); }
body.sik-hstyle--category .sik-nav__link.is-current::after { background: var(--sik-hd-navactive, var(--sik-accent)); }
body.sik-hstyle--category .sik-nav__badge { top: -6px; }

/* --- E-commerce Mega Navigation ------------------------------------------- */
/* For a wide catalogue: search is the primary control, so it takes the whole
   middle track instead of stopping at its comfortable reading width, and the
   category bar is taller and heavier because it is doing most of the work. */
/* `-set` rather than the plain variable: the style takes the whole row only
   while the operator has left "Search field width" on its shipped 560, so an
   explicit width still wins here exactly as it does under every other style. */
body.sik-hstyle--commerce .sik-header__row > .sik-search { max-width: var(--sik-hd-searchw-set, none); }
body.sik-hstyle--commerce .sik-header {
    --sik-hd-navbg-auto: var(--sik-primary-soft);
    --sik-hd-shadow-stuck-auto: var(--sik-shadow-card);
}
body.sik-hstyle--commerce .sik-nav__link {
    min-height: calc(var(--sik-hd-tap, var(--tap)) + var(--sp-3));
    font-weight: var(--sik-hd-navfw-set, var(--fw-bold));
}
/* A rule before the action cluster, so five icons at the end of a very wide row
   still read as one group rather than as items that drifted there. */
@media (min-width: 1024px) {
    body.sik-hstyle--commerce .sik-actions {
        padding-left: var(--sp-4);
        margin-left: var(--sp-2);
        border-left: 1px solid var(--sik-hd-bc, var(--sik-hd-bc-auto, var(--sik-border)));
    }
}

/* --- header dropdown menus (account, notifications) ----------------------- */
/* Below the desktop breakpoint the panels anchor to .sik-actions, because a
   360px panel hung off a single 44px icon would run off the left edge. */
.sik-menu { position: static; display: inline-flex; flex: none; }
.sik-menu--desk { display: none; }
@media (min-width: 1024px) {
    .sik-menu--desk { display: inline-flex; }
    .sik-menu { position: relative; }
}

.sik-menu__panel {
    position: absolute;
    top: calc(100% + var(--sp-2));
    right: 0;
    z-index: var(--z-header);
    width: min(340px, calc(100vw - var(--gutter) * 2));
    max-height: min(80vh, 620px);
    display: flex;
    flex-direction: column;
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-pop);
    overflow: hidden;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px) scale(.99);
    transform-origin: top right;
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                visibility var(--dur-base);
}
.sik-menu__panel[hidden] { display: none; }
.sik-menu.is-open > .sik-menu__panel { opacity: 1; visibility: visible; transform: none; }
.sik-menu__panel--account { overflow-x: hidden; overflow-y: auto; padding-bottom: var(--sp-1); }
.sik-menu__panel--notif { width: min(400px, calc(100vw - var(--gutter) * 2)); }

/* Who is signed in: a quiet identity row, not a coloured band. */
.sik-menu__user {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin: var(--sp-2) var(--sp-2) 0;
    padding: var(--sp-3);
    min-height: var(--tap);
    border-radius: var(--sik-radius);
    background: var(--sik-surface-2);
    color: var(--sik-ink);
    flex: none;
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-menu__user:hover { background: var(--sik-press); color: var(--sik-ink); }
.sik-menu__avatar {
    width: 40px; height: 40px;
    flex: none;
    border-radius: 50%;
    background: var(--sik-ink);
    color: var(--sik-bg);
    display: grid;
    place-items: center;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
}
.sik-menu__ident { flex: 1; min-width: 0; }
.sik-menu__name,
.sik-menu__mail {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-menu__name { font-size: var(--fs-base); font-weight: var(--fw-semibold); }
.sik-menu__mail { font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-menu__go { width: var(--icon-sm); height: var(--icon-sm); flex: none; color: var(--sik-muted); }

.sik-menu__group { padding: var(--sp-1) var(--sp-2) var(--sp-2); }
.sik-menu__group + .sik-menu__group { border-top: 1px solid var(--sik-line); }
.sik-menu__label {
    padding: var(--sp-3) var(--sp-3) var(--sp-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-menu__item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    min-height: 40px;
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--sik-radius-sm);
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    color: var(--sik-ink-2);
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-menu__item { min-height: var(--tap); } }
.sik-menu__item:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-menu__item.is-current { background: var(--sik-hover); color: var(--sik-ink); font-weight: var(--fw-semibold); }
.sik-menu__icon { width: var(--icon-md); height: var(--icon-md); flex: none; color: var(--sik-muted); }
.sik-menu__item:hover .sik-menu__icon,
.sik-menu__item.is-current .sik-menu__icon { color: var(--sik-ink); }
.sik-menu__text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sik-menu__count {
    flex: none;
    min-width: 20px; height: 20px;
    padding-inline: var(--sp-1);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface-2);
    color: var(--sik-ink);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}
.sik-menu__foot { flex: none; padding: var(--sp-2); border-top: 1px solid var(--sik-line); }
.sik-menu__item--danger { color: var(--sik-danger-ink); }
.sik-menu__item--danger .sik-menu__icon { color: var(--sik-danger-ink); }
.sik-menu__item--danger:hover { background: var(--sik-danger-soft); color: var(--sik-danger-ink); }

.sik-menu__intro { flex: none; padding: var(--sp-4); border-bottom: 1px solid var(--sik-line); }
.sik-menu__welcome { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--sik-ink); }
.sik-menu__blurb { margin-top: var(--sp-1); font-size: var(--fs-sm); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-menu__auth { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-2); margin-top: var(--sp-3); }
.sik-menu__auth .sik-btn { min-height: 40px; min-width: 0; }

.sik-menu__head {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--sik-line);
}
.sik-menu__title { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--sik-ink); }
.sik-menu__action { flex: none; }
.sik-menu__list { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: var(--sp-1); }
.sik-menu__list[aria-busy="true"] {
    opacity: .55;
    pointer-events: none;
    transition: opacity var(--dur-fast) var(--ease-out);
}
.sik-menu__more {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: var(--sp-3);
    border-top: 1px solid var(--sik-line);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--sik-ink);
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-menu__more:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-menu__empty { padding: var(--sp-8) var(--sp-5); text-align: center; }
.sik-menu__empty-icon {
    display: block;
    width: var(--icon-2xl); height: var(--icon-2xl);
    margin-inline: auto;
    color: var(--sik-muted);
    opacity: .55;
}
.sik-menu__empty-title { margin-top: var(--sp-3); font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--sik-ink); }
.sik-menu__empty-text { margin-top: var(--sp-1); font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }

/* --- a notification row (bell dropdown, and the feed page) ---------------- */
.sik-notif {
    position: relative;
    display: flex;
    gap: var(--sp-3);
    padding: var(--sp-3);
    border-radius: var(--sik-radius-sm);
    transition: background-color var(--dur-fast) var(--ease-out);
}
a.sik-notif:hover { background: var(--sik-hover); }
.sik-notif.is-unread { background: color-mix(in srgb, var(--sik-primary) 6%, transparent); }
a.sik-notif.is-unread:hover { background: color-mix(in srgb, var(--sik-primary) 11%, transparent); }
.sik-notif__icon {
    width: 38px; height: 38px;
    flex: none;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--tone-bg, var(--sik-surface-2));
    color: var(--tone-ink, var(--sik-muted));
}
.sik-notif__glyph { width: var(--icon-md); height: var(--icon-md); }
.sik-notif__thumb {
    width: 38px; height: 38px;
    flex: none;
    object-fit: cover;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-media);
}
.sik-notif__body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.sik-notif__title { font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: var(--lh-snug); color: var(--sik-ink); }
.sik-notif__text { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-notif__meta { display: flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-2xs); color: var(--sik-muted); }
.sik-notif__type { font-weight: var(--fw-semibold); color: var(--sik-ink-2); }
.sik-notif__dot {
    position: absolute;
    top: var(--sp-3); right: var(--sp-3);
    width: 8px; height: 8px;
    border-radius: 50%;
    background: var(--sik-cta);
}

/* One hue per tone; tint and text are mixed against the current surface and
   ink, so the same chip reads dark-on-pastel in light mode and light-on-deep
   in dark mode. Checked at 4.5:1 or better for every hue in both themes. */
[class*="sik-tone--"] {
    --tone-bg:  color-mix(in srgb, var(--tone, #6B7280) 13%, var(--sik-surface));
    --tone-ink: color-mix(in srgb, var(--tone, #6B7280) 62%, var(--sik-ink));
}
.sik-tone--blue   { --tone: #2563EB; }
.sik-tone--cyan   { --tone: #0891B2; }
.sik-tone--green  { --tone: #16A34A; }
.sik-tone--orange { --tone: #EA580C; }
.sik-tone--red    { --tone: #DC2626; }
.sik-tone--violet { --tone: #7C3AED; }
.sik-tone--amber  { --tone: #D97706; }
.sik-tone--gray   { --tone: #6B7280; }
.sik-tone--navy   { --tone-bg: var(--sik-surface-2); --tone-ink: var(--sik-ink); }

/* --- search --------------------------------------------------------------- */
/* Sized down at narrower desktops so a long navigation cannot crowd it out. */
/* Below 1280px the navigation needs the room, so the field collapses to the
   search icon (which opens the same field beneath the header). */
/* With the navigation moved to its own row, the search field is the only
   elastic thing left between the logo and the actions, so it takes the slack
   instead of being squeezed to a 181px stub whose placeholder read "Search
   pho…". It also appears from 1024px now rather than 1280px: the row that made
   the inline field impossible on a laptop no longer exists. */
.sik-search { position: relative; flex: 1 1 auto; min-width: 200px; max-width: 560px; display: none; margin-inline: auto; }
@media (min-width: 1024px) { .sik-search { display: block; } }
@media (min-width: 1536px) { .sik-search { max-width: 640px; } }

/* The toggle exists only where the inline field does not. From 768 the header
   row carries the real field, so keeping the magnifier as well would be two
   controls on screen opening the same dialog. Ctrl/Cmd+K and "/" still reach
   the dialog at every width, so the shortcut never depends on this button. */
.sik-search-toggle { display: inline-flex; }
@media (min-width: 768px) { .sik-search-toggle { display: none; } }

/* The floating mini search the toggle opens.

   This used to be an in-flow block that expanded the header and pushed the whole
   page down. The layout jumped on open, and on a short screen the suggestions
   were pushed below the fold before they could be read. It is now a real
   floating dialog: fixed, above everything, over a backdrop — so opening it
   costs the page no layout at all, and the panel sizes itself against the
   viewport instead of against whatever room is left under the header.

   It is available at EVERY width. Above 1024px the inline field is still the
   faster path, but Ctrl/Cmd+K and "/" open this from anywhere, so the shortcut
   never lands on a control that happens to be hidden at that breakpoint. */
.sik-searchpop {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    display: grid;
    grid-template-rows: min-content;
    justify-items: center;
    /* Sits high on the viewport rather than centred: the suggestion list grows
       downward, and a vertically centred panel would push it off the bottom of
       a short screen the moment it filled. */
    padding: clamp(var(--sp-2), 4vh, var(--sp-11)) var(--sp-3) var(--sp-5);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.sik-searchpop[hidden] { display: none; }

.sik-searchpop__backdrop {
    position: fixed;
    inset: 0;
    background:  var(--sik-scrim);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
    animation: sik-pop-fade var(--dur-base) var(--ease-out) both;
}

/* Deliberately small. The panel is a search field and its results, nothing
   else — no heading, no keyboard-hint strip, no padding that makes a one-line
   control look like a page. */
.sik-searchpop__panel {
    position: relative;
    width: 100%;
    max-width: 520px;
    /* A grid item defaults to min-width:auto, which refuses to shrink below the
       min-content width of the search field inside it. At 320px that floored the
       panel at 522px — wider than the phone. This is the same class of bug as a
       bare 1fr track, and the same fix. */
    min-width: 0;
    background: var(--sik-elevated);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-pop);
    padding: var(--sp-2);
    /* Same entrance as the modal, same curve. A search dialog that springs past
       its resting size is the sort of motion that reads as cheap the third time
       you open it. */
    animation: sik-pop-in var(--dur-base) var(--ease-out) both;
}
@media (min-width: 1024px) {
    .sik-searchpop__panel { max-width: 560px; }
}

/* Field and close button share one row, so the dialog is exactly as tall as the
   control it contains. min-width:0 on the field stops the flex item refusing to
   shrink below its intrinsic width at 320px. */
.sik-searchpop__bar {
    display: flex;
    /* Not center: the search item grows to include the suggestion list, so
       centring left the close button floating halfway down the panel instead of
       sitting beside the field it closes. Both items are --tap tall, so aligning
       to the start lines them up exactly. */
    align-items: flex-start;
    gap: var(--sp-1);
}
.sik-searchpop__bar > .sik-search { flex: 1 1 auto; min-width: 0; max-width: none; display: block; }
/* The field and its input are flex items too, and each one that keeps its
   auto minimum re-inflates the panel. */
.sik-searchpop .sik-search__field { min-width: 0; }
.sik-searchpop .sik-search__input { min-width: 0; flex: 1 1 auto; }

/* On a phone the in-field Search button, the mic and the close button were three
   trailing controls competing for a 280px row, leaving the input 116px — narrower
   than most product names. The submit goes: the field carries
   enterkeyhint="search", so the keyboard's own key already submits, and the form
   still posts normally without JavaScript. It comes back from 480px where there
   is room for it. */
@media (max-width: 479px) {
    .sik-searchpop .sik-search__submit { display: none; }
}

.sik-searchpop__close {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--tap);
    height: var(--tap);
    border-radius: 50%;
    /* Was --sik-muted-ink, which is not a token this design system defines — an
       undefined custom property makes the whole declaration invalid, so the
       close glyph silently fell back to inherited body text. --sik-muted is
       already 4.83:1 on white and needs no deeper variant. */
    color: var(--sik-muted);
    background: transparent;
    transition: background var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.sik-searchpop__close:hover { background: var(--sik-soft); color: var(--sik-ink); }
.sik-searchpop__close svg { width: var(--icon-md); height: var(--icon-md); }

/* Inside the dialog the dropdown is not a dropdown — it is the body of the
   panel, so it sits in flow, drops the shadow that would only shade the surface
   it already sits on, and sizes against the viewport. */
.sik-searchpop .sik-suggest {
    position: static;
    margin-top: var(--sp-2);
    box-shadow: none;
    border: 0;
    border-top: 1px solid var(--sik-border);
    border-radius: 0;
    /* Leaves room for the panel's own offset and padding so the list never runs
       past the bottom of a short viewport. */
    max-height: min(56vh, 420px);
}
/* The results belong to the panel, not to the field, so they span the full
   width rather than stopping short where the close button begins. */
.sik-searchpop__bar > .sik-search > .sik-suggest,
.sik-searchpop .sik-suggest { width: auto; left: 0; right: 0; }

@keyframes sik-pop-fade { from { opacity: 0 } to { opacity: 1 } }
/* sik-pop-in was declared twice — here and again in the modal section. Keyframe
   names are global and the last declaration wins, so this one was dead and the
   search dialog was silently animating with the modal's curve. There is one
   definition now, in section 25, and both dialogs share it. */

/* The page must not scroll behind the open dialog. */
body.sik-searchpop-open { overflow: hidden; }

.sik-search__field {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    background: var(--sik-surface-2);
    border: 1px solid transparent;
    border-radius: 999px;
    padding-inline: var(--sp-4) var(--sp-1);
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                background var(--dur-base) var(--ease-out);
}
.sik-search__field:focus-within {
    border-color: var(--sik-line-strong);
    box-shadow: var(--sik-focus-ring);
    background: var(--sik-elevated);
}

.sik-search__leading {
    display: inline-flex;
    color: var(--sik-muted);
    flex: none;
    transition: color var(--dur-base) var(--ease-out);
}
/* The three glyphs inside the field were carrying `w-[18px] h-[18px]` — a
   Tailwind arbitrary-value class that this hand-written utility layer never
   defined, so they had no authored size at all. --icon-md IS 18px, and the
   component owns the size the same way .sik-suggest svg does. */
.sik-search__leading svg,
.sik-search__voice-idle svg,
.sik-search__submit svg {
    width: var(--icon-md);
    height: var(--icon-md);
}
.sik-search__field:focus-within .sik-search__leading { color: var(--sik-primary-ink); }

.sik-search__input {
    flex: 1;
    border: 0;
    background: transparent;
    padding-block: var(--sp-3);
    font-size: var(--fs-base);
    font-family: inherit;
    color: var(--sik-text);
    outline: none;
    min-width: 0;
    text-overflow: ellipsis;
}
.sik-search__input::placeholder { color: var(--sik-muted); }
/* Kill the browser's own clear affordance — we render our own. */
.sik-search__input::-webkit-search-cancel-button,
.sik-search__input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }

.sik-search__clear {
    /* Anchors the shared --tap extender below. Measured 26x26 at every
       breakpoint, the smallest interactive control on the storefront. */
    position: relative;
    width: 26px; height: 26px;
    border: 0;
    border-radius: 50%;
    background: color-mix(in srgb, var(--sik-muted) 16%, transparent);
    color: var(--sik-text);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex: none;
    transition: background var(--dur-fast) var(--ease-out);
}
.sik-search__clear:hover { background: color-mix(in srgb, var(--sik-muted) 28%, transparent); }
.sik-search__clear[hidden] { display: none; }

/* --- voice ---------------------------------------------------------------- */
.sik-search__voice {
    position: relative;
    width: 34px; height: 34px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--sik-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    flex: none;
    transition: background var(--dur-base) var(--ease-out),
                color var(--dur-base) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}
.sik-search__voice:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-search__voice:active { transform: scale(.92); }
.sik-search__voice.is-disabled { opacity: .4; cursor: not-allowed; }

.sik-search__voice-live { display: none; }
.sik-search__voice.is-listening {
    background: var(--sik-danger);
    color: #fff;
}
.sik-search__voice.is-listening .sik-search__voice-idle { display: none; }
.sik-search__voice.is-listening .sik-search__voice-live { display: inline-flex; }

/* A pulse ring so "we are recording" reads without relying on colour alone. */
.sik-search__voice.is-listening::before {
    content: "";
    position: absolute;
    inset: -4px;
    border-radius: 50%;
    border: 2px solid var(--sik-danger);
    animation: sik-mic-pulse 1.4s var(--ease-out) infinite;
}
@keyframes sik-mic-pulse {
    0%   { transform: scale(.85); opacity: .85; }
    100% { transform: scale(1.35); opacity: 0; }
}

.sik-search__voice.is-processing { background: var(--sik-primary); color: #fff; }

/* Four bars that bounce while the microphone is open. */
.sik-wave { display: inline-flex; align-items: center; gap: 2px; height: 16px; }
.sik-wave i {
    width: 2.5px;
    height: 5px;
    border-radius: 2px;
    background: currentColor;
    animation: sik-wave 1s var(--ease-in-out) infinite;
}
.sik-wave i:nth-child(2) { animation-delay: .15s; }
.sik-wave i:nth-child(3) { animation-delay: .3s; }
.sik-wave i:nth-child(4) { animation-delay: .45s; }
@keyframes sik-wave {
    0%, 100% { height: 5px; }
    50%      { height: 15px; }
}

.sik-search__voice-status {
    margin: var(--sp-2) 0 0;
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
    display: flex;
    align-items: center;
    gap: var(--sp-2);
}
.sik-search__voice-status.is-listening { color: var(--sik-danger); font-weight: var(--fw-semibold); }
.sik-search__voice-status.is-success   { color: var(--sik-success-ink); font-weight: var(--fw-semibold); }
.sik-search__voice-status.is-error,
.sik-search__voice-status.is-warn      { color: var(--sik-danger); }
.sik-search__voice-status[hidden]      { display: none; }

/* A tip pointing at the microphone, used on the search page's empty states.
   voice-search.js stamps .sik-search--no-voice on the wrapper when the browser
   has no Speech Recognition, so the tip disappears with the button rather than
   pointing at a control that is not there. */
.sik-search__hint {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    margin: var(--sp-3) 0 0;
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
    text-align: left;
}
.sik-search__hint svg {
    width: var(--icon-sm);
    height: var(--icon-sm);
    flex: none;
    color: var(--sik-primary-ink);
}
.sik-search--no-voice + .sik-search__hint { display: none; }

/* Reduced motion: keep the state legible, drop the movement. */
@media (prefers-reduced-motion: reduce) {
    .sik-search__voice.is-listening::before { animation: none; opacity: .5; }
    .sik-wave i { animation: none; height: 10px; }
}

.sik-search__submit {
    /* 34px of paint inside the pill, --tap of target via the section-2b
       extender — which needs a positioned box to centre against. */
    position: relative;
    width: 34px; height: 34px;
    border-radius: 999px;
    background: var(--sik-cta);
    color: #fff;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    border: 0;
    cursor: pointer;
    flex: none;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    transition: background var(--dur-base) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.sik-search__submit:hover { background: var(--sik-cta-hover); }
.sik-search__submit:active { transform: scale(.94); }
.sik-search__submit-label { display: none; }

/* --- variants ------------------------------------------------------------- */
/* Page and drawer variants are full width and taller — they are the primary
   control on their screen, not a header accessory. */
.sik-search--page,
.sik-search--drawer {
    display: block;
    flex: 1 1 auto;
    max-width: none;
    min-width: 0;
    width: 100%;
}
.sik-search--page .sik-search__field,
.sik-search--drawer .sik-search__field {
    border-radius: var(--sik-radius);
    padding-inline: var(--sp-4) var(--sp-2);
    background: var(--sik-surface);
    box-shadow: var(--sik-shadow-soft);
}
.sik-search--page .sik-search__input,
.sik-search--drawer .sik-search__input {
    padding-block: var(--sp-4);
    font-size: var(--fs-md);
}
.sik-search--page .sik-search__submit,
.sik-search--drawer .sik-search__submit {
    width: auto;
    height: 40px;
    border-radius: var(--sik-radius-sm);
    padding-inline: var(--sp-4);
}
@media (min-width: 640px) {
    .sik-search--page .sik-search__submit-label,
    .sik-search--drawer .sik-search__submit-label { display: inline; }
}
.sik-search--page .sik-suggest,
.sik-search--drawer .sik-suggest { top: calc(100% + var(--sp-2)); }

/* --- suggestions dropdown -------------------------------------------------
   Rendered entirely by assets/js/search.js. Idle it shows recent + trending
   searches and a row of shelves to browse; typing swaps it for grouped
   product / category / brand rows. Every row is a real link, so the panel is
   navigable with a keyboard and usable with a screen reader.
   -------------------------------------------------------------------------- */
.sik-suggest {
    position: absolute;
    top: calc(100% + var(--sp-3));
    left: 0; right: 0;
    background: var(--sik-elevated);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-pop);
    max-height: min(72vh, 520px);
    overflow-y: auto;
    /* The panel scrolls on its own; the page underneath must not scroll with
       it once it reaches the end, and the page is never locked either. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    z-index: var(--z-header);
    display: none;
}
.sik-suggest.is-open { display: block; }

/* Icons are drawn inline by the JS, so their weight and size are set here
   rather than repeated on every element. */
.sik-suggest svg {
    width: var(--icon-md);
    height: var(--icon-md);
    stroke-width: var(--icon-stroke);
    stroke-linecap: round;
    stroke-linejoin: round;
    flex: none;
}

.sik-suggest__section { padding: var(--sp-2); }
.sik-suggest__section + .sik-suggest__section,
.sik-suggest__empty + .sik-suggest__section { border-top: 1px solid var(--sik-border); }

.sik-suggest__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3) var(--sp-1);
}
.sik-suggest__label {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-muted);
    line-height: var(--lh-normal);
}
.sik-suggest__label svg { width: var(--icon-xs); height: var(--icon-xs); }

.sik-suggest__action {
    border: 0;
    background: none;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--sik-primary-ink);
    padding: var(--sp-1) var(--sp-2);
    border-radius: var(--sik-radius-sm);
    transition: background var(--dur-fast) var(--ease-out);
}
.sik-suggest__action:hover { background: var(--sik-primary-soft); }

/* --- one result row ------------------------------------------------------- */
.sik-suggest__item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: var(--tap);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--sik-radius-sm);
    color: var(--sik-text);
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out);
}
.sik-suggest__item:hover { background: var(--sik-hover); color: var(--sik-text); }
/* The arrow-key cursor. It gets a ring as well as a tint so it is still
   findable for anyone who cannot separate the two backgrounds by colour. */
.sik-suggest__item.is-active {
    background: var(--sik-primary-soft);
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--sik-primary) 40%, transparent);
}

.sik-suggest__thumb {
    width: var(--tap);
    height: var(--tap);
    flex: none;
    object-fit: contain;
    padding: var(--sp-1);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-soft);
    border: 1px solid var(--sik-border);
}
.sik-suggest__tile {
    width: var(--tap);
    height: var(--tap);
    flex: none;
    display: grid;
    place-items: center;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-soft);
    border: 1px solid var(--sik-border);
    color: var(--sik-muted);
}

.sik-suggest__body { flex: 1; min-width: 0; }
.sik-suggest__name {
    display: block;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-suggest__meta {
    display: block;
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-suggest__side {
    flex: none;
    text-align: right;
    max-width: 40%;
}
.sik-suggest__price {
    display: block;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    color: var(--sik-text);
    white-space: nowrap;
}
.sik-suggest__mrp {
    font-size: var(--fs-2xs);
    color: var(--sik-muted);
    text-decoration: line-through;
    white-space: nowrap;
}
.sik-suggest__off {
    display: inline-block;
    margin-top: var(--sp-1);
    padding-inline: var(--sp-2);
    border-radius: 999px;
    background: color-mix(in srgb, var(--sik-success) 14%, transparent);
    color: var(--sik-success-ink);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    line-height: var(--lh-relaxed);
    white-space: nowrap;
}
.sik-suggest__chev { color: var(--sik-muted); flex: none; }
.sik-suggest__chev svg { width: var(--icon-sm); height: var(--icon-sm); }

/* The matched part of a suggestion. <mark> for the meaning, restyled because
   the browser's yellow highlight does not belong in a product list. */
.sik-suggest__hit {
    background: none;
    color: var(--sik-primary-ink);
    font-weight: var(--fw-bold);
}

/* --- chips (recent + trending + near-miss terms) --------------------------- */
.sik-suggest__chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-3) var(--sp-3);
}
.sik-suggest__chips--center { justify-content: center; }

/* A recent search is two controls in one pill (open it / forget it), so the
   pill is the wrapper and the buttons inside it are unstyled. */
.sik-suggest__chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    border: 1px solid var(--sik-border);
    background: var(--sik-soft);
    border-radius: 999px;
    overflow: hidden;
    transition: border-color var(--dur-fast) var(--ease-out),
                background var(--dur-fast) var(--ease-out);
}
.sik-suggest__chip:hover,
.sik-suggest__chip:focus-within,
.sik-suggest__chip.is-active {
    border-color: var(--sik-primary);
    background: var(--sik-primary-soft);
}
.sik-suggest__chipmain {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    padding: var(--sp-2) var(--sp-3);
    border: 0;
    background: none;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    line-height: var(--lh-normal);
    color: var(--sik-text);
    text-align: left;
}
.sik-suggest__chipmain svg { width: var(--icon-xs); height: var(--icon-xs); color: var(--sik-muted); }
.sik-suggest__chiptext { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sik-suggest__chipx {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--sp-6);
    height: var(--sp-6);
    margin-right: var(--sp-2);
    flex: none;
    border: 0;
    padding: 0;
    background: none;
    border-radius: 50%;
    cursor: pointer;
    color: var(--sik-muted);
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-suggest__chipx:hover {
    background: color-mix(in srgb, var(--sik-muted) 22%, transparent);
    color: var(--sik-text);
}
.sik-suggest__chipx svg { width: var(--icon-xs); height: var(--icon-xs); }

/* --- browse categories ---------------------------------------------------- */
/* A horizontal shelf rather than a wrapping grid: it keeps the idle panel
   short enough that the recent searches above it stay on screen. */
.sik-suggest__cats {
    display: flex;
    gap: var(--sp-3);
    padding: var(--sp-1) var(--sp-3) var(--sp-3);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
}
.sik-suggest__cat {
    flex: none;
    width: var(--sp-12);
    display: grid;
    justify-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-1);
    border-radius: var(--sik-radius-sm);
    scroll-snap-align: start;
    color: var(--sik-text);
    transition: background var(--dur-fast) var(--ease-out);
}
.sik-suggest__cat:hover { background: var(--sik-primary-soft); color: var(--sik-text); }
.sik-suggest__cat.is-active {
    background: var(--sik-primary-soft);
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--sik-primary) 40%, transparent);
}
.sik-suggest__cat img {
    width: var(--sp-9);
    height: var(--sp-9);
    object-fit: contain;
    padding: var(--sp-2);
    border-radius: 50%;
    background: var(--sik-soft);
    border: 1px solid var(--sik-border);
}
.sik-suggest__cat-name {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    text-align: center;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* --- loading, empty, error ------------------------------------------------ */
/* Loading rows are the shared skeleton (section 30b, sikSkel-suggest): real
   .sik-suggest__item boxes with bones inside, so they need no rules here. */
.sik-suggest__empty { padding: var(--sp-6) var(--sp-4) var(--sp-2); text-align: center; }
.sik-suggest__empty svg,
.sik-suggest__error svg {
    display: block;
    width: var(--icon-2xl);
    height: var(--icon-2xl);
    margin-inline: auto;
    color: var(--sik-muted);
}
.sik-suggest__empty-title {
    margin: var(--sp-3) 0 0;
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    overflow-wrap: anywhere;
}
.sik-suggest__empty-text {
    margin: var(--sp-2) auto 0;
    max-width: 40ch;
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
}
.sik-suggest__error {
    padding: var(--sp-6) var(--sp-4);
    text-align: center;
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--sik-danger-ink);
}
.sik-suggest__error p { margin: var(--sp-3) auto 0; max-width: 40ch; }
.sik-suggest__error .sik-btn { margin-top: var(--sp-3); }

/* --- footer --------------------------------------------------------------- */
/* Sticky so "see all results" stays reachable however far the list is scrolled. */
.sik-suggest__footer {
    position: sticky;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: var(--sp-3);
    border-top: 1px solid var(--sik-border);
    background: var(--sik-surface);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--sik-primary-ink);
}
.sik-suggest__footer svg { width: var(--icon-sm); height: var(--icon-sm); }
.sik-suggest__footer:hover { background: var(--sik-primary-soft); color: var(--sik-primary-ink); }
.sik-suggest__footer.is-active {
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
    box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--sik-primary) 40%, transparent);
}

/* Keyboard focus has to be visible on every control the panel draws. */
.sik-suggest a:focus-visible,
.sik-suggest button:focus-visible {
    outline: 2px solid var(--sik-primary);
    outline-offset: -2px;
    border-radius: var(--sik-radius-sm);
}

/* Touch needs the full 44px target; a mouse does not, and forcing it there
   would leave the chip rows twice as tall as they need to be. */
@media (pointer: coarse) {
    .sik-suggest__chipmain,
    .sik-suggest__action { min-height: var(--tap); }
    .sik-suggest__chipx { min-width: var(--sp-7); min-height: var(--sp-7); }
}

@media (max-width: 480px) {
    .sik-suggest { border-radius: var(--sik-radius); max-height: min(68vh, 460px); }
    .sik-suggest__side { max-width: 34%; }
}

/* --------------------------------------------------------------------------
   6c. Header: labelled actions, search shortcuts, browse and deals

   The pieces that turn the utility bar into a storefront header: a word under
   each action icon, a brand submit inside the search field with the store's
   own shortcuts beneath it, the solid "browse everything" button at the head
   of the navigation row, and the deals pill at its tail.

   Everything here is desktop-side. On a phone the bottom bar carries these
   destinations and the labels would cost a row of header height for words a
   thumb does not need.
   -------------------------------------------------------------------------- */

/* --- a word under the icon ------------------------------------------------ */
.sik-action__label { display: none; }

@media (min-width: 1024px) {
    /* The action becomes a two-row column. It keeps --tap as its MINIMUM, so a
       labelled control is never smaller than an unlabelled one. */
    .sik-action {
        flex-direction: column;
        gap: 2px;
        width: auto;
        min-width: var(--tap);
        height: auto;
        min-height: var(--tap);
        padding: var(--sp-1) var(--sp-2);
        border-radius: var(--sik-radius-sm);
    }
    /* Labels wait for 1280px. Between 1024 and 1280 the row is carrying a
       640px search field as well, and "Track order" and "Wishlist" ran into
       each other - two labels reading as one four-word phrase. */
    .sik-action__label {
        display: none;
        font-size: var(--fs-2xs);
        font-weight: var(--fw-medium);
        line-height: 1.2;
        letter-spacing: -0.005em;
        white-space: nowrap;
        color: inherit;
    }
    /* The badge is positioned against the icon, not the column, or it drifts
       out over the label. */
    .sik-action__badge { top: 0; right: var(--sp-1); }
    .sik-action--theme { justify-content: center; }
    .sik-action--theme .sik-action__label { display: none; }
}

/* The theme toggle and the search trigger have no label, so they would sit a
   label's height taller than their neighbours. Centre them instead. */
@media (min-width: 1024px) {
    .sik-actions { align-items: stretch; }
    .sik-action--theme,
    .sik-search-toggle { align-self: center; }
}

.sik-action--track { display: none; }
@media (min-width: 1024px) { .sik-action--track { display: inline-flex; } }

/* --- the search field's brand submit -------------------------------------- */
/* The header variant used to hide its submit entirely and rely on Enter. A
   visible button is the affordance a shopper looks for, and it is the only
   way to search at all on a touch keyboard that shows "Go" rather than a
   search key. */
.sik-search--header .sik-search__submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 36px; height: 36px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm);
    cursor: pointer;
    transition: box-shadow var(--dur-fast) var(--ease-out), filter var(--dur-fast) var(--ease-out);
}
.sik-search--header .sik-search__submit-label { display: none; }
.sik-search--header .sik-search__submit svg { width: var(--icon-sm); height: var(--icon-sm); }
@media (hover: hover) {
    .sik-search--header .sik-search__submit:hover { box-shadow: var(--sik-shadow-brand); filter: brightness(.96); }
}
.sik-search--header .sik-search__submit:active { transform: scale(.94); }

/* --- shortcuts under the field -------------------------------------------- */
.sik-search__popular {
    display: none;
    align-items: center;
    gap: var(--sp-1) var(--sp-3);
    margin-top: 5px;
    padding-inline: var(--sp-4);
    font-size: var(--fs-2xs);
    line-height: 1.4;
    color: var(--sik-muted);
    /* One line only: the row lives inside the header, and a second line of
       shortcuts would push the whole bar taller on a narrow desktop. */
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}
/* Only where the header search itself is on screen and there is room under it. */
@media (min-width: 1200px) { .sik-search--header .sik-search__popular { display: flex; } }

.sik-search__popular-label { font-weight: var(--fw-semibold); color: var(--sik-muted); }
.sik-search__popular a {
    color: var(--sik-ink-2);
    transition: color var(--dur-fast) var(--ease-out);
}
.sik-search__popular a:hover { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 2px; }
/* A dot between shortcuts, drawn rather than typed so it never gets read out. */
.sik-search__popular a + a::before {
    content: "";
    display: inline-block;
    width: 3px; height: 3px;
    margin-right: var(--sp-3);
    border-radius: 50%;
    background: var(--sik-line-strong);
    vertical-align: middle;
}

/* --- browse everything ----------------------------------------------------- */
/* The first mega item, rendered as the row's solid anchor. It keeps every
   .sik-nav__link behaviour - the panel still opens from it - and only the
   skin changes, so the menu builder stays in charge of what it says. */
.sik-nav__link--browse {
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    font-weight: var(--fw-bold);
    border-radius: var(--sik-radius-btn);
    padding-inline: var(--sp-4);
    box-shadow: var(--sik-shadow-brand-sm), var(--sik-lip);
}
.sik-nav__link--browse:hover,
.sik-nav__item.is-open > .sik-nav__link--browse {
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand), var(--sik-lip);
    filter: brightness(.97);
}
/* The shared underline would draw a gold line across a pink button. */
.sik-nav__link--browse::after { display: none; }
.sik-nav__link--browse .sik-nav__caret { opacity: .9; }

/* --- today's deals ---------------------------------------------------------- */
.sik-nav__deal {
    display: none;
    align-items: center;
    gap: var(--sp-2);
    margin-left: auto;
    padding: 0 var(--sp-4);
    min-height: 34px;
    border-radius: var(--sik-radius-pill);
    background: color-mix(in srgb, var(--sik-danger) 12%, var(--sik-surface));
    border: 1px solid color-mix(in srgb, var(--sik-danger) 28%, transparent);
    color: var(--sik-danger-ink);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    white-space: nowrap;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
@media (min-width: 1024px) { .sik-nav__deal { display: inline-flex; } }
.sik-nav__deal-icon { width: var(--icon-sm); height: var(--icon-sm); flex: none; }
@media (hover: hover) {
    .sik-nav__deal:hover {
        background: var(--sik-danger);
        border-color: transparent;
        color: #fff;
        box-shadow: 0 6px 16px -6px color-mix(in srgb, var(--sik-danger) 60%, transparent);
    }
}

/* The nav is a flex row so `margin-left: auto` can push the pill to the end.
   .sik-nav__list keeps its own width rather than stretching, or the menu items
   would space themselves across the whole bar. */
.sik-nav { display: none; }
@media (min-width: 1024px) {
    .sik-nav { display: flex; align-items: center; gap: var(--sp-3); width: 100%; }
    .sik-nav__list { flex: 0 1 auto; }
}

/* --------------------------------------------------------------------------
   6d. Two-row commerce header

   Admin > Appearance > Header > "E-commerce Mega Navigation" is the layout this
   storefront ships with: utility row (wordmark, search, actions) over a
   full-width department bar. The shipped rules put the navigation back inline
   above 1200px, which is right for a small menu and wrong for a catalogue -
   the row then has to hold a logo, a menu, a 640px search field and five
   labelled actions, and the search collapses to a stub. Under this style the
   department bar keeps its own row at every desktop width.
   -------------------------------------------------------------------------- */
@media (min-width: 1200px) {
    body.sik-hstyle--commerce .sik-header__row {
        grid-template-columns: auto minmax(0, 1fr) auto;
    }
    body.sik-hstyle--commerce .sik-header__nav {
        grid-row: 2;
        grid-column: 1 / -1;
        margin-inline: calc(var(--gutter) * -1);
        padding: 0 var(--gutter);
    }
    /* Undo the 1200px rule that strips the band, since the band is the point. */
    body.sik-hstyle--commerce .sik-header .sik-header__nav {
        background: var(--sik-hd-navbg, var(--sik-hd-navbg-auto, transparent));
        border-top: 1px solid var(--sik-hd-bc, var(--sik-hd-bc-auto, var(--sik-line)));
    }
    body.sik-hstyle--commerce .sik-header__row > .sik-search { justify-self: center; }
}

/* The strapline belongs under the wordmark, not beside it: beside it, it reads
   as a second navigation item. flex-wrap does the work, so the burger - which
   is the lead's other child below 1024px - keeps its place when it is there. */
@media (min-width: 768px) {
    body.sik-htagline .sik-header__lead { flex-wrap: wrap; row-gap: 0; }
    body.sik-htagline .sik-header__tagline {
        flex: 1 0 100%;
        margin-left: 0;
        padding-left: 2px;
        border-left: 0;
        font-size: var(--fs-2xs);
        letter-spacing: var(--ls-widest);
        text-transform: uppercase;
        font-weight: var(--fw-bold);
        color: var(--sik-primary-ink);
        line-height: 1.2;
    }
}

/* Five labelled actions need air between them or the words touch. */
@media (min-width: 1024px) {
    .sik-actions { gap: var(--sp-1); }
}
@media (min-width: 1280px) {
    .sik-actions { gap: var(--sp-2); }
}

/* --- corrections after seeing the two-row header render ------------------- */

/* The commerce style makes every nav link 56px tall so the department bar has
   presence. That height on the solid browse button turns it into a lozenge, so
   the button opts out and keeps a button's proportions. */
body.sik-hstyle--commerce .sik-nav__link--browse {
    min-height: 38px;
    align-self: center;
    border-radius: var(--sik-radius-sm);
}
.sik-nav__link--browse .sik-nav__text { font-size: var(--fs-sm); }

/* The department bar is the page's own surface, not a tinted band: the
   announcement strip above it is already wine and the hero below it is wine,
   and a third coloured bar between them leaves no white anywhere near the
   logo. The hairline does the separating. */
body.sik-hstyle--commerce .sik-header {
    --sik-hd-navbg-auto: var(--sik-surface);
}

/* Five labelled actions: "Track order" and "Wishlist" sit shoulder to shoulder
   without this, which reads as one four-word label. */
@media (min-width: 1280px) {
    .sik-actions { gap: var(--sp-3); }
    .sik-action { padding-inline: var(--sp-1); }
    .sik-action__label { display: block; }
}

/* --------------------------------------------------------------------------
   6e. Header, to the reference

   The corrections that take the header from "the right parts" to the intended
   design: a brighter strip with the clock in white cells, a search that ends
   in a labelled button, shortcuts as chips rather than a sentence, and solid
   brand pills at both ends of the department bar.
   -------------------------------------------------------------------------- */

/* --- the strip ------------------------------------------------------------
   Brighter than the wine panel below it, and bounded for contrast: the light
   end is the primary darkened 8%, which clears 5:1 against the white text it
   carries. The undarkened brand pink would sit at 4.3:1 and fail. */
.sik-announce,
body .sik-announce {
    background: var(--sik-hd-ann-bg, linear-gradient(90deg,
        var(--sik-accent) 0%,
        color-mix(in srgb, var(--sik-primary) 78%, var(--sik-accent)) 42%,
        color-mix(in srgb, var(--sik-primary) 92%, #000) 100%));
}

/* The clock, as white cells with the unit under the number - a bare 02:18:45
   needs decoding, and this band is read in passing. */
.sik-announce__cd { gap: var(--sp-1); }
.sik-announce__cell {
    display: inline-grid;
    justify-items: center;
    gap: 0;
    min-width: 34px;
    padding: 2px var(--sp-1) 3px;
    border-radius: var(--sik-radius-xs);
    background: #fff;
    line-height: 1.05;
}
.sik-announce__cell b {
    /* Reset the dark-cell styling this element inherits from section 5. */
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    font-size: var(--fs-xs);
    font-weight: var(--fw-black);
    color: var(--sik-accent);
    font-variant-numeric: tabular-nums;
}
.sik-announce__cell i {
    font-style: normal;
    font-size: 8px;
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: color-mix(in srgb, var(--sik-accent) 72%, #fff);
    opacity: 1;
}

/* --- the search ------------------------------------------------------------
   A labelled button rather than a bare glyph: "Search" is the widest target on
   the row and the one control a first-time shopper looks for by name. */
.sik-search--header .sik-search__submit {
    width: auto;
    min-width: 0;
    height: 40px;
    padding: 0 var(--sp-5);
    gap: var(--sp-2);
    border-radius: var(--sik-radius-pill);
}
@media (min-width: 1024px) {
    .sik-search--header .sik-search__submit-label {
        display: inline;
        font-size: var(--fs-sm);
        font-weight: var(--fw-bold);
    }
}
/* The field's own leading glyph carries the brand, so the row reads as one
   component rather than a grey box with a pink button stuck on the end. */
.sik-search--header .sik-search__leading { color: var(--sik-primary-ink); }

/* --- shortcuts as chips ---------------------------------------------------- */
.sik-search__popular { gap: var(--sp-2); padding-inline: var(--sp-2); }
.sik-search__popular-label { color: var(--sik-ink); font-weight: var(--fw-bold); }
.sik-search__popular a {
    padding: 2px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface-2);
    border: 1px solid var(--sik-line);
    color: var(--sik-ink-2);
    line-height: 1.6;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.sik-search__popular a:hover {
    background: color-mix(in srgb, var(--sik-primary) 10%, var(--sik-surface));
    border-color: color-mix(in srgb, var(--sik-primary) 34%, transparent);
    color: var(--sik-primary-ink);
    text-decoration: none;
}
/* Chips carry their own separation, so the drawn dot between links goes. */
.sik-search__popular a + a::before { content: none; }

/* --- the actions ----------------------------------------------------------- */
@media (min-width: 1024px) {
    .sik-action__icon { width: var(--icon-xl); height: var(--icon-xl); }
    .sik-action__label { font-size: var(--fs-xs); font-weight: var(--fw-semibold); }
    /* The count sits on the icon's shoulder. Brand-coloured, because a grey
       badge on a grey icon is invisible at a glance. */
    .sik-action__badge {
        background: var(--sik-grad-cta);
        color: var(--sik-on-cta);
        box-shadow: 0 0 0 2px var(--sik-bg);
    }
}

/* --- the department bar ----------------------------------------------------- */
/* Both ends of the bar are solid brand pills, so the row is bracketed by the
   two things a shopper is most likely to want: everything, and what is cheap. */
.sik-nav__deal {
    background: var(--sik-grad-cta);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm), var(--sik-lip);
    min-height: 36px;
}
.sik-nav__deal-icon { color: var(--sik-gold-2); }
@media (hover: hover) {
    .sik-nav__deal:hover {
        background: var(--sik-grad-cta);
        border-color: transparent;
        color: var(--sik-on-cta);
        box-shadow: var(--sik-shadow-brand), var(--sik-lip);
        filter: brightness(.97);
    }
}
body.sik-hstyle--commerce .sik-nav__link--browse {
    min-height: 42px;
    padding-inline: var(--sp-5);
    border-radius: var(--sik-radius);
    font-size: var(--fs-base);
}
.sik-nav__link--browse .sik-nav__icon { width: var(--icon-lg); height: var(--icon-lg); }


/* `body .sik-nav__link` in 6b sets colour at (0,1,1) and wins against the
   browse button's own (0,1,0) rule, so the label rendered brand-on-brand.
   Qualifying by body puts them on equal footing and source order decides. */
body .sik-nav__link--browse,
body .sik-nav__link--browse:hover,
body .sik-nav__item.is-open > .sik-nav__link--browse { color: var(--sik-on-cta); }
body .sik-nav__link--browse .sik-nav__text { max-width: none; }

/* The shortcut row is one line inside the search column, so the chips that do
   not fit are hidden rather than half-drawn at the edge. */
@media (max-width: 1439px) {
    .sik-search__popular a:nth-of-type(n+4) { display: none; }
}
@media (max-width: 1279px) {
    .sik-search__popular a:nth-of-type(n+3) { display: none; }
}


/* --- the shortcut chips, with an underline that wipes in ------------------
   The chip carries the shape; the underline carries the hover. It is a
   transform on a pseudo-element rather than text-decoration, so it animates
   on the compositor and the chip's text never shifts by a pixel. */
.sik-search__popular a {
    position: relative;
    overflow: hidden;
}
.sik-search__popular a::after {
    content: "";
    position: absolute;
    left: var(--sp-3);
    right: var(--sp-3);
    bottom: 3px;
    height: 1.5px;
    border-radius: 2px;
    background: var(--sik-grad-brand);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-base) var(--ease-out);
}
@media (hover: hover) {
    .sik-search__popular a:hover::after { transform: scaleX(1); }
    /* text-decoration would sit where the drawn rule is and double it up. */
    .sik-search__popular a:hover { text-decoration: none; }
}
.sik-search__popular a:focus-visible::after { transform: scaleX(1); }
.sik-noanim .sik-search__popular a::after { transition: none; }

/* Six short terms fit where three category names did, so the row shows the
   full set again at the width the search field has. */
@media (max-width: 1439px) { .sik-search__popular a:nth-of-type(n+6) { display: none; } }
@media (max-width: 1279px) { .sik-search__popular a:nth-of-type(n+4) { display: none; } }

/* --- a highlighted navigation item ---------------------------------------
   One item in the bar can be marked as the promotional one (Menu Builder >
   "Highlight"). It is brand-coloured and slightly bolder - never more than
   one, or the bar has no hierarchy left. */
body .sik-nav__link--hot { color: var(--sik-primary-ink); font-weight: var(--fw-bold); }
body .sik-nav__link--hot:hover { color: var(--sik-primary-ink); }
.sik-nav__link--hot .sik-nav__flame {
    width: var(--icon-sm);
    height: var(--icon-sm);
    color: var(--sik-gold);
    flex: none;
}


/* --- the department row has to fit, whatever the menu builder holds -------
   Nine top-level items plus a solid browse button plus the deals pill is more
   than a 1600px row can hold at full label width, and .sik-nav__list is
   deliberately nowrap (a scroll container here would clip every mega panel).
   So the list is allowed to SHRINK instead: flex items refuse to go below
   their content width until min-width is cleared, and the label already knows
   how to ellipsize. The two pills at either end never shrink. */
@media (min-width: 1024px) {
    .sik-nav__list { flex: 1 1 auto; min-width: 0; }
    /* The base rule is flex: 0 0 auto, which refuses to shrink at all - that
       is what let the row run under the deals pill instead of compressing. */
    .sik-nav__item { flex: 0 1 auto; min-width: 0; }
    .sik-nav__link { min-width: 0; }
    .sik-nav__item--mega,
    .sik-nav__item:has(.sik-nav__link--browse) { flex: 0 0 auto; }
    .sik-nav__deal { flex: 0 0 auto; margin-left: var(--sp-4); }
}

/* Below 1200px the row is tighter still, so the item padding steps down
   before any label starts losing characters. */
@media (min-width: 1024px) and (max-width: 1399.98px) {
    body .sik-nav__link { --sik-nav-pad: var(--sp-2); }
}

/* --------------------------------------------------------------------------
   7. Mobile menu / drawers / bottom bar
   -------------------------------------------------------------------------- */
.sik-burger { display: inline-flex; }
@media (min-width: 1024px) { .sik-burger { display: none; } }

.sik-overlay {
    position: fixed;
    inset: 0;
    background: var(--sik-scrim);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--dur-base) var(--ease-out), visibility var(--dur-base);
    z-index: var(--z-overlay);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
}
.sik-overlay.is-open { opacity: 1; visibility: visible; }

.sik-drawer {
    position: fixed;
    top: 0; bottom: 0;
    width: min(400px, 90vw);
    background: var(--sik-elevated);
    z-index: var(--z-drawer);
    display: flex;
    flex-direction: column;
    box-shadow: var(--sik-shadow-pop);
    overflow: hidden;
    /* A closed drawer is visibility:hidden, not merely translated off-screen,
       so its ~40 links leave the tab order and the accessibility tree. The
       delay equals the slide, so the close animation still plays. */
    visibility: hidden;
    transition: transform var(--dur-slow) var(--ease-out),
                visibility 0s linear var(--dur-slow);
}
.sik-drawer--left  { left: 0;  transform: translateX(-102%); border-radius: 0 var(--sik-radius-lg) var(--sik-radius-lg) 0; }
.sik-drawer--right { right: 0; transform: translateX(102%);  border-radius: var(--sik-radius-lg) 0 0 var(--sik-radius-lg); }
.sik-drawer.is-open {
    transform: translateX(0);
    visibility: visible;
    transition: transform var(--dur-slow) var(--ease-out), visibility 0s;
}

.sik-drawer__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    min-height: 64px;
    padding: calc(var(--sp-3) + env(safe-area-inset-top, 0px)) var(--sp-4) var(--sp-3) var(--sp-5);
    border-bottom: 1px solid var(--sik-line);
    flex: none;
}
.sik-drawer__head > a,
.sik-drawer__head > img { display: inline-flex; min-width: 0; }
.sik-drawer__head img { height: 30px; width: auto; }
.sik-drawer__head .sik-iconbtn { background: var(--sik-surface-2); border-color: transparent; }
.sik-drawer__head .sik-iconbtn:hover { background: var(--sik-press); }
.sik-drawer__title { font-size: var(--fs-lg); font-weight: var(--fw-bold); letter-spacing: var(--ls-tight); color: var(--sik-ink); }

.sik-drawer__body {
    flex: 1;
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}
.sik-drawer__foot {
    flex: none;
    border-top: 1px solid var(--sik-line);
    padding: var(--sp-4) var(--sp-5);
    /* The home-indicator inset, or the last button sits in iOS's swipe area. */
    padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom, 0px));
    background: var(--sik-elevated);
}

/* The menu drawer is an inset grouped list: rounded groups of rows on a
   slightly recessed ground, the pattern a phone's own settings screen uses. */
.sik-drawer--left .sik-drawer__body { padding: var(--sp-4) var(--sp-4) var(--sp-6); background: var(--sik-surface-2); }
.sik-drawer--left .sik-drawer__foot { background: var(--sik-surface-2); }

.sik-drawer__account { padding: 0; }
.sik-drawer__welcome {
    margin-bottom: var(--sp-3);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
}
.sik-drawer__authrow { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-2); }
.sik-drawer__authrow .sik-btn { min-height: var(--tap); min-width: 0; }
.sik-drawer__user {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: var(--tap);
    padding: var(--sp-3);
    border-radius: var(--sik-radius);
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    color: var(--sik-ink);
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-drawer__user:hover { background: var(--sik-hover); color: var(--sik-ink); }

.sik-drawer__total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-3);
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
    color: var(--sik-ink);
    font-variant-numeric: tabular-nums;
}
.sik-drawer__actions { display: grid; gap: var(--sp-2); }
.sik-drawer__help {
    display: flex;
    gap: var(--sp-3);
    align-items: center;
    font-size: var(--fs-sm);
    color: var(--sik-muted);
}
.sik-drawer__help svg { color: var(--sik-muted); flex: none; }
.sik-drawer__help a { color: var(--sik-ink); font-weight: var(--fw-semibold); }

/* --- grouped menu --------------------------------------------------------- */
.sik-mmenu__label {
    padding: var(--sp-5) var(--sp-2) var(--sp-2);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-mmenu {
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    overflow: hidden;
}
.sik-mmenu__item + .sik-mmenu__item { border-top: 1px solid var(--sik-line); }

.sik-mmenu__link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    width: 100%;
    min-height: 50px;
    padding: var(--sp-3) var(--sp-4);
    font-family: inherit;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    color: var(--sik-ink);
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-mmenu__link:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-mmenu__link:active { background: var(--sik-press); }
.sik-mmenu__link svg { flex: none; color: var(--sik-muted); transition: transform var(--dur-base) var(--ease-out); }
.sik-mmenu__link > span:not(.sik-mmenu__glyph),
.sik-mmenu__sublink > span:not(.sik-mmenu__glyph) {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-mmenu__item.is-open > .sik-mmenu__link { font-weight: var(--fw-semibold); }
.sik-mmenu__item.is-open > .sik-mmenu__link > svg { transform: rotate(180deg); color: var(--sik-ink); }

/* The glyph chosen in Admin > Menu Builder; it opts out of the caret spin. */
.sik-mmenu__glyph {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-surface-2);
    color: var(--sik-ink);
}
.sik-mmenu__item.is-open > .sik-mmenu__link .sik-mmenu__glyph svg { transform: none; }

.sik-mmenu__link .sik-nav__badge,
.sik-mmenu__sublink .sik-nav__badge { margin-left: var(--sp-2); }
.sik-mmenu__link .sik-nav__badge--lead,
.sik-mmenu__sublink .sik-nav__badge--lead { margin-left: 0; margin-right: var(--sp-2); }
.sik-nav__badge--drawer { top: 0; }

.sik-mmenu__link--danger { color: var(--sik-danger-ink); }
.sik-mmenu__link--danger svg { color: var(--sik-danger-ink); }
.sik-mmenu__link--danger:hover { background: var(--sik-danger-soft); color: var(--sik-danger-ink); }

.sik-mmenu__sub {
    display: none;
    padding-block: 0 var(--sp-2);
    border-top: 1px solid var(--sik-line);
    background: color-mix(in srgb, var(--sik-surface-2) 50%, var(--sik-elevated));
}
.sik-mmenu__item.is-open > .sik-mmenu__sub { display: block; }
.sik-mmenu__sublink {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    min-height: var(--tap);
    padding: var(--sp-2) var(--sp-4) var(--sp-2) var(--sp-6);
    font-size: var(--fs-base);
    color: var(--sik-ink-2);
    transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.sik-mmenu__sublink:hover { color: var(--sik-ink); background: var(--sik-hover); }
.sik-mmenu__sublink--deep { padding-left: var(--sp-9); font-size: var(--fs-sm); }

/* --- appearance switch in the drawer --------------------------------------- */
/* Segmented control: a small set of mutually exclusive options that apply
   immediately. Buttons with aria-pressed rather than radios, because choosing
   IS the action. */
.sik-segmented {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    gap: 2px;
    padding: 3px;
    border-radius: var(--sik-radius);
    background: var(--sik-press);
}
.sik-segmented__opt {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: 40px;
    padding: 0 var(--sp-2);
    border: 0;
    border-radius: calc(var(--sik-radius) - 3px);
    background: transparent;
    color: var(--sik-muted);
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-segmented__opt { min-height: var(--tap); } }
.sik-segmented__opt:hover { color: var(--sik-ink); }
.sik-segmented__opt[aria-pressed="true"] {
    background: var(--sik-elevated);
    color: var(--sik-ink);
    box-shadow: var(--sik-shadow-soft);
}
.sik-drawer__theme .sik-mmenu__label { padding-top: var(--sp-5); }

/* --- bottom navigation on phones ------------------------------------------ */
.sik-bottomnav {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    display: grid;
    /* The track count travels with the markup: the wishlist cell is optional. */
    grid-template-columns: repeat(var(--bottomnav-cols, 5), minmax(0, 1fr));
    z-index: var(--z-header);
    padding-bottom: env(safe-area-inset-bottom, 0);
    background: var(--sik-elevated);
    border-top: 1px solid var(--sik-line);
}
@supports ((-webkit-backdrop-filter: blur(1px)) or (backdrop-filter: blur(1px))) {
    .sik-bottomnav {
        background: color-mix(in srgb, var(--sik-elevated) 88%, transparent);
        -webkit-backdrop-filter: saturate(1.6) blur(16px);
        backdrop-filter: saturate(1.6) blur(16px);
    }
}
@media (min-width: 1024px) { .sik-bottomnav { display: none; } }

.sik-bottomnav__item {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    min-height: var(--sik-bottomnav-h);
    padding: var(--sp-2) var(--sp-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    color: var(--sik-muted);
    -webkit-tap-highlight-color: transparent;
    transition: color var(--dur-fast) var(--ease-out);
}
.sik-bottomnav__item:hover { color: var(--sik-ink); }
.sik-bottomnav__item > span:not(.sik-action__count) {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-bottomnav__item.is-current { color: var(--sik-ink); font-weight: var(--fw-semibold); }
.sik-bottomnav__item.is-current::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 22px;
    height: 2px;
    margin-left: -11px;
    border-radius: 0 0 2px 2px;
    background: var(--sik-primary);
}
.sik-bottomnav__item .sik-action__count { top: 4px; right: 50%; margin-right: -22px; }
/* The bar must not cover page content. --sik-bottomnav-h is 0 above 1024px. */
body.sik-has-bottomnav { padding-bottom: var(--sik-bottomnav-space); }

/* --------------------------------------------------------------------------
   7b. Mobile header and navigation

   The phone's own header, not a squeezed desktop one. Three rows of chrome and
   no more: the offer strip, a bar carrying the burger, the wordmark and the two
   actions that hold a number, and a real search field under them. Below that
   the page starts.

   The bar shrinks as the page scrolls (header.js writes .is-stuck), which buys
   back ~14px of screen without the content ever jumping - only padding and the
   logo's height move, both of them cheap.

   Everything here is scoped under 1024px. Above it the desktop header in 6/6b
   is untouched.
   -------------------------------------------------------------------------- */
/* The phone's search row is hidden by default and switched on by the block
   below. It is rendered at every width so the admin's header preview can
   change width inside a live iframe with no reload - but a desktop must never
   paint it, or the page carries two search fields: the real one in the header
   row and a second copy under the category bar. */
.sik-header__searchrow { display: none; }

@media (max-width: 1023.98px) {

    /* --- the bar ----------------------------------------------------------- */
    .sik-header {
        /* A hairline is not enough separation once the page scrolls under a
           translucent bar; the shadow is what makes it read as a layer. */
        box-shadow: 0 1px 0 var(--sik-line);
    }
    .sik-header.is-stuck { box-shadow: var(--sik-shadow-card); }

    .sik-header__row {
        gap: var(--sp-2);
        min-height: 56px;
        transition: min-height var(--dur-base) var(--ease-out);
    }
    .sik-header.is-stuck .sik-header__row { min-height: 50px; }
    body .sik-logo img { transition: height var(--dur-base) var(--ease-out); }
    .sik-header.is-stuck .sik-logo img { height: 28px; }
    .sik-noanim .sik-header__row,
    .sik-noanim .sik-logo img { transition: none; }

    /* The wordmark takes the middle of the bar. The burger and the two actions
       are both fixed-width, so centring it costs nothing and reads as the
       brand's own bar rather than a toolbar with a logo stuck on the left. */
    .sik-header__lead { flex: 1 1 auto; }
    body.sik-htagline .sik-header__tagline { display: none; }

    /* --- the two actions a phone keeps -------------------------------------- */
    /* Everything in the cluster is desktop-side unless it is marked --phone.
       The bottom bar and the drawer carry the rest, and five icons across a
       375px bar leaves the wordmark 90px. */
    /* Qualified up to (0,2,1) deliberately. Two shipped rules assume the old
       arrangement and would otherwise win on specificity:
       `body.sik-has-bottomnav .sik-action--wishlist` hid the heart because the
       bottom bar carried it, and `body.sik-hsearch--1024 .sik-search-toggle`
       showed the magnifier because the field was behind a dialog. Both
       premises changed: the heart is a header action with a count again, and
       the field is on screen. */
    body .sik-actions > *:not(.sik-action--phone) { display: none; }
    body .sik-actions .sik-search-toggle { display: none; }
    .sik-actions { gap: 2px; }
    body .sik-actions .sik-action--phone {
        display: inline-flex;
        width: var(--tap);
        height: var(--tap);
        min-width: 0;
        padding: 0;
        border-radius: 50%;
    }
    .sik-action--phone .sik-action__label { display: none; }
    .sik-action--phone .sik-action__icon { width: var(--icon-lg); height: var(--icon-lg); }
    .sik-action--phone .sik-action__badge {
        top: 4px;
        right: 2px;
        background: var(--sik-grad-cta);
        color: var(--sik-on-cta);
        box-shadow: 0 0 0 2px var(--sik-bg);
    }

    /* --- the search row ------------------------------------------------------ */
    .sik-header__searchrow {
        display: block;
        padding-bottom: var(--sp-3);
        /* The field is the last thing in the header, so it is what the shrink
           animation trims first. */
        transition: padding-bottom var(--dur-base) var(--ease-out);
    }
    .sik-header.is-stuck .sik-header__searchrow { padding-bottom: var(--sp-2); }
    .sik-noanim .sik-header__searchrow { transition: none; }

    /* The desktop row's own copy of the field stands down here, or the page
       carries two search boxes with two ids and two suggestion panels. */
    .sik-header__row > .sik-search { display: none; }

    .sik-header__searchrow .sik-search { display: block; width: 100%; }
    .sik-header__searchrow .sik-search__field {
        border-radius: var(--sik-radius-pill);
        background: var(--sik-surface-2);
        border: 1px solid var(--sik-line);
        padding-left: var(--sp-4);
        padding-right: 5px;
        min-height: 46px;
        transition: border-color var(--dur-fast) var(--ease-out),
                    box-shadow var(--dur-fast) var(--ease-out),
                    background-color var(--dur-fast) var(--ease-out);
    }
    /* The focus glow. :focus-within, so it lights while the caret is in the
       field rather than only on the wrapper. */
    .sik-header__searchrow .sik-search__field:focus-within {
        background: var(--sik-surface);
        border-color: color-mix(in srgb, var(--sik-primary) 55%, transparent);
        box-shadow: var(--sik-focus-ring);
    }
    .sik-header__searchrow .sik-search__input { font-size: var(--fs-base); }
    /* 16px or iOS zooms the whole page in on focus and never zooms back out. */
    @supports (-webkit-touch-callout: none) {
        .sik-header__searchrow .sik-search__input { font-size: 16px; }
    }
    .sik-header__searchrow .sik-search__submit {
        width: 38px; height: 38px;
        padding: 0;
        border-radius: 50%;
    }
    .sik-header__searchrow .sik-search__submit-label { display: none; }
    .sik-header__searchrow .sik-search__voice { width: 34px; height: 34px; }

    /* The shortcut chips, which the desktop rules hide below 1200px. */
    .sik-header__searchrow .sik-search__popular {
        display: flex;
        flex-wrap: nowrap;
        overflow-x: auto;
        scrollbar-width: none;
        padding-inline: 2px;
        margin-top: var(--sp-2);
        /* The row scrolls past the container's gutter, so it reads as "more
           this way" rather than as a row that happens to end. */
        scroll-snap-type: x proximity;
    }
    .sik-header__searchrow .sik-search__popular::-webkit-scrollbar { display: none; }
    .sik-header__searchrow .sik-search__popular a { flex: none; scroll-snap-align: start; }
    /* The desktop rules hide the later chips; on a scrolling row they all fit. */
    .sik-header__searchrow .sik-search__popular a:nth-of-type(n+3) { display: inline-block; }
    .sik-header.is-stuck .sik-header__searchrow .sik-search__popular { display: none; }
}

/* --------------------------------------------------------------------------
   7c. The drawer, as the phone's real navigation
   -------------------------------------------------------------------------- */

/* A blurred page behind the drawer, where the browser can do it cheaply. The
   scrim alone leaves the page legible enough to compete for attention. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-overlay.is-open {
        -webkit-backdrop-filter: blur(3px) saturate(.9);
        backdrop-filter: blur(3px) saturate(.9);
    }
}

.sik-drawer__head {
    border-bottom: 1px solid var(--sik-line);
    padding-bottom: var(--sp-3);
}

/* --- the sign-in card ------------------------------------------------------ */
.sik-drawer__signin {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand);
    min-height: var(--tap);
}
.sik-drawer__signin:hover { color: var(--sik-on-cta); }
.sik-drawer__signin:active { transform: scale(.99); }
.sik-drawer__signin-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 42px; height: 42px;
    flex: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, .22);
    border: 1px solid rgba(255, 255, 255, .3);
}
.sik-drawer__signin-text { display: grid; gap: 1px; min-width: 0; }
.sik-drawer__signin-text b { font-size: var(--fs-base); font-weight: var(--fw-bold); }
.sik-drawer__signin-text i { font-style: normal; font-size: var(--fs-xs); opacity: .88; }
.sik-drawer__signin-go { width: var(--icon-md); height: var(--icon-md); flex: none; opacity: .9; }

/* --- the menu rows --------------------------------------------------------- */
.sik-mmenu { display: grid; gap: 2px; }
.sik-mmenu__link {
    border-radius: var(--sik-radius);
    min-height: var(--tap);
    gap: var(--sp-3);
    font-weight: var(--fw-medium);
}
/* The glyph tile. Section 7 already excludes .sik-mmenu__glyph from the
   label's flex:1, and an earlier draft here re-stated `> span` without that
   exclusion - which stretched the icon's own span into a 100px pill. The
   tile is styled by its class, so the label rule is left alone. */
.sik-mmenu__link > .sik-mmenu__glyph,
.sik-mmenu__sublink > .sik-mmenu__glyph {
    width: 34px;
    height: 34px;
    flex: none;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: color-mix(in srgb, var(--sik-primary) 9%, var(--sik-surface-2));
    color: var(--sik-primary-ink);
    box-sizing: border-box;
}
.sik-mmenu__sublink > .sik-mmenu__glyph { width: 28px; height: 28px; border-radius: 8px; background: var(--sik-surface-2); color: var(--sik-ink-2); }
/* Section 7 already rotates the chevron on .is-open; it only needed a
   transition to animate rather than snap. */
.sik-mmenu__link > svg { transition: transform var(--dur-base) var(--ease-out); }
.sik-noanim .sik-mmenu__link > svg { transition: none; }

/* You are here. */
.sik-mmenu__link.is-current,
.sik-mmenu__link[aria-current="page"] {
    background: color-mix(in srgb, var(--sik-primary) 10%, transparent);
    color: var(--sik-primary-ink);
    font-weight: var(--fw-bold);
}
.sik-mmenu__link.is-current > svg:first-child {
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
}

/* --- the accordion ---------------------------------------------------------
   grid-template-rows animates where max-height guesses: a submenu with nine
   children and one with two both open at the same speed and neither is cut
   off by a height that turned out to be too small. */
/* The shipped accordion was display:none/block - correct, but it snapped.
   grid-template-rows animates to the content's real height without anyone
   having to guess a max-height, and .sik-mmenu__subinner keeps every child in
   ONE row so the 0fr actually collapses all of them. display is left alone,
   so a collapsed sub still leaves the tab order (visibility does that below). */
.sik-mmenu__sub {
    display: grid;
    grid-template-rows: 0fr;
    overflow: hidden;
    visibility: hidden;
    transition: grid-template-rows var(--dur-base) var(--ease-out), visibility 0s linear var(--dur-base);
}
.sik-mmenu__subinner { min-height: 0; overflow: hidden; }
.sik-mmenu__item.is-open > .sik-mmenu__sub { grid-template-rows: 1fr; visibility: visible; transition: grid-template-rows var(--dur-base) var(--ease-out), visibility 0s; }
.sik-noanim .sik-mmenu__sub { transition: none; }
@media (prefers-reduced-motion: reduce) { .sik-mmenu__sub { transition: none; } }

.sik-mmenu__sublink {
    min-height: var(--tap);
    border-radius: var(--sik-radius-sm);
    padding-left: var(--sp-4);
    gap: var(--sp-3);
    font-size: var(--fs-base);
}
.sik-mmenu__sublink > svg:first-child {
    width: 26px; height: 26px;
    padding: 5px;
    border-radius: 8px;
    background: var(--sik-surface-2);
    color: var(--sik-ink-2);
    flex: none;
    box-sizing: border-box;
}

/* --- the offer card at the foot -------------------------------------------- */
.sik-drawer__promo {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin: var(--sp-3) var(--sp-4) 0;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--sik-radius-lg);
    background: color-mix(in srgb, var(--sik-primary) 10%, var(--sik-surface));
    border: 1px dashed color-mix(in srgb, var(--sik-primary) 34%, transparent);
    color: var(--sik-ink);
    min-height: var(--tap);
}
.sik-drawer__promo:hover { color: var(--sik-ink); background: color-mix(in srgb, var(--sik-primary) 15%, var(--sik-surface)); }
.sik-drawer__promo-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px; height: 38px;
    flex: none;
    border-radius: 50%;
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-drawer__promo-text { display: grid; gap: 1px; min-width: 0; }
.sik-drawer__promo-text b { font-size: var(--fs-sm); font-weight: var(--fw-bold); }
.sik-drawer__promo-text i { font-style: normal; font-size: var(--fs-2xs); color: var(--sik-muted); }
.sik-drawer__promo-go { width: var(--icon-sm); height: var(--icon-sm); flex: none; color: var(--sik-primary-ink); }

/* --------------------------------------------------------------------------
   7d. Bottom bar
   -------------------------------------------------------------------------- */
.sik-bottomnav {
    background: var(--sik-glass-bg);
    border-top: 1px solid var(--sik-glass-line);
    box-shadow: 0 -6px 20px -12px rgba(16, 24, 40, .3);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-bottomnav {
        -webkit-backdrop-filter: var(--sik-glass-blur);
        backdrop-filter: var(--sik-glass-blur);
    }
}

.sik-bottomnav__item {
    position: relative;
    gap: 3px;
    border-radius: var(--sik-radius);
    transition: color var(--dur-fast) var(--ease-out);
}
.sik-bottomnav__item > svg { transition: transform var(--dur-base) var(--ease-spring); }
.sik-bottomnav__item:active > svg { transform: scale(.88); }

/* The active tab: brand ink, a heavier label, and a short bar above it that
   grows in. One indicator, not a filled pill - a filled tab at this size
   swallows the icon. */
.sik-bottomnav__item.is-current { color: var(--sik-primary-ink); }
.sik-bottomnav__item.is-current > span:not(.sik-action__count) { font-weight: var(--fw-bold); }
.sik-bottomnav__item.is-current > svg { transform: translateY(-1px); }
.sik-bottomnav__item::before {
    content: "";
    position: absolute;
    top: 0;
    left: 50%;
    width: 26px;
    height: 3px;
    border-radius: 0 0 3px 3px;
    background: var(--sik-grad-cta);
    transform: translateX(-50%) scaleX(0);
    transition: transform var(--dur-base) var(--ease-out);
}
.sik-bottomnav__item.is-current::before { transform: translateX(-50%) scaleX(1); }
.sik-noanim .sik-bottomnav__item::before,
.sik-noanim .sik-bottomnav__item > svg { transition: none; }

.sik-bottomnav__item .sik-action__count {
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: 0 0 0 2px var(--sik-bg);
}

/* --------------------------------------------------------------------------
   7e. The burger, as a three-line morph

   The button ships as an icon; these rules replace the glyph with three spans
   drawn in CSS so it can become an X. The icon inside stays for anything that
   has not been upgraded - it is simply hidden once the lines exist.
   -------------------------------------------------------------------------- */
.sik-burger {
    position: relative;
    align-items: center;
    justify-content: center;
    width: var(--tap);
    height: var(--tap);
    border-radius: 50%;
}
.sik-burger__box {
    position: relative;
    display: block;
    width: 18px;
    height: 12px;
}
.sik-burger__box i {
    position: absolute;
    left: 0;
    width: 100%;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: transform var(--dur-base) var(--ease-out),
                opacity var(--dur-fast) var(--ease-out);
}
.sik-burger__box i:nth-child(1) { top: 0; }
.sik-burger__box i:nth-child(2) { top: 5px; }
.sik-burger__box i:nth-child(3) { top: 10px; }
/* Open: the outer two rotate into an X and the middle one fades out. */
.sik-burger[aria-expanded="true"] .sik-burger__box i:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.sik-burger[aria-expanded="true"] .sik-burger__box i:nth-child(2) { opacity: 0; transform: scaleX(.4); }
.sik-burger[aria-expanded="true"] .sik-burger__box i:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }
.sik-burger:active { background: var(--sik-press); }
.sik-noanim .sik-burger__box i { transition: none; }
@media (prefers-reduced-motion: reduce) { .sik-burger__box i { transition: none; } }

/* --------------------------------------------------------------------------
   7f. The offer strip on a phone

   Compact, and the message keeps its half of the bar: the strip is the first
   thing on the page, and a store that leads with a dismissible coupon and no
   sentence reads like an advert rather than a shop.
   -------------------------------------------------------------------------- */
@media (max-width: 560px) {
    .sik-announce { font-size: var(--fs-xs); }
    .sik-announce__bar { min-height: 34px; }
    .sik-announce__row,
    a.sik-announce__item { min-height: 34px; }
    /* Reinstated: 7f's whole point is that the ticker stays. It shares the
       strip with the code chip, and the clock steps aside instead. */
    .sik-announce__bar:has(.sik-announce__promo) .sik-announce__msgs { display: block; }
    .sik-announce__promo { flex: 0 1 auto; min-width: 0; justify-content: flex-end; }
    .sik-announce .sik-announce__cd { display: none; }
    .sik-announce__code { padding-inline: var(--sp-2); }
    .sik-announce__msgs { flex: 1 1 40%; }
}
@media (max-width: 379px) {
    /* At the very bottom of the range the code is worth more than the sentence. */
    .sik-announce__bar:has(.sik-announce__promo) .sik-announce__msgs { display: none; }
}

/* --------------------------------------------------------------------------
   8. Hero

   A deep wine panel, lit from two corners: the serif flourish and headline on
   the left with the store's promises under them, the store's own photography
   on the right. A banner with an uploaded image shows that image; one still
   carrying the shipped placeholder art shows a mosaic of real product photos
   instead (see banner_image_is_stock()).

   Everything on the panel is white or gold, so `.sik-onwine` switches the type
   roles once at the top rather than each component carrying its own override.
   Each slide brings its copy and its photography in on a short stagger, and
   the media column drifts against the scroll where the browser can do that
   natively.
   -------------------------------------------------------------------------- */
.sik-hero { position: relative; padding-top: var(--sp-4); }
@media (min-width: 768px) { .sik-hero { padding-top: var(--sp-6); } }

/* Type roles for anything sitting on the wine panel. One place, so a component
   dropped onto it does not need to know it is on a dark background. */
.sik-onwine { --sik-ink: #fff; --sik-ink-2: rgba(255, 255, 255, .82); --sik-muted: rgba(255, 255, 255, .7); }
.sik-onwine .sik-hero__title,
.sik-onwine .sik-hero__text { color: inherit; }

.sik-hero__frame {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    border-radius: var(--sik-radius-lg);
    background: var(--sik-grad-wine);
    color: #fff;
    box-shadow: var(--sik-shadow-lift);
    /* One cell, every slide in it: the frame is always as tall as the TALLEST
       slide, so an autoplay advance cannot move the page. The measurement
       behind this is on .sik-hero__slide below. */
    display: grid;
    grid-template-areas: "sik-hero-slide";
}
/* Two pools of colour bleeding in from opposite corners — the depth in the
   panel is light, not another gradient. One element, painted once. */
.sik-hero__frame::before {
    content: "";
    position: absolute;
    inset: -20% -10%;
    z-index: 0;
    background:
        radial-gradient(38% 52% at 12% 8%, color-mix(in srgb, var(--sik-primary) 46%, transparent) 0%, transparent 68%),
        radial-gradient(42% 56% at 82% 96%, color-mix(in srgb, var(--sik-gold) 26%, transparent) 0%, transparent 66%);
    filter: blur(6px);
    pointer-events: none;
}
/* Gold hairline along the top edge of the panel. */
.sik-hero__frame::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--sik-grad-gold);
    z-index: 2;
    pointer-events: none;
}
/* Every slide occupies the SAME grid cell, so the frame is always as tall as
   the tallest slide and an advance cannot change the page's height.
   `display: none` made the frame exactly as tall as whichever slide was
   showing, and the carousel advances on a 6s timer: each advance re-laid out
   every section below the hero.

   Measured on the mobile homepage, headless Edge at 390x844, autoplay running
   (reduced motion deliberately NOT forced, because the autoplay IS the thing
   under test), layout-shift observer installed before first paint, the
   viewport parked 420px down so the frame's bottom edge and the section under
   it are on screen — a shift whose impact region is below the fold scores 0
   however far it moved the document, which is what made this so hard to pin
   down. Sitting still for 20s, three runs:

       one slide in flow (the old rule)   0.0357 / 0.0356 / 0.0356
       every slide in the cell            0.0007 / 0.0009 / 0.0008

   Before, each autoplay tick is a 0.0135 entry against
   section#w-home_shop_by_category — the section directly under the hero.
   After, the only entries left are the header's own 4px shrink when it
   sticks. A MutationObserver running alongside puts every entry 11ms after
   `is-active` moves from one .sik-hero__slide to the next, so the cause is
   the advance: not the fonts, not the images (every one carries width and
   height), and not the FAQ <details> the last round suspected — those never
   open on their own and contribute nothing.

   The three slides measure 911.6 / 896.7 / 871.2px at 390, so an advance
   moved the page by up to 40px; at 1366 they are 580.5 / 588.3 / 588.3 and
   the same advance measured 0.00015.

   visibility rather than display so the hidden slides still take up the cell:
   it keeps them out of the tab order and the accessibility tree exactly as
   display:none did. The cost is that the off-slide images now have a box in
   the viewport, so `loading="lazy"` stops deferring them and they arrive with
   the first paint rather than with the first advance — two extra requests
   here, and because the art is SVG that is 30 KB on desktop and 11 KB on
   mobile. It is also why the carousel no longer flashes an unpainted panel
   the first time it advances. */
.sik-hero__slide { position: relative; z-index: 1; grid-area: sik-hero-slide; visibility: hidden; }
.sik-hero__slide.is-active { visibility: visible; z-index: 2; animation: sik-fade-in var(--dur-slow) var(--ease-out) both; }
/* A short slide in a tall frame: centre the copy/media block rather than
   leaving the difference as a gap under it. */
.sik-hero__slide > .sik-hero__grid { align-content: center; min-height: 100%; }

/* Slide contents arrive one beat after the slide itself. */
.sik-hero__slide.is-active .sik-hero__copy > * { animation: sik-rise var(--dur-slow) var(--ease-out) both; }
.sik-hero__slide.is-active .sik-hero__copy > :nth-child(1) { animation-delay: .04s; }
.sik-hero__slide.is-active .sik-hero__copy > :nth-child(2) { animation-delay: .1s; }
.sik-hero__slide.is-active .sik-hero__copy > :nth-child(3) { animation-delay: .16s; }
.sik-hero__slide.is-active .sik-hero__copy > :nth-child(n+4) { animation-delay: .22s; }
.sik-hero__slide.is-active .sik-hero__media { animation: sik-pop var(--dur-slower) var(--ease-out) both; animation-delay: .08s; }
.sik-noanim .sik-hero__slide.is-active .sik-hero__copy > *,
.sik-noanim .sik-hero__slide.is-active .sik-hero__media { animation: none; }

/* minmax(0, …) on every track: a bare fr floors at its widest unbreakable
   child, which would scroll the page sideways at 320px. */
.sik-hero__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    gap: var(--sp-7);
    padding: var(--sp-8) var(--sp-5) var(--sp-7);
}
@media (min-width: 640px) { .sik-hero__grid { padding: var(--sp-9) var(--sp-7); } }
@media (min-width: 960px) {
    .sik-hero__grid {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
        gap: var(--sp-9);
        padding: var(--sp-10) var(--sp-10);
        min-height: 480px;
    }
}

.sik-hero__copy { max-width: 560px; }

/* The festive line above the headline. Sized against the headline, not a
   fixed px, so the pair scale together. */
.sik-hero__flourish {
    font-size: clamp(26px, 3.4vw, 40px);
    margin-bottom: var(--sp-1);
}

.sik-hero__title {
    /* Size comes from the display heading roles block in section 2. */
    font-weight: var(--fw-black);
    line-height: 1.04;
    letter-spacing: -0.035em;
    color: #fff;
    text-wrap: balance;
}
/* The second line is the warm one: gold into white, left to right. */
.sik-hero__title span {
    display: block;
    background: linear-gradient(95deg, var(--sik-gold-2) 0%, #FFE9F1 52%, #fff 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
    .sik-hero__title span { color: var(--sik-gold-2); -webkit-text-fill-color: currentColor; }
}

.sik-hero__text {
    margin-top: var(--sp-4);
    max-width: 46ch;
    font-size: var(--fs-md);
    line-height: var(--lh-relaxed);
    color: rgba(255, 255, 255, .84);
    text-wrap: pretty;
}
@media (min-width: 960px) { .sik-hero__text { font-size: var(--fs-lg); line-height: var(--lh-normal); } }
.sik-hero__cta { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-6); }
@media (max-width: 479px) {
    .sik-hero__cta .sik-btn { flex: 1 1 100%; }
}

/* --- the store's promises ------------------------------------------------
   Admin-owned rows (Content > Trust features, hero placement). Three at most:
   the fourth is the one nobody reads. */
.sik-hero__assure {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: var(--sp-3) var(--sp-5);
    margin: var(--sp-7) 0 0;
    padding: var(--sp-5) 0 0;
    border-top: 1px solid rgba(255, 255, 255, .16);
    list-style: none;
}
.sik-hero__assure-item {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    font-size: var(--fs-2xs);
    line-height: var(--lh-snug);
}
.sik-hero__assure-item svg { flex: none; width: var(--icon-md); height: var(--icon-md); color: var(--sik-gold-2); }
.sik-hero__assure-item b { display: block; font-weight: var(--fw-bold); color: #fff; font-size: var(--fs-xs); }
.sik-hero__assure-item i { font-style: normal; color: rgba(255, 255, 255, .68); }

.sik-hero__media { position: relative; min-width: 0; }

/* Parallax without a scroll listener. Chromium runs this off the compositor;
   everywhere else the @supports block simply does not apply and the column
   sits still, which is a perfectly good hero. */
@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) and (min-width: 960px) {
        .sik-hero__media {
            animation: sik-hero-drift linear both;
            animation-timeline: view();
            animation-range: entry 10% exit 90%;
        }
        .sik-noanim .sik-hero__media { animation: none; }
    }
}
@keyframes sik-hero-drift {
    from { transform: translate3d(0, 18px, 0); }
    to   { transform: translate3d(0, -18px, 0); }
}

.sik-hero__img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: var(--sik-radius);
    box-shadow: var(--sik-shadow-pop);
    /* The box is settled before the file arrives, not after it.
       widgets.php used to declare a flat width="860" height="680" against art
       that ships at 900x700, and an operator can upload anything at all, so
       the moment the image decoded Chromium swapped the declared ratio for
       the real one and the box changed height. It now reads the real size off
       the file (widget_img_dims()), so the markup no longer lies - but the
       uploaded art still can be any shape, which is what this rule is for.
       That went unnoticed while only the active slide was in flow: it is the
       one image that loads eagerly, so its correction landed before first
       paint. With every slide in the cell (see .sik-hero__slide) the other
       two load late, and their correction re-sized the frame 3.9px under the
       centred copy — measured 0.0036 on index.php at 1366, three runs, which
       is bigger than the advance this section set out to fix.
       aspect-ratio pins the box to what the markup promised and object-fit
       keeps whatever art lands undistorted inside it, so a decode can no
       longer change any layout. */
    /* The frame the design wants, which is also the shape of the art that
       ships - so nothing is cropped until somebody uploads a different one,
       and then object-fit below decides what happens rather than the box. */
    aspect-ratio: 900 / 700;
    object-fit: cover;
}

/* --- the discount medallion ----------------------------------------------
   The number is measured from the catalogue (catalogue_max_discount()), so it
   is a promise the shop floor can keep. Decorative in the accessibility tree:
   the same claim is in the copy beside it. */
.sik-hero__medal {
    position: absolute;
    top: -10px; left: -12px;
    z-index: 3;
    display: grid;
    place-content: center;
    justify-items: center;
    width: 92px; height: 92px;
    border-radius: 50%;
    background: var(--sik-grad-cta);
    border: 3px solid var(--sik-gold-2);
    box-shadow: var(--sik-shadow-brand-lg);
    color: #fff;
    text-align: center;
    transform: rotate(-8deg);
    animation: sik-float 6s var(--ease-in-out) infinite;
}
.sik-hero__medal small {
    font-size: 9px;
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-gold-soft);
    line-height: 1.2;
}
.sik-hero__medal b { font-size: 27px; font-weight: var(--fw-black); line-height: 1; letter-spacing: -0.03em; }
@media (min-width: 960px) { .sik-hero__medal { width: 104px; height: 104px; top: -14px; left: -26px; } .sik-hero__medal b { font-size: 31px; } }
@media (max-width: 479px) { .sik-hero__medal { width: 76px; height: 76px; top: -8px; left: -4px; } .sik-hero__medal b { font-size: 23px; } }
.sik-noanim .sik-hero__medal { animation: none; }
@media (prefers-reduced-motion: reduce) { .sik-hero__medal { animation: none; } }

/* Product-photo mosaic: one tall tile beside two stacked ones. */
.sik-hero__mosaic {
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    grid-template-rows: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3);
    aspect-ratio: 5 / 4;
}
.sik-hero__mosaic--1 { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); aspect-ratio: 1; }
.sik-hero__mosaic--2 { grid-template-rows: minmax(0, 1fr); aspect-ratio: 16 / 10; }
@media (max-width: 959px) { .sik-hero__mosaic { aspect-ratio: 4 / 3; } }

.sik-hero__tile {
    position: relative;
    display: block;
    min-height: 0;
    overflow: hidden;
    border-radius: var(--sik-radius);
    background: rgba(255, 255, 255, .06);
    border: 1px solid rgba(255, 255, 255, .12);
    box-shadow: 0 14px 30px -14px rgba(0, 0, 0, .6);
    transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.sik-hero__mosaic:not(.sik-hero__mosaic--2) .sik-hero__tile:first-child { grid-row: 1 / span 2; }
.sik-hero__tile img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slower) var(--ease-out);
}
@media (hover: hover) {
    .sik-hero__tile:hover { transform: translate3d(0, var(--lift), 0); box-shadow: 0 22px 44px -16px rgba(0, 0, 0, .72); z-index: 2; }
    .sik-hero__tile:hover img { transform: scale(1.06); }
}

/* One quiet glass label on the lead photo: what it is and what it costs. */
.sik-hero__tag {
    position: absolute;
    left: var(--sp-2);
    right: var(--sp-2);
    bottom: var(--sp-2);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--sik-radius-sm);
    background: rgba(12, 4, 8, .58);
    -webkit-backdrop-filter: saturate(1.4) blur(10px);
    backdrop-filter: saturate(1.4) blur(10px);
    border: 1px solid rgba(255, 255, 255, .14);
    color: #fff;
    font-size: var(--fs-xs);
    line-height: var(--lh-snug);
}
.sik-hero__tag-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: var(--fw-medium); }
.sik-hero__tag-price { flex: none; font-weight: var(--fw-bold); color: var(--sik-gold-2); }
.sik-hero__tile:not(:first-child) .sik-hero__tag { display: none; }
@media (max-width: 479px) { .sik-hero__tag { display: none; } }

/* --- controls ------------------------------------------------------------- */
.sik-hero__controls {
    position: absolute;
    left: 0; right: 0;
    bottom: var(--sp-4);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 3;
    pointer-events: none;
}
.sik-hero__dots { display: flex; align-items: center; gap: var(--sp-2); pointer-events: auto; }
.sik-hero__dot {
    position: relative;
    width: 8px; height: 8px;
    padding: 0;
    border: 0;
    border-radius: var(--sik-radius-pill);
    background: rgba(255, 255, 255, .38);
    cursor: pointer;
    transition: width var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}
/* The 44px hit area comes from the shared extender in section 2b. */
.sik-hero__dot:hover { background: rgba(255, 255, 255, .7); }
.sik-hero__dot.is-active { width: 26px; background: var(--sik-grad-gold); }

/* Arrows sit against the panel edges, the way a shopper expects a carousel to
   work. They are hidden where there is no pointer — a phone swipes instead
   (header.js binds the gesture). */
.sik-hero__arrow {
    position: absolute;
    top: 50%;
    z-index: 4;
    width: 38px; height: 38px;
    display: none;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .22);
    background: rgba(10, 3, 7, .46);
    -webkit-backdrop-filter: blur(8px);
    backdrop-filter: blur(8px);
    color: #fff;
    cursor: pointer;
    transform: translateY(-50%);
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
@media (hover: hover) and (min-width: 768px) { .sik-hero__arrow { display: inline-flex; } }
.sik-hero__arrow--prev { left: var(--sp-3); }
.sik-hero__arrow--next { right: var(--sp-3); }
.sik-hero__arrow:hover {
    background: var(--sik-grad-cta);
    border-color: transparent;
    box-shadow: var(--sik-shadow-brand);
}
.sik-hero__arrow:active { transform: translateY(-50%) scale(.94); }

/* The hero's primary call to action: white on wine, because on this panel the
   brand pink is the background and a pink button would vanish into it. */
.sik-btn--onwine {
    background: #fff;
    color: var(--sik-accent);
    box-shadow: 0 10px 24px -10px rgba(0, 0, 0, .6), var(--sik-lip);
}
.sik-btn--onwine:hover {
    background: var(--sik-gold-soft);
    color: var(--sik-accent);
    box-shadow: 0 16px 30px -12px rgba(0, 0, 0, .66), var(--sik-lip);
}
@media (hover: hover) { .sik-btn--onwine:hover { transform: translate3d(0, var(--lift-sm), 0); } }
.sik-btn--onwine:active { transform: translate3d(0, 1px, 0) scale(.99); box-shadow: inset 0 2px 5px rgba(0, 0, 0, .2); }


/* --- the hero runs edge to edge -------------------------------------------
   The slider is the page's opening statement, so it spans the viewport with
   square corners instead of sitting inside the container as a rounded card.
   Only the OUTER frame changes: .sik-hero__grid keeps its own generous
   padding, so the copy and the photography stay inset from the screen edge.
   The decorative overlays are re-squared with it, or a 18px radius would show
   as a lighter notch in each corner of a now-square panel. */
.sik-hero { padding-top: 0; }
.sik-hero > .sik-container {
    max-width: none;
    padding-inline: 0;
}
.sik-hero__frame {
    border-radius: 0;
    /* A card's lift makes no sense once there is no card edge to lift off. */
    box-shadow: none;
}
.sik-hero__frame::after { border-radius: 0; }

/* The panel bleeds; its CONTENT does not. Without this the headline starts at
   the panel's own padding (56px) while the wordmark and the department bar
   above it start at the container's edge (160px on a 1600px screen), so the
   page reads as two different grids stacked on each other. The controls are
   positioned against the panel, so they are brought in on the same measure. */
@media (min-width: 960px) {
    .sik-hero__grid {
        max-width: var(--sik-container);
        margin-inline: auto;
        padding-inline: var(--gutter);
    }
    .sik-hero__controls {
        left: max(var(--gutter), calc((100vw - var(--sik-container)) / 2 + var(--gutter)));
    }
}

/* --------------------------------------------------------------------------
   9. Trust strip

   Whatever promises the operator has entered in trust_features, shown as one
   surface card instead of a full-bleed hairline band. widget_trust() prints no
   .sik-section wrapper, so the card has to BE the grid and the section's
   vertical rhythm has to live on .sik-trust itself. The one decision worth
   knowing: each position takes a different status hue from the palette via
   nth-child, because four rows of identical grey icons read as one thing, and
   the -soft/-ink pairs are the only tints that re-point themselves in dark.
   -------------------------------------------------------------------------- */

/* The rhythm the widget cannot ask for. Admin > Homepage builder sets this
   row's padding to 'sm', but widget_trust() never calls widget_section_attrs()
   so no .sik-section--sm class is ever printed — --section-y-sm is that choice
   made here instead. */
.sik-trust { padding-block: var(--section-y-sm); }

/* The card. --sik-elevated is white in light and a step up from the page in
   dark, so the "white card" survives the theme without a second rule.
   --sik-shadow-card already carries its own 1px white ring in dark. */
.sik-trust__grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-elevated);
    box-shadow: var(--sik-shadow-card);
}

/* An odd count (3 or 5 rows) leaves the last promise alone in column one with
   a hole beside it. Let it take the full width instead — the phone item is
   already centred, so a full-width last row reads as deliberate. */
@media (max-width: 767.98px) {
    .sik-trust__item:nth-child(odd):last-child { grid-column: 1 / -1; }
}

/* From tablet up the count comes from the data (3 to 6 rows), so the track
   count comes from the width. auto-fit COLLAPSES the tracks it cannot fill,
   which is what keeps a short strip on one line instead of leaving gaps.
   minmax(0, 1fr) is invalid here — auto-fit needs a definite minimum — and a
   bare 180px floor would push the card past the viewport at 320. min() gives
   the floor a definite value and an escape hatch in the same breath. */
@media (min-width: 768px) {
    .sik-trust__grid {
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 180px), 1fr));
        padding: var(--sp-5);
    }
}

/* The live strip is four promises, and between 768 and 1024 a 180px floor only
   leaves room for three tracks — so the fourth drops onto a line of its own.
   Count the children and split them 2x2 in that band; even beats orphaned. */
@media (min-width: 768px) and (max-width: 1023.98px) {
    .sik-trust__grid:has(> .sik-trust__item:nth-child(4):last-child) {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* --6 is printed only when there are exactly six rows, so it can be explicit
   where auto-fit cannot: 3+3 is the only even split that fits before the card
   is wide enough for all six, and pinning six at 1280 removes any dependence
   on where the auto-fit maths happens to land. */
@media (min-width: 768px)  { .sik-trust__grid--6 { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .sik-trust__grid--6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } }

/* Per-position hue. Five slots so 3, 4, 5 and 6 rows all stay colourful, and
   only the -soft/-ink pairs are used: they are measured at 4.5:1 in BOTH
   themes and re-point themselves under [data-theme="dark"]. --sik-gold would
   have been the obvious warm hue and is deliberately not here — gold-soft and
   gold-ink are fixed hex with no dark counterpart, so a gold circle would stay
   cream on a near-black card. --sik-warning is the theme-aware gold. */
.sik-trust__item:nth-child(5n + 1) { --trust-tint: var(--sik-primary-soft); --trust-ink: var(--sik-primary-ink); }
.sik-trust__item:nth-child(5n + 2) { --trust-tint: var(--sik-success-soft); --trust-ink: var(--sik-success-ink); }
.sik-trust__item:nth-child(5n + 3) { --trust-tint: var(--sik-warning-soft); --trust-ink: var(--sik-warning-ink); }
.sik-trust__item:nth-child(5n + 4) { --trust-tint: var(--sik-info-soft);    --trust-ink: var(--sik-info-ink); }
.sik-trust__item:nth-child(5n + 5) { --trust-tint: var(--sik-danger-soft);  --trust-ink: var(--sik-danger-ink); }

/* The cell. <a> when the row carries a link and <div> when it does not, so
   everything here has to hold for both tags; only the hover tint below is
   scoped to the anchor. Stacked and centred on phones. */
.sik-trust__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    padding: var(--sp-3) var(--sp-2);
    border-radius: var(--sik-radius);
    text-align: center;
    color: var(--sik-ink);
    transition: background-color var(--dur-fast) var(--ease-out);
}

/* Icon beside text from tablet. align-items: center is safe across the row
   because the grid stretches every cell to the tallest one, so all the circles
   land on the same optical line even when one subtitle wraps to three lines
   and its neighbour wraps to none. */
@media (min-width: 768px) {
    .sik-trust__item {
        flex-direction: row;
        align-items: center;
        gap: var(--sp-3);
        padding: var(--sp-3);
        text-align: start;
    }
}

/* The circle. flex: none because .sik-ico already refuses to shrink, and a
   squashed circle around a full-size glyph is worse than a wrapped title. */
.sik-trust__icon {
    display: inline-grid;
    place-items: center;
    flex: none;
    width: 40px;
    height: 40px;
    border-radius: var(--sik-radius-pill);
    background: var(--trust-tint);
    color: var(--trust-ink);
    transition: background-color var(--dur-fast) var(--ease-out);
}

/* icon() hard-codes "w-5 h-5", which svg.w-5 sizes at --icon-lg (20px). 18px
   sits better inside a 40px circle; this is (0,1,1) like svg.w-5 but section 9
   is later in the file, so it wins on order — see the 2b. Icon system note. */
.sik-trust__icon svg { width: var(--icon-md); height: var(--icon-md); }

/* The title/subtitle wrapper has no class of its own — the renderer emits a
   bare <span> — so it is reachable only as the icon's sibling. min-width: 0
   lets it shrink below its longest word inside the flex row, and overflow-wrap
   catches a title with no space in it at all: the column is about 104px wide
   at 320 and title is a VARCHAR(120). */
.sik-trust__icon + span {
    display: block;
    min-width: 0;
    overflow-wrap: anywhere;
}
.sik-trust__title {
    display: block;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
}
.sik-trust__sub {
    display: block;
    margin-top: 2px;
    font-size: var(--fs-2xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
}

/* A linked promise is a navigation target. The circle plus padding already
   measures 64px, but a row saved with no subtitle would rest on the title's
   line height alone, so the floor is stated rather than inherited. */
@media (pointer: coarse) {
    a.sik-trust__item { min-height: var(--tap); }
}

/* Hover swaps the cell and the circle: the whole cell takes the position's
   tint and the circle drops back to the card colour. The glyph keeps
   --trust-ink, which the palette already guarantees against --sik-elevated in
   both themes, so the swap costs no second contrast budget.
   The global a:hover tints every link to --sik-primary-ink, which here would
   put a pink title under an amber or blue circle — two accents arguing inside
   one cell — so the colour is held at --sik-ink.
   All of it sits inside (hover: hover) so a tap does not leave a phone cell
   tinted until the next tap lands somewhere else. */
@media (hover: hover) {
    a.sik-trust__item:hover { background: var(--trust-tint); color: var(--sik-ink); }
    a.sik-trust__item:hover .sik-trust__icon { background: var(--sik-elevated); }
    a.sik-trust__item:hover .sik-trust__sub { color: var(--sik-ink-2); }
}

/* The sub-line is the one thing the tint breaks. --sik-muted measures 4.19 to
   4.38:1 on the five light-mode -soft tints and 4.02:1 on the pressed cell —
   under AA for an 11px line, even though it clears it at 4.83:1 on the resting
   card. --sik-ink-2 is the next step up that still reads as secondary, and it
   is mixed from --sik-text and --sik-surface, so dark mode follows for free.
   Dark was never the failing side (6.4:1 muted) but takes the same shade so
   there is one rule, not two. */
a.sik-trust__item:active .sik-trust__sub,
a.sik-trust__item:focus-visible .sik-trust__sub { color: var(--sik-ink-2); }

/* Touch gets its feedback from :active instead, since the block above never
   fires there. */
a.sik-trust__item:active { background: var(--sik-press); }

/* Keyboard focus earns the same tint as hover, so the outline is not the only
   thing marking the cell. The ring is tucked inside the cell because the grid
   gap is the only space there is between one cell and the next. */
a.sik-trust__item:focus-visible {
    background: var(--trust-tint);
    outline-offset: -2px;
}
a.sik-trust__item:focus-visible .sik-trust__icon { background: var(--sik-elevated); }

/* Settings > Theme > enable animations. prefers-reduced-motion is already
   answered by the global --dur-* override, but .sik-noanim is a separate
   switch and has to be answered separately. */
.sik-noanim .sik-trust__item,
.sik-noanim .sik-trust__icon { transition: none; }

/* --------------------------------------------------------------------------
   10. Category tiles

   Two blocks. .sik-cat is the square photo tile, kept exactly as it was
   because the featured brand grid on /brands prints inline border-radius and
   object-fit onto .sik-cat__media and inline styles beat class rules — a
   circle imposed here would give square logos inside circular boxes.
   .sik-catrow is the homepage "shop by category" row: circular tiles that
   scroll and snap on a phone and spread across the container on a desktop.
   -------------------------------------------------------------------------- */
.sik-cat {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    min-width: 0;
    color: var(--sik-ink);
}
.sik-cat__media {
    position: relative;
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--sik-radius);
    background: var(--sik-media);
    border: 1px solid var(--sik-card-border);
}
.sik-cat__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slower) var(--ease-out);
}
/* A logo or icon is artwork, not a photograph: it sits inside the tile. */
.sik-cat__media--contain img { object-fit: contain; padding: 18%; }
@media (hover: hover) { .sik-cat:hover .sik-cat__media img { transform: scale(1.04); } }

.sik-cat__body {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-2);
    min-width: 0;
}
.sik-cat__name {
    min-width: 0;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
}
.sik-cat__count { flex: none; font-size: var(--fs-sm); color: var(--sik-muted); white-space: nowrap; }
.sik-cat:hover { color: var(--sik-ink); }
.sik-cat:hover .sik-cat__name { text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
@media (max-width: 479px) {
    .sik-cat__body { flex-direction: column; gap: 2px; }
    .sik-cat__name { font-size: var(--fs-base); }
    .sik-cat__count { font-size: var(--fs-xs); }
}

/* The circular row. One set of measurements for the whole block; the rail
   wrapper carries them too because the arrow buttons are positioned against
   the circle's centre and custom properties only travel downwards. */
.sik-catrow,
.sik-rail--cats {
    --cat-size: 64px;   /* the circle */
    --cat-w: 86px;      /* the tile, so the label wraps before it crowds its neighbour */
    --cat-ring: color-mix(in srgb, var(--sik-primary) 25%, var(--sik-line));
}
@media (min-width: 768px) {
    .sik-catrow,
    .sik-rail--cats { --cat-size: 72px; --cat-w: 96px; }
}

/* Carousel layout. The row IS the shared rail track, so scroll-snap, the
   hidden scrollbar and the arrow buttons all keep working untouched. Two
   changes: more room above, because a 2px ring plus the hover lift needs more
   than the track's 4px, and centring for a row too short to scroll — a handful
   of circles stranded at the left of a 1280px page reads as a bug. `safe`
   keeps the start of the row reachable once the tiles do overflow, and a
   browser that does not know the keyword drops the declaration and packs from
   the start exactly as before. */
.sik-catrow.sik-rail__track {
    padding-block: var(--sp-2) var(--sp-3);
    justify-content: safe center;
}

/* The rail sizes its slots as a share of the width — geometry meant for tall
   product cards. A circle is a fixed object, so the slot is just the tile and
   the track's own gap does the spacing; left to the rail's maths, a low "per
   view" count stretches a handful of circles across the whole page. */
.sik-catrow .sik-catrow__slot { width: var(--cat-w); }

@media (max-width: 767px) {
    /* Run the row to the screen edge instead of stopping at the container
       gutter, so the last circle is not stranded in a margin. The track is its
       own scroll container, so this cannot push the page sideways. */
    .sik-catrow.sik-rail__track {
        margin-inline: calc(var(--gutter) * -1);
        padding-inline: var(--gutter);
    }
}

/* Grid layout — the builder's other option for this widget. Deliberately not
   .sik-grid: products.js toggles that class on the shop listing, and its row
   rhythm is built for tall product cards. minmax(0,…) because a bare 1fr
   floors at its widest child and a long category name would then overflow. */
.sik-catrow--wrap {
    display: grid;
    grid-template-columns: repeat(var(--cols-mobile, 3), minmax(0, 1fr));
    justify-items: center;
    gap: var(--sp-6) var(--sp-3);
    /* No max-width. Capping the track to one tile plus a gap per column left
       a store with three categories showing a 480px huddle of circles
       centred under a full-width heading, with the heading's own View all
       link stranded 370px away from the last tile. The columns are already
       1fr, so the tiles spread across the container and the row reads as a
       row at any count. */
}
@media (min-width: 768px) {
    .sik-catrow--wrap {
        grid-template-columns: repeat(var(--cols-tablet, 5), minmax(0, 1fr));
    }
}
@media (min-width: 1024px) {
    .sik-catrow--wrap {
        grid-template-columns: repeat(var(--cols-desktop, 8), minmax(0, 1fr));
    }
}

.sik-catrow__tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    width: var(--cat-w);
    max-width: 100%;
    min-width: 0;
    text-align: center;
    color: var(--sik-ink);
    /* The circle is only the top half of the link; rounding the focus ring to
       the card radius stops it reading as a stray rectangle. */
    border-radius: var(--sik-radius);
}

.sik-catrow__ring {
    position: relative;
    display: grid;
    place-items: center;
    flex: none;
    /* The grid columns can come out narrower than the circle when an admin
       asks for four columns on a phone. Clamping the width to the column and
       deriving the height keeps it a circle instead of an ellipse hanging over
       both edges of a 320px screen. */
    width: min(var(--cat-size), 100%);
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-media);
    /* The ring is a shadow, not a border, so the photo keeps the full circle
       and the resting ring and the hover glow can cross-fade in one property. */
    box-shadow: 0 0 0 2px var(--cat-ring), var(--sik-shadow-soft);
    transition: box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}
.sik-catrow__ring img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}
/* A logo or an SVG icon is artwork, not a photograph: it sits inside the
   circle rather than being cropped by it. */
.sik-catrow__ring--contain img { object-fit: contain; padding: 22%; }

/* No picture anywhere — none on the category, no product to borrow one from.
   The initial letter is still the category's own data, which a grey
   placeholder box is not. Display size, so the 3:1 bar applies. */
.sik-catrow__initial {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
    font-size: var(--fs-2xl);
    font-weight: var(--fw-bold);
    line-height: 1;
}

/* The last tile is a destination, not a category: a neutral well and a glyph,
   so it never reads as one more range of products. */
.sik-catrow__ring--all {
    background: var(--sik-surface-2);
    color: var(--sik-ink-2);
    box-shadow: 0 0 0 2px var(--sik-line), var(--sik-shadow-soft);
}

.sik-catrow__label {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    max-width: 100%;
    /* A single unbroken name is the only thing that can widen the tile past
       --cat-w; let it wrap mid-word instead. */
    overflow-wrap: anywhere;
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    transition: color var(--dur-base) var(--ease-out);
}

/* The lift lives on the ring, never on the tile: section 36's
   `[data-anim].is-in { transform: none }` is later in the file at the same
   specificity, so any transform on an element that might carry data-anim is
   dead on arrival — that is exactly how the brand tiles lost theirs. */
@media (hover: hover) {
    .sik-catrow__tile:hover .sik-catrow__ring {
        transform: translateY(var(--lift-sm)) scale(1.03);
        box-shadow: 0 0 0 2px var(--sik-primary), var(--sik-shadow-brand-sm);
    }
    .sik-catrow__tile:hover .sik-catrow__label { color: var(--sik-primary-ink); }
}

/* The arrows are anchored at 38% of a tall product card (section 12). A row of
   circles is a third of that height, so they would float below it. Re-anchored
   on the circle's centre here rather than changed there, because that rule
   also serves the product, brand and review rails. */
.sik-rail--cats .sik-rail__nav { top: calc(var(--sp-2) + var(--cat-size) / 2); }

/* Movement has to be switchable off both ways: the OS setting and Settings >
   Theme > "Enable entrance animations". The colour change stays — that is the
   state, not the animation. The rail's autoplay timer is JS and ignores both;
   the renderer withholds the interval instead. */
.sik-noanim .sik-catrow__ring,
.sik-noanim .sik-catrow__label { transition: none; }
/* ...and the movement itself goes with it. Leaving the transform in place
   under .sik-noanim made the ring jump instantly on hover, which is more
   jarring than the animation it was replacing. */
.sik-noanim .sik-catrow__tile:hover .sik-catrow__ring { transform: none; }
.sik-noanim .sik-catrow.sik-rail__track { scroll-behavior: auto; }
@media (prefers-reduced-motion: reduce) {
    .sik-catrow__ring,
    .sik-catrow__label { transition: none; }
    .sik-catrow.sik-rail__track { scroll-behavior: auto; }
}
@media (prefers-reduced-motion: reduce) and (hover: hover) {
    .sik-catrow__tile:hover .sik-catrow__ring { transform: none; }
}


/* --- the category disc, as a tinted plate ---------------------------------
   The reference row is a line of pale coloured discs with the category's own
   artwork sitting inside each one, not photographs cropped into circles. So a
   tile that carries artwork gets a tint instead of the neutral media well, and
   the tints cycle by position so the row reads as a row rather than as ten
   copies of one swatch.

   Only the --contain tiles are tinted. A tile still borrowing a product
   photograph fills its circle edge to edge, and a tint behind it would never
   be seen anyway.

   The hues are the palette's own status and brand tones mixed down to ~14%,
   so they stay in family with the store and follow a re-theme. They are
   backgrounds for artwork, never for text, so the 4.5:1 bar does not apply -
   the label sits underneath the disc on the page surface. */
.sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-primary) 12%, var(--sik-surface)); }
.sik-catrow__tile:nth-child(6n + 2) .sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-gold) 16%, var(--sik-surface)); }
.sik-catrow__tile:nth-child(6n + 3) .sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-info) 13%, var(--sik-surface)); }
.sik-catrow__tile:nth-child(6n + 4) .sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-success) 13%, var(--sik-surface)); }
.sik-catrow__tile:nth-child(6n + 5) .sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-accent) 12%, var(--sik-surface)); }
.sik-catrow__tile:nth-child(6n + 6) .sik-catrow__ring--contain { background: color-mix(in srgb, var(--sik-warning) 15%, var(--sik-surface)); }

/* Artwork wants air around it, and a shade less than the 22% the square tile
   uses - a disc crops the corners, so the same padding reads as smaller. */
.sik-catrow__ring--contain img { padding: 18%; }

/* The ring itself steps back on a tinted plate: two rings of colour around a
   coloured disc is one too many. The hover ring still arrives. */
.sik-catrow__ring--contain { box-shadow: var(--sik-shadow-soft); }
@media (hover: hover) {
    .sik-catrow__tile:hover .sik-catrow__ring--contain {
        box-shadow: 0 0 0 2px var(--sik-primary), var(--sik-shadow-card);
    }
}

/* The "All categories" tile is a neutral plate with a glyph, so it reads as
   the row's end-stop rather than as another department. */
.sik-catrow__ring--all { background: var(--sik-surface-2); color: var(--sik-primary-ink); }


/* --------------------------------------------------------------------------
   10b. Shop by category: the department grid and its glyphs

   The homepage row used to be a flat line of circles, each showing a
   photograph borrowed from one of its products. It is now the store's
   departments: one card per top-level category, that department's own ranges
   listed underneath as links, and a drawn glyph in place of the borrowed
   photograph. category_groups() and category_icon_svg() in
   includes/widgets.php carry the reasoning for both.

   Nothing above this comment changed. The circular row is still what the
   carousel layout renders, what skeleton_category_grid() mimics, and what the
   designer's own category tiles use, so it keeps its geometry, its tints and
   its hover exactly as they were.
   -------------------------------------------------------------------------- */
.sik-catgrid {
    display: grid;
    /* minmax(0, …) at every step: a bare 1fr floors at its widest child, which
       is how one long category name pushes a two-up grid past 360px. */
    grid-template-columns: repeat(var(--cols-mobile, 1), minmax(0, 1fr));
    gap: var(--sp-3);
    /* Tops aligned, each card its own height. Equal heights were the first
       try, and they gave a department with three ranges under it a 330px card
       and the three with none a 330px empty box each. A department that has
       more to say is allowed to be taller than one that has not. */
    align-items: start;
}
@media (min-width: 768px) {
    .sik-catgrid {
        grid-template-columns: repeat(var(--cols-tablet, 3), minmax(0, 1fr));
        gap: var(--sp-4);
    }
}
@media (min-width: 1024px) {
    .sik-catgrid { grid-template-columns: repeat(var(--cols-desktop, 4), minmax(0, 1fr)); }
}

.sik-catcard {
    display: flex;
    flex-direction: column;
    /* The other half of minmax(0, 1fr): the column may shrink only if the card may. */
    min-width: 0;
    /* The box is the card, not the link inside it: a department and the ranges
       hanging off it are one thing, and an edge drawn around only the link left
       the one card that has ranges looking like loose text beside three boxes. */
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    overflow: hidden;
    /* The plate tint and the glyph's ink travel down from here, so one pair of
       lines per card decides both. */
    --cat-tint: var(--sik-primary-soft);
    --cat-ico:  var(--sik-primary-ink);
    --cat-plate: 56px;
}
/* An unstocked department is not a link (see widget_categories()). The dashed
   edge says so before the tap that would not have happened: it is the one card
   on the row a shopper cannot open. */
.sik-catcard--soon { border-style: dashed; background: transparent; }
@media (min-width: 768px) { .sik-catcard { --cat-plate: 64px; } }

/* The tints cycle by position so four departments read as four things rather
   than four copies of one swatch — the same device .sik-trust__item uses, and
   the same pairs, because each *-soft / *-ink pair is already tuned against
   both the light and the dark surface. Measured on the four cards this store
   renders, glyph against plate: 5.96, 5.57, 6.78 and 5.69 to 1, so the drawing
   clears the 3:1 bar for a non-text graphic several times over and would still
   clear 4.5:1 if it were text. --sik-gold-soft is deliberately not in the
   cycle: it is a fixed pale hex that is never redefined for dark mode, so it
   would burn a hole in a near-black page. */
.sik-catcard:nth-child(4n + 2) { --cat-tint: var(--sik-success-soft); --cat-ico: var(--sik-success-ink); }
.sik-catcard:nth-child(4n + 3) { --cat-tint: var(--sik-info-soft);    --cat-ico: var(--sik-info-ink); }
.sik-catcard:nth-child(4n + 4) { --cat-tint: var(--sik-warning-soft); --cat-ico: var(--sik-warning-ink); }

/* The card's edge is authored above from --sik-line rather than from
   --sik-card-border / --sik-card-shadow. Those are the theme's card pair, and
   on this store they are the Flat pair, transparent and none — right for a
   product card standing on its own photograph, wrong for a department that has
   nothing but a glyph and a name to hold it together. */
.sik-catcard__main {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    padding: var(--sp-3);
    color: var(--sik-ink);
    transition: background-color var(--dur-base) var(--ease-out);
}
/* The glyphs read this: 0 at rest, 1 while a shopper is on the tile. One
   custom property instead of a copy of every part rule per state — see the
   motion note below. */
.sik-catcard__main,
.sik-catcard__subs .sik-catsub,
.sik-catrow__tile { --ci: 0; }
@media (hover: hover) {
    .sik-catcard__main:hover,
    .sik-catcard__subs .sik-catsub:hover,
    .sik-catrow__tile:hover { --ci: 1; }
    .sik-catcard__main:hover { background: var(--sik-surface-2); }
    .sik-catcard__main:hover .sik-catcard__name { color: var(--sik-primary-ink); }
}
/* A tap has no hover, so :active is the only acknowledgement a phone can give. */
.sik-catcard__main:is(:focus-visible, :active),
.sik-catcard__subs .sik-catsub:is(:focus-visible, :active),
.sik-catrow__tile:is(:focus-visible, :active) { --ci: 1; }
/* The global :focus-visible rule rounds the focused element to 4px, which
   would square the top corners of a 12px card for as long as it is focused.
   The offset is negative because the card clips its overflow: an outline drawn
   outside the link would be cut off by the card's own edge. */
.sik-catcard__main:focus-visible {
    border-radius: var(--sik-radius);
    outline-offset: -3px;
}
.sik-catcard__main--soon { cursor: default; }

.sik-catcard__plate {
    position: relative;
    display: grid;
    place-items: center;
    flex: none;
    width: var(--cat-plate);
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--sik-radius-pill);
    background: var(--cat-tint);
    color: var(--cat-ico);
    /* A 0px ring is invisible, so the resting and the active ring are one
       declaration and cross-fade in one property. */
    box-shadow: 0 0 0 calc(var(--ci, 0) * 2px) var(--cat-ico), var(--sik-shadow-soft);
    transform: translateY(calc(var(--ci, 0) * var(--lift-sm)));
    transition: box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}
.sik-catcard__plate img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* A photograph an operator uploaded onto the category fills the plate, so the
   tint behind it would never be seen. */
.sik-catcard__plate--photo { background: var(--sik-media); }
.sik-catcard__plate--contain img { object-fit: contain; padding: 18%; }
/* The end-stop card is a destination, not a department. */
.sik-catcard__plate--all { background: var(--sik-surface-2); color: var(--sik-ink-2); }

.sik-catcard__text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sik-catcard__name {
    min-width: 0;
    font-family: var(--sik-font-display);
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    /* A single unbroken name is the only thing that can widen the card past its
       column; let it break mid-word instead. */
    overflow-wrap: anywhere;
    transition: color var(--dur-base) var(--ease-out);
}
.sik-catcard__meta { font-size: var(--fs-xs); color: var(--sik-muted); }

/* The ranges inside a department. Indented to the parent's text column so they
   read as belonging to the name above them, not to the card's edge. */
.sik-catcard__subs {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin: 0;
    /* Left padding = the card's own padding + the plate + the gap, so the
       ranges start exactly under the department's name. */
    padding: 0 var(--sp-3) var(--sp-3) calc(var(--sp-3) + var(--cat-plate) + var(--sp-3));
    list-style: none;
}
@media (max-width: 479px) {
    /* Measured in a 360px viewport: with the aligned indent the longest range
       name, "String & Curtain Lights", renders in a 120px box against a
       natural width of 120px — it fits with nothing at all to spare, so one
       more word in a name wraps it. The ranges take the card's own padding
       instead, which gives the same row 302px. */
    .sik-catcard__subs { padding-left: var(--sp-3); }
}
.sik-catsub {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    /* A full tap target on a phone, the way .sik-footer__legal a does it; a
       mouse gets the tighter list a nav column should be. */
    min-height: var(--tap);
    padding-inline: var(--sp-2);
    border-radius: var(--sik-radius);
    font-size: var(--fs-xs);
    color: var(--sik-ink-2);
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
@media (min-width: 1024px) { .sik-catsub { min-height: 32px; } }
.sik-catsub__name { min-width: 0; overflow-wrap: anywhere; }
.sik-catsub__n {
    flex: none;
    margin-left: auto;
    font-size: var(--fs-2xs);
    font-variant-numeric: tabular-nums;
    color: var(--sik-muted);
}
@media (hover: hover) {
    .sik-catsub:hover { background: var(--sik-surface-2); color: var(--sik-ink); }
}
.sik-catsub:focus-visible { border-radius: var(--sik-radius); }
.sik-catsub--soon { color: var(--sik-muted); cursor: default; }

/* The circular tile, when the builder asks for the carousel layout: the same
   glyph on the same tinted plate, so the two layouts are one design. The tints
   cycle on the rail's slot rather than on the tile, because the rail wraps
   every tile in one and a :nth-child() on the tile would only ever see 1. */
.sik-catrow { --cat-tint: var(--sik-primary-soft); --cat-ico: var(--sik-primary-ink); }
.sik-catrow__slot:nth-child(4n + 2) { --cat-tint: var(--sik-success-soft); --cat-ico: var(--sik-success-ink); }
.sik-catrow__slot:nth-child(4n + 3) { --cat-tint: var(--sik-info-soft);    --cat-ico: var(--sik-info-ink); }
.sik-catrow__slot:nth-child(4n + 4) { --cat-tint: var(--sik-warning-soft); --cat-ico: var(--sik-warning-ink); }
.sik-catrow__ring--icon {
    background: var(--cat-tint);
    color: var(--cat-ico);
    /* Two rings of colour around a coloured disc is one too many; the active
       ring still arrives, out of --ci. */
    box-shadow: 0 0 0 calc(var(--ci, 0) * 2px) var(--cat-ico), var(--sik-shadow-soft);
}
.sik-catrow__meta { font-size: var(--fs-2xs); color: var(--sik-muted); }
.sik-catrow__tile--soon { cursor: default; }

/* The square tile the designer's category items render, when the category has
   no picture at all: its glyph, rather than the letter in a box this used to
   end at. */
.sik-cat__media--glyph { background: var(--sik-primary-soft); color: var(--sik-primary-ink); }

/* --- the glyphs -----------------------------------------------------------
   One 24x24 stroke drawing per category, in pieces the rules below move. */
.sik-caticon {
    display: block;
    /* A share of the plate rather than the whole of it, so the drawing has air
       around it on a disc. Sized here rather than as padding on the plate,
       because the plate also has to be able to hold a photograph edge to edge. */
    width: 58%;
    height: 58%;
    /* One weight for the set, as .sik-ico does it: the 1.7 in the markup is
       only the no-CSS fallback. A glyph filling a 64px plate carries a shade
       more than a 20px interface glyph or it reads as a hairline. */
    stroke-width: calc(var(--icon-stroke) + 0.15);
    stroke-linecap: round;
    stroke-linejoin: round;
    /* A part that scales past the 24-unit box is meant to be seen, not clipped. */
    overflow: visible;
}
/* A fixed size, not a share of the row: a range link's height is a tap target
   and says nothing about how big the glyph should read beside 12px text. This
   sits AFTER the rule above on purpose — both are one class, so the later one
   is the one that applies, and stating it earlier gave every range in the list
   a 40px drawing and three lines of wrapped name. */
.sik-caticon--chip { flex: none; width: 17px; height: 17px; color: var(--cat-ico); }
/* Part transforms are authored against the shape's own bounding box, so a
   scale grows from the middle of the flame rather than from the corner of the
   SVG's user space. */
.ci-spark, .ci-ray, .ci-bolt, .ci-flame, .ci-loop, .ci-trend {
    transform-box: fill-box;
    transform-origin: 50% 50%;
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}
/* A flame grows from its wick, and a ribbon loop turns about its knot. */
.ci-flame   { transform-origin: 50% 100%; }
.ci-loop--l { transform-origin: 100% 50%; }
.ci-loop--r { transform-origin: 0 50%; }

/* Resting values first, active values second, both out of --ci: the still icon
   has to look deliberate, because the still icon is what a phone, .sik-noanim
   and prefers-reduced-motion all get. Half-lit bulbs on a string, a flame at
   rest, rays drawn close in — every one of them is a sensible drawing before
   anything moves.

   Reading --ci in a calc() instead of repeating each part's rule under :hover,
   :focus-visible and :active: the value is inherited, the transition still
   interpolates when it changes, and each part is described exactly once. */
.ci-spark { opacity: calc(0.45 + 0.55 * var(--ci, 0)); }
.ci-ray   { opacity: calc(0.5 + 0.5 * var(--ci, 0)); transform: scale(calc(1 + 0.16 * var(--ci, 0))); }
.ci-bolt  { opacity: calc(0.55 + 0.45 * var(--ci, 0)); }
.ci-flame { transform: scale(calc(1 + 0.1 * var(--ci, 0))) translateY(calc(var(--ci, 0) * -0.7px)); }
.ci-loop--l { transform: rotate(calc(var(--ci, 0) * -6deg)); }
.ci-loop--r { transform: rotate(calc(var(--ci, 0) * 6deg)); }
.ci-trend { transform: translate(calc(var(--ci, 0) * 0.8px), calc(var(--ci, 0) * -1.3px)); }
/* The string's four bulbs light left to right rather than together. */
.ci-d2 { transition-delay: 60ms; }
.ci-d3 { transition-delay: 120ms; }
.ci-d4 { transition-delay: 180ms; }

/* --- when the glyphs move ------------------------------------------------
   Not a loop. Seven glyphs animating forever sit directly under the hero and
   above the product grid, and a homepage where everything twitches is a
   fairground — on a phone they would also be the only thing moving while the
   shopper scrolls past, for no gain and some battery.

   So: one staggered entrance as the row arrives, which plays once and is the
   only motion a touch device sees; then movement whenever a shopper hovers a
   tile or tabs onto it, where it answers an intention and doubles as the focus
   affordance.

   .is-in is added by app.js when the row scrolls into view. --i is the card's
   index, printed inline by widget_categories(), because a :nth-child() cycle
   could only name a fixed number of cards. The rail's track deliberately
   carries no data-anim, so in the carousel layout there is no entrance and the
   glyphs move on hover and focus alone. */
[data-anim].is-in .sik-caticon--plate {
    animation: sik-caticon-in var(--dur-reveal) var(--ease-out) calc(var(--i, 0) * 60ms) both;
}
@keyframes sik-caticon-in {
    from { opacity: 0; transform: scale(0.72) rotate(-6deg); }
    60%  { opacity: 1; transform: scale(1.06) rotate(1deg); }
    to   { opacity: 1; transform: none; }
}

/* --- motion off ---------------------------------------------------------
   Both switches, because they are two separate requests: the OS setting, and
   Settings > Theme > "Enable entrance animations" = off, which lands as
   body.sik-noanim. Section 1 already collapses --dur-fast and --dur-base to
   .01ms under prefers-reduced-motion, but --dur-reveal is not in that list and
   an animation travels its whole distance regardless, so the entrance is
   cancelled outright rather than sped up.

   The brightening stays under both: which tile a shopper is on is state, not
   decoration. It simply arrives at once. The movement does not — a transform
   that jumps straight to its end is more jarring than the motion it replaced,
   which is the lesson the row above this one already recorded. */
.sik-noanim .sik-caticon,
.sik-noanim .sik-caticon *,
.sik-noanim .sik-catcard__main,
.sik-noanim .sik-catcard__name,
.sik-noanim .sik-catcard__plate,
.sik-noanim .sik-catsub { transition: none; }
/* The entrance is declared at `[data-anim].is-in .sik-caticon--plate`, three
   selectors of weight. A cancel written as `.sik-noanim .sik-caticon--plate`
   is only two and loses to it outright — the glyphs went on playing with the
   switch off, which is the one real bug the first draft of this block shipped.
   Both cancels restate the whole selector instead. */
.sik-noanim [data-anim].is-in .sik-caticon--plate { animation: none; }
.sik-noanim .ci-ray,
.sik-noanim .ci-flame,
.sik-noanim .ci-loop,
.sik-noanim .ci-trend,
.sik-noanim .sik-catcard__plate { transform: none; }
@media (prefers-reduced-motion: reduce) {
    .sik-caticon,
    .sik-caticon *,
    .sik-catcard__main,
    .sik-catcard__name,
    .sik-catcard__plate,
    .sik-catsub { transition: none; }
    /* Equal weight to the entrance rule, and later in the file, which is what
       decides it here. */
    [data-anim].is-in .sik-caticon--plate { animation: none; }
    .ci-ray,
    .ci-flame,
    .ci-loop,
    .ci-trend,
    .sik-catcard__plate { transform: none; }
}

/* --------------------------------------------------------------------------
   11. Product card

   One card for the whole catalogue: a square photo well with the badge and the
   heart floating on it, then brand, name, rating, price, and an Add to cart
   that is deliberately the loudest thing in the card. The box itself is not
   authored here - the border, the shadow and the radius arrive as
   --sik-card-border / --sik-card-shadow / --sik-radius from Settings > Theme,
   so Flat, Bordered, Soft and Elevated stay four different looks instead of
   collapsing into whatever this file hard-codes.
   -------------------------------------------------------------------------- */
.sik-grid {
    display: grid;
    gap: var(--sp-6) var(--sp-3);
    grid-template-columns: repeat(var(--cols-mobile, 2), minmax(0, 1fr));
    /* Every row as tall as the tallest card, so the actions share a baseline. */
    grid-auto-rows: 1fr;
}
/* minmax(0, ...) at every step: a bare 1fr floors at its widest child, which is
   how a long product name used to push a two-up grid past 320px. */
@media (min-width: 768px)  { .sik-grid { grid-template-columns: repeat(var(--cols-tablet, 3), minmax(0, 1fr)); gap: var(--sp-8) var(--sp-5); } }
@media (min-width: 1024px) { .sik-grid { grid-template-columns: repeat(var(--cols-desktop, 4), minmax(0, 1fr)); } }

.sik-card {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    /* The other half of minmax(0, 1fr): the track may shrink only if the card may. */
    min-width: 0;
    border: 1px solid var(--sik-card-border);
    border-radius: var(--sik-radius);
    box-shadow: var(--sik-card-shadow);
    /* Quick View sits inside the square image; 100cqw is that image's height. */
    container-type: inline-size;
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}
.sik-cards-boxed .sik-card { background: var(--sik-surface); overflow: hidden; }
@media (hover: hover) {
    /* Grey depth for the shape, a little brand colour for warmth. Boxed only:
       under the Flat card style there is no surface for a shadow to fall from,
       and a shadow around bare text reads as a rendering fault. The border
       mixes into whatever --sik-card-border is, so Elevated (transparent) gets
       a faint pink edge and Bordered gets a pink-grey hairline. */
    .sik-cards-boxed .sik-card:hover {
        transform: translate3d(0, var(--lift-sm), 0);
        box-shadow: var(--sik-shadow-card), var(--sik-shadow-brand-sm);
        border-color: color-mix(in srgb, var(--sik-primary) 30%, var(--sik-card-border));
    }
}
/* Settings > Theme > enable animations = off has to reach the transitions too,
   not just the keyframes: the lift and the photo scale are the two things here
   that move. prefers-reduced-motion is already covered - every duration in this
   section is a --dur-* token and section 1 collapses those to .01ms. */
.sik-noanim .sik-card:hover,
.sik-noanim .sik-card:hover .sik-card__img--main { transform: none; }

.sik-card__media {
    position: relative;
    display: block;
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--sik-radius);
    background-color: var(--sik-media);
    /* A whisper of brand across the well, so a letterboxed photo sits on
       something warmer than flat grey. Translucent rather than --sik-grad-soft,
       because --sik-media is the admin's colour and should stay the one
       underneath. blog_card() overrides the ratio inline; the tint is harmless
       there because its photo covers the well. */
    background-image: linear-gradient(135deg, color-mix(in srgb, var(--sik-primary) 7%, transparent) 0%, transparent 62%);
}
/* Boxed: the photo full-bleeds to the card edge and .sik-card's overflow does
   the corner clipping. */
.sik-cards-boxed .sik-card__media { border-radius: 0; }
.sik-card__img {
    width: 100%;
    height: 100%;
    /* contain, not cover: a non-square upload is letterboxed on the well rather
       than cropped. The shipped photography is square, so it fills the tile. */
    object-fit: contain;
    transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slower) var(--ease-out);
}
.sik-card__img--hover { position: absolute; inset: 0; opacity: 0; }
@media (hover: hover) {
    .sik-card:hover .sik-card__img--main { transform: scale(1.035); }
    .sik-card:hover .sik-card__img--hover { opacity: 1; }
    /* The cross-fade has no helper class: the renderer only emits the second
       image when hover_url differs from image_url, so the pair has to stay
       directly adjacent inside .sik-card__media for this :has() to see it. */
    .sik-card:hover .sik-card__img--main:has(+ .sik-card__img--hover) { opacity: 0; }
}

/* --- badge ---------------------------------------------------------------- */
.sik-card__badges {
    position: absolute;
    top: var(--sp-2); left: var(--sp-2);
    /* A column: product_card() prints one badge, wishlist.php prints up to three. */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-1);
    z-index: 3;
    /* The badge lies on the photo link - it must never eat the click. */
    pointer-events: none;
}

/* Shared component, not a card part: the nav and mega menu, account, addresses,
   the blog and the API all render .sik-badge too, so only shape and size live
   on the base class. menu_badge_chip() writes colours inline for the soft and
   outline styles, so nothing here may declare a background those two depend on. */
.sik-badge {
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: 0;
    line-height: 1;
    padding: 5px var(--sp-2);
    border-radius: var(--sik-radius-pill);
    /* --sik-on-cta, not --sik-on-inverse: it is #FFFFFF in both themes, and the
       dark theme's softer #ECEDEF would drop the red tone to 4.1:1. */
    color: var(--sik-on-cta);
    white-space: nowrap;
}
/* On the photo the badge is a solid colour pill rather than the frosted chip it
   used to be: only one badge survives the filter in widgets.php, so it can
   afford to be the thing a shopper catches from across a grid. The shadow is
   what stops a pale tone - grey "Out of stock", amber, soft - from dissolving
   into a product photo shot on white. */
.sik-card__badges .sik-badge {
    /* 10px is deliberate: the scale stops at --fs-2xs (11px), and uppercase at
       this tracking already reads a size larger. "Out of stock" has to fit
       inside a ~150px two-up phone card. */
    font-size: 10px;
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    box-shadow: var(--sik-shadow-xs);
}

/* The eight tones in product_badge_colors() (admin/products/_shared.php), plus
   the two the menu builder uses. Each carries its own text colour wherever
   white would not clear 4.5:1 on it. */
.sik-badge--orange { background: var(--sik-cta); color: var(--sik-on-cta); }
.sik-badge--navy   { background: var(--sik-ink); color: var(--sik-bg); }
.sik-badge--red    { background: var(--sik-danger); }
.sik-badge--green  { background: var(--sik-success-ink); color: var(--sik-bg); }
.sik-badge--amber  { background: var(--sik-warning-soft); color: var(--sik-warning-ink); }
/* Violet is the one admin tone the palette has no token for - the status ramp
   stops at info blue. Mixing info into danger lands on the same violet without
   a raw hex, and lands it darker: 6.7:1 under the white label instead of 5.7. */
.sik-badge--purple { background: color-mix(in srgb, var(--sik-info) 58%, var(--sik-danger)); }
/* Grey carries "Out of stock", which product_badges() pushes to first position,
   so it keeps --sik-muted rather than a lighter grey: that clears 4.5:1 on
   --sik-surface-2 in both themes. */
.sik-badge--grey   { background: var(--sik-surface-2); color: var(--sik-muted); }
.sik-badge--soft   { background: var(--sik-primary-soft); color: var(--sik-primary-ink); }

/* Admin > Menu Builder > Badge style = outline. Colours arrive inline from
   menu_badge_chip(), which darkens an admin's pick until it clears 4.5:1. */
.sik-badge--outline {
    background: transparent;
    color: var(--sik-primary-ink);
    border: 1px solid currentColor;
    padding: 4px calc(var(--sp-2) - 1px);
}
.sik-nav__badge.sik-badge--outline { padding: 2px 4px; }

/* One slow breath of opacity, never a loop of scale. */
@keyframes sik-badge-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: .72; }
}
.sik-badge--pulse { animation: sik-badge-pulse calc(var(--dur-slower) * 5) var(--ease-in-out) infinite; }
/* The only infinite animation in this section, so it needs the switch as well:
   reduced motion disarms it through the collapsed --dur-slower, but Settings >
   Theme > enable animations = off has to stop it too. */
.sik-noanim .sik-badge--pulse { animation: none; }

/* --- tools on the photo --------------------------------------------------- */
.sik-card__tools {
    position: absolute;
    top: var(--sp-2); right: var(--sp-2);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    z-index: 4;
}
/* A small white disc on the photograph. 34px is only the visible chip - the
   44px finger target comes from the shared ::after extender in section 2b, so
   nothing here may clip the tools or declare a competing ::after. */
.sik-card__tools .sik-iconbtn {
    width: 34px; height: 34px;
    background: var(--sik-elevated);
    border-color: var(--sik-line);
    box-shadow: var(--sik-shadow-xs);
}
@media (hover: hover) {
    /* Background only, never colour: a saved heart is red through `color`
       (.sik-iconbtn[data-wishlist].is-active, section 4) and wishlist.js paints
       the svg fill from currentColor. A colour declared here would tie with
       that rule and drain the red the moment a pointer landed on it. */
    .sik-card__tools .sik-iconbtn:hover { background: var(--sik-elevated); }
    /* The heart is always there - it is the one tool a shopper looks for on a
       photo. Compare and Quick View arrive on hover where there is a pointer.
       Keyed on [data-wishlist] as well as :first-child because the admin can
       switch either control off (wishlist_shows_on()/compare_shows_on()), so
       the heart is not reliably first; and because wishlist.php puts a Remove
       button first, which has to stay visible there. .is-active is the escape
       hatch that keeps a saved or compared item visible on touch. The new test
       sits inside :where() so the selector stays at three classes and the
       reveal below still wins on hover. */
    .sik-card__tools > :not(:first-child):not(.is-active):where(:not([data-wishlist])) {
        opacity: 0;
        transform: translateY(-4px);
        transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
    }
    .sik-card:hover .sik-card__tools > *,
    .sik-card:focus-within .sik-card__tools > * { opacity: 1; transform: none; }
}

.sik-card__quick {
    position: absolute;
    left: var(--sp-2); right: var(--sp-2);
    /* The card is the query container and the media well is exactly its content
       width at aspect-ratio 1, so 100cqw is the photo's height and this lands
       the control on the bottom of the photo. Inset the media well, pad it or
       change its ratio and the sum stops being true. */
    top: calc(100cqw - 36px - var(--sp-2));
    z-index: 4;
    opacity: 0;
    pointer-events: none;
    transform: translateY(6px);
    transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.sik-card__quick .sik-btn {
    min-height: 36px;   /* the 36px in the calc above */
    /* Glass, because this is the case the token was written for: a control
       floating over content rather than sitting on a surface. */
    background: var(--sik-glass-bg);
    color: var(--sik-ink);
    border-color: var(--sik-glass-line);
    box-shadow: var(--sik-glass-shadow);
    -webkit-backdrop-filter: var(--sik-glass-blur);
    backdrop-filter: var(--sik-glass-blur);
}
@media (hover: hover) {
    .sik-card__quick .sik-btn:hover { background: var(--sik-elevated); color: var(--sik-ink); }
}
.sik-card:hover .sik-card__quick,
.sik-card:focus-within .sik-card__quick { opacity: 1; pointer-events: auto; transform: none; }
/* Pointer-only. Without a hover there is nothing to reveal it with, and on a
   phone the photo itself is the quick view. */
@media (hover: none) { .sik-card__quick { display: none; } }
/* The horizontal card has no square photo for that calc to measure, so it stops
   being a query container. products.js adds .sik-card--row at runtime to cards
   that were rendered square, so this has to work as a plain class reset. */
.sik-card--row { container-type: normal; }

/* --- body ----------------------------------------------------------------- */
.sik-card__body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
    min-width: 0;
    padding: var(--sp-3) 2px 0;
}
.sik-cards-boxed .sik-card__body { padding: var(--sp-3) var(--sp-4) var(--sp-4); }

.sik-card__brand {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-card__name {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    letter-spacing: -0.005em;
    color: var(--sik-ink);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Two lines reserved whether the name fills them or not, so the price rows
       across a grid row start level. */
    min-height: calc(var(--lh-snug) * 2em);
}
/* Brand pink on hover, but the -ink shade: the raw primary at 14px on white is
   ~3.2:1. Dark bands re-derive -ink toward white (section 3), so this still
   reads inside the flash panel and the footer. */
.sik-card__name a:hover { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }

/* The stars themselves are section 23 - gold from --sik-star through a mask -
   so this row only places them and colours the count. It wraps because five
   14px stars plus "4.5 (128)" do not fit a two-up card at 320px: the count
   drops to its own line instead of pushing the row wider than the card. */
.sik-card__rating { display: flex; align-items: center; flex-wrap: wrap; gap: 2px var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }

.sik-card__price { display: flex; align-items: baseline; flex-wrap: wrap; gap: 2px var(--sp-2); }
.sik-price {
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
    letter-spacing: -0.01em;
    color: var(--sik-ink);
    font-variant-numeric: tabular-nums;
}
/* Scoped to the card body because deals.php, compare.php and the product page
   reuse .sik-price standalone. Not on phones: at two-up the price shares its
   line with the MRP and the saving. */
@media (min-width: 768px) { .sik-card__body .sik-price { font-size: var(--fs-xl); } }
.sik-price--mrp { font-size: var(--fs-sm); font-weight: var(--fw-normal); color: var(--sik-muted); text-decoration: line-through; font-variant-numeric: tabular-nums; }
/* The saving, in the green -ink shade so it clears 4.5:1 at 13px; dark bands
   re-derive that shade toward white. */
.sik-price--off { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-success-ink); }

.sik-card__stock { font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--sik-warning-ink); }

/* margin-top: auto is one third of the height-sharing mechanism - .sik-grid's
   grid-auto-rows: 1fr and .sik-card's height: 100% are the rest. Together they
   are what puts every Add to cart in a row on one baseline. */
.sik-card__foot { margin-top: auto; padding-top: var(--sp-3); }

/* The conversion control. add_to_cart_button() renders it with tone `outline`,
   which is right on the product page and wrong in a grid: the one thing a
   shopper is hunting for should not be the quietest element in the card. It is
   painted from .sik-card__foot rather than by restyling .sik-atc--outline, so
   the product page's lg outline button is untouched.

   Two classes on purpose. .sik-flash .sik-atc--outline (section 15) is also two
   and comes later, so a card inside the flash panel keeps the white outline
   that reads on the wine background; and the component's own resting states
   below - [disabled]/.is-out, .is-incart, .is-success - are two classes each
   and come after this, so they still win over the tint. */
.sik-card__foot .sik-atc--outline {
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
    border-color: color-mix(in srgb, var(--sik-primary) 28%, transparent);
}
/* The "already in your cart" dot is --sik-primary, which all but vanishes on
   the tint; currentColor keeps it readable at rest and on the hover gradient. */
.sik-card__foot .sik-atc--choose.has-incart::after { background: currentColor; }
@media (hover: hover) {
    /* Solid brand on hover. :where() carries no specificity, so this stays at
       three classes and still loses to .sik-flash .sik-atc--outline:hover and
       to .sik-atc.is-incart:hover, both later in the file. The two negations
       are why a disabled button - which is every loading button too, since
       SIK.showLoader() sets disabled - and a button mid-success are never lit
       up by a pointer resting on them. */
    .sik-card__foot .sik-atc--outline:hover:where(:not([disabled]):not(.is-success)) {
        background-color: var(--sik-cta);
        background-image: var(--sik-grad-cta);
        color: var(--sik-on-cta);
        border-color: transparent;
    }
}

/* Out of stock: the photo steps back; the disabled button says why. The two
   .is-out in this subtree mean different things - the article dims, the
   .sik-atc further down is genuinely disabled - so the selectors stay apart. */
.sik-card.is-out .sik-card__img { opacity: .55; filter: grayscale(.4); }

/* --- variants ------------------------------------------------------------- */
.sik-card--row { flex-direction: row; gap: var(--sp-4); align-items: stretch; }
.sik-card--row .sik-card__media { width: 38%; max-width: 220px; flex: none; align-self: flex-start; }
.sik-card--row .sik-card__body { padding: var(--sp-1) 0; min-width: 0; }
.sik-cards-boxed .sik-card--row .sik-card__body { padding: var(--sp-4) var(--sp-4) var(--sp-4) 0; }
.sik-card--row .sik-card__rating { flex-wrap: wrap; }
.sik-card--row .sik-card__name { min-height: 0; }
/* The horizontal card carries Quick View as an icon in the tools column. */
.sik-card--row .sik-card__quick { display: none; }
@media (max-width: 560px) {
    .sik-card--row { flex-direction: column; gap: 0; }
    .sik-card--row .sik-card__media { width: 100%; max-width: none; }
    .sik-card--row .sik-card__body { padding: var(--sp-3) 2px 0; }
}
.sik-card--compact .sik-card__foot { padding-top: var(--sp-2); }
.sik-card--minimal .sik-card__foot { display: none; }
.sik-card--premium .sik-card__media { border-radius: var(--sik-radius-lg); }

/* Flash sale, boxed. Section 15 re-points --sik-ink and --sik-muted to white on
   .sik-flash .sik-card, which is correct for a card sitting transparent on the
   wine panel - but with body.sik-cards-boxed, now the default, the card also
   paints itself --sik-surface, so the product name and the price come out white
   on white. Three classes, so this wins whatever the source order, and the
   panel gets back the flat card it was designed around. */
.sik-cards-boxed .sik-flash .sik-card { background: transparent; }
@media (hover: hover) {
    /* No surface to lift, so nothing to cast a shadow; the content still rises. */
    .sik-cards-boxed .sik-flash .sik-card:hover { box-shadow: none; border-color: transparent; }
}
/* Same panel, same cause: section 15 lightens --sik-ink, --sik-muted and
   --sik-primary-ink toward white for copy on the wine background, and the navy,
   grey and soft badge tones read exactly those three for their text. A badge is
   a light pill lying on a photograph, not copy on the panel, so it puts the
   three back to their light-surface shades for its own subtree - otherwise the
   "Out of stock" pill is white on near-white. --sik-text and --sik-surface are
   the admin's own inputs and nothing re-points them. */
.sik-flash .sik-card__badges {
    --sik-ink: var(--sik-text);
    --sik-muted: color-mix(in srgb, var(--sik-text) 62%, var(--sik-surface));
    --sik-primary-ink: color-mix(in srgb, var(--sik-primary) 76%, var(--sik-text));
}

/* ==========================================================================
   .sik-atc - THE Add to Cart control

   One component for every surface. Size and tone are modifiers, so the states
   below look the same wherever it lands. Rendered by add_to_cart_button() in
   includes/widgets.php and driven by assets/js/cart.js.

   States: rest · hover · focus-visible · active · .is-loading · .is-success
           · .is-incart · :disabled/.is-out · .sik-atc--choose (link)
   ========================================================================== */
.sik-atc {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding: 0 var(--sp-4);
    border: 1px solid transparent;
    border-radius: var(--sik-radius-btn);
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    letter-spacing: -0.005em;
    text-align: center;
    cursor: pointer;
    white-space: nowrap;
    -webkit-tap-highlight-color: transparent;
    transition: background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}
.sik-atc--block { width: 100%; }
.sik-atc--sm { min-height: 36px; padding: 0 var(--sp-3); font-size: var(--fs-sm); gap: var(--sp-1); }
@media (pointer: coarse) { .sik-atc--sm { min-height: var(--tap); } }
.sik-atc--md { min-height: 40px; }
@media (pointer: coarse) { .sik-atc--md { min-height: var(--tap); } }
.sik-atc--lg { min-height: 52px; padding: 0 var(--sp-6); font-size: var(--fs-md); }
.sik-atc--icon { padding-inline: 0; width: var(--tap); flex: none; }
.sik-atc--icon.sik-atc--block { width: 100%; }

/* --- tones ---------------------------------------------------------------- */
.sik-atc--primary { background: var(--sik-cta); color: var(--sik-on-cta); }
.sik-atc--primary:hover { background: var(--sik-cta-hover); color: var(--sik-on-cta); }
.sik-atc--navy { background: var(--sik-ink); color: var(--sik-bg); }
.sik-atc--navy:hover { background: color-mix(in srgb, var(--sik-ink) 84%, var(--sik-bg)); color: var(--sik-bg); }
.sik-atc--outline { background: transparent; color: var(--sik-ink); border-color: var(--sik-line-strong); }
.sik-atc--outline:hover { background: var(--sik-ink); border-color: var(--sik-ink); color: var(--sik-bg); }

/* --- interaction ---------------------------------------------------------- */
.sik-atc:active:not([disabled]) { transform: scale(.98); }
.sik-atc:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; }

/* A live link to the product page: the card cannot know which variant. */
.sik-atc--choose { background: transparent; color: var(--sik-ink); border-color: var(--sik-line-strong); }
.sik-atc--choose:hover { background: var(--sik-hover); color: var(--sik-ink); border-color: var(--sik-ink); }
.sik-atc--choose.has-incart::after {
    content: "";
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--sik-primary);
    flex: none;
}

/* Out of stock (server-decided; the element is really disabled). A loading
   button is disabled too, so this doubles as the loading skin - .is-loading
   only takes the cursor and the opacity, and that ordering is deliberate. */
.sik-atc[disabled],
.sik-atc.is-out {
    background: var(--sik-surface-2);
    color: var(--sik-muted);
    border-color: transparent;
    box-shadow: none;
    cursor: not-allowed;
    transform: none;
}

/* Already in the cart: a filled-in state, and the label still says what a
   click does ("In cart - 2"). */
.sik-atc.is-incart {
    background: var(--sik-surface-2);
    color: var(--sik-ink);
    border-color: transparent;
    box-shadow: none;
}
.sik-atc.is-incart:hover { background: var(--sik-press); color: var(--sik-ink); }
.sik-atc--primary.is-incart { background: var(--sik-primary-soft); color: var(--sik-primary-ink); }

.sik-atc.is-loading { cursor: progress; opacity: .85; }

.sik-atc.is-success {
    background: var(--sik-success-ink);
    color: var(--sik-bg);
    border-color: transparent;
    box-shadow: none;
}

/* Icon, tick and spinner share one box and cross-fade, so nothing reflows - and
   the spinner has to stay in the markup, because SIK.showLoader() inserts one
   as the button's first child when it cannot find one, which would drop a
   stray spinner at the head of the flex row mid-click. */
.sik-atc__glyph { position: relative; flex: none; width: var(--icon-sm); height: var(--icon-sm); }
.sik-atc__ico,
.sik-atc__tick,
.sik-atc__spin {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-spring);
}
.sik-atc__glyph svg { width: 100%; height: 100%; }
.sik-atc .sik-spinner { width: 100%; height: 100%; border-width: 2px; }
.sik-atc__tick,
.sik-atc__spin { opacity: 0; transform: scale(.4); }
.sik-atc.is-incart .sik-atc__ico,
.sik-atc.is-success .sik-atc__ico { opacity: 0; transform: scale(.4); }
.sik-atc.is-incart .sik-atc__tick,
.sik-atc.is-success .sik-atc__tick { opacity: 1; transform: none; }
/* Loading wins over every resting state, so it is declared last. */
.sik-atc.is-loading .sik-atc__ico,
.sik-atc.is-loading .sik-atc__tick { opacity: 0; transform: scale(.4); }
.sik-atc.is-loading .sik-atc__spin { opacity: 1; transform: none; }

/* cart.js rewrites this node's text ("Adding…", "In cart - 12"), so it stays a
   single text node: no pseudo content that a label swap would strip. */
.sik-atc__label { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* A two-up phone grid leaves ~150px per card: the glyph gives way first. */
@media (max-width: 400px) {
    .sik-card__foot .sik-atc { font-size: var(--fs-sm); padding-inline: var(--sp-2); }
    .sik-card__foot .sik-atc__glyph { display: none; }
}

/* --------------------------------------------------------------------------
   12. Carousel rail (products, categories, brands, reviews)
   -------------------------------------------------------------------------- */
.sik-rail { position: relative; }
.sik-rail__track {
    display: flex;
    gap: var(--sp-3);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    overscroll-behavior-x: contain;
    padding-block: var(--sp-1) var(--sp-3);
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.sik-rail__track::-webkit-scrollbar { display: none; }
.sik-rail__item {
    scroll-snap-align: start;
    flex: 0 0 auto;
    width: calc((100% - (var(--rail-mobile, 2) - 1) * 12px) / var(--rail-mobile, 2));
}
@media (max-width: 767px) {
    /* A peek of the next card is the affordance that says "this scrolls". */
    .sik-rail__item { width: calc((100% - (var(--rail-mobile, 2) - 1) * 12px) / var(--rail-mobile, 2) - 14px); }
}
@media (min-width: 768px) {
    .sik-rail__track { gap: var(--sp-5); }
    .sik-rail__item { width: calc((100% - (var(--rail-tablet, 3) - 1) * 20px) / var(--rail-tablet, 3)); }
}
@media (min-width: 1024px) {
    .sik-rail__item { width: calc((100% - (var(--rail-desktop, 5) - 1) * 20px) / var(--rail-desktop, 5)); }
}
.sik-rail__nav {
    position: absolute;
    top: 38%;
    transform: translateY(-50%);
    width: 40px; height: 40px;
    border-radius: 50%;
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    box-shadow: var(--sik-shadow-card);
    display: none;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    z-index: 6;
    color: var(--sik-ink);
    transition: background-color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
@media (min-width: 1024px) { .sik-rail__nav { display: inline-flex; } }
.sik-rail__nav:hover { background: var(--sik-surface-2); }
.sik-rail__nav:disabled { opacity: 0; pointer-events: none; }
.sik-rail__nav--prev { left: -20px; }
.sik-rail__nav--next { right: -20px; }
.sik-rail__dots { display: flex; justify-content: center; gap: var(--sp-2); margin-top: var(--sp-4); }
.sik-rail__dot {
    position: relative;
    width: 6px; height: 6px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-line-strong);
    border: 0;
    padding: 0;
    cursor: pointer;
    transition: width var(--dur-base) var(--ease-out), background-color var(--dur-base) var(--ease-out);
}
.sik-rail__dot.is-active { width: 18px; background: var(--sik-ink); }

/* --------------------------------------------------------------------------
   13. Countdown
   -------------------------------------------------------------------------- */
.sik-countdown { display: flex; gap: var(--sp-2); align-items: center; }
.sik-countdown__unit {
    min-width: 52px;
    padding: var(--sp-2);
    text-align: center;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
}
.sik-countdown__num {
    display: block;
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    line-height: 1;
    color: var(--sik-ink);
    font-variant-numeric: tabular-nums;
}
.sik-countdown__label {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    color: var(--sik-muted);
}
.sik-countdown--dark .sik-countdown__unit { background: var(--sik-inverse-hover); border-color: var(--sik-inverse-line); }
.sik-countdown--dark .sik-countdown__num { color: var(--sik-on-inverse); }
.sik-countdown--dark .sik-countdown__label { color: var(--sik-on-inverse-2); }
.sik-countdown--sm .sik-countdown__unit { min-width: 42px; padding: var(--sp-1); }
.sik-countdown--sm .sik-countdown__num { font-size: var(--fs-md); }

/* --------------------------------------------------------------------------
   14. Deal of the day
   -------------------------------------------------------------------------- */
.sik-deal {
    overflow: hidden;
    border-radius: var(--sik-radius-lg);
    background: var(--sik-soft);
}
.sik-deal__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-6);
    padding: var(--sp-6) var(--sp-5);
    align-items: center;
}
@media (min-width: 900px) {
    .sik-deal__grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); padding: var(--sp-9) var(--sp-10); gap: var(--sp-9); }
}
.sik-deal__eyebrow { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-primary-ink); }
.sik-deal__title { margin-top: var(--sp-2); font-weight: var(--fw-bold); line-height: var(--lh-tight); letter-spacing: var(--ls-tighter); color: var(--sik-ink); }
.sik-deal__media { position: relative; }
.sik-deal__media a { display: block; overflow: hidden; border-radius: var(--sik-radius); background: var(--sik-media); aspect-ratio: 1; }
.sik-deal__media img { width: 100%; height: 100%; object-fit: contain; }
.sik-deal__off {
    position: absolute;
    top: var(--sp-3); left: var(--sp-3);
    display: inline-flex;
    align-items: baseline;
    gap: 3px;
    padding: 6px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-cta);
    color: var(--sik-on-cta);
    font-weight: var(--fw-bold);
    line-height: 1;
}
.sik-deal__off b { font-size: var(--fs-md); }
.sik-deal__off span { font-size: var(--fs-2xs); }
.sik-deal__product { display: block; margin-top: var(--sp-5); }
.sik-deal__product-name { display: block; margin-top: 2px; font-size: var(--fs-lg); font-weight: var(--fw-semibold); line-height: var(--lh-snug); color: var(--sik-ink); }
.sik-deal__rating { margin-top: var(--sp-2); }
.sik-deal__price { margin-top: var(--sp-3); }
.sik-deal__price .sik-price { font-size: var(--fs-2xl); }
.sik-deal__meter { margin-top: var(--sp-5); max-width: 340px; }
.sik-deal__meter-row {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-2);
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--sik-muted);
}
.sik-deal__left { color: var(--sik-ink); font-weight: var(--fw-semibold); }
.sik-deal__timer { margin-top: var(--sp-5); }
.sik-deal__timer-label { margin-bottom: var(--sp-2); font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--sik-muted); }
.sik-deal__actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-7); }
.sik-progress { height: 6px; border-radius: var(--sik-radius-pill); background: var(--sik-press); overflow: hidden; }
.sik-progress > span { display: block; height: 100%; border-radius: var(--sik-radius-pill); background: var(--sik-ink); }

/* --------------------------------------------------------------------------
   15. Flash sale

   One unit: a gradient header bar rounded only at the top, sitting on a
   surface panel that holds the rail. The decision worth knowing is the
   translucent black scrim over --sik-grad-flash. That sweep is authored as a
   decorative urgency gradient, it ends near white-orange, and the primary it
   derives from is admin-owned, so plain white text on its light stop measures
   ~2.9:1. The scrim pulls every stop back past 4.5:1 for 12px white without
   replacing the token or inventing a second gradient.
   -------------------------------------------------------------------------- */

/* The block is no longer a dark band. Line 877 lightens --sik-primary-ink and
   --sik-success-ink toward white for .sik-flash, which was right while the
   products sat on navy and washes prices and "In stock" out now that they sit
   on a surface panel. Both go back to their surface-tuned values, per theme,
   because that repoint lives outside this section and cannot be deleted here. */
.sik-flash {
    --sik-primary-ink: color-mix(in srgb, var(--sik-primary) 76%, #000);
    --sik-success-ink: color-mix(in srgb, var(--sik-success) 62%, var(--sik-ink));
}
:root[data-theme="dark"] .sik-flash {
    --sik-primary-ink: color-mix(in srgb, var(--sik-primary) 58%, #fff);
    --sik-success-ink: color-mix(in srgb, var(--sik-success) 60%, #fff);
}

/* Bar plus panel read as one object, so the depth belongs to the pair rather
   than to either half. */
.sik-flash__wrap {
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-card);
}

/* The bar. deals.php reuses .sik-flash__head with hard-coded inline white text
   and no panel beneath it, so the gradient is attached HERE and not to
   .sik-flash: that page inherits the new bar and its inline whites stay on a
   dark bed instead of landing on the page surface. */
.sik-flash__head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3) var(--sp-5);
    margin-bottom: var(--sp-6);
    padding: var(--sp-4) var(--sp-5);
    border-radius: var(--sik-radius-lg);
    color: var(--sik-on-inverse);
    background-image:
        linear-gradient(90deg, rgba(0, 0, 0, .26) 0%, rgba(0, 0, 0, .24) 52%, rgba(0, 0, 0, .30) 100%),
        var(--sik-grad-flash);
    box-shadow: var(--sik-lip);
}
/* Inside the widget the bar is the lid of the panel: no gap, no bottom
   corners. deals.php has no .sik-flash__wrap, so it keeps both. */
.sik-flash__wrap .sik-flash__head {
    margin-bottom: 0;
    border-radius: var(--sik-radius-lg) var(--sik-radius-lg) 0 0;
}
/* A flex child floors at its widest word unless told otherwise, and the title
   is one long line. Written against the element so deals.php's unclassed
   wrapper is covered by the same rule. */
.sik-flash__head > div { min-width: 0; }

.sik-flash__panel {
    padding: var(--sp-5) var(--sp-5) var(--sp-4);
    border: 1px solid var(--sik-line);
    border-top: 0;
    border-radius: 0 0 var(--sik-radius-lg) var(--sik-radius-lg);
    background: var(--sik-surface);
}

.sik-flash__lead {
    display: flex;
    flex: 1 1 260px;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
}

/* Section 2 sizes .sik-flash__title at --fs-2xl, and --fs-3xl from 768px, both
   as plain (0,1,0) rules. A later plain rule therefore pins the 20px the bar
   is built around at every width — a media query here would add no weight. */
.sik-flash__title {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-tight);
    color: var(--sik-on-inverse);
}
/* icon('zap', '', true) emits class="sik-ico" alone — no w-N — so without an
   explicit size the glyph renders at the SVG default 300x150. The circle is
   drawn on the glyph itself rather than on a wrapper, because deals.php puts
   the icon straight inside the h2 and would otherwise miss it. Global
   border-box sizing means 38 − 12 padding − 2 border leaves a 24px glyph. */
.sik-flash__title svg {
    width: 38px;
    height: 38px;
    padding: 6px;
    border: 1px solid rgba(255, 255, 255, .28);
    border-radius: 50%;
    background: rgba(255, 255, 255, .16);
    color: var(--sik-on-inverse);
}
/* Gold is this system's flourish for dark panels, and the accent word is
   20px/800 — large text, where 3:1 applies and the scrimmed bar gives ~3.9:1. */
.sik-flash__accent { color: var(--sik-gold-2); }

/* The qualifier pill. A 10% white fill is as far as it can go: at 16% the 12px
   white text on top drops to 3.8:1 against the bar showing through. */
.sik-flash__sub {
    margin: 0;
    padding: 3px var(--sp-3);
    border: 1px solid rgba(255, 255, 255, .26);
    border-radius: var(--sik-radius-pill);
    background: rgba(255, 255, 255, .10);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-normal);
    color: var(--sik-on-inverse);
}
/* deals.php prints color:rgba(255,255,255,.7) inline on its own subtitle, and
   no external rule can beat an inline one. That read 8.4:1 on the old navy
   band and would be 3.5:1 on this gradient, so the paragraph gets a dark bed
   instead of a colour change: 4.7:1, and no !important anywhere. */
.sik-flash__head > div > p[style] {
    display: inline-block;
    padding: 3px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: rgba(0, 0, 0, .22);
}

.sik-flash__aside {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3) var(--sp-4);
    min-width: 0;
}
.sik-flash__timer {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2) var(--sp-3);
    min-width: 0;
}
/* Was --sik-on-inverse-2: 72% white is 3.5:1 on this bar and this label is
   11px, so it needs the full ink. */
.sik-flash__timer-label {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--sik-on-inverse);
}

/* The cells are scoped to .sik-flash__head rather than to the widget's own
   markup, because deals_countdown() emits .sik-countdown--dark and would
   otherwise keep section 13's dark cells on this light-celled bar. Three
   classes (0,3,0) beat that modifier's (0,2,0) whichever order they land in. */
.sik-flash__head .sik-countdown {
    flex-wrap: nowrap;
    gap: var(--sp-2);
}
.sik-flash__head .sik-countdown .sik-countdown__unit {
    min-width: 54px;
    padding: 5px var(--sp-2);
    border: 0;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-on-inverse);
    box-shadow: var(--sik-shadow-xs);
}
/* --sik-primary-dark is the one brand shade dark mode does NOT re-point, which
   is exactly what this cell needs: the bar is brand-coloured and the cell is
   near-white in both themes, so a theme-reactive ink would invert under it.
   4.7:1 on white, 4.0:1 on the dark theme's #ECEDEF at 20px/700 — large text. */
.sik-flash__head .sik-countdown .sik-countdown__num {
    font-size: var(--fs-xl);
    color: var(--sik-primary-dark);
}
.sik-flash__head .sik-countdown .sik-countdown__label {
    margin-top: 1px;
    font-size: var(--fs-2xs);
    color: color-mix(in srgb, var(--sik-inverse) 72%, var(--sik-on-inverse));
}
/* On expiry app.js replaces the countdown's innerHTML with a bare span, so
   every cell rule above stops matching and the text falls back to inheriting
   white — on a white cell. The expired state has to carry its own colours. */
.sik-flash__head .sik-countdown.is-expired {
    padding: var(--sp-2) var(--sp-4);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-on-inverse);
    color: var(--sik-inverse);
}

/* Section 3 gives .sik-flash .sik-viewall a dark-band treatment — white text on
   a transparent pill — and its :hover is not gated on a fine pointer, so a tap
   would leave white text on this white pill. The resting colours are pinned on
   both states and only the lift is hover-gated. */
.sik-flash__head .sik-viewall,
.sik-flash__head .sik-viewall:hover {
    background: var(--sik-on-inverse);
    border-color: transparent;
    color: var(--sik-inverse);
    box-shadow: var(--sik-shadow-xs);
}
.sik-flash__head .sik-viewall {
    transition: color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
    .sik-flash__head .sik-viewall:hover {
        color: var(--sik-primary-dark);
        box-shadow: var(--sik-shadow-soft);
        transform: translate3d(0, var(--lift-sm), 0);
    }
}

/* Section 12 hangs the rail arrows 20px outside the rail, which inside this
   panel is 20px outside its border and over the page. Pulled back in so they
   stay on the panel. */
.sik-flash__panel .sik-rail__nav--prev { left: -12px; }
.sik-flash__panel .sik-rail__nav--next { right: -12px; }

@media (max-width: 420px) {
    .sik-flash__head { padding: var(--sp-4); gap: var(--sp-3); }
    /* Four cells at a 54px floor plus gaps is 232px, which overflows a 320px
       bar once its padding and the page gutters are taken. The floor goes and
       the digits drop a step so the row can stay unbroken, which is what makes
       a countdown readable at a glance. */
    .sik-flash__head .sik-countdown { gap: 6px; }
    .sik-flash__head .sik-countdown .sik-countdown__unit { min-width: 0; padding: 4px var(--sp-1); }
    .sik-flash__head .sik-countdown .sik-countdown__num { font-size: var(--fs-md); }
    .sik-flash__panel { padding: var(--sp-4) var(--sp-3) var(--sp-3); }
}

/* Belt and braces. widget_flash_sale() no longer prints --w-bg/--w-fg, so
   these never match today — but the builder fields are still editable, and
   app.css:868-870 would paint the section navy and force white type straight
   through the panel, in light mode only. Six classes beat that rule's five. */
:root:not([data-theme="dark"]) .sik-flash[style*="--w-bg"] { background: none; }
:root:not([data-theme="dark"]) .sik-flash[style*="--w-fg"] .sik-flash__panel,
:root:not([data-theme="dark"]) .sik-flash[style*="--w-fg"] .sik-flash__panel :is(h2, h3, .sik-heading__title, .sik-heading__sub) { color: var(--sik-ink); }
:root:not([data-theme="dark"]) .sik-flash[style*="--w-fg"] .sik-flash__head :is(h2, .sik-flash__title) { color: var(--sik-on-inverse); }

/* --------------------------------------------------------------------------
   16. Promo banner

   One deep card per active promo banner - stacked on a phone, three across
   from 768px when three banners are live: a gold serif eyebrow, a short
   headline, the banner's own subtitle, an optional measured "from" price and
   a white pill. The three colours are mixed from --sik-accent / --sik-primary
   / --sik-info against --sik-inverse rather than written out, so re-theming
   moves all three together and no card can drift out of the family.
   -------------------------------------------------------------------------- */
.sik-promos {
    display: grid;
    /* minmax(0, 1fr) and never a bare 1fr: 1fr floors at its widest child, so
       one long unbreakable headline would push the row past a 320px screen. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-5);
}
/* auto-fit rather than repeat(3, ...) because the widget renders one card per
   active banner and that count is whatever Marketing > Banners says today -
   two of the three shipped rows are inside their date window right now, and a
   fixed three-column track would leave a dead column next to them. Empty
   auto-fit tracks collapse, so the cards always fill the row evenly. The
   min(100%, ...) floor is what keeps a track from being wider than the
   container, which a bare 210px would allow. */
@media (min-width: 768px) {
    .sik-promos {
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr));
        gap: var(--sp-4);
    }
}
@media (min-width: 1024px) {
    .sik-promos { gap: var(--sp-5); }
}

.sik-promo {
    /* Variant 1 is also the default, so a fourth banner - or a card that lost
       its variant class - still lands inside the wine family rather than on
       a bare --sik-inverse. */
    --promo-deep: color-mix(in srgb, var(--sik-accent) 24%, var(--sik-inverse));
    --promo-lit:  color-mix(in srgb, var(--sik-accent) 62%, var(--sik-inverse));

    /* The card is dark in both themes, so its type roles are the inverse ones.
       The markup also carries .sik-onwine; this repeats the part that matters
       because a card that lost that one class would render near-black ink on
       a near-black panel. */
    --sik-ink:   var(--sik-on-inverse);
    --sik-ink-2: var(--sik-on-inverse-2);

    position: relative;
    /* The pseudo-element wash and the photo both sit at z-index 0 under the
       copy; isolating stops that ordering leaking into the page. */
    isolation: isolate;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    min-width: 0;
    padding: var(--sp-6) var(--sp-5);
    border-radius: var(--sik-radius-lg);
    background-color: var(--promo-deep);
    box-shadow: var(--sik-shadow-lift), var(--sik-lip);
    color: var(--sik-on-inverse);
    /* A 288px card is narrow enough that one long product or brand name in a
       headline would otherwise run out through the rounded corner. */
    overflow-wrap: break-word;
}
@media (min-width: 768px) { .sik-promo { padding: var(--sp-7) var(--sp-6); } }

/* Wine, plum, indigo. Every stop is a mix of brand tokens against
   --sik-inverse, which is #4A041C in light and near-black in dark - so the
   same three recipes read as a wine family on the light page and a neutral
   -black family on the dark one. White copy clears 4.5:1 against the lightest
   stop of all six results. */
.sik-promo--2 {
    --promo-deep: color-mix(in srgb, var(--sik-primary) 14%, var(--sik-inverse));
    --promo-lit:  color-mix(in srgb, var(--sik-primary) 36%, var(--sik-inverse));
}
/* --sik-info is the only cool hue the brand set has. It is cut with a little
   --sik-accent so the third card is a brand-adjacent indigo rather than the
   status blue a shopper sees on an info alert. */
.sik-promo--3 {
    --promo-deep: color-mix(in srgb, var(--sik-info) 12%, var(--sik-inverse));
    --promo-lit:  color-mix(in srgb, var(--sik-info) 46%,
                      color-mix(in srgb, var(--sik-accent) 18%, var(--sik-inverse)));
}

/* banners.bg_color arrives as --promo-bg rather than as `background:`. Printed
   as a background it beat every class-based gradient in light mode and was
   then flattened by an !important rule in dark; as a custom property it tints
   the card instead of erasing the variant's wash. Light mode only, the same
   call .sik-section[style*="--w-bg"] already makes: a colour picked against a
   white page cannot be trusted on the dark palette. */
:root:not([data-theme="dark"]) .sik-promo[style*="--promo-bg"] { background-color: var(--promo-bg); }

/* Two pools of light from the top corners. The bottom right stays at the deep
   stop because that is where the photo bleeds in, and the copy has to survive
   being laid over the pair of them. */
.sik-promo::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(130% 110% at 0% 0%, var(--promo-lit) 0%, transparent 62%),
        radial-gradient(90% 80% at 100% 4%, color-mix(in srgb, var(--promo-lit) 42%, transparent) 0%, transparent 58%);
}

.sik-promo__photo {
    position: absolute;
    right: -8%;
    bottom: -10%;
    z-index: 0;
    width: 58%;
    max-width: 220px;
    /* Low enough that a product shot on a white background cannot lift the
       panel under the copy out of contrast - measured at .22 against the
       lightest stop of all three variants. */
    opacity: .16;
    pointer-events: none;
    transform-origin: 100% 100%;
    /* A mask reads alpha, not colour: #000 here is "opaque", and it feathers
       the photo's edges into the panel so it looks like a bleed rather than a
       rectangle pasted into the corner. */
    -webkit-mask-image: radial-gradient(125% 125% at 100% 100%, #000 34%, transparent 78%);
            mask-image: radial-gradient(125% 125% at 100% 100%, #000 34%, transparent 78%);
    transition: transform var(--dur-slow) var(--ease-out),
                opacity var(--dur-slow) var(--ease-out);
}
.sik-promo__photo img { display: block; width: 100%; height: auto; }

/* The copy absorbs the spare height so three cards with different amounts of
   text still line their buttons up along the bottom. */
.sik-promo__body { position: relative; z-index: 1; flex: 1 1 auto; min-width: 0; }

/* .sik-flourish is wine by default and only turns gold inside a handful of
   known wrappers - on this card in light theme that would be #90002A on
   near-black. Re-pointed here so the card is not one missing class away from
   an invisible eyebrow. */
.sik-promo .sik-flourish { color: var(--sik-gold-2); }
.sik-promo__eyebrow { font-size: var(--fs-base); }

.sik-promo__title {
    margin-top: var(--sp-2);
    font-size: var(--fs-2xl);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    color: var(--sik-on-inverse);
}
/* The "Display heading roles" block near the top of the file takes
   .sik-promo__title to --fs-4xl from 768px. That was right for one full-width
   panel and is four lines of 36px inside a 230px card. Same specificity,
   later in the file, so it wins without !important - and the 768px case has
   to be restated or the desktop rule simply survives. */
@media (min-width: 768px)  { .sik-promo__title { font-size: var(--fs-xl); } }
@media (min-width: 1024px) { .sik-promo__title { font-size: var(--fs-2xl); } }
/* title_accent is the payoff half of the headline - "₹149", "83% off",
   "over ₹999". On its own line every card gets the same two-line shape
   without the renderer having to guess where the break should fall. */
.sik-promo__title em { display: block; font-style: normal; color: var(--sik-on-inverse-2); }

.sik-promo__sub {
    margin-top: var(--sp-3);
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
    color: var(--sik-on-inverse-2);
    /* banners.description is the fallback when a banner has no subtitle, and
       it is the two-sentence paragraph the old full-width panel carried.
       Clamped so one wordy banner cannot stretch the other two cards. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    line-clamp: 3;
    overflow: hidden;
}

.sik-promo__price {
    margin-top: var(--sp-4);
    display: flex;
    align-items: baseline;
    gap: var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--ls-widest);
    color: var(--sik-on-inverse-2);
}
.sik-promo__price b {
    font-size: var(--fs-3xl);
    font-weight: var(--fw-black);
    line-height: 1;
    letter-spacing: var(--ls-tighter);
    /* --sik-gold-ink is the shade for gold on white and goes muddy on a dark
       panel; --sik-gold-2 clears 6:1 against every stop used here. */
    color: var(--sik-gold-2);
}

.sik-promo__cta {
    position: relative;
    z-index: 1;
    margin-top: var(--sp-5);
    align-self: flex-start;
    max-width: 100%;
    /* .sik-btn is nowrap because it was written for toolbars. A long button
       label in a 248px card has to wrap instead of pushing the grid wide. */
    white-space: normal;
}

/* The reveal rules at the end of this file - `[data-anim].is-in { transform:
   none }` and `.js [data-anim] { transition: ... }` - land after section 16 at
   the same (0,2,0) as a bare .sik-promo, so they would cancel both the lift
   and its timing. Naming the wrapper buys the specificity back. */
.sik-promos .sik-promo[data-anim] {
    transition: opacity var(--dur-slower) var(--ease-out),
                transform var(--dur-slow) var(--ease-out),
                box-shadow var(--dur-slow) var(--ease-out);
}
/* Keyboard users get the depth change without the movement, and outside the
   hover query so a touch device reaches it too. */
.sik-promos .sik-promo:focus-within { box-shadow: var(--sik-shadow-pop), var(--sik-lip); }

@media (hover: hover) {
    .sik-promos .sik-promo:hover {
        transform: translate3d(0, var(--lift), 0);
        box-shadow: var(--sik-shadow-pop), var(--sik-lip);
    }
    .sik-promos .sik-promo:hover .sik-promo__photo { transform: scale(1.07); opacity: .22; }
}

/* The card's own lift is already switched off by the `[data-anim]` rules for
   prefers-reduced-motion and .sik-noanim at the end of the file. The photo is
   not a [data-anim] element, so it has to be said here. */
@media (prefers-reduced-motion: reduce) {
    .sik-promos .sik-promo:hover .sik-promo__photo { transform: none; }
}
.sik-noanim .sik-promos .sik-promo:hover .sik-promo__photo { transform: none; }

/* --------------------------------------------------------------------------
   16b. Offer strip

   Three running promotions side by side: a scratch coupon, the free-delivery
   meter and the offer code. Each is its own card and each can be absent, so
   the row is a flexible grid rather than three fixed thirds - two cards fill
   the width between them rather than leaving a hole where the third was.
   -------------------------------------------------------------------------- */
.sik-strip {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4);
}
@media (min-width: 768px)  { .sik-strip { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); } }

.sik-strip__card {
    position: relative;
    overflow: hidden;
    border-radius: var(--sik-radius-lg);
    min-height: 148px;
    display: flex;
    box-shadow: var(--sik-shadow-card);
}
.sik-strip__body {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    justify-content: center;
    padding: var(--sp-5);
    width: 100%;
    min-width: 0;
}

/* Two brand-coloured cards either side of a neutral one, so the meter - the
   panel carrying a real number - is the one that reads as information rather
   than as an advertisement. */
.sik-strip__card--scratch { background: var(--sik-grad-cta); color: #fff; }
.sik-strip__card--offer   { background: var(--sik-grad-wine); color: #fff; }
.sik-strip__card--ship {
    background: var(--sik-surface);
    border: 1px solid var(--sik-line);
    color: var(--sik-ink);
}

.sik-strip__title {
    font-size: var(--fs-lg);
    font-weight: var(--fw-black);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    margin: 0;
}
.sik-strip__flourish { font-size: var(--fs-lg); }
.sik-strip__fine { font-size: var(--fs-2xs); margin: 0; opacity: .86; }
.sik-strip__card--ship .sik-strip__fine { opacity: 1; color: var(--sik-muted); }

/* --- the delivery meter ---------------------------------------------------- */
.sik-strip__row { display: flex; align-items: center; gap: var(--sp-3); }
.sik-strip__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px; height: 40px;
    flex: none;
    border-radius: 50%;
    background: color-mix(in srgb, var(--sik-primary) 12%, var(--sik-surface));
    color: var(--sik-primary-ink);
}
.sik-strip__eyebrow { margin: 0; font-size: var(--fs-2xs); font-weight: var(--fw-medium); color: var(--sik-muted); }
.sik-strip__amount { margin: 0; font-size: var(--fs-xl); font-weight: var(--fw-black); line-height: 1.1; letter-spacing: var(--ls-tight); }

.sik-strip__meter {
    height: 9px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface-2);
    border: 1px solid var(--sik-line);
    overflow: hidden;
}
.sik-strip__meter-fill {
    display: block;
    height: 100%;
    border-radius: inherit;
    background: var(--sik-grad-cta);
    /* The width is printed inline from the visitor's own subtotal; the
       transition is only here so it animates when the cart changes under it. */
    transition: width var(--dur-slow) var(--ease-out);
}
.sik-noanim .sik-strip__meter-fill { transition: none; }
.sik-strip__won { display: inline-flex; align-items: center; gap: var(--sp-1); color: var(--sik-success-ink); font-weight: var(--fw-bold); }

/* --- the code chips -------------------------------------------------------- */
.sik-strip__code {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    align-self: flex-start;
    min-height: 34px;
    padding: 0 var(--sp-3);
    border-radius: var(--sik-radius-sm);
    background: rgba(255, 255, 255, .14);
    border: 1px dashed rgba(255, 255, 255, .45);
    color: inherit;
    font: inherit;
    font-size: var(--fs-xs);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-strip__code:hover { background: rgba(255, 255, 255, .24); }
.sik-strip__code b { font-weight: var(--fw-bold); letter-spacing: var(--ls-wide); color: var(--sik-gold-2); }
.sik-strip__code svg { width: var(--icon-xs); height: var(--icon-xs); opacity: .8; }

/* --- the scratch panel ------------------------------------------------------
   The prize sits underneath; app.js lays a canvas foil over this box and the
   shopper rubs it off. Before the foil exists - and for anyone using a
   keyboard - the Reveal button does the same job, which is why the code is
   hidden with a filter rather than with display:none: the button reveals it by
   dropping one class, with or without a canvas. */
.sik-scratch {
    position: relative;
    display: grid;
    place-items: center;
    min-height: 56px;
    border-radius: var(--sik-radius-sm);
    background: rgba(255, 255, 255, .16);
    border: 1px dashed rgba(255, 255, 255, .5);
    overflow: hidden;
}
.sik-scratch__prize { display: grid; justify-items: center; gap: 2px; padding: var(--sp-2); }
.sik-scratch__label { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: var(--ls-widest); opacity: .8; }
.sik-scratch__code {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    font-size: var(--fs-lg);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-wider);
    color: var(--sik-gold-2);
    cursor: pointer;
}
/* Unrevealed: the code is blurred out rather than removed, so the box is
   already the right size and nothing shifts when it is revealed. */
.sik-scratch:not(.is-revealed) .sik-scratch__prize { filter: blur(7px); opacity: .55; user-select: none; }
.sik-scratch:not(.is-revealed) .sik-scratch__code { pointer-events: none; }

.sik-scratch__reveal {
    position: absolute;
    inset: 0;
    z-index: 2;
    width: 100%;
    border: 0;
    background: transparent;
    color: #fff;
    font: inherit;
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    cursor: pointer;
}
.sik-scratch.is-revealed .sik-scratch__reveal { display: none; }
/* The foil canvas, once app.js has drawn it. It sits over the button, so the
   button's own label is only read by assistive tech from then on. */
.sik-scratch__foil {
    position: absolute;
    inset: 0;
    z-index: 3;
    touch-action: none;
    cursor: grab;
}
.sik-scratch.is-revealed .sik-scratch__foil { display: none; }

/* --------------------------------------------------------------------------
   16c. Combo offers

   A card for a set of products, and the page that card leads to. The struck
   number in both is what the components cost TODAY bought separately, never
   their MRP, so it is sized as a quiet footnote rather than as a second price.
   The class names below are the contract the combo markup is written against:
   the card comes from combo_card() in includes/widgets.php and the page block
   from combo.php, so a name invented on either side strands a rule here.
   -------------------------------------------------------------------------- */

/* --- the card ------------------------------------------------------------- */
/* Unlike .sik-card this always paints its own surface instead of waiting for
   body.sik-cards-boxed: a combo card holds a nested well of component
   thumbnails, and under the Flat card style that well would float on the page
   with no card around it to belong to. The frame is still the admin's — border,
   shadow and radius all arrive from Settings > Theme — so Flat, Bordered, Soft
   and Elevated stay four different looks. */
.sik-combo {
    position: relative;
    display: flex;
    flex-direction: column;
    height: 100%;
    /* The other half of .sik-grid's minmax(0, 1fr): the track may shrink only
       if the card may. A long combo name used to floor a two-up grid above
       320px exactly this way. */
    min-width: 0;
    overflow: hidden;
    background: var(--sik-surface);
    border: 1px solid var(--sik-card-border);
    border-radius: var(--sik-radius);
    box-shadow: var(--sik-card-shadow);
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out);
}
@media (hover: hover) {
    .sik-combo:hover {
        transform: translate3d(0, var(--lift-sm), 0);
        box-shadow: var(--sik-shadow-card), var(--sik-shadow-brand-sm);
        border-color: color-mix(in srgb, var(--sik-primary) 30%, var(--sik-card-border));
    }
}
/* Settings > Theme > enable animations = off has to reach the transitions too,
   not only the keyframes. prefers-reduced-motion is already covered: every
   duration here is a --dur-* token and section 1 collapses those to .01ms. */
.sik-noanim .sik-combo:hover,
.sik-noanim .sik-combo:hover .sik-combo__img { transform: none; }

/* A group shot, not a packshot: 4:3 rather than the product card's square, so
   three or four items laid out together are not cropped to fit a circle of
   white. */
.sik-combo__media {
    position: relative;
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
    background-color: var(--sik-media);
    /* A whisper of brand across the well, so a letterboxed photo sits on
       something warmer than flat grey. */
    background-image: linear-gradient(135deg, color-mix(in srgb, var(--sik-primary) 7%, transparent) 0%, transparent 62%);
}
.sik-combo__img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slow) var(--ease-out);
}
@media (hover: hover) { .sik-combo:hover .sik-combo__img { transform: scale(1.04); } }

/* Both spellings land on the same rule on purpose: the card writes
   __badges (after .sik-card__badges) and the page block reads more naturally
   as a single __badge, and a mismatch between the two would leave a badge
   sitting in the document flow above the photo. */
.sik-combo__badges,
.sik-combo__badge {
    position: absolute;
    top: var(--sp-2); left: var(--sp-2);
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-1);
    /* The badge lies on the photo link - it must never eat the click. */
    pointer-events: none;
}
/* Mirrors .sik-card__badges .sik-badge, because a combo card sits in the same
   grid row as product cards and two badge treatments side by side read as a
   bug. The shadow is the load-bearing half: combo_badge() only ever returns
   the two palest tones - `soft` for a custom label or Featured, `amber` for
   Best seller - and both dissolve into a group shot taken on white without
   it. */
.sik-combo__badges .sik-badge,
.sik-combo__badge .sik-badge {
    font-size: 10px;
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    box-shadow: var(--sik-shadow-xs);
}

.sik-combo__body {
    display: flex;
    flex-direction: column;
    gap: 6px;
    flex: 1;
    min-width: 0;
    padding: var(--sp-4);
}

.sik-combo__name {
    margin: 0;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    letter-spacing: -0.005em;
    color: var(--sik-ink);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Two lines reserved whether the name fills them or not, so the price rows
       across a grid row start level. */
    min-height: calc(var(--lh-snug) * 2em);
}
.sik-combo__name a { color: inherit; }
/* The -ink shade, not the raw primary: the brand pink at this size on white is
   about 3.2:1. */
.sik-combo__name a:hover {
    color: var(--sik-primary-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
    text-decoration-thickness: 1px;
}

.sik-combo__sub {
    margin: 0;
    min-width: 0;
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
    overflow-wrap: anywhere;
}

/* --- the component thumbnails --------------------------------------------
   A shallow well rather than a bare row: it is the one part of the card that
   is a list of other things, and under the Flat card style there would
   otherwise be no edge telling a shopper where the set stops. */
.sik-combo__items {
    display: flex;
    align-items: center;
    min-width: 0;
    margin: var(--sp-1) 0 0;
    padding: var(--sp-2);
    list-style: none;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-surface-2);
    /* combo_card() only prints four, but a hand-edited card or a wider style
       must clip here rather than widen the grid track it sits in. */
    overflow: hidden;
}
/* Overlapped, the way a set reads: four 44px thumbs plus the "+N" fit inside a
   one-column card at 320px with the gutters still on. */
.sik-combo__items > * + * { margin-left: -10px; }
.sik-combo__thumb {
    position: relative;
    flex: none;
    width: 44px;
    height: 44px;
    border-radius: var(--sik-radius-xs);
    background: var(--sik-surface);
    /* A ring in the well's own colour, so two overlapping photos keep a visible
       edge between them without a second border weight. */
    box-shadow: 0 0 0 2px var(--sik-surface-2);
}
.sik-combo__thumb img {
    display: block;
    width: 100%;
    height: 100%;
    padding: 3px;
    object-fit: contain;
    border-radius: inherit;
}
/* Quantity only when it is more than one, and pinned to the corner rather than
   placed in the row: "x2" is a property of one thumbnail, and a shopper who
   reads it as a separate item has been told the set is bigger than it is. */
.sik-combo__qty {
    position: absolute;
    right: -3px;
    bottom: -3px;
    min-width: 17px;
    padding: 0 3px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-ink);
    color: var(--sik-bg);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    line-height: 17px;
    text-align: center;
}
.sik-combo__more {
    /* Positioned, like .sik-combo__thumb: the thumbs are position:relative, so a
       static "+N" would paint UNDER the fourth thumb and lose its rounded left
       edge to that thumb's ring - the one item in the row overlapping the wrong
       way round. */
    position: relative;
    flex: none;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--sik-radius-xs);
    background: color-mix(in srgb, var(--sik-primary) 12%, var(--sik-surface));
    box-shadow: 0 0 0 2px var(--sik-surface-2);
    color: var(--sik-primary-ink);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
}

/* --- price, saving, stock -------------------------------------------------- */
.sik-combo__price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 2px var(--sp-2);
    min-width: 0;
    margin-top: var(--sp-1);
}
/* Not on phones: at two-up the price shares its row with the struck number and
   the percentage, and all three have to fit. */
@media (min-width: 768px) { .sik-combo__price .sik-price { font-size: var(--fs-xl); } }

/* .sik-gchip carries the colour and the shape; this only places it. max-width
   keeps a long money string inside the card, which .sik-combo's overflow then
   clips rather than letting it set the track's min-content width. */
.sik-combo__save {
    align-self: flex-start;
    max-width: 100%;
    margin-top: 2px;
}

/* Warning ink, matching .sik-card__stock: combo_decorate() only raises
   low_stock at five sets or fewer, so this is scarcity, not inventory. */
.sik-combo__stock {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--sik-warning-ink);
}

.sik-combo__foot { margin-top: auto; padding-top: var(--sp-3); }
.sik-combo__cta { gap: var(--sp-2); }
@media (pointer: coarse) { .sik-combo__cta { min-height: var(--tap); } }

/* --- the four builder card styles ----------------------------------------- */
.sik-combo--compact .sik-combo__body { padding: var(--sp-3); }
.sik-combo--compact .sik-combo__sub,
.sik-combo--minimal .sik-combo__sub { display: none; }
.sik-combo--compact .sik-combo__thumb,
.sik-combo--compact .sik-combo__more { width: 36px; height: 36px; }

/* Minimal drops the box entirely, so the well inside it has to go too - a
   bordered strip floating with no card around it is the one thing that reads
   as a mistake rather than as a style. */
.sik-combo--minimal {
    background: transparent;
    border-color: transparent;
    box-shadow: none;
}
.sik-combo--minimal .sik-combo__media { border-radius: var(--sik-radius); }
.sik-combo--minimal .sik-combo__body { padding: var(--sp-3) 2px 0; }
.sik-combo--minimal .sik-combo__items {
    padding-inline: 0;
    background: transparent;
    border-color: transparent;
}
.sik-combo--minimal .sik-combo__thumb,
.sik-combo--minimal .sik-combo__more { box-shadow: 0 0 0 2px var(--sik-surface); }

/* Premium is the gold flourish, as a hairline. Gold is never a background for
   dark text in this palette - it is the warm highlight on an edge. */
.sik-combo--premium { border-color: color-mix(in srgb, var(--sik-gold) 44%, var(--sik-line)); }
.sik-combo--premium .sik-combo__name { letter-spacing: var(--ls-tight); }

/* Horizontal only above the tablet breakpoint: below it there is no width for
   a photo column and a body side by side, so the card stays stacked. */
@media (min-width: 768px) {
    .sik-combo--row { flex-direction: row; }
    .sik-combo--row .sik-combo__media {
        flex: none;
        width: 42%;
        max-width: 260px;
        aspect-ratio: auto;
    }
}

/* --- the combo page --------------------------------------------------------
   Photo and gallery on one side, the set and its price on the other, with the
   component list underneath on a phone. The buy panel is a real panel rather
   than loose text: it is the only place a shopper confirms what the set costs
   before leaving the page. */
.sik-combopage {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-6);
}
@media (min-width: 1024px) {
    .sik-combopage {
        grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
        gap: var(--sp-8);
        align-items: start;
    }
}

.sik-combopage__media {
    position: relative;
    overflow: hidden;
    border-radius: var(--sik-radius-lg);
    background-color: var(--sik-media);
    background-image: linear-gradient(135deg, color-mix(in srgb, var(--sik-primary) 7%, transparent) 0%, transparent 62%);
}
.sik-combopage__img { display: block; width: 100%; height: auto; aspect-ratio: 4 / 3; object-fit: cover; }
.sik-combopage__gallery {
    display: flex;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
    padding-bottom: var(--sp-1);
    list-style: none;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}
.sik-combopage__gthumb {
    flex: none;
    width: 64px;
    height: 64px;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-surface);
    overflow: hidden;
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease-out);
}
.sik-combopage__gthumb img { display: block; width: 100%; height: 100%; padding: 4px; object-fit: contain; }
.sik-combopage__gthumb.is-active { border-color: var(--sik-primary); }
@media (pointer: coarse) { .sik-combopage__gthumb { width: var(--tap); height: var(--tap); } }

.sik-combopage__body { display: flex; flex-direction: column; gap: var(--sp-3); min-width: 0; }
.sik-combopage__title {
    margin: 0;
    font-size: var(--fs-3xl);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
    overflow-wrap: anywhere;
}
.sik-combopage__sub { margin: 0; font-size: var(--fs-md); line-height: var(--lh-relaxed); color: var(--sik-muted); }

/* The buy panel. Its own surface and hairline, so it survives every card style
   the theme prints - none of those tokens belong to a page block. */
.sik-combopage__buy {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-5);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-surface-2);
}
.sik-combopage__price { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--sp-1) var(--sp-3); min-width: 0; }
.sik-combopage__price .sik-price { font-size: var(--fs-3xl); letter-spacing: var(--ls-tight); }
.sik-combopage__price .sik-price--mrp { font-size: var(--fs-md); }
.sik-combopage__save { align-self: flex-start; max-width: 100%; }
.sik-combopage__stock { font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-warning-ink); }
.sik-combopage__cta { width: 100%; }
@media (pointer: coarse) { .sik-combopage__cta { min-height: var(--tap); } }
/* What the set is made of, restated in words under the price: a shopper about
   to pay for four things at once should not have to scroll back to the photo
   to check which four. */
.sik-combopage__note { margin: 0; font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }

.sik-combopage__items { display: grid; gap: var(--sp-3); list-style: none; margin: 0; padding: 0; }
.sik-combopage__item {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    padding: var(--sp-3);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
}
.sik-combopage__ithumb {
    flex: none;
    width: 56px;
    height: 56px;
    padding: var(--sp-1);
    object-fit: contain;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-surface);
}
/* minmax(0, …) in spirit: a long unbroken product name must not push the row
   wider than the card, which is what a bare flex child would do. */
.sik-combopage__ilines { flex: 1; min-width: 0; display: grid; gap: 2px; }
.sik-combopage__iname {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    overflow-wrap: anywhere;
}
a.sik-combopage__iname:hover { color: var(--sik-primary-ink); }
.sik-combopage__imeta { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-combopage__iprice { flex: none; font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--sik-ink); }

.sik-combopage__desc { font-size: var(--fs-base); line-height: var(--lh-relaxed); color: var(--sik-ink-2); }
.sik-combopage__desc > * + * { margin-top: var(--sp-3); }

/* --- narrow phones --------------------------------------------------------- */
@media (max-width: 400px) {
    .sik-combo__body { padding: var(--sp-3); }
    .sik-combo__items { padding: var(--sp-1) var(--sp-2); }
    .sik-combopage__title { font-size: var(--fs-2xl); }
    .sik-combopage__buy { padding: var(--sp-4); }
    /* The price drops below the name rather than beside it, so neither has to
       give up characters to the other at 320px. */
    .sik-combopage__item { flex-wrap: wrap; }
    .sik-combopage__iprice { margin-left: calc(56px + var(--sp-3)); }
}

/* --------------------------------------------------------------------------
   17. Stats band
   -------------------------------------------------------------------------- */
.sik-stats {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-6) var(--sp-4);
}
@media (min-width: 768px) { .sik-stats { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.sik-stat { display: flex; flex-direction: column; gap: var(--sp-1); }
.sik-stat__icon { display: none; }
.sik-stat__value { font-size: var(--fs-3xl); font-weight: var(--fw-bold); letter-spacing: var(--ls-tighter); color: var(--sik-ink); line-height: 1; }
.sik-stat__label { font-size: var(--fs-sm); color: var(--sik-muted); }

/* --------------------------------------------------------------------------
   18. Testimonials and customer reviews
   -------------------------------------------------------------------------- */
.sik-quote {
    height: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-6);
    border-radius: var(--sik-radius);
    background: var(--sik-surface-2);
}
.sik-quote__text { flex: 1; font-size: var(--fs-md); line-height: var(--lh-relaxed); color: var(--sik-ink-2); }
.sik-quote__author { display: flex; align-items: center; gap: var(--sp-3); }
.sik-quote__avatar { width: 40px; height: 40px; border-radius: 50%; object-fit: cover; background: var(--sik-media); flex: none; }
.sik-quote__name { display: block; font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-quote__role { display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }

/* The homepage review wall: an overall score, then real approved reviews. */
.sik-reviews { display: grid; gap: var(--sp-6); }
@media (min-width: 1024px) { .sik-reviews { grid-template-columns: 280px minmax(0, 1fr); gap: var(--sp-9); align-items: start; } }
.sik-reviews__summary { display: grid; gap: var(--sp-2); align-content: start; }
.sik-reviews__score {
    font-size: var(--fs-5xl);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-tighter);
    line-height: 1;
    color: var(--sik-ink);
    font-variant-numeric: tabular-nums;
}
.sik-reviews__count { font-size: var(--fs-sm); color: var(--sik-muted); }
.sik-reviews__list { display: grid; gap: var(--sp-4); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 640px)  { .sik-reviews__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1280px) { .sik-reviews__list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

.sik-review {
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
    padding: var(--sp-5);
    border-radius: var(--sik-radius);
    background: var(--sik-surface-2);
    min-width: 0;
}
.sik-review__head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.sik-review__verified { display: inline-flex; align-items: center; gap: var(--sp-1); font-size: var(--fs-xs); font-weight: var(--fw-medium); color: var(--sik-success-ink); }
.sik-review__title { font-size: var(--fs-md); font-weight: var(--fw-semibold); line-height: var(--lh-snug); color: var(--sik-ink); }
.sik-review__text {
    font-size: var(--fs-base);
    line-height: var(--lh-relaxed);
    color: var(--sik-ink-2);
    display: -webkit-box;
    -webkit-line-clamp: 5;
    -webkit-box-orient: vertical;
    overflow: hidden;
    overflow-wrap: anywhere;
}
.sik-review__meta { margin-top: auto; display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-1) var(--sp-2); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-review__author { font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-review__product {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--sik-line);
    font-size: var(--fs-sm);
    color: var(--sik-ink);
    min-width: 0;
}
.sik-review__product img { width: 40px; height: 40px; border-radius: var(--sik-radius-sm); object-fit: cover; background: var(--sik-media); flex: none; }
.sik-review__product span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sik-review__product:hover span { text-decoration: underline; text-underline-offset: 3px; }

/* --------------------------------------------------------------------------
   18b. FAQ

   Native <details>: keyboard, screen readers and find-in-page all work with
   no script, and every question stays in the DOM for search engines.
   Used by the homepage FAQ widget and the /faq page.
   -------------------------------------------------------------------------- */
.sik-faq { display: grid; gap: var(--sp-6); }
@media (min-width: 1024px) {
    .sik-faq { grid-template-columns: minmax(0, 360px) minmax(0, 1fr); gap: var(--sp-10); align-items: start; }
    .sik-faq > .sik-heading { position: sticky; top: calc(var(--sik-header-h) + var(--sp-6)); margin-bottom: 0; }
    .sik-faq > .sik-heading.sik-heading--row { display: grid; justify-items: start; }
}
.sik-faq__list { border-top: 1px solid var(--sik-line); }
.sik-faq__item { border-bottom: 1px solid var(--sik-line); }
.sik-faq__q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    min-height: 60px;
    padding: var(--sp-4) 0;
    cursor: pointer;
    list-style: none;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    -webkit-tap-highlight-color: transparent;
}
.sik-faq__q::-webkit-details-marker { display: none; }
.sik-faq__q:hover { color: var(--sik-ink); }
.sik-faq__q:focus-visible { outline-offset: 4px; }
.sik-faq__icon {
    position: relative;
    flex: none;
    width: 28px; height: 28px;
    border-radius: 50%;
    background: var(--sik-surface-2);
    transition: background-color var(--dur-fast) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.sik-faq__icon::before,
.sik-faq__icon::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 11px; height: 1.6px;
    margin: -0.8px 0 0 -5.5px;
    border-radius: 2px;
    background: var(--sik-ink);
    transition: transform var(--dur-base) var(--ease-out);
}
.sik-faq__icon::after { transform: rotate(90deg); }
.sik-faq__item[open] .sik-faq__icon { background: var(--sik-ink); }
.sik-faq__item[open] .sik-faq__icon::before,
.sik-faq__item[open] .sik-faq__icon::after { background: var(--sik-bg); }
.sik-faq__item[open] .sik-faq__icon::after { transform: rotate(0deg); }
.sik-faq__a {
    padding: 0 var(--sp-10) var(--sp-5) 0;
    max-width: 72ch;
    font-size: var(--fs-base);
    line-height: var(--lh-relaxed);
    color: var(--sik-ink-2);
    overflow-wrap: anywhere;
}
@media (max-width: 639px) { .sik-faq__a { padding-right: 0; } }
.sik-faq__item[open] .sik-faq__a { animation: sik-faq-open var(--dur-base) var(--ease-out) both; }
@keyframes sik-faq-open { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }


/* --- the FAQ list, as cards ------------------------------------------------
   The hairline list reads as a table of contents; on a festive storefront the
   questions should read as things you can open. Each one becomes a surface
   card that lifts slightly on hover and takes a brand tint when it is open, so
   the answer you are reading is obviously the one you asked for.

   The answer still cannot transition its height - a closed <details> hides its
   children outright, so there is nothing to transition from. It animates on
   reveal instead, which is the part a reader actually notices. */
.sik-faq__list {
    display: grid;
    gap: var(--sp-3);
    /* The old top hairline belonged to a list that no longer exists. */
    border-top: 0;
}
.sik-faq__item {
    border: 1px solid var(--sik-line);
    border-bottom: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    overflow: hidden;
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out),
                background-color var(--dur-base) var(--ease-out);
}
@media (hover: hover) {
    .sik-faq__item:hover { border-color: var(--sik-line-strong); box-shadow: var(--sik-shadow-soft); }
}
/* Open: a brand-tinted card with a brand hairline down its leading edge, so a
   long page of questions still shows at a glance which one is expanded. */
.sik-faq__item[open] {
    border-color: color-mix(in srgb, var(--sik-primary) 34%, transparent);
    background: color-mix(in srgb, var(--sik-primary) 4%, var(--sik-surface));
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-noanim .sik-faq__item { transition: none; }

.sik-faq__q {
    padding: var(--sp-4) var(--sp-5);
    min-height: 62px;
}
.sik-faq__item[open] .sik-faq__q { color: var(--sik-primary-ink); }

/* The plus becomes the brand gradient when open, rather than flat ink. */
.sik-faq__icon {
    width: 30px; height: 30px;
    background: color-mix(in srgb, var(--sik-primary) 10%, var(--sik-surface-2));
}
.sik-faq__icon::before,
.sik-faq__icon::after { background: var(--sik-primary-ink); }
.sik-faq__item[open] .sik-faq__icon { background: var(--sik-grad-cta); box-shadow: var(--sik-shadow-brand-sm); }
.sik-faq__item[open] .sik-faq__icon::before,
.sik-faq__item[open] .sik-faq__icon::after { background: var(--sik-on-cta); }

.sik-faq__a {
    padding: 0 var(--sp-5) var(--sp-5);
    /* The answer lines up under the question, not under the icon column. */
    max-width: 76ch;
}
@media (max-width: 639px) { .sik-faq__a { padding-inline: var(--sp-4); } }
@media (max-width: 639px) { .sik-faq__q { padding-inline: var(--sp-4); } }

/* On a dark panel the tint has to come from the surface, not from white. */
[data-theme="dark"] .sik-faq__item[open] {
    background: color-mix(in srgb, var(--sik-primary) 12%, var(--sik-surface));
}

/* --------------------------------------------------------------------------
   18c. Offers — coupons the store chose to advertise

   Shared by the homepage offer rail, the product page and the cart. Each one
   is a real coupon, so the code is always something checkout will accept.
   -------------------------------------------------------------------------- */
.sik-offers { display: grid; gap: var(--sp-2); }
.sik-offer {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-width: 0;
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    border: 1px dashed var(--sik-line-strong);
    border-radius: var(--sik-radius);
    background: var(--sik-elevated);
}
.sik-offer__icon {
    flex: none;
    width: 34px; height: 34px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
}
.sik-offer__body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.sik-offer__title { font-size: var(--fs-base); font-weight: var(--fw-semibold); line-height: var(--lh-snug); color: var(--sik-ink); }
.sik-offer__terms { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-offer__code {
    flex: none;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 36px;
    padding: 0 var(--sp-3);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-surface-2);
    color: var(--sik-ink);
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    letter-spacing: .04em;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), border-color var(--dur-fast) var(--ease-out);
}
.sik-offer__code:hover { background: var(--sik-press); border-color: var(--sik-line-strong); }
.sik-offer__code svg { width: var(--icon-xs); height: var(--icon-xs); color: var(--sik-muted); }
@media (pointer: coarse) { .sik-offer__code { min-height: var(--tap); } }
@media (max-width: 400px) {
    .sik-offer { flex-wrap: wrap; }
    .sik-offer__code { margin-left: calc(34px + var(--sp-3)); }
}
.sik-offers--rail { grid-template-columns: minmax(0, 1fr); }
@media (min-width: 768px)  { .sik-offers--rail { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .sik-offers--rail { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* --------------------------------------------------------------------------
   18d. Brand tiles
   -------------------------------------------------------------------------- */
.sik-brandtile {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    height: 100%;
    padding: var(--sp-6) var(--sp-4);
    border-radius: var(--sik-radius);
    background: var(--sik-surface-2);
    color: var(--sik-ink);
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-brandtile:hover { background: var(--sik-press); color: var(--sik-ink); }
.sik-brandtile img { height: 38px; width: auto; object-fit: contain; }
.sik-brandtile span { font-size: var(--fs-xs); color: var(--sik-muted); }

/* --------------------------------------------------------------------------
   19. Newsletter

   One rounded band on a soft brand wash: an envelope in a raised circle, the
   heading pair, and the signup field beside them. The field classes are shared
   with the newsletter popup in includes/footer.php, which has no .sik-news
   around it — so the .sik-news__form base stays neutral enough to sit in an
   admin-coloured modal, and everything the band adds (pill, white fill, brand
   border, pill button) is scoped under .sik-news.
   -------------------------------------------------------------------------- */
.sik-news {
    /* --sik-muted measures 4.3:1 on the brand wash at 13px, which is under AA.
       This softens the band's own ink instead and composites over the wash
       rather than over white: 4.8:1 in light, 5.3:1 in dark, gold blob
       included. Declared on the band so the note inherits the same value. */
    --news-muted: color-mix(in srgb, var(--sik-ink) 64%, transparent);
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    gap: var(--sp-5) var(--sp-8);
    padding: var(--sp-7) var(--sp-5);
    border-radius: var(--sik-radius-lg);
    /* The wash lives on the band, not on .sik-section: an admin's bg_color
       paints the section, and the band has to keep its identity on top of
       whatever that is. --sik-grad-soft re-points in dark mode on its own. */
    background: var(--sik-grad-soft);
    border: 1px solid color-mix(in srgb, var(--sik-primary) 14%, var(--sik-line));
    /* The blobs below are ::before/::after, and this is a grid — without
       position they would be laid out as two extra grid items. */
    position: relative;
    overflow: hidden; /* Safari < 16 has no `clip`; it gets the scroll box. */
    /* clip rather than hidden: `hidden` makes the band a scroll container, and
       the focus ring on the field then scrolls the band sideways instead of
       simply being drawn over the padding. */
    overflow: clip;
}
/* Copy and field sit above the decorative layer. */
.sik-news > * { position: relative; z-index: 1; }

/* The envelope/gift motif as light at each end rather than artwork. The widget
   row names assets/images/banners/newsletter-diagonal.svg but the renderer
   never emits it, so nothing here may depend on that file existing. */
.sik-news::before,
.sik-news::after {
    content: "";
    position: absolute;
    z-index: 0;
    width: 280px;
    aspect-ratio: 1;
    border-radius: 50%;
    pointer-events: none;
}
.sik-news::before {
    inset-block-start: -120px;
    inset-inline-start: -90px;
    background: radial-gradient(circle, color-mix(in srgb, var(--sik-primary) 22%, transparent) 0%, transparent 68%);
}
/* Gold is the festive third colour and is never type here — it is a tint under
   the field end of the band, so the 3:1 floor for text does not apply. */
.sik-news::after {
    inset-block-end: -130px;
    inset-inline-end: -80px;
    background: radial-gradient(circle, color-mix(in srgb, var(--sik-gold) 18%, transparent) 0%, transparent 68%);
}

.sik-news__copy { min-width: 0; display: flex; align-items: center; gap: var(--sp-4); }
/* The text column, not the row: without min-width the title's longest word
   sets the flex basis and the band overflows at 320. */
.sik-news__lines { min-width: 0; }

/* The circle behind the envelope. --sik-elevated, not a literal white, so it
   still reads as a raised disc on the dark palette instead of a hole. */
.sik-news__ico {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 48px;
    height: 48px;
    border-radius: 50%;
    background: var(--sik-elevated);
    color: var(--sik-primary-ink);
    box-shadow: var(--sik-shadow-soft);
}

/* Size is set here because the shared display-heading roles near the top of
   the file put .sik-news__title at --fs-2xl and raise it to --fs-3xl at 768px.
   Same specificity, later in the file, so no !important is needed. */
.sik-news__title {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-tighter);
    line-height: var(--lh-tight);
    color: var(--sik-ink);
    text-wrap: balance;
    /* An admin can type one long unbroken word into the title field. */
    overflow-wrap: break-word;
}
/* The second tone of the heading pair. An admin's text_color reaches the h2
   (widget_section_attrs prints it as --w-fg and app.css recolours h2 in light
   mode) but not this span, which is the point: the accent word stays brand
   whatever colour is chosen, and it is always read against the band's own
   wash rather than against the section colour that --w-fg was picked for. */
.sik-news__accent { color: var(--sik-primary-ink); }
.sik-news__text {
    margin-top: var(--sp-2);
    max-width: 52ch;
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--news-muted);
    text-wrap: pretty;
}

.sik-news__action { min-width: 0; }
/* The consent line, which only exists when the widget row carries one. */
.sik-news__note {
    margin-top: var(--sp-2);
    /* Form padding plus input padding, so the note starts under the typed
       address rather than under the pill's edge. */
    padding-inline: var(--sp-5);
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--news-muted);
}

/* The field + button pair, also used by the newsletter popup. */
.sik-news__form {
    display: flex;
    gap: var(--sp-1);
    min-width: 0;
    padding: var(--sp-1);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-btn);
    /* Opaque, and load-bearing: popup_ink_style() in includes/menu-functions.php
       applies a popup's ink element by element rather than re-pointing
       --sik-text, on the understanding that this fill is what stops a navy
       popup turning into white-on-white typing. */
    background: var(--sik-elevated);
    transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sik-news__form:focus-within { border-color: var(--sik-line-strong); box-shadow: var(--sik-focus-ring); }
.sik-news__input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    padding: 0 var(--sp-4);
    min-height: 42px;
    font: inherit;
    /* 16px exactly. Anything smaller and iOS zooms the page on focus. */
    font-size: 16px;
    /* Transparent so it cannot square off the pill's rounded left edge — the
       opaque fill is the form's, above. The colour, on the other hand, is
       explicit for the same popup reason: `inherit` here loses the address
       being typed the moment a popup is given its own text colour. */
    background: transparent;
    color: var(--sik-text);
}
.sik-news__input::placeholder { color: var(--sik-placeholder); }
.sik-news__form .sik-btn { flex: none; }
/* A finger needs the whole tap height, popup included. .sik-btn already
   carries min-height: var(--tap), so only the field falls short of it. */
@media (pointer: coarse) { .sik-news__input { min-height: var(--tap); } }

/* --- the band's own field: pill, white fill, a border you can see ---------- */
.sik-news .sik-news__form {
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface);
    /* --sik-line disappears against the wash; the brand tint is what makes the
       field read as a control rather than a lighter patch of band. */
    border-color: color-mix(in srgb, var(--sik-primary) 32%, var(--sik-line));
    box-shadow: var(--sik-shadow-xs);
}
/* The resting shadow above is more specific than the base :focus-within, so
   the ring has to be restated or focus goes unmarked inside the band. */
.sik-news .sik-news__form:focus-within {
    border-color: var(--sik-primary);
    box-shadow: var(--sik-focus-ring);
}
.sik-news .sik-news__form .sik-btn { border-radius: var(--sik-radius-pill); padding-inline: var(--sp-6); }

/* The arrow is a real glyph in the markup: .sik-btn--primary::after is already
   the sheen sweep and the button is overflow:hidden, so there is no spare
   pseudo-element to draw one with. */
@media (hover: hover) {
    .sik-news .sik-btn svg { transition: transform var(--dur-fast) var(--ease-out); }
    .sik-news .sik-btn:hover svg { transform: translate3d(2px, 0, 0); }
}
/* The global reduced-motion block only neutralises [data-anim], the ticker and
   .sik-pulse, so the nudge switches itself off here, and again under Settings
   > Theme > enable animations. */
@media (prefers-reduced-motion: reduce) {
    .sik-news .sik-btn svg,
    .sik-news .sik-btn:hover svg { transition: none; transform: none; }
}
.sik-noanim .sik-news .sik-btn svg,
.sik-noanim .sik-news .sik-btn:hover svg { transition: none; transform: none; }

@media (min-width: 768px) {
    .sik-news { grid-template-columns: minmax(0, 1fr) minmax(0, 440px); padding: var(--sp-9) var(--sp-10); }
    .sik-news__ico { width: 56px; height: 56px; }
    .sik-news__ico svg { width: var(--icon-xl); height: var(--icon-xl); }
    /* The shared heading role raises this to --fs-3xl at exactly this width.
       Here the copy sits beside the field rather than above it, so 30px would
       squeeze the field into a column too narrow to type an address in.
       Restated inside the query so the pin survives section 19 ever moving. */
    .sik-news__title { font-size: var(--fs-2xl); }
}

/* 401–767px deliberately keeps the field as one row under the copy: there is
   room for both at that width, and a stacked pair reads as a form to fill in
   rather than one box to drop an address into. Below 400 there is not. */
@media (max-width: 400px) {
    .sik-news { padding: var(--sp-6) var(--sp-4); }
    .sik-news__copy { gap: var(--sp-3); }
    .sik-news__ico { width: 44px; height: 44px; }
    .sik-news__form { flex-direction: column; border-radius: var(--sik-radius); }
    .sik-news .sik-news__form { border-radius: var(--sik-radius-lg); }
    .sik-news__input { min-height: var(--tap); }
}

/* --------------------------------------------------------------------------
   20. Footer

   Brand, the builder's columns, and a legal bar — the last thing anyone sees,
   so it is deliberately quiet rather than a dumping ground: a wine-to-near-
   black wash lit from one corner, a gradient hairline along the top edge, and
   one short brand underline per heading. Settings > Theme > Footer still picks
   that dark surface or a light one, and every rule below reads the --footer-*
   roles rather than a colour — which is why this section has no dark-theme
   rule of its own and must not grow one. Link columns are disclosures below
   640px and plain lists above it.
   -------------------------------------------------------------------------- */
.sik-footer {
    --footer-bg:     var(--sik-inverse);
    --footer-ink:    var(--sik-on-inverse-2);
    --footer-strong: var(--sik-on-inverse);
    --footer-line:   var(--sik-inverse-line);
    --footer-tint:   var(--sik-inverse-hover);
    /* The warm highlight (contact glyphs). Gold is the DARK-panel highlight and
       is not readable at glyph size on a light surface, so the light variant
       swaps it for the brand ink — the same trade .sik-flourish makes in 1e. */
    --footer-accent: var(--sik-gold-2);
    /* The wash is its own property so --footer-bg can stay a plain COLOUR: it
       is the backstop painted under the gradient, and a background-color is the
       one layer that still renders if a gradient token is ever left unset.

       Three layers, top to bottom: a pool of brand colour in the top corner, a
       veil, then the sweep. The veil is there because --sik-grad-wine is built
       for a HERO — its 48% stop is the accent at 62%, which is the lightest
       thing on the panel AND is whatever colour a store types into Settings >
       Theme. Body copy here is 13px and the legal bar is 12px, so that stop has
       to clear 4.5:1 for any accent, not just the shipped one. Undimmed it
       measures 4.5:1 in dark theme and 3.3:1 against a light accent; veiled in
       the panel colour it is 7.1:1 / 4.8:1. The wine still reads, it is simply
       a shade deeper than the hero — which is what "the last thing anyone sees,
       deliberately quiet" wants anyway. */
    --footer-wash:
        radial-gradient(72% 130% at 12% 0%,
            color-mix(in srgb, var(--sik-primary) 22%, transparent) 0%, transparent 62%),
        linear-gradient(color-mix(in srgb, var(--sik-inverse) 34%, transparent),
                        color-mix(in srgb, var(--sik-inverse) 34%, transparent)),
        var(--sik-grad-wine);
    position: relative;
    /* Painted on .sik-footer and never on .sik-footer__main: checkout carries
       body.sik-focus, which hides __main outright (app.css:9344). Put the wash
       there instead and the only band left on checkout — the legal bar — sits
       on the page background with footer ink on it. */
    background-color: var(--footer-bg);
    background-image: var(--footer-wash);
    color: var(--footer-ink);
    font-size: var(--fs-sm);
}

/* Settings > Theme > Footer = light. This has to stay a genuinely light
   surface, not a paler wine: footer.php:123 calls brand_logo() with $light
   false here, so a light footer ships BOTH wordmark files and
   .sik-logo__light / .sik-logo__dark (app.css:1595) swap by PAGE theme. Wash
   this variant in wine and a light-configured store renders its dark-ink
   wordmark on a dark panel in light mode. */
body.sik-footer-light .sik-footer {
    --footer-bg:     var(--sik-soft);
    /* Was plain --sik-muted, which measures 4.54:1 on the shipped soft grey —
       one nudge of Settings > Theme > Muted colour away from failing body
       contrast, and the legal bar sets this ink at 12px. Pulled toward the ink
       for margin (6.2:1) without losing the muted read. */
    --footer-ink:    color-mix(in srgb, var(--sik-muted) 84%, var(--sik-ink));
    --footer-strong: var(--sik-ink);
    --footer-line:   var(--sik-line);
    --footer-tint:   var(--sik-hover);
    --footer-accent: var(--sik-primary-ink);
    --footer-wash:   none;
}

/* The top edge. footer.php has no .sik-grad-rule element to hang this on, so
   it is a pseudo on the footer, which is already positioned. Three stops
   rather than --sik-grad-brand because ONE rule has to read on both surfaces:
   the gold centre carries it against wine, the pink end carries it against the
   light soft grey. It also replaces the 1px --sik-line border the light
   variant used to carry — a hairline stacked under a 3px rule reads as a
   smudge, not as two decisions. */
.sik-footer::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: linear-gradient(90deg,
        var(--sik-primary) 0%, var(--sik-gold) 48%, var(--sik-accent) 100%);
    pointer-events: none;
}

.sik-footer a { color: var(--footer-ink); transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out); }
.sik-footer a:hover { color: var(--footer-strong); }
/* The global ring is --sik-primary, which lands at 3.7:1 on the wine wash —
   legal, but the footer is the one place where a plain white ring is simply
   easier to find. */
.sik-footer :focus-visible { outline-color: var(--footer-strong); }
.sik-footer ul { list-style: none; margin: 0; padding: 0; }
.sik-footer .sik-prose { color: var(--footer-ink); font-size: var(--fs-sm); max-width: none; }
.sik-footer .sik-prose a { color: var(--footer-strong); text-decoration: underline; }
.sik-footer__glyph { width: var(--icon-sm); height: var(--icon-sm); flex: none; }

.sik-footer__main { padding-block: var(--sp-9) var(--sp-7); }
@media (min-width: 640px) { .sik-footer__main { padding-block: var(--sp-11) var(--sp-8); } }

/* THREE layout modes, and the phone one is load-bearing: `gap: 0 var(--sp-6)`
   has a ZERO row gap on purpose, because every .sik-footer__col carries its own
   border-top as the accordion divider. Add a row gap here and each divider
   double-spaces. minmax(0, 1fr) rather than a bare 1fr: a bare fr floors at its
   widest unbreakable child, which is how a support alias used to scroll the
   page sideways at 320px. */
.sik-footer__grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0 var(--sp-6);
}
@media (min-width: 640px) {
    .sik-footer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--sp-8) var(--sp-6); }
    .sik-footer__brand { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
    /* Flex, not grid, from here up: grid-column stops applying and the ratios
       below take over, so the footer reads the same with two columns or six —
       every column is admin-driven and any of them can be absent. */
    .sik-footer__grid { display: flex; flex-wrap: wrap; gap: var(--sp-8); }
    .sik-footer__brand { flex: 2 1 280px; max-width: 400px; margin-right: auto; }
    .sik-footer__col { flex: 1 1 140px; }
    .sik-footer__col--contact { flex: 1.3 1 220px; }
}

/* --- brand block ----------------------------------------------------------- */
.sik-footer__brand { display: grid; align-content: start; gap: var(--sp-4); padding-bottom: var(--sp-6); }
@media (min-width: 640px) { .sik-footer__brand { padding-bottom: 0; } }
.sik-footer__logo { display: inline-flex; align-items: center; justify-self: start; min-height: var(--tap); }
/* Height and width only. `display: block` here would defeat the
   .sik-logo__dark { display: none } swap at app.css:1595, and a light footer —
   which ships both files — would render the two wordmarks stacked. */
.sik-footer__logo img { height: 36px; width: auto; max-width: 100%; }
/* Used TWICE — the brand blurb (a <p>) and the "about" column body (a <div> of
   sanitised HTML). The shared measure and muted ink are the whole reason an
   "about" column type exists separately from "html". */
.sik-footer__about { font-size: var(--fs-sm); line-height: var(--lh-relaxed); color: var(--footer-ink); max-width: 44ch; }

/* --- social ---------------------------------------------------------------- */
.sik-social { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sik-social li { display: inline-flex; }
a.sik-social__link {
    /* NOT optional. app.css:947 hangs the shared 44px tap-area pseudo off this
       element; drop `position: relative` and that absolutely positioned pseudo
       escapes to .sik-footer and becomes one 44px blob over the whole footer.
       The visible circle only gets to be 36px because of it. */
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    /* Derived from --footer-strong rather than --footer-line: the line token is
       a divider weight and a whole circle drawn at it reads as a smudge.
       social_icon() returns a FILLED svg, so nothing here may assume stroke. */
    border: 1px solid color-mix(in srgb, var(--footer-strong) 22%, transparent);
    background: var(--footer-tint);
    color: var(--footer-strong);
    transition: border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out);
}
/* The per-network modifiers footer.php:134 emits. They are deliberately all
   the same fill: six network colours in a row under a festive wine footer
   reads as a browser toolbar, not as a brand. Declared anyway so the hook is
   live and a store that wants Instagram pink has exactly one place to change —
   and so a network with no modifier still falls back to the brand sweep. */
.sik-social__link--facebook,
.sik-social__link--instagram,
.sik-social__link--twitter,
.sik-social__link--youtube,
.sik-social__link--linkedin,
.sik-social__link--pinterest { --social-fill: var(--sik-grad-cta); }
/* Colour alone would be legal unguarded, but this one fills AND lifts, so it
   belongs to hover-capable pointers only — on a touch screen a :hover state
   sticks after the tap and leaves one circle filled for the rest of the visit.
   --sik-grad-cta is the contrast-bounded sweep, so the glyph clears 5:1 even
   on its lightest stop. */
@media (hover: hover) {
    a.sik-social__link:hover {
        background: var(--social-fill, var(--sik-grad-cta));
        border-color: transparent;
        color: var(--sik-on-cta);
        transform: translate3d(0, var(--lift-sm), 0);
        box-shadow: var(--sik-shadow-brand-sm);
    }
}
.sik-social__glyph { width: var(--icon-sm); height: var(--icon-sm); }

/* --- payment tiles --------------------------------------------------------- */
/* Renders only when a payment column exists AND PaymentGatewayFactory has
   something to offer, so nothing may assume the strip is present. */
.sik-footer__pay { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sik-footer__pay-item {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: var(--sp-10);
    min-height: var(--sp-7);
    padding: var(--sp-1) var(--sp-2);
    border-radius: var(--sik-radius-sm);
    border: 1px solid var(--footer-line);
    background: var(--footer-tint);
}
/* Method artwork is uploaded as-is in Settings > Payment and most card marks
   are dark ink on transparent, which simply disappears on a wine footer. The
   plate goes ONLY on a tile that actually carries an image, because the
   missing-logo fallback is a <span> of --footer-ink and that would be
   white-on-white. Engines without :has() fall through to the tint — i.e. to
   exactly the tile this footer shipped before. */
.sik-footer__pay-item:has(img) {
    background: rgba(255, 255, 255, .92);
    /* Black, not white: the edge has to define the PLATE, and the plate is
       white in both variants. A white border here vanished the tile outline
       entirely on body.sik-footer-light, where the surface is already
       near-white — the tiles read as one ragged blob. */
    border-color: rgba(0, 0, 0, .12);
}
.sik-footer__pay-item img { height: var(--sp-5); width: auto; max-width: 100%; display: block; }
.sik-footer__pay-item span { font-size: var(--fs-xs); line-height: var(--lh-tight); color: var(--footer-ink); white-space: nowrap; }

/* --- column head ----------------------------------------------------------- */
/* position: relative exists for exactly one reason — to contain the absolutely
   positioned .sik-footer__toggle that covers this whole row on phones. */
.sik-footer__head {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
}
.sik-footer__title {
    position: relative;
    min-width: 0;
    font-size: var(--fs-base);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-tight);
    line-height: var(--lh-snug);
    color: var(--footer-strong);
}
/* The brand underline. Offset below the text rather than paid for with
   padding-bottom, because on phones the accordion head is a centred 52px tap
   row and padding there would push the title off its optical centre. It is a
   pseudo on the title, so the toggle — a later positioned sibling — paints
   over it; that is fine, and it is why the toggle must stay backgroundless. */
.sik-footer__title::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: calc(var(--sp-2) * -1);
    width: var(--sp-6);
    height: 2px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-brand);
}
/* Covers the whole heading row on phones; display:none from 640px up, where
   the panel is always open, so aria-expanded never reports a state that is not
   real. No background of its own — see the underline above. */
.sik-footer__toggle {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    background: none;
    border: 0;
    color: var(--footer-ink);
    cursor: pointer;
}
.sik-footer__chev { width: var(--icon-sm); height: var(--icon-sm); transition: transform var(--dur-base) var(--ease-out); }
/* The state app.js writes onto [data-acc-item]. Dropping this or the panel
   rule below is the silent failure: the panel still opens and the chevron
   still points down, so nobody notices until a shopper does. */
.sik-footer__col--acc.is-open .sik-footer__chev { transform: rotate(180deg); }

/* --- columns / disclosure -------------------------------------------------- */
/* The per-column border-top IS the accordion divider at phone width (see the
   zero row gap on the grid). Only link lists get --acc and [data-acc-item];
   contact / about / html / payment are always open and buy their own top
   padding instead. There is no [data-acc-single] here, so several panels can
   be open at once — the rules below must not assume otherwise. */
/* min-width: 0 because THIS is the grid item at 640px and the flex item at
   1024px, and both default to min-width: auto — i.e. they floor at their
   widest unbreakable child and push past the minmax(0, 1fr) track, which is
   how a long support alias used to scroll the page sideways. The track cap
   alone does not do it; the item has to be allowed to shrink. */
.sik-footer__col { border-top: 1px solid var(--footer-line); min-width: 0; }
.sik-footer__col:not(.sik-footer__col--acc) { padding-top: var(--sp-5); }
.sik-footer__col--acc .sik-footer__head { min-height: 52px; margin-bottom: 0; }
.sik-footer__col--acc .sik-footer__panel { display: none; padding-bottom: var(--sp-4); }
.sik-footer__col--acc.is-open .sik-footer__panel { display: block; }
/* The same allowance one level in, so a panel cannot floor the column it was
   just granted the right to shrink. The bare selector had no base rule before
   this one. */
.sik-footer__panel { min-width: 0; }
@media (min-width: 640px) {
    .sik-footer__col { border-top: 0; }
    .sik-footer__col:not(.sik-footer__col--acc) { padding-top: 0; }
    /* Back to the base --sp-4: above 640px an accordion head and a contact
       head sit in the same row, and a 4px difference between them reads as a
       misalignment rather than as rhythm. --sp-4 clears the 10px the title
       underline hangs below the text. */
    .sik-footer__col--acc .sik-footer__head { min-height: 0; margin-bottom: var(--sp-4); }
    .sik-footer__col--acc .sik-footer__panel { display: block; padding-bottom: 0; }
    .sik-footer__toggle { display: none; }
}

/* --- link lists ------------------------------------------------------------ */
.sik-footer__links { display: grid; }
.sik-footer__links a {
    display: flex;
    align-items: center;
    min-height: var(--tap);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    transition: color var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
    /* A 2px nudge, not a lift: a whole column of links rising at once looks
       like the list is still loading. */
    .sik-footer__links a:hover { transform: translate3d(2px, 0, 0); }
}
/* Only from 1024px, where the pointer is a mouse and the rows can tighten into
   a real list. --tap is non-negotiable while a finger is doing the aiming. */
@media (min-width: 1024px) { .sik-footer__links a { min-height: 0; padding-block: 6px; } }

/* --- contact --------------------------------------------------------------- */
/* overflow-wrap: anywhere plus the wider --contact track exist because a
   support alias broke mid-word at a single track width ("shopinnkart.co / m"). */
.sik-footer__contact { display: grid; gap: var(--sp-3); overflow-wrap: anywhere; }
.sik-footer__contact li {
    display: grid;
    grid-template-columns: 26px minmax(0, 1fr);
    align-items: start;
    gap: var(--sp-3);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
}
/* A quiet chip rather than a bare glyph, so an address, a phone number and an
   email read as a list of channels instead of three stray icons.
   --footer-accent is gold on the wine surface and brand ink on the light one;
   each clears the 3:1 a meaningful graphic needs on its own background. */
.sik-footer__contact-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    flex: none;
    border-radius: 50%;
    background: var(--footer-tint);
    color: var(--footer-accent);
}
@media (pointer: coarse) {
    /* Reaches --tap without changing the visual rhythm: the extra height is
       handed straight back as negative block margin, so the rows stay --sp-3
       apart and the column does not grow by 25px per contact line. */
    .sik-footer__contact a { display: inline-flex; align-items: center; min-height: var(--tap); margin-block: calc((var(--tap) - 1.5em) / -2); }
}

/* --- bottom bar ------------------------------------------------------------ */
/* The only band checkout renders, since body.sik-focus hides __main. It is
   left transparent on purpose: tinting it would darken the light variant's
   surface under 12px muted ink and drop that text below 4.5:1. */
.sik-footer__bar { border-top: 1px solid var(--footer-line); }
.sik-footer__bar-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2) var(--sp-6);
    padding-block: var(--sp-5);
    font-size: var(--fs-xs);
    color: var(--footer-ink);
}
.sik-footer__copy { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-4); }
/* Who built the store. It sits in the same flex row as the copyright and
   wraps with it, so on a phone it takes its own line rather than crowding the
   year. Muted on purpose: it is an attribution, not a claim competing with the
   store's own name. */
.sik-footer__credit { font-size: var(--fs-xs); color: var(--footer-ink, inherit); }

/* The credit reads as two voices: the sentence in the UI face at footer
   weight, and the name it ends on in the display italic the storefront already
   uses for its flourishes. Playfair is deliberately NOT preloaded and is
   declared font-display: optional with ascent/descent overrides that sit
   Georgia italic on exactly the same line box - so a cold view sets the name
   in Georgia italic, in the same position, and it never reflows. No second
   font is downloaded for this line.

   No underline. text-decoration cannot be given its own colour and cannot
   animate, so the rule is a pseudo-element: it grows from the leading edge on
   hover, and is simply present on keyboard focus. */
.sik-credit__link {
    display: inline-flex;
    align-items: baseline;
    gap: .42em;
    color: inherit;
    text-decoration: none;
    border-radius: 3px;
}
.sik-credit__lead { opacity: .72; letter-spacing: var(--ls-wide); }
.sik-credit__name {
    position: relative;
    font-family: var(--sik-font-display);
    font-style: italic;
    font-weight: 600;
    font-size: 1.16em;
    line-height: 1;
    /* A company name broken across two lines stops being a name. */
    white-space: nowrap;
}
/* inset-inline rather than left/right so the rule follows the writing
   direction; 1.5px because a 1px hairline under an italic serif at this size
   reads as a rendering artefact rather than an underline. */
.sik-credit__name::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: -3px;
    height: 1.5px;
    border-radius: 2px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform var(--dur-fast) var(--ease-out);
}
a.sik-credit__link:hover,
a.sik-credit__link:focus-visible { color: var(--footer-strong, inherit); }
a.sik-credit__link:hover .sik-credit__name::after,
a.sik-credit__link:focus-visible .sik-credit__name::after { transform: scaleX(1); }
a.sik-credit__link:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
/* Without a credit_url the helper emits a <span>, not an <a>. It is text, so
   it gets no rule, no hover and no focus ring - promising a link that is not
   there is worse than plain words. */
span.sik-credit__link { cursor: default; }
span.sik-credit__link .sik-credit__name::after { content: none; }

/* Both motion switches, as the block below this one explains: the OS setting
   and Settings > Theme > "Enable entrance animations" = off. A scaleX that
   collapses to .01ms still animates; these drop the transition and show the
   rule outright, so the hover still says something. */
@media (prefers-reduced-motion: reduce) {
    .sik-credit__name::after { transition: none; }
}
.sik-noanim .sik-credit__name::after { transition: none; }

/* Renders only when Settings > Store has a gst_number. Boxed so it reads as a
   registration number rather than a second sentence of the copyright line;
   nowrap because a GSTIN broken across two lines is not a GSTIN. */
.sik-footer__gst {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--sp-2);
    border-radius: var(--sik-radius-pill);
    border: 1px solid var(--footer-line);
    letter-spacing: var(--ls-wide);
    white-space: nowrap;
}
/* Renders only when at least one of the five legal slugs is active and set to
   show in the footer, so the bar has to look finished without it. */
.sik-footer__legal { display: flex; flex-wrap: wrap; align-items: center; gap: 0 var(--sp-5); }
.sik-footer__legal a { display: inline-flex; align-items: center; min-height: var(--tap); font-size: var(--fs-xs); }
@media (min-width: 1024px) { .sik-footer__legal a { min-height: 28px; } }
@media (max-width: 639px) {
    .sik-footer__bar-row { justify-content: center; text-align: center; }
    .sik-footer__copy, .sik-footer__legal { justify-content: center; }
}

/* --- motion off ------------------------------------------------------------
   Both switches, because they are two different requests: the OS setting, and
   Settings > Theme > "Enable entrance animations" = off, which lands as
   body.sik-noanim and which app.js:676 treats as reduced motion. Durations
   already collapse to .01ms under prefers-reduced-motion through the tokens in
   section 1, but a transform still travels that distance — so the two hovers
   that move are cancelled outright. The chevron keeps its rotation under both:
   it is the visible half of aria-expanded, not decoration, and only its
   transition is dropped.

   Nothing in this section sets opacity on .sik-footer__grid or
   .sik-footer__bar-row. Section 36 owns those two through [data-anim], and a
   second opinion about their opacity is how the entire link grid once ended up
   invisible for every visitor whose reveal never fired. */
@media (prefers-reduced-motion: reduce) {
    a.sik-social__link:hover,
    .sik-footer__links a:hover { transform: none; }
    .sik-footer__chev { transition: none; }
}
.sik-noanim a.sik-social__link:hover,
.sik-noanim .sik-footer__links a:hover { transform: none; }
.sik-noanim .sik-footer__chev { transition: none; }




/* --- payment acceptance marks ---------------------------------------------
   White plates, because every card network's artwork is drawn to sit on white
   and a wine footer would drain it. The plate is the constant; what sits on it
   is a wordmark, or for Mastercard a shape - see payment_mark_html(). Which
   marks appear is decided by accepted_payment_marks() from the gateways that
   are actually enabled, so this row can never advertise a rail checkout will
   refuse. */
.sik-paymark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 46px;
    height: 30px;
    padding: 0 var(--sp-2);
    border-radius: 6px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .18);
}
.sik-paymark__word {
    font-size: 12px;
    font-weight: var(--fw-black);
    line-height: 1;
    font-style: italic;
    white-space: nowrap;
}
/* RuPay and UPI set their names upright; only Visa and Amex slant. */
.sik-paymark__word:has(span) { font-style: normal; letter-spacing: 0; }
.sik-paymark__svg { width: 32px; height: 20px; display: block; }
/* (0,2,0): the footer declares `.sik-footer__pay-item span { color: footer-ink }`
   at (0,1,1), which painted the Cash wordmark white on a white plate and the
   wallet glyph with it, since the svg inherits currentColor. */
.sik-paymark .sik-paymark__cod,
.sik-paymark .sik-paymark__cod b {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    color: #1F2937;
}
.sik-paymark__cod svg { width: 15px; height: 15px; }
.sik-paymark__cod b { font-size: 11px; font-weight: var(--fw-bold); }

/* The names checkout will really list, under the marks. */
.sik-footer__paynote {
    margin: var(--sp-2) 0 0;
    font-size: var(--fs-2xs);
    line-height: var(--lh-normal);
    color: var(--sik-on-inverse-2);
}


/* --- Discover, and wider wordmarks ---------------------------------------- */
/* "Discover" and "Mastercard" need more room than a four-letter mark, so the
   plate grows for them instead of squeezing the word. */
.sik-paymark__word--wide { font-style: normal; letter-spacing: -.01em; }
/* The orange arc the card is recognised by, drawn rather than imported. */
.sik-paymark__arc {
    display: inline-block;
    width: 9px;
    height: 9px;
    margin-left: 3px;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 30%, #F79E1B 0%, #E85C0D 70%);
}

/* --- social marks in their own colours -------------------------------------
   One pink hover for six different platforms made them read as a row of
   generic buttons. Each network now fills with its own brand colour, which is
   what makes an icon recognisable before the shape is even read. The resting
   state stays monochrome so the footer is not a fruit salad. */
.sik-social__link--facebook  { --social-fill: #1877F2; }
.sik-social__link--instagram { --social-fill: linear-gradient(45deg, #F58529 0%, #DD2A7B 50%, #8134AF 100%); }
.sik-social__link--twitter   { --social-fill: #0F1419; }
.sik-social__link--youtube   { --social-fill: #FF0000; }
.sik-social__link--linkedin  { --social-fill: #0A66C2; }
.sik-social__link--pinterest { --social-fill: #E60023; }
.sik-social__link--whatsapp  { --social-fill: #25D366; }
.sik-social__link--telegram  { --social-fill: #229ED9; }
.sik-social__link--snapchat  { --social-fill: #FFFC00; }
/* Snapchat's yellow cannot carry white; its own mark is black on yellow. */
.sik-social__link--snapchat:hover,
.sik-social__link--snapchat:focus-visible { color: #0B0B0B; }

/* --------------------------------------------------------------------------
   21. Breadcrumbs & pagination
   -------------------------------------------------------------------------- */
.sik-breadcrumb ol {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-1) var(--sp-2);
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: var(--fs-sm);
    color: var(--sik-muted);
}
.sik-breadcrumb li { display: inline-flex; align-items: center; gap: var(--sp-2); min-width: 0; }
.sik-breadcrumb a { color: var(--sik-muted); }
.sik-breadcrumb a:hover { color: var(--sik-ink); }
.sik-breadcrumb li[aria-current] span {
    color: var(--sik-ink);
    font-weight: var(--fw-medium);
    max-width: min(46ch, 70vw);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-breadcrumb-sep { display: inline-flex; color: var(--sik-line-strong); }

.sik-pager { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: var(--sp-1); margin-top: var(--sp-9); }
.sik-pager__link {
    min-width: 40px; height: 40px;
    padding-inline: var(--sp-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--sik-radius-pill);
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    font-variant-numeric: tabular-nums;
    color: var(--sik-ink-2);
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-pager__link { min-width: var(--tap); height: var(--tap); } }
.sik-pager__link:hover { background: var(--sik-hover); color: var(--sik-ink); }
.sik-pager__link.is-current { background: var(--sik-ink); color: var(--sik-bg); }
.sik-pager__link.is-disabled { opacity: .35; pointer-events: none; }
.sik-pager__gap { padding-inline: var(--sp-2); color: var(--sik-muted); }

/* Every call site is body-size link text, where the raw brand tone fails AA. */
.text-primary { color: var(--sik-primary-ink); }

/* --------------------------------------------------------------------------
   22. Forms
   -------------------------------------------------------------------------- */
.sik-field { display: block; margin-bottom: var(--sp-5); }
.sik-label { display: block; margin-bottom: var(--sp-2); font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--sik-ink); }
.sik-label .req { color: var(--sik-danger-ink); }

.sik-input, .sik-select, .sik-textarea {
    width: 100%;
    min-height: var(--tap);
    padding: 0 var(--sp-4);
    border: 1px solid var(--sik-input-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-input-bg);
    color: var(--sik-ink);
    font: inherit;
    font-size: var(--fs-base);
    outline: none;
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out);
}
.sik-textarea { min-height: 120px; padding-block: var(--sp-3); line-height: var(--lh-normal); resize: vertical; }
.sik-input:hover, .sik-select:hover, .sik-textarea:hover { border-color: var(--sik-line-strong); }
.sik-input:focus, .sik-select:focus, .sik-textarea:focus { border-color: var(--sik-ink); box-shadow: var(--sik-focus-ring); }
.sik-input::placeholder, .sik-textarea::placeholder { color: var(--sik-placeholder); opacity: 1; }
.sik-input:disabled, .sik-select:disabled, .sik-textarea:disabled { opacity: .6; cursor: not-allowed; }
/* 16px stops iOS zooming the page when a field takes focus. */
@media (max-width: 767px) {
    .sik-input, .sik-select, .sik-textarea, .sik-qty input, .sik-search__input { font-size: 16px; }
}
.sik-input.is-invalid, .sik-select.is-invalid, .sik-textarea.is-invalid { border-color: var(--sik-danger); }
.sik-input.is-invalid:focus, .sik-select.is-invalid:focus, .sik-textarea.is-invalid:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--sik-danger) 22%, transparent);
}
.sik-select {
    appearance: none;
    padding-right: var(--sp-9);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239096A0' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right var(--sp-3) center;
    background-size: var(--icon-sm);
    cursor: pointer;
}
.sik-error { display: block; margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-danger-ink); }
.sik-help { display: block; margin-top: var(--sp-1); font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }

.sik-check { display: flex; align-items: flex-start; gap: var(--sp-3); font-size: var(--fs-base); line-height: var(--lh-normal); cursor: pointer; }
.sik-check input[type="checkbox"], .sik-check input[type="radio"] {
    width: 18px; height: 18px;
    margin-top: 2px;
    flex: none;
    accent-color: var(--sik-ink);
    cursor: pointer;
}

/* Quantity stepper. */
.sik-qty {
    display: inline-flex;
    align-items: center;
    border: 1px solid var(--sik-input-line);
    border-radius: var(--sik-radius-btn);
    background: var(--sik-input-bg);
    overflow: hidden;
    transition: border-color var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sik-qty button {
    width: 42px; height: 42px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    background: transparent;
    color: var(--sik-ink);
    font-size: var(--fs-md);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out);
}
.sik-qty button:hover:not(:disabled) { background: var(--sik-hover); }
.sik-qty button:active:not(:disabled) { background: var(--sik-press); }
.sik-qty button:disabled { opacity: .35; cursor: not-allowed; }
.sik-qty:focus-within { border-color: var(--sik-ink); box-shadow: var(--sik-focus-ring); }
.sik-qty input {
    width: 40px;
    height: 42px;
    border: 0;
    background: transparent;
    color: var(--sik-ink);
    text-align: center;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    font-variant-numeric: tabular-nums;
    outline: none;
    -moz-appearance: textfield;
}
.sik-qty input::-webkit-outer-spin-button,
.sik-qty input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* --------------------------------------------------------------------------
   23. Star rating
   -------------------------------------------------------------------------- */
.sik-stars { display: inline-flex; gap: 1px; line-height: 0; }
.sik-star { position: relative; display: inline-block; color: var(--sik-line-strong); }
.sik-star svg { display: block; }
.sik-star::after {
    content: "";
    position: absolute;
    inset: 0;
    width: var(--fill, 0%);
    overflow: hidden;
    background: var(--sik-star);
    -webkit-mask: var(--star-mask) no-repeat center / contain;
            mask: var(--star-mask) no-repeat center / contain;
    --star-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='black'%3E%3Cpath d='M12 3.5l2.6 5.4 5.9.8-4.3 4.1 1 5.9-5.2-2.8-5.2 2.8 1-5.9L3.5 9.7l5.9-.8z'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   24. Toasts

   A floating glass card with a status-coloured bar down its leading edge: top
   right under the header on a desktop, above the bottom bar on a phone, where
   the stack reverses so the newest one lands nearest the thumb. Glass rather
   than the old inverted ink slab, because a toast fires over the hero, over a
   product photo and over an open modal, and a tinted-but-readable card belongs
   to the festive page in a way a black rectangle never did. The blur sits
   inside @supports with an opaque background outside it - unreadable text over
   a photograph is the one failure mode this component cannot afford.
   -------------------------------------------------------------------------- */
.sik-toasts {
    position: fixed;
    top: calc(var(--sik-header-h) + var(--sp-3));
    right: var(--sp-4);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    width: min(380px, calc(100vw - 32px));
    /* The stack is a hole in the page; only the cards themselves catch clicks,
       or a toast parked in the corner swallows taps on whatever is under it. */
    pointer-events: none;
}
@media (max-width: 640px) {
    .sik-toasts {
        top: auto;
        bottom: var(--sp-3);
        left: var(--sp-3);
        right: var(--sp-3);
        width: auto;
        /* column-reverse, so the newest toast is the one closest to the thumb.
           Drop the reverse and every new message appears furthest from the
           finger, behind the ones it just replaced. */
        flex-direction: column-reverse;
    }
    /* The bottom-bar offset is charged only where a bottom bar exists. The
       admin shell renders the same #sikToasts (admin/includes/footer.php:25)
       and a storefront with Settings > mobile_bottom_nav off has no bar
       either; both used to float 60px above nothing. */
    body.sik-has-bottomnav .sik-toasts { bottom: calc(var(--sik-bottomnav-space) + var(--sp-3)); }
}

.sik-toast {
    /* One tone per toast, set once by the modifier and read by both the bar and
       the glyph. --toast-tone is the saturated hue - decorative, so the 3:1
       floor for graphics is the bar it has to clear. --toast-ink is the shade
       that reaches 4.5:1 on a light surface, which is what the icon is drawn
       in. Defaulted to info here because #sikFlash can hand SIK.toast a type
       nobody wrote a modifier for. */
    --toast-tone: var(--sik-info);
    --toast-ink:  var(--sik-info-ink);

    position: relative;
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-3) var(--sp-3) var(--sp-4);
    border-radius: var(--sik-radius);
    /* Opaque first. The @supports block below swaps in the translucent version
       only where backdrop-filter exists; everywhere else this is what renders,
       and it is readable on its own. Same shape as .sik-glass (section 1e). */
    background: var(--sik-elevated);
    border: 1px solid var(--sik-glass-line);
    color: var(--sik-ink-2);
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
    box-shadow: var(--sik-shadow-pop);
    /* Clips the leading bar to the card's radius. */
    overflow: hidden;
    pointer-events: auto;
    animation: sik-toast-in var(--dur-slow) var(--ease-out) both;
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-toast {
        /* 92%, not the 76% of --sik-glass-bg: a toast lands over a dark hero
           photo as often as over the page, and at 76% the body ink loses its
           4.5:1 there. It still reads as glass - the blur does most of it. */
        background: color-mix(in srgb, var(--sik-elevated) 92%, transparent);
        -webkit-backdrop-filter: var(--sik-glass-blur);
        backdrop-filter: var(--sik-glass-blur);
    }
}
/* The status bar. A pseudo-element rather than border-left, so the padding -
   and with it the optical left edge of the text - does not move with it. */
.sik-toast::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 4px;
    background: var(--toast-tone);
    pointer-events: none;
}
.sik-toast--success { --toast-tone: var(--sik-success); --toast-ink: var(--sik-success-ink); }
.sik-toast--error   { --toast-tone: var(--sik-danger);  --toast-ink: var(--sik-danger-ink); }
.sik-toast--warning { --toast-tone: var(--sik-warning); --toast-ink: var(--sik-warning-ink); }
.sik-toast--info    { --toast-tone: var(--sik-info);    --toast-ink: var(--sik-info-ink); }

.sik-toast.is-leaving {
    /* notifications.js removes the node 240ms after .is-leaving goes on, so the
       leave has to finish inside 240ms. --dur-base (.2s) does; --dur-slow
       (.32s) gets cut off mid-fade and the card blinks out of existence. */
    animation: sik-toast-out var(--dur-base) var(--ease-in-out) both;
}

.sik-toast__icon { flex: none; margin-top: 1px; display: inline-flex; color: var(--toast-ink); }
.sik-toast__icon svg { width: var(--icon-md); height: var(--icon-md); }

.sik-toast__body { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.sik-toast__title { display: block; font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-toast__action {
    display: inline-block;
    margin-top: var(--sp-1);
    font-weight: var(--fw-semibold);
    color: var(--sik-primary-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}
.sik-toast__action:hover { color: var(--sik-primary-ink); }
.sik-toast__action:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; border-radius: var(--sik-radius-xs); }

.sik-toast__close {
    position: relative;
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px; height: 28px;
    margin: -2px -2px -2px 0;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    /* Role colours, not the inverted pair this used to carry. The same X is
       rendered on a pop-in (includes/footer.php:732), which is a LIGHT card -
       the old var(--sik-bg) glyph was white-on-white there and only survived
       because popup_ink_style() happened to print an inline colour over it. */
    color: var(--sik-muted);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-toast__close:hover { color: var(--sik-ink); background: var(--sik-hover); }
.sik-toast__close:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 1px; }
/* 28px is a mouse target; on a finger it is half of one. The negative margin
   pulls the extra width back out of the card's rhythm rather than inflating
   the row. Same pattern as .sik-minicart__remove in section 26. */
@media (pointer: coarse) {
    .sik-toast__close {
        width: var(--tap); height: var(--tap);
        margin: calc(var(--sp-2) * -1) calc(var(--sp-2) * -1) calc(var(--sp-2) * -1) 0;
    }
}
/* The same button on a pop-in, where the card is --sik-elevated or an admin
   colour rather than glass. Stated outright so a shared class is not left
   depending on one inline style to stay visible. */
.sik-popin .sik-toast__close { color: var(--sik-muted); }
.sik-popin .sik-toast__close:hover { color: var(--sik-ink); background: var(--sik-hover); }

@keyframes sik-toast-in  { from { opacity: 0; transform: translateY(-8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes sik-toast-out { to { opacity: 0; transform: translateY(-6px) scale(.98); } }
/* Re-declared inside the query on purpose: on a phone the stack is anchored to
   the bottom, so a card has to rise from below rather than drop out of the
   bottom bar. Keyframe names are global and the LAST matching declaration
   wins, so nothing named sik-toast-in/out may appear after this block - a
   later copy would put the desktop curve back on phones too. */
@media (max-width: 640px) {
    @keyframes sik-toast-in  { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
    @keyframes sik-toast-out { to { opacity: 0; transform: translateY(12px); } }
}

/* Both switches, not one. prefers-reduced-motion was already covered through
   the --dur-* collapse in section 1, but Settings > Theme > animations off
   sets .sik-noanim and touches no token, so until now the toasts ignored it.
   `animation: none` rather than a zero duration, because both keyframes fill
   `both` from opacity:0 and anything that leaves the fill standing risks a
   toast that is never painted. */
@media (prefers-reduced-motion: reduce) {
    .sik-toast,
    .sik-toast.is-leaving { animation: none; }
}
.sik-noanim .sik-toast,
.sik-noanim .sik-toast.is-leaving { animation: none; }

/* --------------------------------------------------------------------------
   25. Modal / quick view / popups

   A brand-tinted scrim, then a lifted surface that scales in: centred on a
   desktop, a bottom sheet on a phone where the thumb is, and a small centred
   card at every size for a promotional interruption. The base
   .sik-modal__panel stays deliberately bare - no border, ring, gradient or
   padding - because the lightbox (products.js:312) hands it
   background:transparent;box-shadow:none inline, and any of those would paint
   a visible frame around a transparent image viewer. Every festive treatment
   in this section is therefore scoped to one named dialog.
   -------------------------------------------------------------------------- */
.sik-modal {
    position: fixed;
    inset: 0;
    z-index: var(--z-modal);
    /* display is the ONLY open mechanism: app.js toggles .is-open and
       aria-hidden and nothing else, so this cannot become opacity/visibility
       without editing the script. It must also stay a FLEX container -
       popup_anchor_style() (includes/menu-functions.php:732-749) writes inline
       align-items/justify-content for the seven off-centre popup positions. */
    display: none;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
}
.sik-modal.is-open { display: flex; }

.sik-modal__backdrop {
    position: absolute;
    inset: 0;
    /* A pool of brand light over the scrim rather than flat black, so an open
       dialog still reads as part of the festive page. The scrim underneath is
       what carries the separation; the tint is only decoration on top of it. */
    background:
        radial-gradient(72% 56% at 50% 0%, color-mix(in srgb, var(--sik-primary) 14%, transparent) 0%, transparent 70%),
        var(--sik-scrim);
    animation: sik-pop-fade var(--dur-base) var(--ease-out) both;
}
/* --sik-scrim is opaque enough to stand on its own, so the blur is garnish and
   is asked for only where it exists - the same @supports shape .sik-glass and
   .sik-bottomnav use. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-modal__backdrop {
        -webkit-backdrop-filter: blur(4px);
        backdrop-filter: blur(4px);
    }
}

.sik-modal__panel {
    position: relative;
    width: 100%;
    /* A flex item keeps min-width:auto and refuses to shrink below its widest
       unbreakable child - the same bug that floored .sik-searchpop__panel at
       522px on a 320px screen, and the same fix. */
    min-width: 0;
    max-width: 920px;
    max-height: min(90vh, 780px);
    overflow-y: auto;
    overscroll-behavior: contain;
    background: var(--sik-elevated);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-pop);
    animation: sik-pop-in var(--dur-base) var(--ease-out) both;
}
.sik-modal__panel--sm { max-width: 400px; }
.sik-modal__panel--md { max-width: 520px; }
/* popup_size_options() returns sm|md|lg, and "lg" had no rule at all: every
   admin who picked Large silently got the unstyled 920px default - wider than
   any popup card is meant to be. */
.sik-modal__panel--lg { max-width: 720px; }

@media (max-width: 640px) {
    /* Every dialog except a promotional interruption becomes a bottom sheet.
       Both halves matter: drop the :not() and an advertisement takes over the
       whole screen; drop the sheet and a form opens in the middle of the page
       with its controls out of thumb reach. */
    .sik-modal:not(.sik-modal--promo) { align-items: flex-end; padding: 0; }
    .sik-modal:not(.sik-modal--promo) .sik-modal__panel {
        max-width: none;
        max-height: 92vh;
        border-radius: var(--sik-radius-lg) var(--sik-radius-lg) 0 0;
        padding-bottom: env(safe-area-inset-bottom, 0px);
        animation-name: sik-sheet-in;
    }
}

/* --- the promotional card ---------------------------------------------------
   popup_theme_style() prints background:<admin hex> inline on this panel, so a
   gradient or a glass background here would simply lose to it. The gold
   hairline is a pseudo-element for exactly that reason: it sits on top of
   whatever colour the admin chose. */
.sik-modal--promo .sik-modal__panel {
    max-width: 380px;
    max-height: min(86vh, 600px);
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.sik-modal--promo .sik-modal__panel::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--sik-grad-gold);
    z-index: 2;
    pointer-events: none;
}
.sik-modal--promo .sik-modal__panel--md { max-width: 420px; }
/* "Large" on a promo is still a card, not a sheet. */
.sik-modal--promo .sik-modal__panel--lg { max-width: 460px; }
.sik-modal--promo .sik-modal__poster {
    width: 100%;
    height: 140px;
    object-fit: cover;
    border-radius: var(--sik-radius-lg) var(--sik-radius-lg) 0 0;
    flex: none;
}
/* The panel is overflow:hidden and the BODY scrolls, which is what keeps the
   close button pinned to the card instead of scrolling out of reach on a long
   promo. Quick view below borrows the same arrangement. */
.sik-modal--promo .sik-modal__body {
    min-height: 0;
    padding: var(--sp-6);
    overflow-y: auto;
    overscroll-behavior: contain;
    text-align: center;
    scrollbar-width: thin;
}
.sik-modal--promo .sik-modal__title {
    margin-bottom: var(--sp-2);
    font-size: var(--fs-xl);
    font-weight: var(--fw-bold);
    line-height: var(--lh-tight);
    letter-spacing: var(--ls-tight);
}
.sik-modal--promo .sik-modal__sub { font-size: var(--fs-base); color: var(--sik-muted); }
.sik-modal--promo .sik-modal__note { margin-top: var(--sp-3); font-size: var(--fs-sm); }
.sik-modal--promo .sik-modal__note ul,
.sik-modal--promo .sik-modal__note ol { display: inline-block; margin-inline: auto; text-align: left; }
@media (max-width: 480px) {
    .sik-modal--promo .sik-modal__panel,
    .sik-modal--promo .sik-modal__panel--md,
    .sik-modal--promo .sik-modal__panel--lg { max-width: min(340px, calc(100vw - 32px)); }
    .sik-modal--promo .sik-modal__poster { height: 110px; }
    .sik-modal--promo .sik-modal__body { padding: var(--sp-5); }
}

/* --- the close target -------------------------------------------------------
   Opaque on purpose. On the lightbox the panel behind this button is
   transparent, so it is drawn straight onto the photograph; a ghost button
   would vanish against a pale product shot. */
.sik-modal__close {
    position: absolute;
    top: var(--sp-3); right: var(--sp-3);
    z-index: 3;
    width: 36px; height: 36px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--sik-line);
    border-radius: 50%;
    background: var(--sik-elevated);
    color: var(--sik-ink);
    box-shadow: var(--sik-shadow-soft);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
/* Colour only, nothing that moves, so this needs no (hover: hover) guard. The
   moment a lift or a scale goes in here, it does. */
.sik-modal__close:hover { background: var(--sik-press); border-color: var(--sik-line-strong); color: var(--sik-ink); }
.sik-modal__close:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; }
/* 36px was below the 44px floor on every touch device, on the one control a
   shopper reaches for when they want out. */
@media (pointer: coarse) { .sik-modal__close { width: var(--tap); height: var(--tap); } }

/* --- quick view -------------------------------------------------------------
   The body is a .sik-pdp (api/products/details.php:99), so section 28 already
   sits the photo and the details side by side from 768px up and section 11's
   .sik-atc is the loudest thing in the right-hand column. Two things needed
   fixing here. .sik-modal__panel was the scroll container while
   .sik-modal__close is absolutely positioned inside it, so on a long product
   the way out scrolled off the top - the panel now clips and the body scrolls.
   And the inline padding:22px on [data-quickview-body] beats anything this
   section could set without !important, so the panel, not the body, carries
   the layout. */
#sikQuickView .sik-modal__panel {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
#sikQuickView .sik-modal__panel::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--sik-grad-brand);
    z-index: 2;
    pointer-events: none;
}
#sikQuickViewBody {
    flex: 1 1 auto;
    /* Without this a flex item will not scroll: its minimum is its content. */
    min-height: 0;
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}
@media (max-width: 767px) {
    /* Tighter than section 28's 520px cap, because 92vh of sheet is a much
       shorter canvas than a page: a full-width square gallery pushed the price
       and the Add to cart below the fold of the sheet. 767px, not 640px,
       because that is where section 28 drops .sik-pdp to a single column. */
    #sikQuickView [data-gallery] { max-width: 320px; }
}
/* Sheet grab handle. 640px, matching the phone-sheet query above and NOT the
   767px one: between 641 and 767px the dialog is still a centred card, and a
   handle there advertises a drag gesture that does nothing. Scoped to this
   dialog and never to the base panel - on the transparent lightbox it would be
   a bar floating in mid-air. */
@media (max-width: 640px) {
    #sikQuickView .sik-modal__panel::after {
        content: "";
        position: absolute;
        top: var(--sp-2);
        left: 50%;
        width: 40px;
        height: 4px;
        margin-left: -20px;
        border-radius: var(--sik-radius-pill);
        background: var(--sik-line-strong);
        z-index: 2;
        pointer-events: none;
    }
}

/* --- the lightbox -----------------------------------------------------------
   Nothing to declare. products.js gives the panel
   background:transparent;box-shadow:none;max-width inline and the image
   carries its own white plate and radius. The point is that the base panel
   above stays bare enough for that to keep working. */

@keyframes sik-pop-in { from { opacity: 0; transform: translateY(8px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes sik-sheet-in { from { transform: translateY(100%); } to { transform: none; } }
/* Not consumed anywhere in sections 24-26. It is the only definition of the
   hero slide crossfade (.sik-hero__slide.is-active, section 8) and it lives
   here because keyframe names are global; delete it as unused and the hero
   stops cross-fading. */
@keyframes sik-fade-in { from { opacity: 0; } to { opacity: 1; } }
/* sik-pop-in is shared with .sik-searchpop__panel (section 6) - the search
   dialog and the modal are deliberately one entrance, and section 6 carries
   the matching note. Re-curve it here and Ctrl/Cmd+K changes with you.
   sik-pop-fade, used by .sik-modal__backdrop above, is the mirror case: it is
   DEFINED in section 6 (line 2717) and only consumed here. Reuse the name; do
   not redeclare it, or the two dialogs drift apart again. */

/* --- pop-ins ----------------------------------------------------------------
   The quiet cousin of the modal: a corner card with no scrim that never blocks
   the page. It sits at --z-sticky (50), BELOW --z-header (60), so it cannot
   cover a sticky header - which is why --top and --top-right offset themselves
   by --sik-header-h rather than trying to outrank it. */
.sik-popin {
    position: fixed;
    z-index: var(--z-sticky);
    max-width: min(340px, calc(100vw - 24px));
    display: none;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    /* popup_theme_style() may print a background inline and beat this, so the
       brand mark is a pseudo-element that survives either way. */
    background: var(--sik-elevated);
    box-shadow: var(--sik-shadow-pop);
    overflow: hidden;
}
.sik-popin::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    background: var(--sik-grad-brand);
    pointer-events: none;
}
.sik-popin.is-open { display: flex; animation: sik-popin-in var(--dur-slow) var(--ease-out) both; }
.sik-popin--bottom-left  { left: 16px;  bottom: 16px; }
.sik-popin--bottom-right { right: 16px; bottom: 16px; }
.sik-popin--top-right    { right: 16px; top: calc(var(--sik-header-h) + 16px); }
.sik-popin--bottom       { left: 50%; bottom: 16px; transform: translateX(-50%); }
.sik-popin--top          { left: 50%; top: calc(var(--sik-header-h) + 16px); transform: translateX(-50%); }
.sik-popin--left         { left: 16px;  top: 50%; transform: translateY(-50%); }
.sik-popin--right        { right: 16px; top: 50%; transform: translateY(-50%); }
@media (max-width: 640px) {
    /* One position on a phone: a full-width card above the bottom bar. The
       transform reset is what stops the four centred variants sliding half
       their own width off the screen. */
    .sik-popin {
        left: 12px; right: 12px;
        top: auto;
        bottom: calc(var(--sik-bottomnav-space) + 12px);
        max-width: none;
        transform: none;
    }
}
@keyframes sik-popin-in { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

/* Same two switches as the toasts, and the same reason for `animation: none`
   rather than a shorter duration: every keyframe above fills `both` from
   opacity:0, so killing only the duration would risk a dialog that opens
   invisible. The selectors are pitched at .is-open specificity (0,4,0 / 0,2,0)
   so they beat the animation-name the phone-sheet query sets, whatever the
   source order ends up being after a splice. */
@media (prefers-reduced-motion: reduce) {
    .sik-modal.is-open .sik-modal__panel,
    .sik-modal.is-open .sik-modal__backdrop,
    .sik-popin.is-open { animation: none; }
}
.sik-noanim .sik-modal.is-open .sik-modal__panel,
.sik-noanim .sik-modal.is-open .sik-modal__backdrop,
.sik-noanim .sik-popin.is-open { animation: none; }

/* --------------------------------------------------------------------------
   26. Mini cart drawer

   The drawer SHELL is not here: .sik-drawer / --right / __head / __body /
   __foot / __total / __actions are section 7, and they already own the slide
   from the right, the visibility delay that takes a closed drawer out of the
   tab order, the safe-area padding and the flex column that pins the footer to
   the bottom. This section is everything that goes inside - the free-delivery
   meter, the line items, the empty state - plus the scope-tuning that makes
   #sikCartDrawer feel festive without touching the shell every other drawer
   shares. The checkout button is .sik-btn--primary from section 4, so the
   brand gradient, the coloured depth and the sheen all arrive with it; only
   its surroundings are set here.

   .sik-summary rides along at the end because it has always lived in this
   section, but it is NOT a drawer component: eight storefront pages, the admin
   order screen and invoice.php - the print path - all render it. That is why
   it stays flat and role-token driven while everything above it does not.
   -------------------------------------------------------------------------- */

/* --- the drawer, scope-tuned ---------------------------------------------- */
#sikCartDrawer .sik-drawer__head {
    position: relative;
    /* A whisper of brand at the top of the panel; the rule below covers the
       shell's grey hairline with the gradient version of the same line. */
    background: var(--sik-grad-soft);
}
#sikCartDrawer .sik-drawer__head::after {
    content: "";
    position: absolute;
    inset: auto 0 -1px 0;
    height: 2px;
    background: var(--sik-grad-brand);
    pointer-events: none;
}
#sikCartDrawer .sik-drawer__title { font-size: var(--fs-lg); letter-spacing: var(--ls-tight); }

/* No `display` here, ever. cart.js shows and hides this footer with
   el.hidden (#sikCartDrawerFoot), and the [hidden] rule from section 1b only
   wins while nothing later declares a display on it. Section 7 already pins
   the footer with flex: none inside the drawer's column, so there is nothing
   to gain and a whole empty-cart state to lose. */
#sikCartDrawer .sik-drawer__foot {
    /* The line-item list scrolls under this, so it needs an edge of its own -
       a hairline alone reads as the bottom border of the last row. Derived
       from --sik-ink rather than written as rgba(16,24,40,.45): in light mode
       that is the same near-black, but a fixed dark sheen is invisible against
       the dark theme's own #17181C footer, which is exactly where the footer
       needs the separation most. Mixed from the ink role, it flips to a faint
       upward glow there instead. */
    box-shadow: 0 -12px 24px -20px color-mix(in srgb, var(--sik-ink) 45%, transparent);
}
#sikCartDrawer .sik-drawer__total { font-size: var(--fs-xl); }
/* The amount, in the brand's readable ink. --sik-primary-ink is the shade
   declared for type on a light surface; a gradient fill would drop below 4.5:1
   at this size, which is why the total is flat brand and not .sik-gradient-text. */
#sikCartDrawer .sik-drawer__total > :last-child { color: var(--sik-primary-ink); }
#sikCartDrawer .sik-drawer__actions .sik-btn { min-height: var(--tap); }

/* The empty state is section 30's .sik-empty, injected by cart.js with
   assets/images/placeholders/empty-cart.svg and a Start shopping button. Only
   the proportions change here: a 400px drawer is not a page, and the
   page-sized artwork filled the whole panel. */
#sikCartDrawer .sik-empty { padding: var(--sp-9) var(--sp-5); }
#sikCartDrawer .sik-empty > img,
#sikCartDrawer .sik-empty > svg { width: calc(var(--icon-2xl) * 3); margin-bottom: var(--sp-4); }
#sikCartDrawer .sik-empty__text { margin-bottom: var(--sp-4); }

/* --- line items ------------------------------------------------------------ */
.sik-minicart__item {
    /* position: relative is load-bearing twice over: .sik-minicart__remove is
       absolutely positioned against it, and section 34's
       .sik-minicart__item.sik-loading::before spinner (z-index 6) centres on
       it. Nothing added inside this row may sit at or above z-index 6, or it
       covers the spinner that says the row is busy. */
    position: relative;
    display: flex;
    gap: var(--sp-3);
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--sik-line);
    transition: background-color var(--dur-fast) var(--ease-out);
}
/* A tint, not a lift - but still behind the hover guard, because a hover state
   that sticks on a touch row looks like a selection the shopper did not make. */
@media (hover: hover) {
    .sik-minicart__item:hover { background: var(--sik-hover); }
}
.sik-noanim .sik-minicart__item { transition: none; }

.sik-minicart__img {
    width: 72px; height: 72px;
    flex: none;
    object-fit: cover;
    border-radius: var(--sik-radius-sm);
    /* Self-contained on purpose: order-success.php:169/173 renders this class
       outside any drawer, so its size and its edge cannot come from a parent. */
    border: 1px solid var(--sik-line);
    background: var(--sik-media);
}
.sik-minicart__name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* Clears the absolutely positioned remove button in the corner. */
    padding-right: var(--sp-6);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
}
.sik-minicart__name:hover { color: var(--sik-primary-ink); }
.sik-minicart__variant { margin-top: 2px; font-size: var(--fs-xs); color: var(--sik-muted); }
/* Out of stock: the row stays, dimmed, and cart.js swaps the quantity stepper
   for "Remove to continue" - the shopper has to act on it before checkout will
   take the order. */
.sik-minicart__item.is-out .sik-minicart__img,
.sik-minicart__item.is-out .sik-minicart__name { opacity: .55; }

.sik-minicart__remove {
    position: absolute;
    top: var(--sp-3);
    right: var(--sp-3);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 50%;
    background: transparent;
    color: var(--sik-muted);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-minicart__remove { width: var(--tap); height: var(--tap); top: var(--sp-1); right: var(--sp-1); } }
.sik-minicart__remove:hover { background: var(--sik-hover); color: var(--sik-danger-ink); }
.sik-minicart__remove:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; }

.sik-minicart__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
}
/* The stepper is page-sized by default; in a 400px drawer, beside a 72px
   thumbnail, it has to leave the line total somewhere to sit. It still grows
   to a real target on a finger. */
.sik-minicart__item .sik-qty button,
.sik-minicart__item .sik-qty input { height: 34px; }
.sik-minicart__item .sik-qty button { width: 34px; }
.sik-minicart__item .sik-qty input { width: 34px; }
@media (pointer: coarse) {
    .sik-minicart__item .sik-qty button,
    .sik-minicart__item .sik-qty input { height: 40px; }
    .sik-minicart__item .sik-qty button { width: 40px; }
}

/* --- free-delivery progress -------------------------------------------------
   The same meter and the same sweep as the homepage offer strip (section 16b),
   so the number a shopper met on the front page is the number that greets them
   here.

   No `display` declared, deliberately: cart.js sets el.hidden on
   [data-ship-bar] and [data-ship-done] (cart.js:161-164), and a display in this
   rule would beat [hidden] at equal specificity. Give this a display:flex and
   it needs a `[hidden] { display: none }` companion, the way .sik-couponform
   and .sik-coupon__offers already do in section 34. */
.sik-shipbar {
    padding: var(--sp-4) var(--sp-5);
    border-bottom: 1px solid var(--sik-line);
    background: var(--sik-grad-soft);
    font-size: var(--fs-sm);
    color: var(--sik-ink-2);
}
.sik-shipbar strong { color: var(--sik-ink); font-weight: var(--fw-semibold); }
/* The unlocked variant reads as a win rather than as one more instruction. */
.sik-shipbar[data-ship-done] strong { color: var(--sik-success-ink); }
.sik-shipbar .sik-progress { margin-top: var(--sp-2); }
.sik-shipbar .sik-progress > span {
    background: var(--sik-grad-cta);
    /* The width is written inline by cart.js as the cart changes; the
       transition is only here so the bar slides to its new value rather than
       jumping to it. */
    transition: width var(--dur-slow) var(--ease-out);
}
.sik-noanim .sik-shipbar .sik-progress > span { transition: none; }

/* --- coupon applied ---------------------------------------------------------
   The dashed green box on the cart page's "coupon applied" row. Section 34
   (line 9191) declares .sik-coupon__on a SECOND time, for its display:flex and
   its typography; that block is later and wins on conflict, but it does not
   restate the box - delete this rule and the dashed frame quietly disappears
   from cart.php. */
.sik-coupon__on {
    padding: var(--sp-3);
    border: 1px dashed color-mix(in srgb, var(--sik-success) 55%, var(--sik-surface));
    border-radius: var(--sik-radius-sm);
    background: var(--sik-success-soft);
}

/* --- the totals list --------------------------------------------------------
   Flat, and the one thing in sections 24-26 that has to stay that way. It
   renders on cart, checkout, order-details (twice), order-success, profile,
   order-tracking (twice), admin/orders/view.php and invoice.php - the print
   path in section 35. A gradient, a glass panel, a coloured shadow or a
   dark-only treatment here lands on a printed invoice and on an admin order
   screen. The festive version of this block is .sik-summarycard in section 34,
   which wraps it on the storefront and leaves the print path alone. */
.sik-summary { display: grid; gap: var(--sp-3); font-size: var(--fs-base); }
.sik-summary__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); color: var(--sik-ink-2); }
/* tabular-nums so the rupee amounts line up down the column instead of
   wandering with the digit widths. */
.sik-summary__row > :last-child { color: var(--sik-ink); font-variant-numeric: tabular-nums; }
.sik-summary__row--total {
    margin-top: var(--sp-1);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-line);
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
    color: var(--sik-ink);
}
.sik-summary__row--save,
.sik-summary__row--save > :last-child { color: var(--sik-success-ink); font-weight: var(--fw-medium); }

/* --------------------------------------------------------------------------
   27. Listing: layout, filters, toolbar

   The catalogue, rendered by eight pages through includes/product-listing.php:
   a filter card that sticks beside the grid from 1024 up, the same groups
   again inside a left drawer below it, and a glass toolbar that rides down the
   page carrying the result count, the sort control and the filter trigger.

   The one decision worth knowing: selection is brand-coloured, never grey.
   A ticked row takes a pink wash and a pink native tick, a chosen colour takes
   a brand ring with the coloured shadow under it, and an applied filter comes
   back as a brand chip above the grid. That is only reachable from here
   because product_listing_assets() prints `.sik-swatch:has(input:checked)`
   AFTER this stylesheet at (0,2,1) - so every selected-swatch rule below
   carries a `.sik-swatches` ancestor purely to out-specify it. Drop the
   ancestor and the shopper's colour goes back to the old ink ring on all
   eight pages.
   -------------------------------------------------------------------------- */

/* Both sticky things in the listing hang off one offset. --sik-header-h is the
   height header.js measures, and it is only *above* the viewport top when the
   header is genuinely pinned there - Appearance > Header = sticky, the shipped
   default. When it is not, that offset parks the sidebar and the toolbar a
   header's worth down the screen with nothing but page in the gap, which is
   what the :has() line below fixes. A browser with no :has() drops that line
   and keeps the default, so the shipped configuration degrades safely. */
.sik-listing__body { --listing-stick: calc(var(--sik-header-h) + var(--sp-4)); }
body:not(:has(.sik-header--sticky)) .sik-listing__body { --listing-stick: var(--sp-4); }

.sik-listing { padding-top: var(--sp-4); }
.sik-listing__head { margin: var(--sp-5) 0 var(--sp-7); }
/* A brand hairline closing the header off, fading out rather than ruling the
   full width - the heading keeps the weight, the rule only marks where the
   catalogue starts. product_listing_header() always emits .sik-listing__head,
   banner or no banner, so this is the one divider the section can rely on. */
.sik-listing__head::after {
    content: "";
    display: block;
    height: 2px;
    margin-top: var(--sp-6);
    border-radius: var(--sik-radius-pill);
    background: linear-gradient(90deg,
        var(--sik-primary) 0%,
        var(--sik-accent) 18%,
        color-mix(in srgb, var(--sik-accent) 22%, transparent) 46%,
        transparent 78%);
}
.sik-listing__banner {
    display: block;
    width: 100%;
    max-height: 240px;
    margin-bottom: var(--sp-6);
    object-fit: cover;
    border-radius: var(--sik-radius-lg);
    /* The well shows while a 1200x240 upload decodes, so a slow connection
       gets the shape of the page instead of a collapsing header. */
    background: var(--sik-media);
    box-shadow: var(--sik-shadow-card);
}
.sik-listing__headrow { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
.sik-listing__logo {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 80px; height: 80px;
    padding: var(--sp-3);
    border-radius: var(--sik-radius);
    /* A brand wash instead of the flat grey well: brand logos are usually
       transparent PNGs, and the tint is what keeps one from reading as an
       image that failed to load. */
    background: var(--sik-grad-soft);
    border: 1px solid var(--sik-line);
    box-shadow: var(--sik-shadow-xs);
}
.sik-listing__logo img { max-width: 100%; max-height: 100%; object-fit: contain; }
/* .sik-listing__heading / __title / __sub are deliberately absent here: they
   are styled in the "Listing header" block at the tail of section 38, which is
   later in the file and would beat anything written at this point anyway. */
.sik-listing__desc { margin-top: var(--sp-4); max-width: 80ch; font-size: var(--fs-base); color: var(--sik-ink-2); }
.sik-listing__pills { margin-top: var(--sp-5); }
.sik-listing__body { padding-bottom: var(--section-y); }
/* minmax(0, ...) on both tracks. A bare 1fr floors at its widest unbreakable
   child - a long product name - and that is what used to scroll the listing
   sideways at 320px. */
.sik-listing__layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); align-items: start; }
@media (min-width: 1024px) { .sik-listing__layout { grid-template-columns: 248px minmax(0, 1fr); gap: var(--sp-9); } }
/* The responsive contract is doubled on purpose: the aside is hidden below
   1024 AND .sik-filters is hidden below 1024, so the sidebar copy of the
   filters cannot leak into the phone layout if one of the two is ever edited. */
.sik-listing__aside { display: none; }
@media (min-width: 1024px) { .sik-listing__aside { display: block; } }
/* The no-JS "Apply filters" submit sits outside .sik-filters, so it needs its
   own air. It is only in the DOM at all with scripting off. */
.sik-listing__aside noscript .sik-btn { margin-top: var(--sp-4); }
.sik-listing__count { margin-top: var(--sp-4); text-align: center; font-size: var(--fs-xs); color: var(--sik-muted); font-variant-numeric: tabular-nums; }
/* products.js swaps .sik-grid and .sik-list on the SAME element, and the empty
   state rendered inside it carries style="grid-column:1/-1" - so this has to
   stay a single-column grid or that inline span stops resolving. Tighter than
   the grid's row gap because a row card is already a wide, heavy object. */
.sik-list { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }

/* --- the sidebar ----------------------------------------------------------- */
.sik-filters { display: none; }
@media (min-width: 1024px) {
    .sik-filters {
        display: block;
        position: sticky;
        top: var(--listing-stick);
        max-height: calc(100vh - var(--listing-stick) - var(--sp-5));
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: var(--sp-4) var(--sp-3) var(--sp-5) var(--sp-4);
        border: 1px solid var(--sik-line);
        border-radius: var(--sik-radius-lg);
        background: var(--sik-surface);
        box-shadow: var(--sik-shadow-soft);
        scrollbar-width: thin;
    }
}
.sik-filters__head {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: 0 var(--sp-2) var(--sp-3);
}
/* A brand rule rather than the grey hairline: the one place in the sidebar
   that gets to carry the sweep. Absolutely placed so it spans the head's full
   width while the label keeps the same 8px text column as every row below. */
.sik-filters__head::after {
    content: "";
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 2px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-brand);
}
.sik-filters__title {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
}
/* --sik-grad-cta, not --sik-grad-brand: white strokes need 3:1 and the CTA
   sweep is the one that is contrast-bounded for white at every stop, so a
   re-themed pale primary cannot wash the glyph out. Sized here rather than at
   the call site, which passes w-4; (0,1,1) equals svg.w-4 but comes later. */
.sik-filters__title svg {
    width: 26px; height: 26px;
    padding: 5px;
    border-radius: var(--sik-radius-xs);
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-filters__clear { font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--sik-muted); }
@media (hover: hover) { .sik-filters__clear:hover { color: var(--sik-primary-ink); } }

/* --- one filter group ------------------------------------------------------ */
.sik-filter { padding-block: var(--sp-1) var(--sp-2); border-bottom: 1px solid var(--sik-line); }
/* Nothing follows the last group in either copy, so its hairline only ever
   drew a line across the bottom of the card. */
.sik-filter:last-child { border-bottom: 0; }
/* The head MUST stay a direct child of .sik-filter: the inline click handler
   toggles is-collapsed on head.parentNode. */
.sik-filter__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
    width: 100%;
    min-height: var(--tap);
    padding: 0 var(--sp-2);
    border: 0;
    background: none;
    cursor: pointer;
    font: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    text-align: left;
    color: var(--sik-ink);
    transition: color var(--dur-fast) var(--ease-out);
}
/* The chevron sits in a chip so open/closed is a shape the eye catches from
   the edge of vision, not just a 16px stroke that happens to point a different
   way. Same (0,1,1) as svg.w-4 and later in the file, so this is what sizes
   it. */
.sik-filter__head svg {
    width: 24px; height: 24px;
    padding: 4px;
    border-radius: var(--sik-radius-xs);
    background: var(--sik-surface-2);
    color: var(--sik-muted);
    transition: transform var(--dur-base) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
.sik-filter.is-collapsed .sik-filter__head svg { transform: rotate(-90deg); }
@media (hover: hover) {
    .sik-filter__head:hover { color: var(--sik-primary-ink); }
    .sik-filter__head:hover svg { background: color-mix(in srgb, var(--sik-primary) 13%, transparent); color: var(--sik-primary-ink); }
}
/* A group holding a filter says so even while it is shut - the whole point of
   letting a shopper close one. value="" is the "Any rating" / "Any discount" /
   "All products" radio, which is checked by default and must not count as a
   choice, or three groups would light up on an unfiltered listing. */
.sik-filter:has(input:checked:not([value=""])) .sik-filter__head { color: var(--sik-primary-ink); }
.sik-filter:has(input:checked:not([value=""])) .sik-filter__head svg {
    background: color-mix(in srgb, var(--sik-primary) 15%, transparent);
    color: var(--sik-primary-ink);
}
/* Price is two number boxes, not checkboxes, so it reads the printed attribute
   instead: PHP writes value="" when the box is empty and the real figure once
   the filter has been applied. It lights up on the result rather than
   mid-keystroke, which is the honest moment anyway. */
.sik-filter:has(.sik-price-inputs .sik-input:not([value=""])) .sik-filter__head { color: var(--sik-primary-ink); }
.sik-filter:has(.sik-price-inputs .sik-input:not([value=""])) .sik-filter__head svg {
    background: color-mix(in srgb, var(--sik-primary) 15%, transparent);
    color: var(--sik-primary-ink);
}

/* The JS contract is only the .is-collapsed class on .sik-filter, and nothing
   animates against display:none, so the open/closed affordance is the chevron
   chip - never a height. Inline padding stays at 0: the body is a scroll
   container, and anything hanging past its content box on the right becomes
   scrollable overflow and hands the colour row a horizontal scrollbar. */
.sik-filter__body {
    max-height: 280px;
    padding-block: var(--sp-1) var(--sp-2);
    overflow-y: auto;
    overscroll-behavior: contain;
    scrollbar-width: thin;
}
.sik-filter.is-collapsed .sik-filter__body { display: none; }

.sik-filter__option {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2);
    border-radius: var(--sik-radius-sm);
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
    color: var(--sik-ink-2);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { .sik-filter__option:hover { background: var(--sik-hover); color: var(--sik-ink); } }
/* 8px of the indent is now the row's own padding, so the nested step is --sp-7
   to land on the same 24px offset it has always had. */
.sik-filter__option--nested { padding-left: var(--sp-7); }
@media (pointer: coarse) { .sik-filter__option { min-height: var(--tap); } }
/* The native input stays the native input. It is the control that gets
   submitted, the inline mirror script sets twin.checked on it and products.js
   sets input.checked = false when a chip is removed - hiding it behind a drawn
   box would break all three and take the row out of the keyboard path.
   accent-color is how it gets a brand tick instead: the browser picks the
   mark's own colour against the accent, so contrast holds through a re-theme
   and in dark mode. 18px matches .sik-check in section 22. */
.sik-filter__option input {
    flex: none;
    width: 18px; height: 18px;
    accent-color: var(--sik-primary);
    cursor: pointer;
}
/* Unmistakable selection: a brand wash across the whole row, not a grey tick
   the eye has to hunt for. :has() on the label is the same pattern the inline
   <style> already uses for swatches, so the file stays consistent about it. */
.sik-filter__option:has(input:checked:not([value=""])) {
    background: color-mix(in srgb, var(--sik-primary) 10%, transparent);
    color: var(--sik-ink);
    font-weight: var(--fw-medium);
}
.sik-filter__count { margin-left: auto; font-size: var(--fs-xs); color: var(--sik-muted); font-variant-numeric: tabular-nums; }
.sik-filter__option:has(input:checked:not([value=""])) .sik-filter__count { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }
.sik-filter__note { margin-top: var(--sp-2); padding-inline: var(--sp-2); font-size: var(--fs-xs); color: var(--sik-muted); }
/* The rating rows wrap rating_stars(), which prints --fill inline on each
   star. Nothing here may touch .sik-stars / .sik-star or the partial fill in
   section 23 stops resolving. */
.sik-filter__stars { display: inline-flex; align-items: center; gap: var(--sp-2); }

/* --- price ----------------------------------------------------------------- */
/* The muted colour is for the en dash between the two boxes, which is a bare
   <span aria-hidden> in the markup. */
.sik-price-inputs { display: flex; align-items: center; gap: var(--sp-2); padding-inline: var(--sp-2); color: var(--sik-muted); }
.sik-price-inputs + .sik-range { margin-top: var(--sp-4); }
/* Deliberately no border-color here: .sik-input.is-invalid has to keep winning
   on these two boxes, and a colour declared at this specificity would eat it. */
.sik-price-inputs .sik-input { min-height: 40px; min-width: 0; padding-inline: var(--sp-3); font-variant-numeric: tabular-nums; }
/* 40px was the one control left in this section under --tap on a phone. */
@media (pointer: coarse) { .sik-price-inputs .sik-input { min-height: var(--tap); } }

/* One max-only slider, not a dual thumb: data-price-range points it at the
   max-price box by id and the box is what gets submitted. */
.sik-range { display: flex; align-items: center; gap: var(--sp-3); padding-inline: var(--sp-2); }
.sik-range input[type="range"] {
    flex: 1;
    min-width: 0;
    height: 28px;
    accent-color: var(--sik-primary);
    cursor: pointer;
}
@media (pointer: coarse) { .sik-range input[type="range"] { height: var(--tap); } }

/* --- the phone's filter drawer --------------------------------------------- */
/* Plain rows on the drawer surface, not the inset grouped list the menu drawer
   uses. Both of these beat section 7's .sik-drawer--left pair at equal (0,2,0)
   by being later in the file. */
.sik-drawer__body.sik-filterdrawer__body { padding: var(--sp-2) var(--sp-4) var(--sp-4); background: var(--sik-elevated); }
/* One column, and the source order does the rest: "Clear all" is first in the
   markup and the submit second, so the brand button lands at the bottom of the
   sheet where the thumb already is - without `order`, which would have split
   the visual order from the tab order. The label is a static
   <span class="sik-btn__label">Show results</span> and neither products.js nor
   the inline script writes a count into it, so CSS cannot make it say
   "Show N results" without a markup change. */
.sik-drawer__foot.sik-filterdrawer__foot {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-2);
    background: var(--sik-elevated);
    /* Pure black used as a sheen. Section 7's flex layout already pins this
       footer; the shadow is what tells the eye the list scrolls under it. */
    box-shadow: 0 -10px 24px -16px rgba(0, 0, 0, .38);
}
.sik-filterdrawer__foot .sik-btn--primary { min-height: 52px; font-size: var(--fs-md); }
/* Quieter than a stock outline button so the two do not shout at the same
   volume. It stays an <a>: products.js reads getAttribute('href') off it, so
   anything that is not an anchor loses the search query on Clear all. */
.sik-filterdrawer__foot .sik-btn--outline {
    min-height: var(--tap);
    border-color: var(--sik-line);
    color: var(--sik-muted);
    font-weight: var(--fw-medium);
}
@media (hover: hover) {
    .sik-filterdrawer__foot .sik-btn--outline:hover {
        background: var(--sik-hover);
        border-color: var(--sik-line-strong);
        color: var(--sik-primary-ink);
    }
}

/* The trigger. `.sik-btn.sik-filtertoggle` is (0,2,0), so it beats
   .sik-btn--outline and its hover without depending on source order. */
.sik-btn.sik-filtertoggle {
    min-height: var(--tap);
    background: color-mix(in srgb, var(--sik-primary) 10%, var(--sik-surface));
    border-color: color-mix(in srgb, var(--sik-primary) 26%, transparent);
    color: var(--sik-primary-ink);
    font-weight: var(--fw-semibold);
}
@media (hover: hover) {
    .sik-btn.sik-filtertoggle:hover {
        background: color-mix(in srgb, var(--sik-primary) 17%, var(--sik-surface));
        border-color: color-mix(in srgb, var(--sik-primary) 40%, transparent);
        color: var(--sik-primary-ink);
    }
}
@media (min-width: 1024px) { .sik-btn.sik-filtertoggle { display: none; } }
/* Without JavaScript the drawer opens as a link target instead. (1,1,0) keeps
   it ahead of .sik-drawer--left's translateX(-102%) in section 7. */
#sikFilterDrawer:target { transform: translateX(0); visibility: visible; }

/* --- colour swatches -------------------------------------------------------
   Not a listing component: product.php and api/products/details.php render a
   swatch as a bare <button> with no children and products.js drives
   .is-active / .is-disabled on it. On the listing it is a <label> wrapping a
   real checkbox, and the inline <style> hides that input and reveals the check
   glyph. Both ::after (the 44px tap extender) and ::before (the disabled
   strike) are already spoken for, so a selected ring has to be a box-shadow. */
.sik-swatches { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
/* The tap extender overhangs the 32px dot by 6px each side. .sik-filter__body
   is a scroll container, so the right-hand overhang counts as scrollable
   overflow and put a horizontal scrollbar under every colour row. Eight pixels
   absorbs it, and it lines the dots up with the 8px text column above. */
.sik-filter__body .sik-swatches { padding-inline: var(--sp-2); }
.sik-swatch {
    /* The carve ring has to be the colour of whatever the swatch actually sits
       on. It was hard-coded to --sik-bg, but inside the drawer the surface is
       --sik-elevated, which in dark mode is two steps lighter than the page -
       so a selected colour in the drawer drew a dark notch around itself. */
    --swatch-ground: var(--sik-bg);
    position: relative;
    width: 32px; height: 32px;
    padding: 0;
    border-radius: 50%;
    border: 1px solid var(--sik-line-strong);
    box-shadow: inset 0 0 0 2px var(--swatch-ground);
    cursor: pointer;
    transition: transform var(--dur-fast) var(--ease-out), box-shadow var(--dur-fast) var(--ease-out);
}
.sik-filterdrawer__body .sik-swatch,
.sik-modal__panel .sik-swatch { --swatch-ground: var(--sik-elevated); }
.sik-swatch::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 44px; height: 44px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
}
@media (hover: hover) {
    /* A scale is movement, so it lives in here and dies with .sik-noanim. */
    .sik-swatch:hover { transform: scale(1.06); }
    .sik-swatch.is-disabled:hover { transform: none; }
}
.sik-noanim .sik-swatch:hover { transform: none; }
/* WHY the .sik-swatches ancestor: product_listing_assets() prints
   `.sik-swatch:has(input:checked)` after this file at (0,2,1). A plain
   `.sik-swatch.is-active` is (0,2,0) and earlier, so it loses and the colour a
   shopper picked keeps the old grey ink ring. (0,3,0) wins, and every renderer
   - listing, product.php, quick view - wraps its swatches in .sik-swatches. */
.sik-swatches .sik-swatch.is-active {
    border-color: transparent;
    box-shadow: inset 0 0 0 2px var(--swatch-ground),
                0 0 0 2px var(--sik-primary),
                var(--sik-shadow-brand-sm);
}
/* The same ring driven by the real checkbox, which is the only thing that
   reacts when a box is ticked without a reload - .is-active is server-rendered
   first paint only. Its own rule rather than a selector list, so a browser
   with no :has() still gets the line above instead of dropping both. */
.sik-swatches .sik-swatch:has(input:checked) {
    border-color: transparent;
    box-shadow: inset 0 0 0 2px var(--swatch-ground),
                0 0 0 2px var(--sik-primary),
                var(--sik-shadow-brand-sm);
}
.sik-swatch.is-disabled { opacity: .38; cursor: not-allowed; }
.sik-swatch.is-disabled::before {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: 128%; height: 1.5px;
    border-radius: 2px;
    background: var(--sik-muted);
    transform: translate(-50%, -50%) rotate(-45deg);
    pointer-events: none;
}

/* --- pills -----------------------------------------------------------------
   Also not a listing component: blog.php, faq.php, brands.php, search.php and
   purchase-history.php render .sik-pill as an <a>, product.php and quick view
   render it as a <button> variant picker. `font: inherit` is what keeps those
   two from setting in different faces. */
.sik-pills { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sik-pill {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    flex-shrink: 0;
    min-height: 40px;
    padding: 0 var(--sp-4);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface);
    color: var(--sik-ink-2);
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out),
                color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-pill { min-height: var(--tap); } }
@media (hover: hover) {
    .sik-pill:hover {
        background: color-mix(in srgb, var(--sik-primary) 8%, var(--sik-surface));
        border-color: color-mix(in srgb, var(--sik-primary) 42%, var(--sik-line-strong));
        color: var(--sik-primary-ink);
    }
}
/* Filled with the CTA sweep rather than flat ink: every stop of --sik-grad-cta
   clears 4.5:1 against white, which a raw --sik-primary would not. */
.sik-pill.is-active {
    background-color: var(--sik-cta);
    background-image: var(--sik-grad-cta);
    border-color: transparent;
    color: var(--sik-on-cta);
    font-weight: var(--fw-semibold);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-pill.is-disabled { opacity: .4; cursor: not-allowed; text-decoration: line-through; }
/* Was `opacity: .6` over inherited ink, which lands near 3:1 - under AA for
   what is still body-sized text. Weight, not transparency, steps it back. */
.sik-pill__count { color: var(--sik-muted); font-weight: var(--fw-normal); font-variant-numeric: tabular-nums; }
.sik-pill.is-active .sik-pill__count { color: var(--sik-on-cta); }

/* --- applied-filter chips --------------------------------------------------- */
.sik-chips { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sik-chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    min-height: 34px;
    padding: 0 var(--sp-1) 0 var(--sp-3);
    border: 1px solid color-mix(in srgb, var(--sik-primary) 24%, transparent);
    border-radius: var(--sik-radius-pill);
    background: color-mix(in srgb, var(--sik-primary) 10%, var(--sik-surface));
    color: var(--sik-primary-ink);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
}
@media (pointer: coarse) { .sik-chip { min-height: 40px; } }
/* position: relative is load-bearing. The 44px tap circle on this button is
   .sik-chip button::after, owned by the shared extender group in section 2b;
   with no positioned parent here that circle escapes to whatever ancestor
   happens to be positioned and starts eating clicks elsewhere on the page. */
.sik-chip button {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px; height: 26px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: var(--sik-primary-ink);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) { .sik-chip button:hover { background: color-mix(in srgb, var(--sik-primary) 20%, transparent); } }
/* Kept dashed and neutral on purpose. api/products/list.php re-renders this
   control after every filter change as a bare .sik-chip carrying inline
   background / border / color declarations, and inline wins - so a
   brand-tinted Clear-all would silently turn grey the moment the grid
   refreshed. This is the shape that survives both renderers. */
.sik-chip--clear {
    padding: 0 var(--sp-3);
    border: 1px dashed var(--sik-line-strong);
    background: transparent;
    color: var(--sik-muted);
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    cursor: pointer;
}
@media (hover: hover) {
    .sik-chip--clear:hover { color: var(--sik-primary-ink); border-color: color-mix(in srgb, var(--sik-primary) 50%, var(--sik-line-strong)); }
}
.sik-active-filters { margin-bottom: var(--sp-5); }
/* Load-bearing: the chips builder returns '' when nothing is selected, so
   without this the wrapper leaves a --sp-5 hole above the grid on every
   unfiltered listing. */
.sik-active-filters:empty { display: none; }
/* The chip row is replaced wholesale by innerHTML on every filter change, so
   chips arrive rather than blink. `both` plus an explicit `animation: none` in
   each kill switch means the fill mode goes with the animation - nothing is
   ever left sitting at opacity 0 if the animation does not run. */
.sik-chips > * { animation: sik-pop var(--dur-base) var(--ease-out) both; }
@media (prefers-reduced-motion: reduce) { .sik-chips > * { animation: none; } }
.sik-noanim .sik-chips > * { animation: none; }
/* Settings > Theme > enable animations = off has to reach the transitions that
   move something, not just the keyframes. The chevron rotate is the only other
   one in this section. */
.sik-noanim .sik-filter__head svg { transition: none; }

/* --- the toolbar ------------------------------------------------------------
   Rides down with the results so the count, the sort and the filter trigger
   are never more than a glance away. Glass because product photography scrolls
   under it - an opaque bar would read as a hole punched through the grid. The
   sort <select> reaches its form through form="sikFilterForm", which is
   position-independent, so sticking this changes nothing about submission. */
.sik-toolbar {
    position: sticky;
    top: var(--listing-stick);
    /* --z-sticky, below --z-header: a sticky header is the thing that wins the
       strip at the top of the screen, and the drawer and its overlay sit
       higher again so an open filter sheet still dims this. */
    z-index: var(--z-sticky);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-bottom: var(--sp-5);
    padding: var(--sp-2);
    border: 1px solid var(--sik-glass-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    box-shadow: var(--sik-shadow-soft);
}
@media (min-width: 768px) { .sik-toolbar { padding: var(--sp-2) var(--sp-4); } }
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sik-toolbar {
        /* Denser than --sik-glass-bg (76%): this bar carries 13px text over
           product photos rather than over a flat band, and --sik-glass-bg is
           tuned for the header's much larger type. The opaque rule above is
           the fallback, so text is never left floating on a photograph. */
        background: color-mix(in srgb, var(--sik-surface) 88%, transparent);
        -webkit-backdrop-filter: var(--sik-glass-blur);
        backdrop-filter: var(--sik-glass-blur);
    }
}
/* --sik-ink-2, not --sik-muted: muted grey measures 4.8:1 on a solid surface
   and there is no headroom left for it once the bar is only 88% opaque. */
.sik-toolbar__meta { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; font-size: var(--fs-sm); color: var(--sik-ink-2); }
.sik-toolbar__meta b { color: var(--sik-primary-ink); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
.sik-toolbar__controls { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
/* 176 + the gap + the view toggle is wider than the 320px content box, so the
   toggle wraps to its own line there instead of pushing the page sideways. No
   border-color here either, for the same reason as the price boxes:
   .sik-select.is-invalid has to keep winning. */
.sik-toolbar__sort { width: auto; min-width: 176px; min-height: 40px; font-weight: var(--fw-medium); }
@media (pointer: coarse) { .sik-toolbar__sort { min-height: var(--tap); } }

/* Styled for both a and button although the markup only prints anchors:
   products.js toggles .is-active on whatever [data-view-mode] element it
   finds, and nothing stops a future renderer emitting the other one. */
.sik-viewtoggle {
    display: inline-flex;
    flex: none;
    gap: 2px;
    padding: 3px;
    border-radius: var(--sik-radius-sm);
    border: 1px solid var(--sik-line);
    background: var(--sik-surface-2);
}
.sik-viewtoggle a,
.sik-viewtoggle button {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px; height: 34px;
    border: 0;
    border-radius: calc(var(--sik-radius-sm) - 2px);
    background: none;
    color: var(--sik-muted);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-viewtoggle a, .sik-viewtoggle button { width: 40px; height: 38px; } }
@media (hover: hover) {
    .sik-viewtoggle a:hover,
    .sik-viewtoggle button:hover { color: var(--sik-primary-ink); }
}
.sik-viewtoggle a.is-active,
.sik-viewtoggle button.is-active {
    background-color: var(--sik-cta);
    background-image: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm);
}

/* --------------------------------------------------------------------------
   28. Product detail page

   Two components share this number. The PDP block below is the gallery + buy
   column pair, and it is NOT product-page-only: api/products/details.php
   renders the same .sik-pdp / .sik-gallery__* / .sik-variant* markup inside a
   modal for Quick View, so nothing here may assume a full viewport. The one
   decision worth knowing: the phone buy bar is `position: sticky` on the
   actions row rather than a fixed bar, because product.php has no bar element
   to hang a fixed one off - and a sticky box cannot cover the page's last line,
   since it releases at the bottom of its own containing block. The cost is that
   it only pins while the buy column is on screen; see the note above the bar
   for the one <div> that would fix that.

   Below the banner: the Compare page (compare.php + assets/js/compare.js),
   which has no section number of its own.
   -------------------------------------------------------------------------- */

/* Splitting at 900 left 768-899 (iPad portrait, most Android tablets) with a
   full-width square gallery - a 720-851px tall image that pushed the title,
   price and Add to Cart entirely below the fold. 768 matches where the rest of
   the system switches. Below that the gallery is capped so it stays in view. */
/* minmax(0, 1fr) rather than a bare 1fr: a bare fr track takes its minimum from
   the widest unbreakable child, so the gallery and spec content were forcing
   the column to ~400px and pushing the page sideways on a 320px screen.
   Capping the minimum at 0 lets those children shrink and scroll instead. */
.sik-pdp { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-7); }
@media (max-width: 767px) {
    .sik-pdp > [data-gallery] { max-width: 520px; margin-inline: auto; width: 100%; }
}
@media (min-width: 768px) {
    /* start, not stretch: the gallery is square and the buy column is as tall as
       its panels, so stretching only ever gave the shorter one dead space. */
    .sik-pdp { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-7); align-items: start; }
}
@media (min-width: 1024px) { .sik-pdp { gap: var(--sp-9); } }

/* --- gallery --------------------------------------------------------------- */
/* The media well. Same recipe as .sik-card__media (section 11): the admin's
   --sik-media underneath, a whisper of brand across the top-left corner, so a
   letterboxed photo sits on something warmer than flat grey. */
.sik-gallery__main {
    position: relative;
    isolation: isolate;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background-color: var(--sik-media);
    background-image: linear-gradient(135deg, color-mix(in srgb, var(--sik-primary) 7%, transparent) 0%, transparent 62%);
    box-shadow: var(--sik-shadow-card);
    aspect-ratio: 1;
    overflow: hidden;
    /* Initial value ONLY. products.js:276-284 writes main.style.cursor inline on
       every click and on mouseleave, so from the first interaction this
       declaration is dead - nothing may be built on the CSS cursor. */
    cursor: zoom-in;
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out);
}
/* The brand hairline along the top edge, the same device the hero panel uses.
   Above the photo, pointer-events off so it never eats the zoom click. */
.sik-gallery__main::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: var(--sik-grad-brand);
    opacity: .9;
    z-index: 2;
    pointer-events: none;
}
@media (hover: hover) {
    .sik-gallery__main:hover {
        border-color: color-mix(in srgb, var(--sik-primary) 30%, var(--sik-line));
        box-shadow: var(--sik-shadow-card), var(--sik-shadow-brand-sm);
    }
}
.sik-gallery__main img {
    width: 100%; height: 100%;
    object-fit: contain;
    padding: 8%;
    /* The zoom is a 2x scale - a transform, so it composites; --dur-slow gives
       it enough time to read as a zoom rather than a jump.
       transform-origin is deliberately NOT declared: products.js writes it
       inline on every mousemove, so a value here would be gone by the first
       pointer move and would only mislead the next reader. */
    transition: transform var(--dur-slow) var(--ease-out);
}
.sik-gallery__main.is-zoomed img { transform: scale(2); padding: 0; }
/* House rule 4, both switches. --dur-slow already collapses to .01ms under
   prefers-reduced-motion (section 1a), but this 2x scale is the one real
   animation on the page so it is switched off explicitly rather than by
   inference - and Settings > Theme > enable animations = off is NOT covered by
   that collapse. Only the travel goes; the inspection zoom still works. */
@media (prefers-reduced-motion: reduce) {
    .sik-gallery__main img { transition: none; }
}
.sik-noanim .sik-gallery__main img { transition: none; }

/* product.php:196 prints `background:#fff` inline on the expand button, so on a
   dark-theme gallery well a white disc sits on near-black. An inline style
   attribute can only be beaten with !important; these two declarations beat
   that one attribute and nothing else, and the value they restore is
   .sik-iconbtn's own --sik-elevated (section 4) that the hex was overriding. */
[data-gallery] [data-gallery-expand] {
    background: var(--sik-elevated) !important;
    border-color: var(--sik-glass-line);
    box-shadow: var(--sik-shadow-soft);
}
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    [data-gallery] [data-gallery-expand] {
        background: var(--sik-glass-bg) !important;   /* same inline #fff, see above */
        -webkit-backdrop-filter: var(--sik-glass-blur);
        backdrop-filter: var(--sik-glass-blur);
    }
}
/* The discount tag on the photo. product.php positions it inline; this only
   paints it, so the badge on the biggest photo in the store gets the brand
   sweep rather than the flat --sik-cta a grid card gets. */
.sik-pdp [data-gallery] .sik-badge--orange {
    background-image: var(--sik-grad-cta);
    box-shadow: var(--sik-shadow-brand-sm);
}

/* The thumb strip stays a horizontally ordered, clickable list at every size,
   because products.js:293-301 answers a 45px touch swipe by calling .click() on
   the next or previous [data-gallery-thumb] - hiding or reordering one in the
   DOM would silently break swiping between images. */
.sik-gallery__thumbs {
    display: flex;
    gap: var(--sp-3);
    margin-top: var(--sp-3);
    overflow-x: auto;
    scrollbar-width: none;
    padding-bottom: var(--sp-1);
}
.sik-gallery__thumbs::-webkit-scrollbar { display: none; }
.sik-gallery__thumb {
    /* 72px is load-bearing: product.php:210 and details.php both print
       width="72" height="72" on the <img>, so any other size reserves the wrong
       box while the thumbnail loads. */
    width: 72px; height: 72px;
    border-radius: var(--sik-radius-sm);
    border: 2px solid var(--sik-line);
    background: var(--sik-media);
    cursor: pointer;
    flex: none;
    padding: var(--sp-1);
    transition: border-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
.sik-gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }
@media (hover: hover) {
    .sik-gallery__thumb:hover { border-color: color-mix(in srgb, var(--sik-primary) 45%, var(--sik-line)); }
}
/* Inset, not offset: the strip is overflow-x: auto, which computes overflow-y
   to auto as well, so an outward ring on the first or last thumb is clipped. */
.sik-gallery__thumb:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: -3px; }
/* The active thumb is not the whole truth about what is on screen: selecting a
   variant writes mainImg.src directly (products.js:441-444) without touching
   any thumb, so this marks the last thumb CHOSEN, never "the image showing". */
.sik-gallery__thumb.is-active {
    border-color: var(--sik-primary);
    box-shadow: var(--sik-shadow-brand-sm);
}

/* Desktop: the strip becomes a column beside the photo. Placed with explicit
   grid lines rather than by reordering the markup, so tab order and the swipe
   handler above still see the source order. :has() keeps the 72px track from
   opening as an empty gutter on a product with one image - and where :has() is
   unsupported the strip simply stays the row it is on a phone. */
@media (min-width: 1024px) {
    .sik-pdp > [data-gallery]:has(.sik-gallery__thumbs) {
        display: grid;
        grid-template-columns: 72px minmax(0, 1fr);
        gap: var(--sp-3);
        align-items: start;
    }
    .sik-pdp > [data-gallery]:has(.sik-gallery__thumbs) > :first-child { grid-area: 1 / 2 / 2 / 3; }
    .sik-pdp > [data-gallery] > .sik-gallery__thumbs {
        grid-area: 1 / 1 / 2 / 2;
        flex-direction: column;
        margin-top: 0;
        padding-bottom: 0;
        overflow-x: hidden;
        overflow-y: auto;
        /* The same 520px the phone gallery is capped at. Nine images would
           otherwise make the rail taller than the photo it belongs to. */
        max-height: 520px;
    }
}

/* --- buy column ------------------------------------------------------------ */
/* No font-size here on purpose: .sik-pdp__title is one of three display heading
   roles and section 2 (app.css:818 / 831) owns both of its sizes. A size set in
   this section would win on source order and quietly orphan that role. */
.sik-pdp__title {
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
}

.sik-pdp__meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-4);
    font-size: var(--fs-sm);
    color: var(--sik-muted);
}
/* The ratings link is <a href="#reviews" data-tab="reviews">, and SIK.initTabs
   (app.js:607) collects EVERY [data-tab] inside the page-level [data-tabs]
   wrapper - so this anchor is handed .is-active and aria-selected="true"
   whenever the Reviews tab is open. Nothing in this section may key off a bare
   .is-active or a bare [data-tab]; every tab rule below carries .sik-tab. */
.sik-pdp__meta a { color: inherit; }
@media (hover: hover) {
    .sik-pdp__meta a:hover { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 3px; }
}
.sik-pdp__meta a:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 3px; border-radius: var(--sik-radius-xs); }

.sik-pdp__price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-3);
    margin: var(--sp-4) 0;
}
.sik-pdp__price .sik-price { font-size: var(--fs-3xl); letter-spacing: var(--ls-tight); }
.sik-pdp__price .sik-price--mrp { font-size: var(--fs-md); }
/* The saving, as a gradient chip. --sik-grad-cta is the contrast-bounded sweep
   (section 1c): every stop on it clears 4.5:1 against white, so --sik-on-cta is
   safe here at 12px. */
.sik-pdp__price .sik-price--off {
    display: inline-flex;
    align-items: center;
    padding: 3px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    box-shadow: var(--sik-shadow-brand-sm);
}
/* THE TRAP SECTION 1b EXISTS FOR. [hidden] { display: none } is declared at
   app.css:419, near the top, so the `display: inline-flex` just above - later,
   same specificity - beats it. products.js:387-397 toggles .hidden on
   [data-variant-mrp] and [data-variant-off], and product.php:250-256 ships them
   hidden when there is no discount: without this line a full-price variant
   starts showing a struck-through MRP and a "0% OFF" chip. */
.sik-pdp__price [hidden] { display: none; }

/* --- variant chips --------------------------------------------------------- */
.sik-variant { margin-bottom: var(--sp-5); }
/* Rendered as a <span> by product.php and a <div> by details.php, so without an
   explicit display the margin-bottom was silently dropped on the product page
   and the label sat flush against its swatches. */
.sik-variant__label {
    display: block;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
    margin-bottom: var(--sp-2);
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
    color: var(--sik-ink);
}
.sik-variant__label b { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }

/* .sik-pill / .sik-swatch are section 27 and belong to the listing filters too.
   What follows are PDP-scoped skins for the two states that have to read across
   a room on a buy page - not a redefinition: the base, the hover and the 44px
   coarse-pointer floor all still come from 27.

   Disabled is declared after selected on purpose. refreshAvailability()
   (products.js:355-372) sets .is-disabled AND the real disabled property, and
   it can land on a chip that is still .is-active when the shopper's other
   choice rules that combination out. */
.sik-pdp .sik-pill.is-active {
    background-color: var(--sik-cta);
    background-image: var(--sik-grad-cta);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-pdp .sik-swatch.is-active {
    box-shadow: inset 0 0 0 2px var(--sik-bg), 0 0 0 2px var(--sik-primary), var(--sik-shadow-brand-sm);
    border-color: transparent;
}
.sik-pdp .sik-pill.is-disabled {
    background-color: var(--sik-surface-2);
    background-image: none;
    border-style: dashed;
    border-color: var(--sik-line);
    color: var(--sik-muted);
    box-shadow: none;
}
.sik-pdp .sik-swatch.is-disabled { box-shadow: inset 0 0 0 2px var(--sik-bg); }
.sik-pdp .sik-pill:focus-visible,
.sik-pdp .sik-swatch:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; }

/* The stepper is section 22; only its ring is brand-tinted on this surface. */
.sik-pdp .sik-qty:focus-within { border-color: var(--sik-primary); box-shadow: var(--sik-focus-ring); }

/* --- the two calls to action ----------------------------------------------- */
/* add_to_cart_button() renders Add to cart with tone `outline` and Buy now with
   tone `primary` (product.php:356-357), which makes Buy now the loudest control
   on the page. Keyed on the data hooks rather than on the tone classes, so the
   intent survives a tone change in the template and Quick View - which passes
   the same variant_from - inherits the same hierarchy.

   This does fight section 11's tone contract: an `--outline` button on this one
   surface stops looking like an outline button. It is scoped to .sik-pdp and to
   buttons wired to a variant picker, so no other surface moves.

   :where() carries no specificity, so the negations are free, and the
   component's own resting skins - [disabled] (which is every loading button
   too, since SIK.showLoader() disables), .is-out, .is-incart, .is-success -
   still paint themselves. Without them an out-of-stock variant would sit under
   a full brand gradient reading "Out of stock". */
.sik-pdp [data-add-cart][data-variant-from]:where(:not([disabled]):not(.is-out):not(.is-incart):not(.is-success)) {
    background-color: var(--sik-cta);
    background-image: var(--sik-grad-cta);
    color: var(--sik-on-cta);
    border-color: transparent;
    box-shadow: var(--sik-shadow-brand);
}
/* Buy now steps back into the quiet outline Add to cart gave up. */
.sik-pdp [data-buy-now][data-variant-from]:where(:not([disabled]):not(.is-out):not(.is-incart):not(.is-success)) {
    background-color: transparent;
    background-image: none;
    color: var(--sik-ink);
    border-color: var(--sik-line-strong);
    box-shadow: none;
}
@media (hover: hover) {
    .sik-pdp [data-add-cart][data-variant-from]:hover:where(:not([disabled]):not(.is-out):not(.is-incart):not(.is-success)) {
        box-shadow: var(--sik-shadow-brand-lg);
        transform: translate3d(0, var(--lift-sm), 0);
    }
    .sik-pdp [data-buy-now][data-variant-from]:hover:where(:not([disabled]):not(.is-out):not(.is-incart):not(.is-success)) {
        background-color: var(--sik-hover);
        border-color: var(--sik-ink);
        color: var(--sik-ink);
    }
}
/* The press. Section 11's .sik-atc:active is a flat scale(.98); on the biggest
   button in the store, a press that also sinks a pixel and loses its glow reads
   as weight rather than as a wobble. */
.sik-pdp [data-add-cart][data-variant-from]:active:not([disabled]),
.sik-pdp [data-buy-now][data-variant-from]:active:not([disabled]) {
    transform: translate3d(0, 1px, 0) scale(.985);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-pdp [data-add-cart][data-variant-from]:focus-visible,
.sik-pdp [data-buy-now][data-variant-from]:focus-visible {
    outline: 2px solid var(--sik-primary);
    outline-offset: 3px;
}
/* prefers-reduced-motion is covered by the --dur-* collapse in section 1a, but
   Settings > Theme > enable animations = off has to be told about the two
   transforms directly. */
.sik-noanim .sik-pdp [data-add-cart][data-variant-from]:hover,
.sik-noanim .sik-pdp [data-buy-now][data-variant-from]:hover,
.sik-noanim .sik-pdp [data-add-cart][data-variant-from]:active,
.sik-noanim .sik-pdp [data-buy-now][data-variant-from]:active { transform: none; }

/* The wishlist / compare toggle. wishlist.js:25 and compare.js:19 put .is-active
   on these, and the only rule for it today lives in product.php's own <style>
   (lines 142-170) using `color: var(--sik-primary)` - the raw fill shade, about
   3.2:1 at 13px. Three classes, so this beats that page-local rule without
   !important and the saved state finally clears AA. */
.sik-pdp .sik-btn.is-active {
    background: var(--sik-primary-soft);
    border-color: color-mix(in srgb, var(--sik-primary) 46%, transparent);
    color: var(--sik-primary-ink);
    box-shadow: var(--sik-ring-brand);
}

/* --- the phone buy bar ------------------------------------------------------ */
/* There is no bar element in product.php and this file may not add one, so the
   actions row IS the bar: sticky, not fixed. Two things follow, and both are
   why it was built this way.

   1. A sticky box is still in flow, so it can never cover the last line of the
      page - the failure body.sik-has-bottomnav (app.css:4098) exists to patch
      for the fixed bottom bar. Nothing to extend, and no --sik-buybar-h to keep
      in step with a height nobody measures.
   2. It only pins while the buy COLUMN is on screen: through the wishlist row,
      the delivery panel and the offers panel, then it releases at the tabs. To
      carry it down the whole page, product.php needs ONE wrapper - a
      <div class="sik-pdp__buy"> around the price, the actions and the panels.
      Ask for it and this rule moves to that class unchanged.

   The 767px query is the single-column layout, not a guard on the bottom bar:
   --sik-bottomnav-h is already 0px above 1024 (app.css:407), so the calc below
   collapses on its own and must not be double-guarded. */
@media (max-width: 767px) {
    .sik-pdp .grid.gap-2 {
        position: sticky;
        bottom: calc(var(--sik-bottomnav-space) + var(--sp-2));
        /* One under --z-sticky. The compare bar is fixed at --z-sticky and can
           be open on this page at the same time; it is the transient thing the
           shopper just triggered, so it wins the overlap - and the rule at the
           end of this block steps the buy bar over it, the same
           geometry-not-z-index trick the float stack uses at app.css:8716. */
        z-index: calc(var(--z-sticky) - 1);
        /* Two columns even below 640, where sm:grid-cols-2 has not kicked in:
           stacked, two 52px --lg buttons plus the tray is ~136px of a phone
           screen. minmax(0, ...) so the labels ellipsise (.sik-atc__label is
           already overflow: hidden) instead of widening the track past 320px. */
        grid-template-columns: repeat(2, minmax(0, 1fr));
        padding: var(--sp-2);
        border-radius: var(--sik-radius-lg);
        border: 1px solid var(--sik-glass-line);
        background: var(--sik-elevated);
        box-shadow: var(--sik-shadow-pop);
    }
    /* Genuinely floating over content, which is the one case section 1c allows
       glass for. The opaque --sik-elevated above is the fallback, so the
       buttons are never unreadable over a photo where backdrop-filter is not
       supported. */
    @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        .sik-pdp .grid.gap-2 {
            background: var(--sik-glass-bg);
            -webkit-backdrop-filter: var(--sik-glass-blur);
            backdrop-filter: var(--sik-glass-blur);
        }
    }
    /* The row is chrome now, so the --lg size comes down to the tap floor: 44px
       plus the tray padding is a 68px bar instead of a 76px one, and --tap is
       still met exactly rather than exceeded. */
    .sik-pdp .grid.gap-2 .sik-atc--lg {
        min-height: var(--tap);
        padding-inline: var(--sp-3);
        font-size: var(--fs-base);
    }
    @media (max-width: 360px) {
        /* ~128px per button at 320px: the glyph gives way before the label
           does, exactly as it does in a two-up card (section 11). */
        .sik-pdp .grid.gap-2 .sik-atc__glyph { display: none; }
    }
    body.sik-has-comparebar .sik-pdp .grid.gap-2 {
        bottom: calc(var(--sik-bottomnav-space) + var(--sik-comparebar-h) + var(--sp-2));
    }
}
/* Quick View renders .sik-pdp inside .sik-modal__panel, which has overflow-y:
   auto and is therefore its own scrollport - a sticky tray in there would pin
   against the sheet instead of the page. details.php lays its actions out with
   an inline flex row today, so this is insurance rather than a live fix. */
.sik-modal .sik-pdp .grid.gap-2 {
    position: static;
    background: none;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    border: 0;
    box-shadow: none;
    padding: 0;
}

/* --- delivery panel and offers --------------------------------------------- */
.sik-pincode { display: flex; gap: var(--sp-2); max-width: 340px; }
/* min-width: 0 - an <input> carries an intrinsic default width (~177px) as its
   min-content size, so flex: 1 alone cannot shrink it and the Check button gets
   pushed out of the row. At 320px this had ~13px of slack left. */
.sik-pincode input { flex: 1; min-width: 0; }

/* Section 1b again, and this one is a LIVE bug. product.php:412 ships
   <div class="sik-alert sik-alert--info" data-pincode-result hidden>, and
   .sik-alert { display: flex } (app.css:8834) beats [hidden] (app.css:419) at
   equal specificity because it comes later - so an empty info-blue box renders
   under the PIN field on every product page. Keyed on the ATTRIBUTE, never on a
   class: products.js:485-493 rewrites resultNode.className wholesale to
   'sik-alert sik-alert--success|--warning|--error', so any class added to this
   node is destroyed by the first check. */
[data-pincode-result][hidden] { display: none; }
/* .sik-alert carries margin-bottom for stacked page alerts; inside the panel
   body it only opened a gap above the free-delivery note. Two selectors deep
   because section 32 is LATER in this file than section 28, so a bare
   [data-pincode-result] would tie with .sik-alert and lose on source order. */
.sik-pdp [data-pincode-result] { margin-bottom: 0; }

/* .sik-offers / .sik-offer are section 18c and the cart renders them too, so
   this only loosens the stack inside the panel on this page. */
.sik-pdp .sik-panel__body .sik-offers { gap: var(--sp-3); }

/* --- tabs ------------------------------------------------------------------- */
/* SITEWIDE, not PDP. orders.php:78 and notifications.php:82 render .sik-tab as
   an <a> inside .sik-tabs--filters, and admin/products/_form.php:147 renders it
   as a <button> over admin.css. Section 38 (app.css:9746) extends these and
   assumes this block supplies the colour, the padding, the border-bottom and
   the .is-active underline - so everything here reads role tokens only and
   nothing assumes a light card underneath. */
.sik-tabs { border-bottom: 1px solid var(--sik-line); display: flex; gap: var(--sp-1); overflow-x: auto; scrollbar-width: none; }
.sik-tabs::-webkit-scrollbar { display: none; }
.sik-tab {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    /* Was var(--sp-3) var(--sp-5), which measured ~38px tall - under --tap on a
       phone, and section 38 only patched that for the --filters variant. */
    min-height: 40px;
    padding: var(--sp-2) var(--sp-5);
    font-size: var(--fs-base);
    font-weight: var(--fw-bold);
    color: var(--sik-muted);
    border: 0;
    background: none;
    cursor: pointer;
    border-bottom: 2px solid transparent;
    white-space: nowrap;
    transition: color var(--dur-fast) var(--ease-out),
                border-color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-tab { min-height: var(--tap); } }
/* The display above is later in the file than [hidden] at app.css:419 and ties
   with it, so a tab a template ships hidden would render. Re-stated per the
   note in section 1b. The two-class form is here as well because section 38's
   `.sik-tabs--filters .sik-tab { display: inline-flex }` is later again and
   would otherwise re-open the same hole on the orders and notification
   screens. */
.sik-tab[hidden],
.sik-tabs .sik-tab[hidden] { display: none; }
.sik-tab:hover { color: var(--sik-ink); }
/* Inset: .sik-tabs is overflow-x: auto, which computes overflow-y to auto too,
   so an outward ring on the first or last tab is clipped. */
.sik-tab:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: -2px; border-radius: var(--sik-radius-xs); }
/* The label is 14px text (needs 4.5:1 -> ink); the 2px rule under it is a
   non-text indicator and keeps the bright tone, which only needs 3:1. */
.sik-tab.is-active { color: var(--sik-primary-ink); border-bottom-color: var(--sik-primary); }
/* The gradient indicator lies exactly over that solid border, so if the
   pseudo-element is ever suppressed the flat brand rule is still underneath.
   bottom: -2px reaches the border box, not past it, so the strip's overflow
   does not clip it. Two classes, always including .sik-tab: a bare .is-active
   would also catch the ratings anchor in .sik-pdp__meta (see the note there).
   --sik-grad-cta, not --sik-grad-brand: this bar is a state indicator and owes
   3:1 against the surface, and the light end of the identity sweep is the raw
   accent, which lands nearer 2:1 on white. --sik-grad-cta is the stop-bounded
   one (section 1c) and clears it along its whole length. */
.sik-tab.is-active::after {
    content: "";
    position: absolute;
    left: var(--sp-3); right: var(--sp-3); bottom: -2px;
    height: 2px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-cta);
}
.sik-tabpanel { display: none; padding-top: var(--sp-6); }
.sik-tabpanel.is-active { display: block; }

/* --- spec table -------------------------------------------------------------- */
/* Also rendered by admin/products/view.php and api/index.php, so the stacked
   form below must not be scoped to the product page. */
.sik-spectable { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
.sik-spectable th, .sik-spectable td { padding: var(--sp-3) var(--sp-4); text-align: left; border-bottom: 1px solid var(--sik-line); vertical-align: top; }
.sik-spectable th { width: 38%; font-weight: var(--fw-semibold); color: var(--sik-muted); background: var(--sik-surface-2); }
.sik-spectable td { color: var(--sik-ink-2); }
/* A two-column spec table with long values cannot fit a 360px phone. Stack the
   rows into label-over-value cards instead of clipping them. */
@media (max-width: 560px) {
    .sik-spectable, .sik-spectable tbody, .sik-spectable tr, .sik-spectable th, .sik-spectable td { display: block; width: auto; }
    .sik-spectable tr { border-bottom: 1px solid var(--sik-line); padding-block: var(--sp-1); }
    .sik-spectable tr:last-child { border-bottom: 0; }
    .sik-spectable th {
        border-bottom: 0;
        background: none;
        padding: var(--sp-3) 0 2px;
        font-size: var(--fs-xs);
        text-transform: uppercase;
        letter-spacing: var(--ls-wider);
    }
    .sik-spectable td { border-bottom: 0; padding: 0 0 var(--sp-3); }
}
.sik-spectable caption {
    text-align: left;
    font-size: var(--fs-xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-ink);
    padding: var(--sp-4) 0 var(--sp-2);
}

/* --- the rating input --------------------------------------------------------- */
/* .sik-ratepick lives ONLY in product.php's inline <style> (lines 142-170)
   today, and the design system's home for it is here. That block sits in the
   document, later than this file, so at equal specificity it still wins: these
   rules change nothing until it is deleted, and fix two things the moment it
   is. The raw #D1D5DB rest colour, which does not re-point in dark mode, and a
   :hover scale with no (hover: hover) guard, which fires on the first tap of a
   touch device. */
.sik-ratepick { display: inline-flex; }
.sik-ratepick button {
    background: none;
    border: 0;
    line-height: 0;
    /* The --tap area has to grow this button IN FLOW. Five out-of-flow 44px
       ::after circles on the old 32px pitch overlapped, and because a later
       sibling paints over an earlier one, a click on the right half of star N
       registered as star N+1 - the shared extender in section 2b is wrong for a
       row of adjacent targets. Padding tiles the five hit areas edge to edge,
       which is also why the gap is 0. */
    padding: calc((var(--tap) - var(--icon-xl)) / 2);
    cursor: pointer;
    color: var(--sik-line-strong);
    transition: color var(--dur-fast) var(--ease-out),
                transform var(--dur-fast) var(--ease-out);
}
/* The component owns the glyph size; the call site passes `w-6 h-6`. */
.sik-ratepick button svg { width: var(--icon-xl); height: var(--icon-xl); }
@media (hover: hover) {
    .sik-ratepick button:hover { transform: scale(1.14); color: var(--sik-star); }
}
.sik-noanim .sik-ratepick button:hover { transform: none; }
.sik-ratepick button:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: -4px; border-radius: var(--sik-radius-sm); }
/* --sik-star, the same amber .sik-star paints a rendered rating with, so the
   stars do not change colour between picking a rating and seeing it back. */
.sik-ratepick button.is-selected { color: var(--sik-star); }

/* ==========================================================================
   Compare page  (compare.php + assets/js/compare.js)

   TWO PANES, ONE MATRIX. `position: sticky; top:` resolves against the nearest
   scrolling ancestor, and a wrapper with `overflow-x: auto` computes its
   overflow-y to `auto` too - so a single scrolling wrapper becomes the
   scrollport and the page scroll can no longer pin anything inside it.
   (Measured in Edge: the header cell tracked its wrapper to -241px instead of
   staying put. `overflow-y: clip` does not rescue it either; it computes to
   `hidden` and the box is still the scrollport.)

   So the header row is its own pane, sticky against the PAGE, and the rows are
   a second pane below it. Each pane pins its own first column with `left: 0`,
   which works normally, and compare.js mirrors scrollLeft between the two.
   ========================================================================== */
.sik-cmp {
    /* Sized so the label column plus one product column always fit the
       narrowest viewport: 320px less the 16px gutters is 288px, and
       104 + 184 = 288. Above 1024 the pair fits four columns inside the
       container, so nothing scrolls until a fifth product is added. */
    --cmp-attr: 104px;
    --cmp-col: 184px;
    /* Ceiling for a column once the spare width is shared out. Without it, two
       products on a 1440 screen get a 528px column each and the comparison
       stops reading as a comparison. */
    --cmp-colmax: 320px;
    --cmp-n: 1;
    margin-top: var(--sp-5);
}
@media (min-width: 480px)  { .sik-cmp { --cmp-attr: 128px; --cmp-col: 196px; } }
@media (min-width: 1024px) { .sik-cmp { --cmp-attr: 176px; --cmp-col: 200px; } }

.sik-cmp__pane {
    overflow-x: auto;
    /* The page must never scroll sideways because a pane ran out of room. */
    overscroll-behavior-x: contain;
    background: var(--sik-surface);
}
/* One scrollbar for the pair. A second one under the pinned header would be a
   control that scrolls something the reader cannot see. */
.sik-cmp__pane--head { scrollbar-width: none; }
.sik-cmp__pane--head::-webkit-scrollbar { display: none; }
.sik-cmp__pane--body::-webkit-scrollbar { height: 8px; }
.sik-cmp__pane--body::-webkit-scrollbar-thumb { background: var(--sik-line); border-radius: var(--sik-radius-pill); }
.sik-cmp__pane--body:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: -2px; }

/* Sticky against the page, and under --z-header so it passes beneath the site
   header rather than over it. compare.js:198 reads this `top` back with
   getComputedStyle to build its IntersectionObserver rootMargin, so it has to
   stay a resolvable length - never `auto`. */
.sik-cmp__head {
    position: sticky;
    top: var(--sp-2);
    z-index: var(--z-sticky);
    border-bottom: 1px solid var(--sik-line);
}
/* Only clear the header when there IS a pinned header - Admin > Settings >
   Header can turn that off, and the offset would then be a gap. */
body:has(.sik-header--sticky) .sik-cmp__head { top: var(--sik-header-h); }

.sik-cmp__sentinel { height: 1px; margin-bottom: -1px; }

.sik-cmp__table {
    table-layout: fixed;
    width: 100%;
    min-width: calc(var(--cmp-attr) + var(--cmp-n) * var(--cmp-col));
    max-width: calc(var(--cmp-attr) + var(--cmp-n) * var(--cmp-colmax));
    /* separate, not collapse: a collapsed table paints the shared cell edges
       itself, and a sticky cell then leaves its borders behind as it detaches. */
    border-collapse: separate;
    border-spacing: 0;
    font-size: var(--fs-sm);
    /* Positioning root for the <caption class="sik-sr">. See the note on the
       cells below - the same trap, one level up. */
    position: relative;
}
.sik-cmp__c0 { width: var(--cmp-attr); }

/* EVERY cell is a positioning root. `.sik-sr` is position:absolute, and with no
   positioned ancestor inside the pane it resolves against the initial
   containing block instead: the pane cannot clip a box that is not laid out
   inside it, so the hidden labels of the right-hand columns dragged the whole
   DOCUMENT sideways. Measured before this line: documentElement.scrollWidth was
   764px on a 320px viewport, i.e. the page itself scrolled. After: 320px.
   (.sik-cmp__corner and .sik-cmp__label are deliberately absent - they are
   already position:sticky, which is a positioning root too.) */
.sik-cmp__srhead th,
.sik-cmp__col,
.sik-cmp__cell,
.sik-cmp__group { position: relative; }

/* The zero-height heading row that keeps every body cell associated with its
   product for a screen reader; the visible heads are in the pane above. */
.sik-cmp__srhead th { padding: 0; border: 0; height: 0; line-height: 0; }

/* --- pinned first column -------------------------------------------------- */
/* z-index 1, not a token: these cells only have to beat their own row's
   siblings as the pane scrolls sideways. The page-level scale is on the pane. */
.sik-cmp__corner,
.sik-cmp__label {
    position: sticky;
    left: 0;
    z-index: 1;
    background: var(--sik-surface-2);
    border-right: 1px solid var(--sik-line);
}

.sik-cmp__corner {
    padding: var(--sp-4);
    text-align: left;
    vertical-align: bottom;
}
.sik-cmp__cornerlead {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-cmp__cornernum {
    display: block;
    margin-top: var(--sp-1);
    font-size: var(--fs-base);
    font-weight: var(--fw-black);
    color: var(--sik-ink);
}

/* --- product column head -------------------------------------------------- */
.sik-cmp__col {
    padding: var(--sp-3);
    text-align: left;
    vertical-align: top;
    border-right: 1px solid var(--sik-line);
    background: var(--sik-surface);
}
.sik-cmp__card { position: relative; display: flex; flex-direction: column; gap: var(--sp-2); }

.sik-cmp__tools {
    position: absolute;
    top: 0; left: 0; right: 0;
    display: flex;
    justify-content: space-between;
    pointer-events: none;
}
.sik-cmp__tools > * { pointer-events: auto; }
/* --sik-on-cta rather than the raw #fff this used to carry: it is #FFFFFF in
   both themes, where --sik-on-inverse softens to #ECEDEF in the dark palette
   and would drop the danger tone under 4.5:1. */
.sik-cmp__drop:hover { background: var(--sik-danger); border-color: var(--sik-danger); color: var(--sik-on-cta); }

.sik-cmp__thumb { display: block; height: 104px; }
.sik-cmp__thumb img { width: 100%; height: 100%; object-fit: contain; }

.sik-cmp__eyebrow {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-cmp__name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
}
.sik-cmp__name:hover { color: var(--sik-primary-ink); }
.sik-cmp__headprice { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.sik-cmp__headprice .sik-price { font-size: var(--fs-md); }
.sik-cmp__acts { display: flex; gap: var(--sp-2); }
.sik-cmp__acts .sik-atc { flex: 1; min-width: 0; }

/* Pinned state: the card re-lays itself as a strip - thumbnail and tools on one
   line, then name, price, buy - and sheds the brand line and the second line of
   the name. 270px of header becomes 168px, which is what makes it usable on a
   phone. The tap targets do NOT shrink; 44px stays 44px.

   The tools stop being an overlay here on purpose: absolutely positioned over a
   44px thumbnail they covered the product entirely. */
.sik-cmp__head.is-stuck {
    box-shadow: var(--sik-shadow-card);
    border-bottom-color: color-mix(in srgb, var(--sik-primary) 34%, var(--sik-line));
}
.sik-cmp__head.is-stuck .sik-cmp__card {
    display: grid;
    grid-template-columns: 44px minmax(0, 1fr);
    align-items: center;
    gap: var(--sp-2);
}
.sik-cmp__head.is-stuck .sik-cmp__thumb     { grid-area: 1 / 1 / 2 / 2; height: 44px; }
.sik-cmp__head.is-stuck .sik-cmp__tools     { grid-area: 1 / 2 / 2 / 3; position: static; justify-content: flex-end; }
.sik-cmp__head.is-stuck .sik-cmp__name      { grid-area: 2 / 1 / 3 / -1; -webkit-line-clamp: 1; }
.sik-cmp__head.is-stuck .sik-cmp__headprice { grid-area: 3 / 1 / 4 / -1; }
.sik-cmp__head.is-stuck .sik-cmp__acts      { grid-area: 4 / 1 / 5 / -1; }
.sik-cmp__head.is-stuck .sik-cmp__eyebrow   { display: none; }
.sik-cmp__head.is-stuck .sik-cmp__cornerlead { display: none; }

/* --- rows ----------------------------------------------------------------- */
.sik-cmp__label,
.sik-cmp__cell {
    padding: var(--sp-3) var(--sp-4);
    text-align: left;
    vertical-align: top;
    border-bottom: 1px solid var(--sik-line);
    line-height: var(--lh-normal);
}
.sik-cmp__label {
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    color: var(--sik-ink);
}
.sik-cmp__cell {
    border-right: 1px solid var(--sik-line);
    background: var(--sik-surface);
    color: var(--sik-ink-2);
    word-break: break-word;
}
@media (hover: hover) {
    .sik-cmp__row:hover .sik-cmp__cell { background: var(--sik-surface-2); }
}

/* A row whose values are not all the same earns a marker on its label. The
   accent is the whole "clear visual difference" cue - tinting every differing
   cell instead turns most of the table pink and says nothing. */
.sik-cmp__row.is-diff .sik-cmp__label { box-shadow: inset var(--sp-1) 0 0 var(--sik-primary); }

/* Rendered by compare.php:530 and styled nowhere until now. The tint lives on
   the <tr> and the <th> over it goes transparent, so each attribute group reads
   as a band across the full width of the matrix rather than as one more row. */
.sik-cmp__grouprow { background: var(--sik-surface-2); }
.sik-cmp__group {
    padding: var(--sp-4) var(--sp-4) var(--sp-2);
    text-align: left;
    background: transparent;
    border-bottom: 1px solid var(--sik-line);
}
/* The heading spans the whole table, so its text would scroll out of sight with
   the columns; the span rides the pane instead. */
.sik-cmp__group span {
    position: sticky;
    left: var(--sp-4);
    display: inline-block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-ink);
}

/* --- cell contents -------------------------------------------------------- */
.sik-cmp__none { color: var(--sik-muted); }
.sik-cmp__link { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }
.sik-cmp__link:hover { text-decoration: underline; text-underline-offset: 3px; }
.sik-cmp__sku { font-size: var(--fs-xs); letter-spacing: var(--ls-wide); color: var(--sik-muted); }
.sik-cmp__sub { display: block; margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-cmp__off { font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--sik-success-ink); }
.sik-cmp__yes { display: inline-flex; align-items: center; gap: var(--sp-1); font-weight: var(--fw-semibold); color: var(--sik-success-ink); }
.sik-cmp__no { color: var(--sik-muted); }
.sik-cmp__list { margin: 0; padding-left: var(--sp-4); display: grid; gap: var(--sp-1); }
.sik-cmp__list li { font-size: var(--fs-xs); line-height: var(--lh-normal); }
.sik-cmp__cell .sik-price { display: block; font-size: var(--fs-lg); }
.sik-cmp__cell .sik-price--mrp { display: inline-block; margin-top: var(--sp-1); }

/* The winner mark. Only four rows can ever carry it - lower price, bigger
   discount, higher rating, in stock - because those are the only orderings this
   catalogue can defend. "8 Modes" versus "4 Modes" is a difference.
   color-mix against the CURRENT surface, so the tint follows the dark theme
   down instead of washing out. */
.sik-cmp__cell.is-best { background: color-mix(in srgb, var(--sik-success) 7%, var(--sik-surface)); }
@media (hover: hover) {
    .sik-cmp__row:hover .sik-cmp__cell.is-best { background: color-mix(in srgb, var(--sik-success) 12%, var(--sik-surface)); }
}
.sik-cmp__best {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-1);
    margin-top: var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--sik-success-ink);
}

/* --- toolbar, limit notice, footer ---------------------------------------- */
/* .sik-heading__sub centres itself inside a 620px measure, which reads as a
   stray centred line under a left-aligned H1. */
.sik-cmp__intro .sik-heading__sub { margin-inline: 0; }

.sik-cmp__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-top: var(--sp-5);
}
.sik-cmp__baracts { display: flex; gap: var(--sp-2); margin-left: auto; }
/* The -ink shade: Clear is a text button on a light surface and the raw danger
   hue is a fill colour, ~4.0:1 at this size. */
.sik-cmp__clear:hover { color: var(--sik-danger-ink); }

.sik-cmp__switch {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    color: var(--sik-ink);
    cursor: pointer;
}
.sik-cmp__switch input { width: 18px; height: 18px; accent-color: var(--sik-primary); cursor: pointer; }
.sik-cmp__switchcount {
    padding: 0 var(--sp-2);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    color: var(--sik-ink);
}
/* Hiding whole <tr>s, so the pinned header above is untouched. */
.sik-cmp.is-diffonly .sik-cmp__row.is-same { display: none; }

.sik-cmp__limit { margin-top: var(--sp-4); }

.sik-cmp__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    flex-wrap: wrap;
    margin-top: var(--sp-6);
}
.sik-cmp__footnote { font-size: var(--fs-xs); color: var(--sik-muted); margin: 0; }

/* --- empty state ---------------------------------------------------------- */
/* The heading block above already supplies the breathing room .sik-empty
   assumes it has to create for itself. */
.sik-cmp__empty { padding-top: var(--sp-6); }
.sik-cmp__steps {
    list-style: none;
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
    gap: var(--sp-3);
    margin: 0 auto var(--sp-6);
    padding: 0;
    /* Wide enough for the three steps to sit on one line from tablet up, and
       narrow enough that they stack rather than orphan a word on a phone. */
    max-width: 760px;
}
.sik-cmp__steps li {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-4);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-pill);
    font-size: var(--fs-xs);
    color: var(--sik-ink-2);
}
/* --sik-cta, not the raw --sik-primary this used to carry with a raw #fff on
   top: white on the shipped primary is ~3.2:1, and this numeral is 11px bold -
   body size, so it needs 4.5:1. --sik-cta is the primary darkened until white
   clears that, which is the whole reason the role exists. */
.sik-cmp__steps li > span {
    display: grid;
    place-items: center;
    width: var(--icon-lg);
    height: var(--icon-lg);
    border-radius: 50%;
    background: var(--sik-cta);
    color: var(--sik-on-cta);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    flex: none;
}
.sik-cmp__steps svg { color: var(--sik-primary-ink); }
.sik-cmp__emptyacts { display: flex; gap: var(--sp-3); justify-content: center; flex-wrap: wrap; }

/* --- floating compare bar -------------------------------------------------- */
/* The shadow tokens all fall downward and are invisible under a bar parked at
   the bottom of the screen, so the separation is carried by the gradient rule
   along the top edge instead - and --sik-shadow-pop is still worth having for
   the 1px ring it puts around the whole box. Together they replace a raw
   rgba(15, 33, 67, .12): a navy shadow left over from before the wine palette,
   which did not re-point in dark mode. */
.sik-comparebar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    background: var(--sik-elevated);
    border-top: 1px solid var(--sik-line);
    box-shadow: var(--sik-shadow-pop);
    /* Below --z-header, so while it is sliding it passes behind the bottom bar
       instead of across it; open, it parks above the bar via --sik-bottomnav-space
       and the two never overlap. The PDP buy bar sits one layer below this. */
    z-index: var(--z-sticky);
    transform: translateY(105%);
    transition: transform var(--dur-slow) var(--ease-out);
    padding: var(--sp-3) 0;
}
.sik-comparebar::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 2px;
    background: var(--sik-grad-brand);
    pointer-events: none;
}
.sik-comparebar.is-open { transform: none; }
/* prefers-reduced-motion collapses --dur-slow to .01ms; the admin's global
   animation switch has to be told separately. */
.sik-noanim .sik-comparebar { transition: none; }
@media (max-width: 1023px) { .sik-comparebar.is-open { bottom: var(--sik-bottomnav-space); } }
.sik-comparebar__items { display: flex; gap: var(--sp-3); overflow-x: auto; scrollbar-width: none; }
.sik-comparebar__items::-webkit-scrollbar { display: none; }
.sik-comparebar__item--empty {
    border-style: dashed;
    display: grid;
    place-items: center;
    color: var(--sik-muted);
    font-size: var(--fs-2xs);
}
.sik-comparebar__item {
    position: relative;
    /* 62px is load-bearing: compare.js:57 prints width="54" height="54" on the
       thumbnail against this box, so the image has reserved space while it
       loads. */
    width: 62px; height: 62px;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    padding: var(--sp-1);
    flex: none;
    background: var(--sik-media);
}
.sik-comparebar__item img { width: 100%; height: 100%; object-fit: contain; }
/* 20px, under --tap, and deliberately so: it is a corner affordance on a 62px
   chip and the whole strip duplicates a full-size Remove in the matrix.
   --sik-on-cta is #FFFFFF in both themes; the glyph is a non-text indicator, so
   3:1 on --sik-danger is the bar it has to clear. */
.sik-comparebar__remove {
    position: absolute;
    top: -6px; right: -6px;
    width: 20px; height: 20px;
    border-radius: 50%;
    background: var(--sik-danger);
    color: var(--sik-on-cta);
    border: 2px solid var(--sik-elevated);
    display: grid;
    place-items: center;
    cursor: pointer;
    padding: 0;
}

/* --------------------------------------------------------------------------
   29. Order timeline & status badges

   Two components that happen to share a page. `.sik-status` is the store's only
   status pill — forty admin screens and a dozen storefront files paint every
   badge in the product through it (admin.css:6 says so outright), so it stays
   small, inline-flex and cheap, and the festive treatment it gets is one
   hairline of its own hue. The timeline is the order rail: vertical on a phone,
   a horizontal stepper once the column it sits in is genuinely wide enough —
   measured with a container query rather than a viewport breakpoint, because
   .sik-tracking__main is NARROWER at 960px, where it gains a 340px rail beside
   it, than it is at 900px. A media query would flip it horizontal at exactly
   the width where it stops fitting.
   -------------------------------------------------------------------------- */

/* --- the pill ---------------------------------------------------------------
   Box metrics are deliberately unchanged from the version this replaces: the
   same pill sits in dense admin table cells on every list screen, in the
   topbar role chip and inline in storefront copy, and a taller pill there
   re-flows forty pages. Only colour and the edge are new.

   products.js:405 does `node.className = 'sik-status sik-status--' + tone`,
   wiping every other class off the variant stock pill, so everything below has
   to work from those two classes alone — no parent scope, no extra modifier. */
.sik-status {
    /* wishlist.php:212 hangs style="align-self:flex-start" on one of these
       inside a flex column. block or grid here and it stops shrinking to fit. */
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--sik-radius-pill);
    text-transform: uppercase;
    letter-spacing: var(--ls-wider);
}
/* flex: none — the dot is a flex item, and without it a two-word label in a
   narrow admin table cell squeezes the dot into an oval. */
.sik-status::before {
    content: "";
    flex: none;
    width: 6px; height: 6px;
    border-radius: 50%;
    background: currentColor;
}

/* The attribute substring is the whole mechanism: a modifier declares one
   --tone and this rule derives the tint and the text from it against the
   CURRENT surface and ink, so the dark theme needs no second set of rules. */
[class*="sik-status--"] {
    background: color-mix(in srgb, var(--tone, var(--sik-muted)) 13%, var(--sik-surface));
    color: color-mix(in srgb, var(--tone, var(--sik-muted)) 62%, var(--sik-ink));
    /* A hairline in the pill's own hue. The tint is mixed against --sik-surface,
       but the pill is routinely dropped somewhere else — a striped admin row,
       the tracking hero's blush, a .sik-summary line — where a 13% wash has
       nothing to sit against and the pill stops reading as a pill. The label
       still carries the meaning; this only gives the shape an edge. */
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tone, var(--sik-muted)) 22%, transparent);
}

/* --- the ten tones ----------------------------------------------------------
   config/constants.php:37 ORDER_STATUS_COLORS names ten hues, and the class is
   always the TONE, never the status: there is no .sik-status--delivered and no
   template could emit one. Two of the ten — cyan (shipped) and orange
   (returned) — appear ONLY inside that PHP map and never as a literal string
   anywhere in the tree, so they survive a grep-driven edit exactly until
   someone looks at a shipped order.

   Only four map onto a status token (amber→warning, blue→info, green→success,
   red→danger) and gray is --sik-muted. The other five are built from the same
   four by interpolating in HSL, which walks the hue circle and keeps the
   chroma: mixing blue into red in sRGB travels through grey and lands on a
   muddy mauve, which is how indigo and violet stop being distinguishable from
   each other. The trailing sRGB mix with #000/#fff is a shade operator, not a
   colour — it carries no hue, and it is the same move --sik-primary-dark and
   --sik-primary-ink already make in section 1.

   Every derived tone lands within a degree of the hue it replaces and clears
   4.5:1 as pill text in BOTH themes. Worst cases measured: teal 5.95:1 light,
   indigo 5.41:1 dark. Nothing here is a raw colour, so a store that re-themes
   its status hues re-themes all ten. */
.sik-status--amber  { --tone: var(--sik-warning); }
.sik-status--blue   { --tone: var(--sik-info); }
.sik-status--green  { --tone: var(--sik-success); }
.sik-status--red    { --tone: var(--sik-danger); }
.sik-status--gray   { --tone: var(--sik-muted); }
.sik-status--orange { --tone: color-mix(in hsl, var(--sik-warning) 64%, var(--sik-danger)); }
.sik-status--indigo { --tone: color-mix(in srgb, color-mix(in hsl, var(--sik-info) 84%, var(--sik-danger)) 88%, #fff); }
.sik-status--violet { --tone: color-mix(in srgb, color-mix(in hsl, var(--sik-info) 70%, var(--sik-danger)) 88%, #fff); }
.sik-status--cyan   { --tone: color-mix(in srgb, color-mix(in hsl, var(--sik-info) 63%, var(--sik-success)) 78%, #000); }
.sik-status--teal   { --tone: color-mix(in srgb, color-mix(in hsl, var(--sik-info) 41%, var(--sik-success)) 74%, #000); }

/* --- .sik-timeline : the plain rail -----------------------------------------
   order-success.php:206 ("What happens next") and admin/orders/view.php:556
   (status history). Label, optional free note, time — no icons, no states
   beyond done/current, and admin puts BOTH is-done and is-current on the last
   row, so the is-current rules are stated after the is-done ones and win on
   source order at equal specificity. */
.sik-timeline { position: relative; padding-left: var(--sp-7); }
.sik-timeline::before {
    content: "";
    position: absolute;
    left: 11px; top: 8px; bottom: 8px;
    width: 2px;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-line);
}
.sik-timeline__step { position: relative; padding-bottom: var(--sp-6); }
.sik-timeline__step:last-child { padding-bottom: 0; }
.sik-timeline__dot {
    position: absolute;
    /* Pulled back by exactly the rail's indent so the 24px dot lands centred
       on the 2px spine at left:11px. Derived from the same token as
       .sik-timeline's padding-left — the two can no longer drift apart. */
    left: calc(var(--sp-7) * -1); top: 1px;
    width: 24px; height: 24px;
    border-radius: 50%;
    background: var(--sik-surface);
    border: 2px solid var(--sik-line);
    display: grid;
    place-items: center;
    color: transparent;
    z-index: 2;
}
/* The fill is shaded toward black rather than reading --sik-success-ink,
   because the -ink tokens flip LIGHT in the dark theme: white on dark-mode
   success-ink measured 2.02:1, an invisible tick on every completed stage of
   every order confirmation opened after dark. Shading the hue itself gives one
   colour that carries --sik-on-cta in both themes (5.10:1). */
.sik-timeline__step.is-done .sik-timeline__dot {
    background: color-mix(in srgb, var(--sik-success) 78%, #000);
    border-color: color-mix(in srgb, var(--sik-success) 78%, #000);
    color: var(--sik-on-cta);
}
/* The one stage a shopper is looking for gets the brand: --sik-grad-cta is the
   contrast-bounded sweep, so the tick clears 4.5:1 at either end of it. */
.sik-timeline__step.is-current .sik-timeline__dot {
    background: var(--sik-grad-cta);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand-sm),
                0 0 0 4px color-mix(in srgb, var(--sik-primary) 18%, transparent);
}
.sik-timeline__label { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-timeline__step:not(.is-done) .sik-timeline__label { color: var(--sik-muted); }
.sik-timeline__time { font-size: var(--fs-xs); color: var(--sik-muted); margin-top: 2px; }
/* Only rendered inside the rich rail, but owned here so it is never an
   unstyled element if a template ever reaches for it on its own. */
.sik-timeline__body { min-width: 0; }

/* --- .sik-timeline--rich : the tracking timeline --------------------------
   The plain timeline above is a label + a time. The tracking page needs a
   stage icon, a description and three visually distinct states, so the
   modifier re-lays the same elements as a two-column grid (dot | body) and
   draws its own connector per step. Everything is scoped under the modifier,
   so order-success.php and admin/orders/view.php keep the plain version
   untouched.

   The three states are mutually exclusive and each is carried by TWO signals,
   never colour alone: done = filled dot + tick-through connector + a real
   timestamp; current = brand dot with a halo, a tinted card and a "you are
   here" chip; pending = dashed hollow dot, muted label and the word Pending
   where the date would be. A stage with no recorded time says so rather than
   borrowing its neighbour's. */
.sik-timeline--rich { padding-left: 0; list-style: none; margin: 0; }
.sik-timeline--rich::before { content: none; }
.sik-timeline--rich .sik-timeline__step {
    position: relative;
    display: grid;
    grid-template-columns: 34px minmax(0, 1fr);
    column-gap: var(--sp-4);
    padding-bottom: var(--sp-5);
}
.sik-timeline--rich .sik-timeline__step:last-child { padding-bottom: 0; }
/* Connector for THIS step down to the next dot. Drawn per step rather than as
   one spine so the completed part of the rail is coloured by the steps
   themselves — the run length can never disagree with the states.

   Geometry is hand-tied to the dot: left is half of 34 minus half of 2, top is
   the dot's own height. The 479px block below restates all four numbers for
   the 30px dot; they move together or they do not move. */
.sik-timeline--rich .sik-timeline__step:not(:last-child)::after {
    content: "";
    position: absolute;
    left: 16px;
    top: calc(34px + var(--sp-1));
    bottom: var(--sp-1);
    width: 2px;
    border-radius: 2px;
    background: var(--sik-line);
    z-index: 0;
}
/* is-linked is set by order_timeline_html() when the NEXT stage has happened,
   not from is-done, because order_tracking_insert_payment_stage() drops a
   completed COD payment AFTER the current delivery stage: "everything left of
   current is done" is false on every cash order in the shop.
   is-linked-stop is always appended after is-linked, so at equal specificity
   source order here decides — the stop colour has to come second. */
.sik-timeline--rich .sik-timeline__step.is-linked::after { background: var(--sik-success-ink); }
.sik-timeline--rich .sik-timeline__step.is-linked-stop::after { background: var(--sik-danger-ink); }

.sik-timeline--rich .sik-timeline__dot {
    /* Relative, not static: the dot has to paint over the connector and the
       pulse ring below anchors to it. `inset: auto` is load-bearing, not
       tidiness — the plain timeline's dot is absolute at
       left: calc(var(--sp-7) * -1), and the instant this stops being static
       those offsets start applying as a RELATIVE shift and drag every dot 32px
       off its own rail, in both the vertical and the horizontal layout. */
    position: relative;
    inset: auto;
    z-index: 1;
    width: 34px; height: 34px;
    border-radius: 50%;
    background: var(--sik-surface);
    border: 2px solid var(--sik-line);
    color: var(--sik-muted);
    display: grid;
    place-items: center;
}
/* The three dots differ by FILL, not by hue: a store themed green would
   otherwise draw "completed" and "current" in two shades of the same colour
   and the customer could not tell them apart. done = soft fill, solid ring.
   current = brand gradient, halo, pulse. pending = hollow, dashed. */
.sik-timeline--rich .sik-timeline__step.is-done .sik-timeline__dot {
    background: color-mix(in srgb, var(--sik-success) 14%, var(--sik-surface));
    border-color: var(--sik-success-ink);
    color: var(--sik-success-ink);
}
.sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__dot {
    background: var(--sik-grad-cta);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: var(--sik-shadow-brand),
                0 0 0 5px color-mix(in srgb, var(--sik-primary) 18%, transparent);
}
.sik-timeline--rich .sik-timeline__step.is-pending .sik-timeline__dot {
    background: var(--sik-surface);
    border-style: dashed;
    color: var(--sik-muted);
}
.sik-timeline--rich .sik-timeline__step.is-stopped.is-current .sik-timeline__dot {
    background: color-mix(in srgb, var(--sik-danger) 78%, #000);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--sik-danger) 16%, transparent);
}
/* A delivered order is finished, not in flight: its current stage keeps the
   halo that marks "this is where you are" but wears the success colour. */
.sik-timeline--rich .sik-timeline__step.is-complete.is-current .sik-timeline__dot {
    background: color-mix(in srgb, var(--sik-success) 78%, #000);
    border-color: transparent;
    color: var(--sik-on-cta);
    box-shadow: 0 0 0 5px color-mix(in srgb, var(--sik-success) 18%, transparent);
}

/* --- the pulse --------------------------------------------------------------
   The first motion this section has ever had, and it is decoration on top of a
   signal that is already static: the halo on the dot is a box-shadow and is
   always there. So the ring may be switched off outright without hiding
   anything — which is what both guards at the end of this section do.

   It rides a pseudo-element rather than animating the dot's own box-shadow, so
   only opacity and transform move (section 1d's rule) and the browser keeps it
   off the main thread. position:absolute also takes it out of the dot's grid,
   which would otherwise auto-place it into a second row and push the stage
   icon off centre.

   Only the in-flight current stage pulses. A cancelled or delivered order has
   stopped moving; a throbbing ring on it would promise an update that is never
   coming — and that exclusion is a separate `content: none` rule rather than a
   :not() chain on the line below, so the two off switches at the end of this
   section stay short enough to still out-specify it. A guard that loses on
   specificity is a guard that does nothing.

   The keyframe name is section-scoped on purpose: section 30 below used to
   re-declare `sik-shimmer` and silently rebound the shared one for the whole
   file, and that is not a mistake worth making twice. */
.sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__dot::after {
    content: "";
    position: absolute;
    inset: -7px;
    border-radius: 50%;
    border: 2px solid color-mix(in srgb, var(--sik-primary) 55%, transparent);
    /* Base state is invisible. animation-fill-mode is none, so if the
       animation never starts the ring simply is not there, rather than
       freezing mid-pulse on top of the icon. */
    opacity: 0;
    pointer-events: none;
    animation: sik-trk-pulse 2.6s var(--ease-out) infinite;
}
.sik-timeline--rich .sik-timeline__step.is-stopped .sik-timeline__dot::after,
.sik-timeline--rich .sik-timeline__step.is-complete .sik-timeline__dot::after { content: none; }
@keyframes sik-trk-pulse {
    0%   { opacity: .7; transform: scale(.84); }
    70%  { opacity: 0;  transform: scale(1.28); }
    100% { opacity: 0;  transform: scale(1.28); }
}

.sik-timeline--rich .sik-timeline__body { min-width: 0; padding-top: var(--sp-2); }
.sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__body {
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid color-mix(in srgb, var(--sik-primary) 22%, transparent);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-primary-soft);
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-timeline--rich .sik-timeline__step.is-stopped.is-current .sik-timeline__body {
    border-color: color-mix(in srgb, var(--sik-danger) 24%, transparent);
    background: var(--sik-danger-soft);
    box-shadow: none;
}
.sik-timeline--rich .sik-timeline__step.is-complete.is-current .sik-timeline__body {
    border-color: color-mix(in srgb, var(--sik-success) 26%, transparent);
    background: var(--sik-success-soft);
    box-shadow: none;
}

/* The label holds a visually-hidden .sik-sr prefix ("Current stage: " /
   "Not yet reached: "). It is position:absolute, so it is not a flex item and
   the gap below gives it no phantom slot — which is exactly why this stays
   flex and does not become a grid with the same gap. */
.sik-timeline--rich .sik-timeline__label {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
    overflow-wrap: anywhere;
}
.sik-timeline--rich .sik-timeline__step.is-pending .sik-timeline__label {
    color: var(--sik-muted);
    font-weight: var(--fw-semibold);
}
.sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__label { color: var(--sik-primary-ink); }
.sik-timeline--rich .sik-timeline__step.is-stopped.is-current .sik-timeline__label { color: var(--sik-danger-ink); }
.sik-timeline--rich .sik-timeline__step.is-complete.is-current .sik-timeline__label { color: var(--sik-success-ink); }

/* Dense chip padding — the 1px is the documented exception, matching
   .sik-track__step-of in section 38, because a 4px block padding visibly
   inflates a pill whose line-height is already 1.

   It renders one of three strings decided in PHP — "You are here", "Final
   status", "Completed" — and only on the current stage. Nothing here may
   invent that text with content:. */
.sik-timeline__chip {
    padding: 1px var(--sp-2);
    border-radius: var(--sik-radius-pill);
    background: color-mix(in srgb, var(--sik-primary) 16%, var(--sik-surface));
    color: var(--sik-primary-ink);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    white-space: nowrap;
}
.sik-timeline__step.is-stopped .sik-timeline__chip {
    background: color-mix(in srgb, var(--sik-danger) 14%, var(--sik-surface));
    color: var(--sik-danger-ink);
}
.sik-timeline__step.is-complete .sik-timeline__chip {
    background: color-mix(in srgb, var(--sik-success) 15%, var(--sik-surface));
    color: var(--sik-success-ink);
}

.sik-timeline--rich .sik-timeline__time {
    display: flex;
    align-items: center;
    gap: var(--sp-1);
    margin-top: var(--sp-1);
    font-size: var(--fs-xs);
    color: var(--sik-muted);
}
.sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__time {
    color: var(--sik-primary-ink);
    font-weight: var(--fw-semibold);
}
.sik-timeline--rich .sik-timeline__step.is-stopped.is-current .sik-timeline__time { color: var(--sik-danger-ink); }
.sik-timeline--rich .sik-timeline__step.is-complete.is-current .sik-timeline__time { color: var(--sik-success-ink); }
/* Carries two different strings in one class — "Pending" (never reached) and
   "Time not recorded" (passed but untimed). No colour, so it inherits whichever
   state colour __time is wearing; on a current stage with no recorded time that
   is the only thing keeping it off 4.28:1 against the pink card. */
.sik-timeline__pending { font-weight: var(--fw-semibold); }
/* The admin's recorded note, arbitrary length and the only real prose on the
   rail. --sik-ink-2, not --sik-muted: inside the current stage's tinted card
   muted measures 4.28:1, under the floor for 13px body text. */
.sik-timeline__desc {
    margin-top: var(--sp-2);
    font-size: var(--fs-sm);
    line-height: var(--lh-relaxed);
    color: var(--sik-ink-2);
    overflow-wrap: anywhere;
}
.sik-timeline__live {
    margin-top: var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-normal);
    color: var(--sik-primary-ink);
    overflow-wrap: anywhere;
}

/* A phone gives the body every pixel it can: the rail narrows, it never wraps
   under the text. All four dot-tied numbers move together. */
@media (max-width: 479px) {
    .sik-timeline--rich .sik-timeline__step {
        grid-template-columns: 30px minmax(0, 1fr);
        column-gap: var(--sp-3);
    }
    .sik-timeline--rich .sik-timeline__step:not(:last-child)::after {
        left: 14px;
        top: calc(30px + var(--sp-1));
    }
    .sik-timeline--rich .sik-timeline__dot { width: 30px; height: 30px; }
    .sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__body {
        padding-inline: var(--sp-3);
    }
}

/* --- the horizontal stepper -------------------------------------------------
   Turned on by the width of .sik-tracking__main itself, not the viewport. The
   viewport is the wrong ruler here: the same rail is 804px wide at a 900px
   window and 502px wide at 960px, where .sik-tracking__grid hands 340px of it
   to the facts rail — a min-width media query goes horizontal precisely where
   the columns stop fitting. Asking the container instead gets that right for
   free, and it also answers order-details.php, where this same <ol> renders
   inside a narrow .sik-panel__body that declares no container at all: no
   container, no match, the rail stays vertical. Browsers without container
   queries land in the same place.

   680px is the floor because the busiest order has eight stages (seven
   ORDER_TIMELINE steps plus the inserted payment stage) and eight columns need
   roughly 80px each before a two-word label stops fitting on two lines. A
   cancelled order renders as few as two, which simply gives each column more
   room. Nothing is dropped going horizontal — the timestamp, the recorded note
   and the live line all stay, which is why the columns end ragged at the
   bottom rather than on a shared baseline. Truncating an admin's note to keep
   a tidy grid would be throwing away the only thing on the stage that is not
   already obvious. */
.sik-tracking__main { container: sik-trk / inline-size; }

@container sik-trk (min-width: 680px) {
    .sik-timeline--rich {
        display: grid;
        grid-auto-flow: column;
        /* minmax(0, 1fr), never a bare 1fr: an unbreakable tracking note would
           floor the track at its own width and push the rail sideways. */
        grid-auto-columns: minmax(0, 1fr);
        column-gap: var(--sp-2);
        align-items: start;
    }
    .sik-timeline--rich .sik-timeline__step {
        grid-template-columns: minmax(0, 1fr);
        row-gap: var(--sp-3);
        column-gap: 0;
        padding-bottom: 0;
        text-align: center;
    }
    .sik-timeline--rich .sik-timeline__dot { justify-self: center; }
    .sik-timeline--rich .sik-timeline__body {
        padding-top: 0;
        /* Only bites on a two- or three-stage order, where a full-width column
           of centred text would read as a paragraph rather than a stage. */
        max-width: 32ch;
        margin-inline: auto;
    }
    /* The connector becomes the rail between two dots. All columns are equal
       (1fr), so the next dot's centre is half a column plus the gap past this
       step's right edge; 23px is the 17px dot radius plus a 6px breath at each
       end. Change the dot size and these two numbers change with it. */
    .sik-timeline--rich .sik-timeline__step:not(:last-child)::after {
        top: 16px;
        bottom: auto;
        left: calc(50% + 23px);
        right: calc(-50% - var(--sp-2) + 23px);
        width: auto;
        height: 2px;
    }
    .sik-timeline--rich .sik-timeline__label { justify-content: center; font-size: var(--fs-sm); }
    .sik-timeline--rich .sik-timeline__time { justify-content: center; font-size: var(--fs-2xs); }
    .sik-timeline--rich .sik-timeline__desc { font-size: var(--fs-xs); line-height: var(--lh-normal); }
    .sik-timeline--rich .sik-timeline__live { font-size: var(--fs-xs); }
    .sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__body {
        padding: var(--sp-3);
    }
}

/* Both switches, because they are not the same switch. The global
   prefers-reduced-motion block at the top of this file only collapses the
   --dur-* tokens; it cannot reach a keyframe animation, which is the one kind
   of motion that never stops on its own.

   Each selector matches the pulse rule's own specificity exactly — the media
   one ties and wins on source order, the .sik-noanim one adds a class and
   wins outright. Switching the ring off hides nothing: the halo that marks
   the current stage is a box-shadow on the dot and is always there. */
@media (prefers-reduced-motion: reduce) {
    .sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__dot::after {
        animation: none;
        opacity: 0;
    }
}
.sik-noanim .sik-timeline--rich .sik-timeline__step.is-current .sik-timeline__dot::after {
    animation: none;
    opacity: 0;
}

/* --- .sik-tracking : the order tracking result ----------------------------
   One component rendered by order_tracking_panel_html(), shared by
   track-order.php (server render) and /api/orders/track.php (the same HTML
   injected into [data-track-result]). The side rail reuses .sik-panel and
   .sik-summary rather than restating a card.

   Nothing in here may use .sik-reveal or any other opacity-0-until-observed
   pattern: account.js:191 assigns panel.innerHTML and never calls
   SIK.refresh() or SIK.initAnimations() on it, so .is-in would never arrive
   and the whole answer would render invisible on the AJAX path. */
.sik-tracking { display: grid; gap: var(--sp-5); }

/* A blush, not the wine panel the homepage hero wears — and that is the one
   decision in this section worth knowing. A .sik-status pill sits inside this
   header while mixing its own tint against --sik-surface; put a deep panel
   behind it and the pill computes its colours against a surface that is not
   there. A 9% brand wash keeps that maths honest, and the festive weight is
   carried by the gold edge and the corner light instead. */
.sik-tracking__hero {
    position: relative;
    /* A stacking context, so the two z-index:-1 washes below paint above this
       element's own background and below its text without a single child
       needing a z-index of its own. */
    isolation: isolate;
    overflow: hidden;
    border: 1px solid color-mix(in srgb, var(--sik-primary) 16%, var(--sik-line));
    border-radius: var(--sik-radius-lg);
    background: var(--sik-grad-soft);
    box-shadow: var(--sik-shadow-card);
    padding: var(--sp-6) var(--sp-5) var(--sp-5);
    display: grid;
    gap: var(--sp-3);
}
/* Brand light bleeding in from the top-left, the homepage hero's move at a
   fraction of the strength. It stays left because .sik-tracking__state — and
   the status pill in it — is pinned right from 768px. */
.sik-tracking__hero::before {
    content: "";
    position: absolute;
    inset: -40% 45% 25% -25%;
    z-index: -1;
    background: radial-gradient(60% 60% at 34% 42%,
                color-mix(in srgb, var(--sik-primary) 18%, transparent) 0%, transparent 70%);
    pointer-events: none;
}
/* The gold hairline along the top edge — the storefront's festive signature,
   and the only place gold appears in this section. Gold is never type here:
   --sik-gold-ink clears 4.5:1 on white but only 3.27:1 on the dark theme's
   surface, so it cannot be a label that has to survive both. */
.sik-tracking__hero::after {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    z-index: -1;
    height: 3px;
    background: var(--sik-grad-gold);
    pointer-events: none;
}
.sik-tracking__heroTop {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-4);
}
.sik-tracking__id { min-width: 0; }
.sik-tracking__eyebrow {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    /* Brand ink rather than --sik-muted: muted measures 4.19:1 on the wash,
       under the floor at 11px. The pink also makes the order number the first
       thing the eye lands on, which is what the shopper came to check.

       Deepened one step toward --sik-ink, and that step is the whole point of
       the declaration. This label sits directly on ::before's corner light,
       and --sik-primary-ink is only ever guaranteed against --sik-surface —
       not against the surface PLUS an 18% wash of its own hue, which is the
       one background in this section that primary-ink never accounted for.
       Unmixed it measures 4.83:1 where the label actually falls and 4.49:1 at
       the centre of the light on the shipped pink, and 4.29:1 / 4.05:1 on the
       no-settings default primary — and 11px uppercase at --ls-widest is the
       least forgiving type in the section, so it cannot ride a 4.4. Mixing
       toward --sik-ink moves the right way in BOTH themes, darker on white
       and lighter on the near-black, because --sik-ink is a role. Worst case
       anywhere in the wash is now 5.17:1. */
    color: color-mix(in srgb, var(--sik-primary-ink) 82%, var(--sik-ink));
}
.sik-tracking__number {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-1);
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
    overflow-wrap: anywhere;
}
@media (min-width: 768px) { .sik-tracking__number { font-size: var(--fs-2xl); } }
.sik-tracking__placed { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-ink-2); }
.sik-tracking__state { display: grid; gap: var(--sp-2); justify-items: start; min-width: 0; }
.sik-tracking__headline {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-normal);
    color: var(--sik-ink);
}
/* The delivery promise, as a chip rather than a line of text: it is the second
   thing anyone opening this page is looking for. */
.sik-tracking__eta {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: 3px var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-primary-soft);
    box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--sik-primary) 20%, transparent);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    color: var(--sik-primary-ink);
}
.sik-tracking__reason {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-danger-soft);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--sik-danger-ink);
    overflow-wrap: anywhere;
}
@media (min-width: 768px) {
    .sik-tracking__state { justify-items: end; text-align: right; max-width: 320px; }
}

.sik-tracking__grid { display: grid; gap: var(--sp-5); align-items: start; }
@media (min-width: 960px) {
    .sik-tracking__grid { grid-template-columns: minmax(0, 1fr) 340px; }
}
.sik-tracking__main {
    min-width: 0;
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-surface);
    box-shadow: var(--sik-shadow-soft);
    padding: var(--sp-5);
}
.sik-tracking__h {
    font-size: var(--fs-lg);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-tight);
    color: var(--sik-ink);
    margin-bottom: var(--sp-5);
}
/* A short brand rule under the heading — the same gradient hairline
   .sik-grad-rule draws, without needing an element in the markup for it. */
.sik-tracking__h::after {
    content: "";
    display: block;
    width: 34px; height: 2px;
    margin-top: var(--sp-2);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-grad-brand);
}
.sik-tracking__note {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-2);
    margin-top: var(--sp-5);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-line);
    font-size: var(--fs-xs);
    line-height: var(--lh-normal);
    color: var(--sik-muted);
}
.sik-tracking__side { display: grid; gap: var(--sp-4); align-content: start; min-width: 0; }
/* Between a phone and the two-column desktop the rail is the full page width,
   where a three-row Shipment card alone across 768px reads as a gap. Pairing
   them up there costs nothing and keeps the block dense. The items card is
   always last and always spans, because its rows are the widest.

   Child + :last-child, not a class: the Shipment panel is conditional on
   $showShipment, so this rail holds two or three panels. Append a fourth in
   order_tracking_panel_html() and the span moves with it. */
@media (min-width: 640px) and (max-width: 959px) {
    .sik-tracking__side { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sik-tracking__side > .sik-panel:last-child { grid-column: 1 / -1; }
}

.sik-tracking__k { font-size: var(--fs-sm); color: var(--sik-muted); }
.sik-tracking__v { font-size: var(--fs-sm); font-weight: var(--fw-semibold); text-align: right; min-width: 0; overflow-wrap: anywhere; }
.sik-tracking__row--wrap { flex-wrap: wrap; }
.sik-tracking__awb { display: flex; align-items: center; gap: var(--sp-2); margin-left: auto; }
.sik-tracking__link { margin-top: var(--sp-4); }
.sik-tracking__hint { margin-top: var(--sp-2); font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }

.sik-tracking__addrName { font-size: var(--fs-base); font-weight: var(--fw-bold); color: var(--sik-ink); }
.sik-tracking__addr {
    margin-top: var(--sp-1);
    font-style: normal;
    font-size: var(--fs-sm);
    line-height: var(--lh-relaxed);
    color: var(--sik-muted);
    overflow-wrap: anywhere;
}
.sik-tracking__phone {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-3);
    font-size: var(--fs-sm);
    color: var(--sik-ink);
}

.sik-tracking__count { font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-tracking__items { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-4); }
.sik-tracking__item { display: flex; align-items: flex-start; gap: var(--sp-3); }
/* An <a> when the product still exists and a <span> when it was deleted, so
   display is set here rather than left to the element: an inline <span> and an
   inline <a> would both collapse the 56px box. */
.sik-tracking__thumb {
    flex: none;
    width: 56px; height: 56px;
    padding: var(--sp-1);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-media);
    display: grid;
    place-items: center;
    overflow: hidden;
    transition: border-color var(--dur-base) var(--ease-out),
                box-shadow var(--dur-base) var(--ease-out);
}
/* The link version is the only one that is pickable, so only it answers the
   pointer. Inside hover:hover, because on a touch screen :hover sticks after
   the tap and leaves one thumbnail permanently lit. */
@media (hover: hover) {
    a.sik-tracking__thumb:hover {
        border-color: color-mix(in srgb, var(--sik-primary) 40%, transparent);
        box-shadow: var(--sik-shadow-brand-sm);
    }
}
.sik-noanim .sik-tracking__thumb { transition: none; }
.sik-tracking__thumb img { width: 100%; height: 100%; object-fit: contain; }
.sik-tracking__itemBody { flex: 1 1 auto; min-width: 0; }
.sik-tracking__itemName { font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: var(--lh-normal); color: var(--sik-ink); overflow-wrap: anywhere; }
.sik-tracking__itemMeta { margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-tracking__itemPrice { flex: none; font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--sik-ink); white-space: nowrap; }
.sik-tracking__totals { margin-top: var(--sp-4); padding-top: var(--sp-4); border-top: 1px solid var(--sik-line); }
/* The two buttons here are .sik-btn--sm; the 44px tap target comes from that
   component, not from this rule. */
.sik-tracking__foot {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-line);
}

/* --------------------------------------------------------------------------
   30. Empty & loading states
   -------------------------------------------------------------------------- */
.sik-empty { text-align: center; padding: var(--sp-10) var(--sp-5); }
/* Direct children only. As a descendant selector this also blew up every icon
   nested inside the empty state's own call-to-action button to 148px.

   The illustration size lives here rather than at the call site: templates were
   passing w-10 / w-12 / w-14 / w-16 for the same slot and every one of them was
   already being overridden by this rule, so the classes were decorative drift.
   Derived from the icon scale so it is still one system, not a stray 112px. */
.sik-empty > img,
.sik-empty > svg {
    display: block;
    width: calc(var(--icon-2xl) * 4);
    height: auto;
    margin: 0 auto var(--sp-5);
    opacity: .55;
    color: var(--sik-muted);
}
/* An empty state nested inside a panel (a checkout step, one order's item
   list) is a paragraph of the page, not the whole page: same component, half
   the air and half the artwork. Replaces the eyeballed inline
   `style="padding:28px 12px"` those call sites were carrying. */
.sik-empty--sm { padding: var(--sp-7) var(--sp-3); }
.sik-empty--sm > img,
.sik-empty--sm > svg { width: calc(var(--icon-2xl) * 2); margin-bottom: var(--sp-4); }

.sik-empty__title { font-size: var(--fs-lg); font-weight: var(--fw-bold); margin-bottom: var(--sp-2); color: var(--sik-ink); }
.sik-empty__text { color: var(--sik-muted); font-size: var(--fs-base); max-width: 42ch; margin: 0 auto var(--sp-5); }
.sik-empty__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-3); }

/* The confirmation medallion on order-success. Same family as .sik-empty: a
   page-level status mark. It was a span carrying six inline declarations and a
   `w-7 h-7` glyph — the last numeric icon class on the storefront — plus a raw
   #DCFCE7 that the soft-success colour-mix used everywhere else already covers. */
.sik-successmark {
    display: grid;
    place-items: center;
    width: var(--sp-11);
    height: var(--sp-11);
    margin: 0 auto var(--sp-4);
    border-radius: 50%;
    background: color-mix(in srgb, var(--sik-success) 14%, transparent);
    color: var(--sik-success-ink);
}
.sik-successmark svg { width: var(--icon-2xl); height: var(--icon-2xl); }

/* Skeletons used to live here as .sik-skeleton: a 300px grey block per card
   (the real card is ~450px, so every filter change moved the footer twice)
   and a second `@keyframes sik-shimmer` that silently rebound the shared one
   in section 1d. Both are gone; the system that replaced them is section 30b. */

.sik-loading { position: relative; pointer-events: none; }
.sik-loading::after {
    content: "";
    position: absolute;
    inset: 0;
    background:  color-mix(in srgb, var(--sik-surface) 65%, transparent);
    z-index: 5;
    border-radius: inherit;
}

/* Top progress bar for AJAX navigation.

   Nothing renders this today — no PHP or JS in the project emits .sik-progressbar
   — so it is left in place for whoever wires it up, but corrected first: it sat
   at z-index 300, above --z-toast, and it grew by animating `width`. It is now
   driven by --sik-progress (0 to 1) through a scaleX, so the fill composites,
   and it shares the top layer with the other global feedback surfaces. */
.sik-progressbar {
    position: fixed;
    top: 0; left: 0;
    height: 3px;
    width: 100%;
    transform-origin: left center;
    transform: scaleX(var(--sik-progress, 0));
    background: linear-gradient(90deg, var(--sik-primary), var(--sik-accent));
    z-index: var(--z-toast);
    transition: transform var(--dur-base) var(--ease-out),
                opacity var(--dur-slow) var(--ease-out);
    opacity: 0;
}
.sik-progressbar.is-active { opacity: 1; }

/* --------------------------------------------------------------------------
   30b. Skeleton loading

   A shimmering stand-in shaped exactly like the content that is on its way.

   WHERE, AND ONLY WHERE. Nearly every storefront page is rendered on the
   server and arrives complete, so there is no gap for a skeleton to fill, and
   none is manufactured: nothing that is already in the HTML is ever deferred
   to show one. Skeletons appear only where content genuinely arrives later -
   the listing grid on a filter / sort / page change and on "Show more", Quick
   View, the mini-cart before its first sync, search suggestions, widgets the
   homepage builder marks "lazy load" - and on images, which load after the
   page everywhere. includes/skeletons.php has the full map.

   HOW A SKELETON IS THE RIGHT SIZE. A skeleton reuses the real component's
   classes for every box (.sik-card, .sik-card__media, .sik-card__body,
   .sik-pdp, .sik-minicart__item ...) and swaps only the content for bones, so
   padding, aspect ratio, the reserved two-line name and the button height all
   come from the component's own rules. Text is a .sik-skel__line inside a
   .sik-skel__text span inside an element with the real text class: the line
   box takes its height from that class's font and line-height, so one bone
   line is exactly one line of real text tall. The span matters inside flex
   rows - a bare bone there would be blockified and collapse to its own 0.7em.

   MOTION. One soft light band per skeleton, left to right, then a rest: it
   travels for 65% of the cycle and waits for the remainder, so a screen of
   skeletons breathes rather than strobes. The band is a transform on
   ::after, so it composites. It is white-ish on purpose - over the card's own
   white surface it is invisible, and it shows only where it crosses a bone.
   Reduced motion, and Settings > Theme > animations off, remove the band and
   leave the static tint.

   TIMING, in the scripts: a region that refetches keeps its old content,
   dimmed, for the first 120ms, and only then shows skeletons - a fast answer
   never flashes grey. Nothing waits on purpose after the answer lands. A
   failed request always ends in .sik-loaderr with a Retry, never in a
   skeleton that shimmers forever.
   -------------------------------------------------------------------------- */
:root {
    /* The bone. A whisper of ink over the surface, so it re-derives for a
       re-themed store and needs no hex. The well is lighter: large areas
       (a photo, a thumbnail) read heavy at the text-bone tone. */
    --sik-skel-base:   color-mix(in srgb, var(--sik-ink) 7%, var(--sik-surface));
    --sik-skel-well:   color-mix(in srgb, var(--sik-ink) 4.5%, var(--sik-surface));
    --sik-skel-shine:  rgba(255, 255, 255, .62);
    --sik-skel-dur:    1.8s;
    --sik-skel-radius: var(--sik-radius-xs);
}
:root[data-theme="dark"] {
    /* On a near-black page the tint has to be carried further to read at
       all, and the band drops to a glint - white at .62 would flash. */
    --sik-skel-base:   color-mix(in srgb, var(--sik-ink) 10%, var(--sik-surface));
    --sik-skel-well:   color-mix(in srgb, var(--sik-ink) 7%, var(--sik-surface));
    --sik-skel-shine:  rgba(255, 255, 255, .06);
}

/* --- the component root: inert, clipped, carrying the band ---------------- */
.sik-skel {
    position: relative;
    overflow: hidden;
    pointer-events: none;
    user-select: none;
}
.sik-skel::after {
    content: "";
    position: absolute;
    inset: 0;
    /* Above the card's badge (3) and tools (4) so the band crosses them too. */
    z-index: 5;
    background: linear-gradient(100deg, transparent 18%, var(--sik-skel-shine) 50%, transparent 82%);
    transform: translateX(-100%);
    animation: sik-skel-sweep var(--sik-skel-dur) var(--ease-in-out) infinite;
    pointer-events: none;
}
@keyframes sik-skel-sweep {
    0%        { transform: translateX(-100%); }
    65%, 100% { transform: translateX(100%); }
}

/* --- bones ------------------------------------------------------------------ */
.sik-skel__bone {
    display: block;
    background: var(--sik-skel-base);
    border-radius: var(--sik-skel-radius);
}
/* The wrapper that gives a bone a real line box (see the header note). */
.sik-skel__text { display: inline; }
.sik-skel__line {
    display: inline-block;
    vertical-align: middle;
    width: var(--w, 100%);
    max-width: 100%;
    height: .72em;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-skel-base);
}
/* A media area. Applied to the real well class (.sik-card__media,
   .sik-gallery__main, .sik-minicart__img ...), so its aspect ratio, radius
   and border are the component's; only the fill changes. Later in the file
   than all of those, so it wins at equal specificity. */
.sik-skel__well {
    background-color: var(--sik-skel-well);
    background-image: none;
}
.sik-skel__disc { width: 34px; height: 34px; border-radius: 50%; }
.sik-skel__pill { height: 20px; border-radius: var(--sik-radius-pill); }
.sik-skel__btn  { height: var(--tap); border-radius: var(--sik-radius-btn); }
/* The card's Add to cart bone is the real .sik-atc (for its min-height), so
   it keeps the button's own corner rather than the bone's. */
.sik-atc.sik-skel__bone { display: flex; border-radius: var(--sik-radius-btn); }
.sik-skel__qty { width: 104px; height: 34px; border-radius: var(--sik-radius-sm); }
@media (pointer: coarse) { .sik-skel__qty { width: 122px; height: 40px; } }

/* --- <ProductCardSkeleton /> ------------------------------------------------ */
/* Everything else - box, radius, border, padding, gap, the 1:1 well, the
   two-line name, the button height - is .sik-card's own. */
.sik-skel-card .sik-card__tools .sik-skel__disc { box-shadow: var(--sik-shadow-xs); }
/* rating_stars(): five 14px stars with 1px gaps. */
.sik-skel__stars { width: 74px; height: 14px; border-radius: var(--sik-radius-pill); }
/* The horizontal card's name has no reserved second line (section 11), and
   on a wide row it is nearly always one line; its caption runs to three
   lines only once the row stacks at 560px. */
@media (min-width: 561px) {
    .sik-card--row.sik-skel-card .sik-card__name .sik-skel__text:last-child,
    .sik-card--row.sik-skel-card .sik-caption .sik-skel__text:last-child { display: none; }
}
/* The wishlist disc and badge pill sit ON the photo, so they are drawn a step
   darker than the well they lie on. */
.sik-skel-card .sik-card__badges .sik-skel__bone,
.sik-skel-card .sik-card__tools .sik-skel__bone { background: var(--sik-skel-base); }
.sik-skel-track { overflow: hidden; }

/* --- <ProductDetailSkeleton /> (Quick View) ---------------------------------- */
/* The spacings below are api/products/details.php's inline ones, so the
   skeleton rows land where the real rows will. */
.sik-skel-detail .sik-pdp__price { align-items: center; }
.sik-skel-detail__chips { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: var(--sp-4); }
.sik-skel-detail__text { margin-bottom: 18px; font-size: 13.5px; line-height: 1.6; }
.sik-skel-detail__row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 12px; margin-bottom: var(--sp-4); }
.sik-skel-detail__row .sik-skel__btn { height: 40px; }
.sik-skel-detail__row--qty .sik-skel__btn { height: var(--tap); }
.sik-skel-detail__note { font-size: 12px; }
.sik-skel-detail__features { margin-top: 18px; font-size: 13px; line-height: 2; }
.sik-skel-hero__actions { display: flex; flex-wrap: wrap; gap: 10px; }
.sik-skel-hero__actions .sik-skel__btn { height: 52px; }

/* Quick View holds still while its body arrives. The panel used to be
   centred, so when the product replaced the skeleton and the panel changed
   height, the whole dialog - close button included - jumped by half the
   difference. Anchored to the top it only ever grows downward, and on a phone
   the sheet is simply its full 92vh from the start, so nothing that is
   already on screen moves when the content lands. */
@media (min-width: 641px) {
    #sikQuickView.sik-modal { align-items: flex-start; }
    #sikQuickView .sik-modal__panel {
        margin-top: max(var(--sp-4), 4vh);
        max-height: min(88vh, 780px);
    }
}
@media (max-width: 640px) {
    #sikQuickView .sik-modal__panel { height: 92vh; }
}

/* Search rows start where the text will: the search page's hero block is
   centred, and the panel would otherwise inherit that for its bones. */
.sik-skel-suggest .sik-suggest__head,
.sik-skel-suggest .sik-suggest__body { text-align: left; }

/* --- <CartSkeleton /> -------------------------------------------------------- */
.sik-skel-cartline__body { flex: 1; min-width: 0; }
.sik-skel-cartline .sik-minicart__name { padding-right: var(--sp-6); }

/* --- <CategorySkeleton />, <HeroSkeleton />, blog cards ---------------------- */
.sik-catrow.sik-skel .sik-catrow__ring { box-shadow: none; }
.sik-skel-hero {
    display: grid;
    align-items: center;
    gap: var(--sp-7);
    min-height: clamp(360px, 40vw, 520px);
    padding: clamp(var(--sp-6), 4vw, var(--sp-10));
    border-radius: var(--sik-radius-lg);
    background: var(--sik-skel-well);
}
@media (min-width: 900px) { .sik-skel-hero { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr); } }
.sik-skel-hero__title { margin: var(--sp-4) 0; font-size: var(--fs-hero); line-height: var(--lh-tight); }
.sik-skel-hero__text { margin-bottom: var(--sp-6); font-size: var(--fs-md); line-height: var(--lh-relaxed); }
.sik-skel-hero__art { aspect-ratio: 1; max-height: 440px; border-radius: var(--sik-radius-lg); background: var(--sik-skel-base); }
@media (max-width: 899px) { .sik-skel-hero__art { display: none; } }
.sik-skel-blog__text { font-size: 13px; }
.sik-skel-blog__date { margin-top: auto; padding-top: var(--sp-2); font-size: 11.5px; }

/* --- <TableSkeleton /> and list rows ------------------------------------------ */
.sik-skel-table { width: 100%; border-collapse: collapse; }
.sik-skel-table td {
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--sik-line);
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
}
.sik-skel-row {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border-bottom: 1px solid var(--sik-line);
}
.sik-skel-row .sik-skel__disc { flex: none; width: 36px; height: 36px; }
.sik-skel-row__body { flex: 1; min-width: 0; }
.sik-skel-row__title { font-size: var(--fs-sm); font-weight: var(--fw-semibold); line-height: var(--lh-snug); }
.sik-skel-row__text { margin-top: 2px; font-size: var(--fs-xs); line-height: var(--lh-normal); }

/* The notification-centre row (account.js "Load older notifications"). Every
   box is the real row's - .sik-feeditem, .sik-notif, .sik-notif__icon and the
   .sik-feeditem__tools cluster - so only the fill is different and the height
   tracks the component. The tools are reserved rather than skipped because
   under 480px they wrap below the row and carry 44px of its height with them.

   The bone inside a tool is the glyph's size, not the 44px target's: a filled
   44px square would read as a button that is about to be tappable, which it
   is not. */
.sik-skel-feedrow__tool { display: grid; place-items: center; }
.sik-skel-feedrow__tool::before {
    content: "";
    width: var(--icon-md);
    height: var(--icon-md);
    border-radius: var(--sik-skel-radius);
    background: var(--sik-skel-base);
}

/* --- region states ------------------------------------------------------------ */
/* The first 120ms of a refetch: the old results stay where they are, quieter,
   so a fast answer swaps straight in without a grey flash in between. */
.sik-stale { opacity: .55; transition: opacity var(--dur-base) var(--ease-out); pointer-events: none; }

/* Content arriving in place of a skeleton fades up from nothing. No `to`
   keyframe and no fill, so it ends on whatever opacity the element already
   has - an out-of-stock card's .55 photo stays .55. */
@keyframes sik-skel-reveal { from { opacity: 0; } }
.sik-skel-reveal { animation: sik-skel-reveal var(--dur-slow) var(--ease-out); }

/* A figure being recalculated (checkout totals): same width, same line, the
   digits hidden under a shimmer, so the column does not twitch. The fill-color
   rather than `color`, because some of these spans carry an inline colour. */
.sik-recalc { pointer-events: none; }
[data-total].is-recalc {
    -webkit-text-fill-color: transparent;
    border-radius: var(--sik-radius-xs);
    background: var(--sik-skel-base) linear-gradient(100deg, transparent 18%, var(--sik-skel-shine) 50%, transparent 82%) no-repeat;
    background-size: 60% 100%;
    animation: sik-skel-well-sweep var(--sik-skel-dur) var(--ease-in-out) infinite;
}

/* The failure state that replaces a skeleton. It is .sik-empty --sm, so it
   reads as part of the page; inside a grid it spans every column. */
.sik-loaderr { grid-column: 1 / -1; }
.sik-loaderr > svg { color: var(--sik-warning-ink); opacity: .85; }
.sik-loaderr .sik-empty__text { margin-bottom: var(--sp-4); }
/* A lazy homepage section that failed keeps its skeleton's height (app.js
   writes it as min-height), with the message centred in that space. */
.sik-lazy-widget.is-failed { display: grid; place-items: center; padding-inline: var(--gutter); }
/* Without JavaScript nothing ever fetches that section's body, so its reserved
   skeleton would shimmer for as long as the page is open - a promise of content
   that cannot arrive. header.php marks the document with .js on the first line
   of the page, so this takes the whole shell away for the visitors who will
   never get the fetch (cart.php's recommendations were the visible case). */
html:not(.js) .sik-lazy-widget { display: none; }

/* "Show more products" - appends the next page under the grid. */
.sik-loadmore {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-8);
}
.sik-loadmore[hidden] { display: none; }
.sik-loadmore__meta { font-size: var(--fs-xs); color: var(--sik-muted); font-variant-numeric: tabular-nums; }
/* With a Show more button under it the pager is the secondary control. */
.sik-loadmore:not([hidden]) + [data-pagination] .sik-pager { margin-top: var(--sp-5); }

/* --- images ------------------------------------------------------------------- */
/* Every storefront <img> already carries width/height and sits in a well with
   a fixed aspect ratio, so an image never moves the page when it arrives;
   what was missing was the in-between. SIK.images (app.js) marks a lazy image
   .is-pending only if it comes on screen before it has loaded - an image
   that is ready by the time it is seen gets no effect at all - and the
   shimmer is the <img> element's own background, which is exactly what shows
   through before its pixels arrive. On load it fades in; on error it swaps to
   a neutral glyph instead of the browser's broken-image icon and alt text. */
.js img.is-pending {
    background-color: var(--sik-skel-well);
    background-image: linear-gradient(100deg, transparent 18%, var(--sik-skel-shine) 50%, transparent 82%);
    background-repeat: no-repeat;
    background-size: 60% 100%;
    animation: sik-skel-well-sweep var(--sik-skel-dur) var(--ease-in-out) infinite;
}
@keyframes sik-skel-well-sweep {
    0%        { background-position: -150% 0; }
    65%, 100% { background-position: 250% 0; }
}
img.is-shown { animation: sik-skel-reveal var(--dur-slow) var(--ease-out); }
/* !important for the two geometry properties only: blog cards write
   object-fit/padding inline on their <img>, and a broken image there would
   otherwise stretch the glyph across the whole card.

   The inset that keeps the glyph small inside a large card used to be
   `padding: 34%` here. A percentage padding resolves against the containing
   block's WIDTH, so on an <img> sized by height alone - the header and footer
   wordmarks - it came to more than twice the element's own height and the box
   grew rather than kept its shape. The inset now lives in the artwork itself
   (BROKEN_IMG in app.js draws a 24-unit glyph on a 72-unit canvas), which
   cannot distort any box it lands in. */
img.is-broken {
    object-fit: contain !important;
    padding: 0 !important;
    background-color: var(--sik-skel-well);
}

/* --- reduced motion and the admin's animation switch ---------------------------- */
@media (prefers-reduced-motion: reduce) {
    .sik-skel::after { display: none; }
    .js img.is-pending,
    [data-total].is-recalc { animation: none; background-image: none; }
}
.sik-noanim .sik-skel::after { display: none; }
.sik-noanim img.is-pending,
.sik-noanim [data-total].is-recalc { animation: none; background-image: none; }
.sik-noanim .sik-skel-reveal,
.sik-noanim img.is-shown { animation: none; }

/* --------------------------------------------------------------------------
   31. Floating widgets
   -------------------------------------------------------------------------- */
.sik-float {
    position: fixed;
    /* Page-level floating chrome, same layer as the compare bar it steps over.
       Both sit under the overlay, so an open drawer dims them like everything
       else on the page. */
    z-index: var(--z-sticky);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}
/* Four stacks, one per `floating_buttons.position` value. The two bottom ones
   clear the phone bottom bar and the compare bar; the two middle ones are
   vertically centred and never meet either, so they need no offset at all. */
.sik-float--bottom-right { right: 16px; bottom: calc(var(--sik-bottomnav-space) + 16px); }
.sik-float--bottom-left  { left: 16px;  bottom: calc(var(--sik-bottomnav-space) + 16px); }
.sik-float--middle-right { right: 16px; top: 50%; transform: translateY(-50%); }
.sik-float--middle-left  { left: 16px;  top: 50%; transform: translateY(-50%); }
/* The compare bar and the float stack share --z-sticky, so an open compare bar
   would otherwise sit across the WhatsApp and back-to-top buttons. compare.js
   flags the body while the bar is up and the stack steps over it — geometry
   rather than a z-index arms race, which is why they can share a layer. */
body.sik-has-comparebar .sik-float--bottom-right,
body.sik-has-comparebar .sik-float--bottom-left {
    bottom: calc(var(--sik-bottomnav-space) + var(--sik-comparebar-h) + 16px);
}
.sik-float__btn {
    position: relative;
    width: var(--sik-float-size, 46px);
    height: var(--sik-float-size, 46px);
    border-radius: 999px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    /* The theme's own dark, not a literal: a re-themed store re-themes these.
       A row that sets its own colour overrides both from the admin. */
    background: var(--sik-navy);
    color:  var(--sik-on-inverse);
    box-shadow: var(--sik-shadow-card);
    border: 0;
    cursor: pointer;
    text-decoration: none;
    transition: transform var(--dur-base) var(--ease-out),
                background-color var(--dur-base) var(--ease-out),
                opacity var(--dur-base) var(--ease-out);
}
/* 46px square already clears --tap, so these two need no ::after extender —
   the glyph inside is what was wrong. The WhatsApp link passed `w-6 h-6` (24px)
   and the back-to-top button directly below it passed `w-5 h-5` (20px), so the
   two buttons in one floating stack drew visibly different-sized icons. The
   component owns the size now and they match. */
.sik-float__btn svg { width: var(--icon-xl); height: var(--icon-xl); flex: none; }
.sik-float__btn:hover { transform: translateY(-3px); }
.sik-float__btn--sm { --sik-float-size: 38px; }
.sik-float__btn--sm svg { width: var(--icon-md); height: var(--icon-md); }
.sik-float__btn--md { --sik-float-size: 46px; }
.sik-float__btn--lg { --sik-float-size: 56px; }
.sik-float__btn--lg svg { width: var(--icon-2xl); height: var(--icon-2xl); }

/* The label. Hidden by default — the button is a glyph with an aria-label —
   and revealed as a pill when the admin turns "Show label" on, which is the
   only case where the control stops being a circle. */
.sik-float__label {
    display: none;
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}
.sik-float__btn--labelled {
    width: auto;
    padding-inline: var(--sp-4);
    border-radius: 999px;
}
.sik-float__btn--labelled .sik-float__label { display: inline; }

/* Tooltip. Pointer devices only: on a touch screen there is no hover to show
   it in and the label pill is the answer instead. */
@media (hover: hover) {
    .sik-float__btn::after {
        content: attr(data-float-tip);
        position: absolute;
        top: 50%;
        transform: translateY(-50%);
        padding: var(--sp-1) var(--sp-3);
        border-radius: var(--sik-radius-sm);
        background: var(--sik-inverse);
        color:  var(--sik-on-inverse);
        font-size: var(--fs-xs);
        white-space: nowrap;
        opacity: 0;
        pointer-events: none;
        transition: opacity var(--dur-fast) var(--ease-out);
    }
    /* The chip opens away from the edge the stack is pinned to. */
    .sik-float--bottom-right .sik-float__btn::after,
    .sik-float--middle-right .sik-float__btn::after { right: calc(100% + var(--sp-2)); }
    .sik-float--bottom-left .sik-float__btn::after,
    .sik-float--middle-left .sik-float__btn::after  { left: calc(100% + var(--sp-2)); }
    .sik-float__btn:hover::after,
    .sik-float__btn:focus-visible::after { opacity: 1; }
    /* The pill already says it. */
    .sik-float__btn--labelled::after { content: none; }
}

/* Attention animations. One per row, admin-chosen, and all three are off under
   prefers-reduced-motion through the token layer: --dur-* collapse to 0s there,
   and these durations are written from them. */
.sik-float__btn--pulse::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: inherit;
    opacity: .45;
    animation: sik-float-pulse calc(var(--dur-slow) * 6) var(--ease-out) infinite;
}
@keyframes sik-float-pulse {
    0%   { transform: scale(1);    opacity: .45; }
    70%  { transform: scale(1.55); opacity: 0; }
    100% { transform: scale(1.55); opacity: 0; }
}
.sik-float__btn--bounce { animation: sik-float-bounce calc(var(--dur-slow) * 5) var(--ease-out) infinite; }
@keyframes sik-float-bounce {
    0%, 70%, 100% { transform: translateY(0); }
    80%           { transform: translateY(-6px); }
    90%           { transform: translateY(-3px); }
}
.sik-float__btn--float { animation: sik-float-drift calc(var(--dur-slow) * 8) var(--ease-in-out) infinite; }
@keyframes sik-float-drift {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(-5px); }
}

/* Back to top starts out of sight AND out of the tab order — app.js drops the
   hidden attribute and adds .is-visible together once the page has scrolled, so
   a keyboard user never lands on a button they cannot see. */
.sik-float__btn--top { opacity: 0; visibility: hidden; }
.sik-float__btn--top.is-visible { opacity: 1; visibility: visible; }

/* --------------------------------------------------------------------------
   32. Alerts
   -------------------------------------------------------------------------- */
.sik-alert {
    display: flex;
    gap: var(--sp-3);
    align-items: flex-start;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--sik-radius-sm);
    font-size: var(--fs-base);
    border: 1px solid;
    margin-bottom: var(--sp-4);
}
.sik-alert--success { background: var(--sik-success-soft); border-color: color-mix(in srgb, var(--sik-success) 28%, var(--sik-surface)); color: var(--sik-success-ink); }
.sik-alert--error { background: var(--sik-danger-soft); border-color: color-mix(in srgb, var(--sik-danger) 28%, var(--sik-surface)); color: var(--sik-danger-ink); }
.sik-alert--warning { background: var(--sik-warning-soft); border-color: color-mix(in srgb, var(--sik-warning) 28%, var(--sik-surface)); color: var(--sik-warning-ink); }
.sik-alert--info { background: var(--sik-info-soft); border-color: color-mix(in srgb, var(--sik-info) 28%, var(--sik-surface)); color: var(--sik-info-ink); }
.sik-alert svg { flex: none; margin-top: 1px; }

/* --------------------------------------------------------------------------
   33. Account layout
   -------------------------------------------------------------------------- */
/* minmax(0, …), never a bare 1fr. A bare 1fr takes its minimum from the widest
   unbreakable child, and below 900px that child is the horizontal nav rail —
   twelve nowrap pills, ~1850px of min-content. The track grew to fit them and
   dragged every card on the page out with it, ~1545px past a 320px viewport.
   The rail scrolls inside itself; the track must not size to it. */
.sik-account { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
@media (min-width: 900px) { .sik-account { grid-template-columns: 260px minmax(0, 1fr); gap: var(--sp-7); } }

.sik-accountnav {
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius);
    overflow: hidden;
    background:  var(--sik-surface);
    align-self: start;
}
.sik-accountnav__head { padding: var(--sp-5); background: var(--sik-inverse); color:  var(--sik-on-inverse); display: flex; gap: var(--sp-3); align-items: center; }
.sik-accountnav__avatar {
    width: 44px; height: 44px;
    border-radius: 50%;
    background: var(--sik-primary);
    color: #fff;
    display: grid;
    place-items: center;
    font-weight: var(--fw-black);
    flex: none;
}
/* Group heading. The sidebar and the header dropdown render from the same
   account_menu(), so they carry the same three groups. */
.sik-accountnav__label {
    padding: var(--sp-4) var(--sp-5) var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-widest);
    text-transform: uppercase;
    color: var(--sik-muted);
    background: var(--sik-surface);
}
.sik-accountnav__link {
    /* The badge's screen-reader text is .sik-sr, i.e. position:absolute. Without
       a positioned ancestor inside the rail it resolved against the initial
       containing block and sat wherever its link had scrolled to — 961px out,
       which the horizontal scroller could not clip and the document grew to
       include. Containing it here keeps that inside the scroller. */
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    width: 100%;
    min-height: var(--tap);
    padding: var(--sp-3) var(--sp-5);
    font-family: inherit;
    font-size: var(--fs-base);
    font-weight: var(--fw-medium);
    color: var(--sik-muted);
    background: none;
    border: 0;
    text-align: left;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-accountnav__link:hover { background: var(--sik-soft); color: var(--sik-primary-ink); }
.sik-accountnav__link:hover .sik-menu__icon,
.sik-accountnav__link.is-current .sik-menu__icon { color: currentColor; }
.sik-accountnav__link.is-current {
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
    font-weight: var(--fw-semibold);
    box-shadow: inset 3px 0 0 var(--sik-primary);
}
.sik-accountnav__link--danger { color: var(--sik-danger-ink); border-top: 1px solid var(--sik-border); }
.sik-accountnav__link--danger .sik-menu__icon { color: var(--sik-danger-ink); }
.sik-accountnav__link--danger:hover {
    background: color-mix(in srgb, var(--sik-danger) 10%, var(--sik-surface));
    color: var(--sik-danger-ink);
}

/* Below the two-column split the full sidebar rendered at page width above the
   content — nine 48px rows plus the header, about 545px, so the first screen of
   every account page was navigation and nothing else. It becomes a horizontal
   pill rail instead, which keeps the page's own H1 in the first viewport. */
@media (max-width: 899px) {
    .sik-accountnav {
        border: 0;
        border-radius: 0;
        overflow: visible;
        background: none;
    }
    .sik-accountnav__head { border-radius: var(--sik-radius); }
    .sik-accountnav > nav {
        display: flex;
        align-items: center;
        gap: var(--sp-2);
        margin-top: var(--sp-3);
        padding-bottom: var(--sp-1);
        overflow-x: auto;
        scrollbar-width: none;
        -webkit-overflow-scrolling: touch;
    }
    .sik-accountnav > nav::-webkit-scrollbar { display: none; }
    .sik-accountnav > nav > form { flex: none; margin: 0; }
    /* The group headings are a vertical device. In a horizontal rail they read
       as stray words between pills, so the rail keeps the order and drops them. */
    .sik-accountnav__label { display: none; }
    .sik-accountnav__link {
        flex: none;
        width: auto;
        min-height: var(--tap);
        white-space: nowrap;
        border: 1px solid var(--sik-border);
        border-radius: 999px;
        padding: var(--sp-2) var(--sp-4);
        background: var(--sik-surface);
    }
    .sik-accountnav__link--danger { border-top: 1px solid var(--sik-border); }
    .sik-accountnav__link.is-current {
        box-shadow: none;
        border-color: var(--sik-primary);
        background: var(--sik-primary-soft);
    }
}

.sik-panel {
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-surface);
    overflow: hidden;
}
.sik-panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-3);
    padding: var(--sp-5) var(--sp-6) 0;
}
.sik-panel__title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); color: var(--sik-ink); }
.sik-panel__body { padding: var(--sp-5) var(--sp-6) var(--sp-6); }
@media (max-width: 479px) {
    .sik-panel__head { padding: var(--sp-4) var(--sp-4) 0; }
    .sik-panel__body { padding: var(--sp-4); }
}

/* --------------------------------------------------------------------------
   34. Cart & checkout

   Two columns from 1024px — the work on the left, a sticky summary on the
   right — and one column below it. The summary card is the same component on
   both pages, so the numbers a shopper saw in the cart look identical at the
   moment they pay. The decision worth knowing: this is the one surface that
   does NOT get the festive treatment. There is exactly one gradient here and
   it is already spent — .sik-btn--primary on Checkout / Place order (section
   4). Everything else earns its brand from flat --sik-primary on the three
   things a shopper has to be certain about: which address, which courier,
   which way they are paying. A second gradient anywhere in this section would
   put the pay button in a fair fight, and the pay button must never be in one.
   -------------------------------------------------------------------------- */
.sik-pagehead {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-2) var(--sp-4);
    margin: var(--sp-5) 0 var(--sp-7);
}
.sik-pagehead__title { font-size: var(--fs-3xl); font-weight: var(--fw-bold); letter-spacing: var(--ls-tighter); line-height: var(--lh-tight); }
@media (min-width: 768px) { .sik-pagehead__title { font-size: var(--fs-4xl); } }
/* "3 items" is a live count that cart.js rewrites in place — tabular figures
   stop the line reflowing by a pixel every time a stepper is tapped. */
.sik-pagehead__meta { font-size: var(--fs-base); color: var(--sik-muted); font-variant-numeric: tabular-nums; }

.sik-checkout,
.sik-cartpage__layout {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-7);
    align-items: start;
}
@media (min-width: 1024px) {
    .sik-checkout,
    .sik-cartpage__layout { grid-template-columns: minmax(0, 1fr) 400px; gap: var(--sp-9); }
}
.sik-checkout__aside { display: grid; gap: var(--sp-4); min-width: 0; }
@media (min-width: 1024px) {
    .sik-checkout__aside {
        position: sticky;
        /* header.js measures the header and writes --sik-header-h; reading the
           token rather than a number is what keeps the sticky offset right
           when the announcement bar or a two-row header changes the height. */
        top: calc(var(--sik-header-h) + var(--sp-5));
        max-height: calc(100vh - var(--sik-header-h) - var(--sp-8));
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-width: thin;
        /* overflow-y:auto forces overflow-x to auto as well, so the column
           clips at its own edge — and the widest thing inside it is the pay
           button's --sik-shadow-brand, which spreads 20px. Without this inset
           the only piece of coloured depth on the page got sliced down both
           sides. Padding rather than a negative margin: the track is a fixed
           400px and pulling the aside wider would push the container sideways.
           order-success.php and track-order.php share this class, so their
           panels inset by the same 8px. */
        padding-inline: var(--sp-2);
        padding-bottom: var(--sp-2);
    }
}

/* --- cart lines ----------------------------------------------------------- */
.sik-cartpage__alerts { display: grid; gap: var(--sp-3); margin-bottom: var(--sp-6); }
.sik-cartpage__alerts .sik-alert { margin: 0; }

/* Cards rather than a ruled list. Each line is its own <form> that posts on
   its own, and a boxed row says that far better than a shared hairline — it
   also gives section 30's .sik-loading veil a shape to round off against
   while cart.js is mid-update. */
.sik-cartlist { display: grid; gap: var(--sp-3); }
.sik-cartline {
    position: relative;
    display: flex;
    gap: var(--sp-4);
    margin: 0;
    padding: var(--sp-4);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    transition: border-color var(--dur-fast) var(--ease-out);
}
@media (max-width: 479px) { .sik-cartline { padding: var(--sp-3); gap: var(--sp-3); } }
/* Colour only, and no lift: a row that jumps under the cursor on the page
   where someone is checking a total reads as instability, not polish. */
@media (hover: hover) {
    .sik-cartline:hover { border-color: color-mix(in srgb, var(--sik-primary) 26%, var(--sik-line)); }
}
.sik-cartline > div { min-width: 0; }
.sik-cartline__media {
    flex: none;
    width: 112px;
    aspect-ratio: 1;
    overflow: hidden;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-media);
}
/* 84px is not a free choice: the <img> in cart.php carries width="112"
   height="112", so the box has to stay square at both sizes or the space the
   browser reserves and the space the photo paints disagree and the row jumps
   as the image loads. */
@media (max-width: 479px) { .sik-cartline__media { width: 84px; } }
.sik-cartline__media img { width: 100%; height: 100%; object-fit: cover; }
.sik-cartline.is-out .sik-cartline__media img { opacity: .5; filter: grayscale(.5); }
.sik-cartline__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.sik-cartline__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-4); }
.sik-cartline__info { min-width: 0; display: grid; gap: var(--sp-1); }
.sik-cartline__name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    /* A pasted product name with no spaces in it is the one thing on this row
       that cannot wrap on its own. */
    overflow-wrap: anywhere;
    font-size: var(--fs-md);
    font-weight: var(--fw-medium);
    line-height: var(--lh-snug);
    color: var(--sik-ink);
    transition: color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
    .sik-cartline__name:hover { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1px; }
}
.sik-cartline__meta { font-size: var(--fs-sm); color: var(--sik-muted); }
.sik-cartline__each { display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--sp-1) var(--sp-2); font-size: var(--fs-sm); color: var(--sik-ink-2); }
.sik-cartline__total { flex: none; font-size: var(--fs-md); font-weight: var(--fw-semibold); color: var(--sik-ink); font-variant-numeric: tabular-nums; }

/* Stock trouble is the one thing on this page a shopper must not scroll past,
   so it is a tinted pill rather than coloured text. --sik-warning-ink on
   --sik-warning-soft measures 5.8:1 and the danger pair 6.2:1, both well over
   body-text minimum, and both re-point with their tokens in the dark theme. */
.sik-cartline__warn {
    display: inline-flex;
    align-self: flex-start;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-1) var(--sp-3);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-warning-soft);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--sik-warning-ink);
}
.sik-cartline__warn.is-error { background: var(--sik-danger-soft); color: var(--sik-danger-ink); }
.sik-cartline__controls { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-3); margin-top: auto; padding-top: var(--sp-2); }
.sik-cartline__max { font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-cartline__remove {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 36px;
    margin-left: auto;
    padding: 0 var(--sp-3);
    border: 0;
    border-radius: var(--sik-radius-sm);
    background: none;
    color: var(--sik-muted);
    font: inherit;
    font-size: var(--fs-sm);
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
@media (pointer: coarse) { .sik-cartline__remove { min-height: var(--tap); } }
@media (hover: hover) {
    .sik-cartline__remove:hover { color: var(--sik-danger-ink); background: var(--sik-danger-soft); }
}
.sik-cartline__remove:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; }

/* SIK.showLoader() injects <span class="sik-spinner"> as the control's first
   child and hideLoader() never takes it out again; section 4 hides it again
   for .sik-btn only. Remove is a bare <button>, so without this pair one
   removal left a ring spinning inside it for the rest of the session. */
.sik-cartline__remove .sik-spinner { display: none; }
.sik-cartline__remove.is-loading .sik-spinner { display: inline-block; }
.sik-cartline__remove.is-loading .sik-btn__label { opacity: .7; }
.sik-cartline__remove:disabled { cursor: not-allowed; }

/* The row is busy: the veil comes from section 30, the ring from here, and
   the ring is brand-coloured because this is the one moment the shopper is
   waiting on us. Both the line on the cart page and the row cart.js renders
   into the section 26 drawer need it, which is why the two selectors travel
   together — drop the second and the drawer silently loses its spinner. */
.sik-cartline.sik-loading::before,
.sik-minicart__item.sik-loading::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--icon-xl);
    height: var(--icon-xl);
    margin: calc(var(--icon-xl) / -2);
    border: 2px solid color-mix(in srgb, var(--sik-primary) 24%, transparent);
    border-top-color: var(--sik-primary);
    border-radius: 50%;
    z-index: 6;
    animation: sik-spin .6s linear infinite;
}
/* Both switches, not just one. The ring hardcodes its duration, so the global
   --dur-* collapse at the top of the file cannot reach it, and Settings >
   Theme > enable animations has to be able to stop it too — that second guard
   was missing. The ring itself stays visible either way: a stopped indicator
   still says "working", an absent one says nothing at all. */
@media (prefers-reduced-motion: reduce) {
    .sik-cartline.sik-loading::before,
    .sik-minicart__item.sik-loading::before { animation: none; }
}
.sik-noanim .sik-cartline.sik-loading::before,
.sik-noanim .sik-minicart__item.sik-loading::before { animation: none; }

.sik-cartpage__back {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    margin-top: var(--sp-4);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--sik-ink);
    transition: color var(--dur-fast) var(--ease-out);
}
@media (hover: hover) {
    .sik-cartpage__back:hover { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 3px; }
}
.sik-cartpage__back:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; border-radius: var(--sik-radius-xs); }

/* --- the summary card (cart + checkout) ----------------------------------- */
/* A tinted well with a hairline, not another white panel: on checkout every
   block in the left column is a .sik-panel (section 33) on --sik-surface, so
   the money column has to read as a different kind of object. No shadow on
   the card itself — the sticky aside clips at its own edge, and the only
   depth worth spending there is the pay button's. */
.sik-summarycard {
    padding: var(--sp-6);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    background: var(--sik-surface-2);
    min-width: 0;
}
@media (max-width: 479px) { .sik-summarycard { padding: var(--sp-5); } }
.sik-summarycard__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-5); }
.sik-summarycard__title { font-size: var(--fs-lg); font-weight: var(--fw-semibold); letter-spacing: var(--ls-tight); }
/* cart.php prints the title as a direct child, with no __head around it. */
.sik-summarycard > .sik-summarycard__title { margin-bottom: var(--sp-5); }
/* "Sign in", "Use a new address", "Edit". [data-new-address] is a real click
   target for checkout.js, so it gets a finger-sized one on touch — it had no
   min-height at all and sat at roughly 18px. */
.sik-summarycard__link {
    display: inline-flex;
    align-items: center;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--sik-primary-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}
@media (pointer: coarse) { .sik-summarycard__link { min-height: var(--tap); } }
.sik-summarycard__link:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; border-radius: var(--sik-radius-xs); }
.sik-summarycard__cta { display: grid; gap: var(--sp-3); margin-top: var(--sp-6); }
.sik-summarycard__note { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); text-align: center; }
.sik-summarycard__note a { color: var(--sik-primary-ink); text-decoration: underline; text-underline-offset: 2px; }

/* Free-delivery meter. Green, not brand: it marks a threshold a shopper has
   either crossed or not, and green is the one colour that already says so. */
.sik-shipnote { margin-bottom: var(--sp-5); font-size: var(--fs-sm); color: var(--sik-ink-2); }
.sik-shipnote strong { color: var(--sik-ink); font-weight: var(--fw-semibold); }
.sik-shipnote .sik-progress { margin-top: var(--sp-2); }
.sik-shipnote .sik-progress > span {
    background: var(--sik-success);
    /* cart.js writes the width inline on every cart change, so the fill should
       travel rather than teleport. --dur-slow collapses under reduced motion. */
    transition: width var(--dur-slow) var(--ease-out);
}
.sik-noanim .sik-shipnote .sik-progress > span { transition: none; }
.sik-shipnote--done { display: flex; align-items: center; gap: var(--sp-2); color: var(--sik-success-ink); font-weight: var(--fw-medium); }
/* utilities.css loads first and app.css uses no @layer, so the global
   [hidden] rule in section 1b loses to any later class rule that sets
   display. cart.js toggles el.hidden on both notes, so the guard has to be
   re-stated here or "Add ₹0 more for free delivery" shows on a cart that has
   already earned it. */
.sik-shipnote[hidden] { display: none; }

.sik-summaryitems {
    display: grid;
    gap: var(--sp-4);
    margin-bottom: var(--sp-5);
    padding-bottom: var(--sp-5);
    border-bottom: 1px solid var(--sik-line);
}
.sik-summaryitem { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.sik-summaryitem__media { position: relative; flex: none; width: 56px; height: 56px; border-radius: var(--sik-radius-sm); background: var(--sik-media); }
.sik-summaryitem__media img { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }
/* The one festive spark in the money column. --sik-cta rather than
   --sik-primary: this is 11px bold text, white on the raw primary measures
   3.5:1, and white on the darkened CTA shade clears 4.7:1. */
.sik-summaryitem__qty {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--sik-radius-pill);
    background: var(--sik-cta);
    color: var(--sik-on-cta);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
    box-shadow: var(--sik-shadow-brand-sm);
}
.sik-summaryitem__name { flex: 1; min-width: 0; font-size: var(--fs-sm); font-weight: var(--fw-medium); line-height: var(--lh-snug); color: var(--sik-ink); }
.sik-summaryitem__name span:first-child { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.sik-summaryitem__variant { display: block; font-size: var(--fs-xs); font-weight: var(--fw-normal); color: var(--sik-muted); }
.sik-summaryitem__price { flex: none; font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-ink); font-variant-numeric: tabular-nums; }

/* The same [hidden] trap as .sik-shipnote, three more ways in — all three are
   consumed here, which is why the guards live here rather than where the base
   rules do.
     1. .sik-tag (just below) sets display:inline-flex. cart.php prints the
        coupon badge as <span class="sik-tag" data-coupon-badge hidden> and
        cart.js toggles el.hidden, so an empty pill sat next to "Discount" on
        every cart with no coupon on it.
     2. .sik-summary__row (section 26) sets display:flex. cart.php and
        checkout.php mark the discount, tax, saving, payment_charge and
        payment_discount rows hidden and the two scripts toggle .hidden on
        them, so every checkout opened showing "Payment handling fee ₹0.00".
     3. .sik-help (section 22) sets display:block, and the delivery estimate
        is printed hidden and filled in by checkout.js after a PIN lookup. */
.sik-tag[hidden] { display: none; }
.sik-summary__row[hidden] { display: none; }
.sik-help[data-delivery-eta][hidden] { display: none; }

/* --- coupons --------------------------------------------------------------- */
.sik-couponform { display: flex; gap: var(--sp-2); }
.sik-couponform[hidden] { display: none; }
/* A typed code is upper-case whatever the shopper does, and the tracking
   matches the offer code chip in section 18c so the field and the chips under
   it read as the same kind of thing. */
.sik-couponform .sik-input { min-width: 0; text-transform: uppercase; letter-spacing: .04em; }
.sik-couponform .sik-input::placeholder { text-transform: none; letter-spacing: 0; }
/* Apply stays on .sik-btn--navy. It sits in the same column as Checkout, and
   the moment it turns brand-coloured the shopper has two loud buttons and no
   way to tell which one finishes the order. */
.sik-couponform .sik-btn { flex: none; }
.sik-coupon__on { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); }
.sik-coupon__on strong { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-success-ink); }
.sik-coupon__saving { display: block; margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-coupon__offers { display: grid; gap: var(--sp-2); margin-top: var(--sp-5); }
.sik-coupon__offers[hidden] { display: none; }
.sik-coupon__label {
    margin-bottom: var(--sp-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
/* A coupon this basket does not qualify for yet: the icon drops its brand
   tint so the eligible ones keep it, and the terms carry the warning ink that
   explains why. The .sik-offer base is section 18c and stays there. */
.sik-offer.is-locked .sik-offer__icon { background: var(--sik-surface-2); color: var(--sik-muted); }
.sik-offer.is-locked .sik-offer__terms { color: var(--sik-warning-ink); }
.sik-offer__code.is-disabled { opacity: .5; cursor: default; }
.sik-offer__form { flex: none; margin: 0; }
/* [data-coupon-apply] is a bare <button>, not a .sik-btn, so it needs the same
   spinner pair .sik-cartline__remove does or applying a coupon leaves a ring
   turning inside the code chip forever. */
.sik-offer__code .sik-spinner { display: none; }
.sik-offer__code.is-loading .sik-spinner { display: inline-block; }

/* --- checkout -------------------------------------------------------------- */
/* The authored height of the phone pay bar, declared on the page that owns it
   so the bar and the padding that has to clear it read one number instead of
   two eyeballed ones: sp-3 top + sp-3 bottom + a 52px .sik-btn--lg + the 1px
   top border = 78. */
.sik-checkoutpage { --sik-paybar-h: 78px; }
.sik-checkoutpage__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: var(--sp-4) var(--sp-6);
    margin: var(--sp-4) 0 var(--sp-7);
}
.sik-steps {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin: 0;
    /* The block padding is room for a focus ring: the rail is an overflow
       scroller, so anything drawn outside the box is clipped. */
    padding: var(--sp-1) 0;
    list-style: none;
    overflow-x: auto;
    scrollbar-width: none;
}
.sik-steps::-webkit-scrollbar { display: none; }
.sik-step { display: inline-flex; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); font-weight: var(--fw-medium); color: var(--sik-muted); white-space: nowrap; }
/* "Cart" is a real link back to the basket, so on a touch screen it needs a
   real target — the pip alone is 24px. */
@media (pointer: coarse) { a.sik-step { min-height: var(--tap); } }
@media (hover: hover) { a.sik-step:hover { color: var(--sik-primary-ink); } }
a.sik-step:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; border-radius: var(--sik-radius-xs); }
.sik-step__num {
    flex: none;
    width: 24px; height: 24px;
    display: grid;
    place-items: center;
    border-radius: 50%;
    background: var(--sik-surface-2);
    color: var(--sik-muted);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
}
/* Where you are is brand-coloured, where you have been is green. --sik-cta
   again rather than --sik-primary: the numeral is 12px, which is body size,
   and only the darkened shade carries white text at 4.5:1. This pip used to
   be --sik-ink, i.e. the same colour as every inactive label on the page. */
.sik-step.is-active { color: var(--sik-ink); }
.sik-step.is-active .sik-step__num { background: var(--sik-cta); color: var(--sik-on-cta); box-shadow: var(--sik-shadow-brand-sm); }
.sik-step.is-done { color: var(--sik-ink-2); }
.sik-step.is-done .sik-step__num { background: var(--sik-success-soft); color: var(--sik-success-ink); }
.sik-step__line { flex: none; width: 28px; height: 1px; background: var(--sik-line-strong); }

.sik-checkout__main { display: grid; gap: var(--sp-5); min-width: 0; }
/* minmax(0, …) on both tracks: a bare 1fr floors at its widest unbreakable
   child, and a long pre-filled address line would drag the form past 320px. */
.sik-formgrid { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-4); }
@media (min-width: 640px) { .sik-formgrid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.sik-formgrid > .sik-field { margin: 0; }
.sik-formgrid__full { grid-column: 1 / -1; }
.sik-sectionlabel {
    margin-bottom: var(--sp-3);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}

.sik-addresscards { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--sp-3); margin-bottom: var(--sp-6); }
@media (min-width: 640px) { .sik-addresscards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

/* The three choice lists — address, courier, payment — are one component: a
   radio in the gutter and the whole label as the target. */
.sik-addresscard,
.sik-paymethod {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--sik-input-line);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out),
                box-shadow var(--dur-fast) var(--ease-out);
}
/* addresses.php renders .sik-addresscard as a plain <div> with no radio and
   stacked block children, so the row layout above laid its badges, name,
   address and buttons out side by side. Where :has() exists that page gets
   its stack back; where it does not the selector is dropped whole and nothing
   changes, and checkout is untouched either way. */
.sik-addresscard:not(:has(input)) { display: block; }
@media (hover: hover) {
    .sik-addresscard:hover,
    .sik-paymethod:hover { border-color: var(--sik-line-strong); background: var(--sik-hover); }
}
/* The single highest-value brand moment on this surface. These were selected
   with --sik-ink, the same ink as every unselected label on the page: brand
   border + brand ring + a 6% wash says it without raising the volume. The
   wash is kept that light on purpose — --sik-muted on it measures 4.55:1 at
   13px and a heavier tint drops the description under 4.5 — and the two lines
   carrying real information step up to --sik-ink-2 on selection anyway. */
.sik-addresscard.is-selected,
.sik-paymethod.is-selected {
    border-color: var(--sik-primary);
    background: color-mix(in srgb, var(--sik-primary) 6%, var(--sik-surface));
    box-shadow: var(--sik-ring-brand);
}
.sik-addresscard.is-selected .sik-addresscard__text,
.sik-paymethod.is-selected .sik-paymethod__desc { color: var(--sik-ink-2); }
/* The radio takes the focus, so the card has to be the thing that shows it.
   :has(input:focus-visible) rather than :focus-within — a radio matches
   :focus on a plain mouse click, so :focus-within would ring every card a
   shopper merely picked. Without :has() there is no ring, which is what this
   section had before either way. */
.sik-addresscard:has(input:focus-visible),
.sik-paymethod:has(input:focus-visible) { border-color: var(--sik-primary); box-shadow: var(--sik-focus-ring); }
.sik-addresscard input,
.sik-paymethod input { flex: none; width: 18px; height: 18px; margin-top: 2px; accent-color: var(--sik-primary); cursor: pointer; }
.sik-addresscard__body { min-width: 0; }
.sik-addresscard__name { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); font-size: var(--fs-sm); font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-addresscard__text { display: block; margin-top: var(--sp-1); font-size: var(--fs-sm); line-height: var(--lh-normal); color: var(--sik-muted); overflow-wrap: anywhere; }

.sik-tag {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--sp-2);
    border-radius: var(--sik-radius-pill);
    background: var(--sik-surface-2);
    color: var(--sik-ink-2);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-medium);
    line-height: 1.5;
    white-space: nowrap;
}
.sik-tag--success { background: var(--sik-success-soft); color: var(--sik-success-ink); }
.sik-tag--warning { background: var(--sik-warning-soft); color: var(--sik-warning-ink); }

.sik-options { display: grid; gap: var(--sp-3); }
.sik-paymethod__body { flex: 1; min-width: 0; display: grid; gap: var(--sp-1); }
.sik-paymethod__row { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: var(--sp-1) var(--sp-4); }
.sik-paymethod__name { display: inline-flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2); font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--sik-ink); }
.sik-paymethod__price { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--sik-ink); font-variant-numeric: tabular-nums; }
.sik-paymethod__price.is-free { color: var(--sik-success-ink); }
.sik-paymethod__desc { font-size: var(--fs-sm); line-height: var(--lh-normal); color: var(--sik-muted); }
/* Cash on delivery at a PIN code that does not support it. checkout.js also
   sets wrap.style.opacity = '.5' inline, which beats anything declared here,
   so this rule deliberately sets no opacity of its own — it supplies only
   what the inline style cannot: the dead surface and the cursor. Before this
   the state had no rule at all and the inline fade was the entire signal. */
.sik-paymethod.is-disabled {
    border-color: var(--sik-line);
    background: var(--sik-surface-2);
    /* checkout.js clears cod.checked but never fires a change event, so the
       .is-selected class it set earlier survives the disable. Both states are
       (0,2,0) and this one is later, so the surface and border already win -
       the ring does not, because .is-selected is the only rule that sets one.
       Without this a COD card the PIN code just rejected still wore the brand
       halo that means "this is the one you chose". */
    box-shadow: none;
    cursor: not-allowed;
}
.sik-paymethod.is-disabled input { cursor: not-allowed; }

.sik-checkout__divider { height: 1px; margin: var(--sp-6) 0; border: 0; background: var(--sik-line); }
.sik-checkout__note summary {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--sik-ink);
    cursor: pointer;
    list-style: none;
}
.sik-checkout__note summary::-webkit-details-marker { display: none; }
.sik-checkout__note summary:focus-visible { outline: 2px solid var(--sik-primary); outline-offset: 2px; border-radius: var(--sik-radius-xs); }
/* The plus turns into a cross. --dur-base collapses to .01ms under
   prefers-reduced-motion; .sik-noanim needs saying explicitly. */
.sik-checkout__note summary svg { transition: transform var(--dur-base) var(--ease-out); }
.sik-checkout__note[open] summary svg { transform: rotate(45deg); }
.sik-noanim .sik-checkout__note summary svg { transition: none; }
.sik-checkout__note .sik-field { margin: var(--sp-2) 0 0; }

/* The billing block collapses from the checkbox alone, so unticking it still
   reveals the fields when checkout.js has not loaded. The label and the div
   have to stay adjacent siblings for this to work — do not wrap either one. */
#sikBillingToggle:has(#sikBillingSame:checked) + #sikBillingFields { display: none; }

/* Sticky pay bar on phones: the total and the button, always in reach. */
.sik-paybar { display: none; }
@media (max-width: 1023px) {
    .sik-paybar {
        position: fixed;
        left: 0; right: 0; bottom: 0;
        z-index: var(--z-header);
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--sp-3);
        min-height: var(--sik-paybar-h, 78px);
        padding: var(--sp-3) var(--gutter) calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
        /* Genuinely floating chrome, so it takes the house glass treatment —
           but opaque first, because a total that is 76% legible is not a
           total. The lift is a black sheen rather than brand colour: the bar
           is a neutral surface and a pink shadow under it would read as a
           rendering fault. */
        background: var(--sik-elevated);
        border-top: 1px solid var(--sik-line);
        box-shadow: 0 -8px 24px -12px rgba(0, 0, 0, .18);
    }
    @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        .sik-paybar {
            background: var(--sik-glass-bg);
            -webkit-backdrop-filter: var(--sik-glass-blur);
            backdrop-filter: var(--sik-glass-blur);
        }
    }
    .sik-paybar__total { display: grid; min-width: 0; line-height: 1.2; }
    .sik-paybar__label { font-size: var(--fs-xs); color: var(--sik-muted); }
    /* On the opaque fallback the label sits on --sik-elevated, where
       --sik-muted measures 4.83:1 and is fine. Behind glass it is not: 24% of
       whatever is scrolling under the bar comes through, and one dark product
       thumbnail or the navy Apply button drops 12px --sik-muted to about
       2.9:1 in light and 3.6:1 in dark. So the blur is only ever granted
       together with the heavier ink. A second @supports block rather than a
       line in the first one, because it has to land AFTER the rule it is
       correcting - both are (0,1,0) and source order decides. Section 1c's own
       .sik-glass makes the same call: nothing muted goes on glass. */
    @supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
        .sik-paybar__label { color: var(--sik-ink-2); }
    }
    .sik-paybar__amount { font-size: var(--fs-lg); font-weight: var(--fw-bold); color: var(--sik-ink); font-variant-numeric: tabular-nums; }
    .sik-paybar .sik-btn { flex: 1 1 auto; max-width: 260px; }
    /* Derived from the bar rather than eyeballed: this was a hardcoded 110px,
       which was wrong by however far the bar's real height had drifted. */
    .sik-checkoutpage { padding-bottom: calc(var(--sik-paybar-h) + env(safe-area-inset-bottom, 0px) + var(--sp-5)); }
    /* Place order is rendered twice — once in the bar, once in the summary
       card. Only one of the two may be on screen at a time. */
    .sik-summarycard__cta--wide { display: none; }
}

/* --- motion switches ------------------------------------------------------ */
/* Settings > Theme > enable animations. prefers-reduced-motion is already
   answered by the global --dur-* collapse in section 1a, but .sik-noanim does
   NOT touch those tokens - it only kills named animations - so every
   transition in this section has to be switched off by name as well. The
   width and transform pair further up are already covered; these five are the
   colour and border fades on the two card lists and the three links. Missing
   this is how a "no animation" store still had every cart row fading its
   border under the cursor. */
.sik-noanim .sik-cartline,
.sik-noanim .sik-cartline__name,
.sik-noanim .sik-cartline__remove,
.sik-noanim .sik-cartpage__back,
.sik-noanim .sik-addresscard,
.sik-noanim .sik-paymethod { transition: none; }

/* --- focus mode: checkout without the storefront around it ----------------- */
/* body.sik-focus is set by includes/header.php on the checkout page. The logo,
   cart, account and theme stay; every exit that is not "finish the order" or
   "go back" steps aside.

   !important, once, on one shared declaration: each of these nine components
   sets its own `display` at its own breakpoint — the nav is flex from 1024px,
   the bottom bar is a grid below it, the search swaps between two elements —
   so a plain `display: none` here loses to whichever of those rules is more
   specific or simply later in the file. Beating them one at a time would mean
   re-stating nine media queries inside this section. */
.sik-focus .sik-announce,
.sik-focus .sik-header__nav,
.sik-focus .sik-header__row > .sik-search,
.sik-focus .sik-search-toggle,
.sik-focus .sik-burger,
.sik-focus .sik-action--wishlist,
.sik-focus .sik-action--compare,
.sik-focus .sik-bottomnav,
.sik-focus .sik-footer__main { display: none !important; }
/* The bottom bar is gone, so the space the body reserved for it goes too. */
body.sik-focus.sik-has-bottomnav { padding-bottom: 0; }
@media (max-width: 1023px) {
    /* What floats above the fold now clears the pay bar, not the bottom nav. */
    .sik-focus .sik-float--bottom-right,
    .sik-focus .sik-float--bottom-left { bottom: calc(96px + env(safe-area-inset-bottom, 0px)); }
}


/* --- a combo in the basket -------------------------------------------------
   One card for the set, with its contents listed inside it. The brand edge is
   what tells the eye this line is not an ordinary product before any of the
   text is read. */
.sik-cartline--combo {
    border-color: color-mix(in srgb, var(--sik-primary) 30%, var(--sik-line));
    background: color-mix(in srgb, var(--sik-primary) 4%, var(--sik-surface));
}
/* The info column is a grid, so align-self alone left the chip stretched into
   a full-width bar. It needs both axes pinned and its own intrinsic width. */
.sik-cartline__combochip {
    align-self: flex-start;
    justify-self: start;
    width: max-content;
    max-width: 100%;
    margin-bottom: 6px;
}

.sik-cartline__combolist {
    display: grid;
    gap: 6px;
    margin: var(--sp-3) 0 0;
    padding: var(--sp-3) 0 0;
    border-top: 1px dashed color-mix(in srgb, var(--sik-primary) 26%, transparent);
    list-style: none;
}
.sik-cartline__combolist li {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-width: 0;
    font-size: var(--fs-xs);
    color: var(--sik-ink-2);
}
.sik-cartline__combolist img {
    width: 28px; height: 28px;
    flex: none;
    border-radius: 6px;
    object-fit: cover;
    background: var(--sik-media);
}
.sik-cartline__combolist span {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.sik-cartline__combolist b { flex: none; font-weight: var(--fw-semibold); color: var(--sik-muted); }

/* The saving, said once and in the one place the eye checks. */
.sik-cartline__saved {
    display: block;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    color: var(--sik-success-ink);
}
.sik-cartline__was {
    display: block;
    font-size: var(--fs-xs);
    color: var(--sik-muted);
    text-decoration: line-through;
}
/* "Change quantity" is a link to the combo page, not a stepper - the per-order
   cap and every component's stock are checked together there. */
a.sik-cartline__max { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }
a.sik-cartline__max:hover { text-decoration: underline; text-underline-offset: 3px; }

/* --------------------------------------------------------------------------
   35. Invoice (print)
   -------------------------------------------------------------------------- */
.sik-invoice { max-width: 820px; margin-inline: auto; background: #fff; padding: var(--sp-7); }
.sik-invoice table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.sik-invoice th, .sik-invoice td { padding: var(--sp-3); border-bottom: 1px solid var(--sik-border); text-align: left; }
.sik-invoice thead th { background: var(--sik-soft); font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: var(--ls-wider); }
.sik-invoice tfoot td { border-bottom: 0; }

@media print {
    .sik-announce, .sik-header, .sik-footer, .sik-bottomnav, .sik-float,
    .sik-toasts, .sik-comparebar, .no-print { display: none !important; }
    body { background: #fff; padding: 0; }
    .sik-invoice { padding: 0; max-width: none; }
    a[href]::after { content: ""; }
}

/* --------------------------------------------------------------------------
   36. Entrance animations
   -------------------------------------------------------------------------- */
/* Scoped to .js (set inline in the head before first paint) so content is only
   ever hidden when there is JavaScript present to reveal it. Unconditionally
   hiding here meant any failure to reveal - scripts disabled or blocked, an
   error earlier in the bundle, an IntersectionObserver callback that never
   fires - left the element invisible forever. That is a bad trade for an
   entrance animation, and it is how the entire footer link grid ended up at
   opacity:0. */
.js [data-anim] { opacity: 0; }
[data-anim].is-in { opacity: 1; }
/* The starting transforms are scoped to .js for the same reason as the opacity:
   without scripts the element must sit exactly where it belongs, not 22px below
   it. Durations come from the tokens so the whole system still collapses under
   prefers-reduced-motion. */
.js [data-anim="fade-up"]    { transform: translateY(22px); }
.js [data-anim="fade-down"]  { transform: translateY(-22px); }
.js [data-anim="fade-left"]  { transform: translateX(24px); }
.js [data-anim="fade-right"] { transform: translateX(-24px); }
.js [data-anim="zoom-in"]    { transform: scale(.94); }
.js [data-anim] {
    transition: opacity var(--dur-slower) var(--ease-out),
                transform var(--dur-slower) var(--ease-out);
}
[data-anim].is-in { transform: none; }

.sik-pulse { animation: sik-pulse 2s ease-in-out infinite; }
@keyframes sik-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }

/* Anything animation-related must yield to the OS accessibility setting. */
@media (prefers-reduced-motion: reduce) {
    [data-anim] { opacity: 1 !important; transform: none !important; transition: none !important; }
    .sik-ticker__track, .sik-pulse { animation: none !important; }
    html { scroll-behavior: auto; }
}
/* Settings > Theme > "Enable entrance animations" off: content is simply there. */
.sik-noanim [data-anim] { opacity: 1 !important; transform: none !important; transition: none !important; }

/* --------------------------------------------------------------------------
   37. Utility odds and ends
   -------------------------------------------------------------------------- */
.sik-divider { height: 1px; background: var(--sik-border); border: 0; margin-block: var(--sp-6); }
.sik-scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.sik-scroll-x::-webkit-scrollbar { height: 6px; }
.sik-scroll-x::-webkit-scrollbar-thumb { background: var(--sik-border); border-radius: 999px; }
.sik-sr {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Skip link: off-screen until a keyboard user tabs to it.

   It is the first thing in the tab order and it has to be visible above
   everything on the page, including an open modal or a toast. --z-toast is the
   top of the scale; the skip link is declared after .sik-toasts, so on a tie it
   paints above it — which is the outcome we want, because a bypass-blocks
   control the user cannot see is a control they do not have. */
.sik-skip {
    position: absolute;
    left: 12px;
    top: 12px;
    z-index: var(--z-toast);
    background:  var(--sik-surface);
    color: var(--sik-ink);
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    padding: var(--sp-3) var(--sp-5);
    border-radius: var(--sik-radius-sm);
    box-shadow: var(--sik-shadow-lift);
    /* Parked off-screen with a transform, not with `top`. Animating `top`
       relayouts on every keyboard focus; a translate stays on the compositor. */
    transform: translateY(calc(-100% - var(--sp-6)));
    transition: transform var(--dur-fast) var(--ease-out);
}
.sik-skip:focus { transform: none; color: var(--sik-primary-ink); }
/* The skip target is focusable only so focus can land there; it must never
   show a focus ring of its own. */
#sikMain:focus { outline: none; }
/* Long-form body copy: the About page, every policy page and any CMS page.
   These run the full 1280px container, so the measure has to be capped or a
   privacy policy renders at ~150 characters per line. */
.sik-prose {
    font-size: var(--fs-md);
    line-height: var(--lh-relaxed);
    color: var(--sik-text);
}
/* The measure sits on the text elements, not on the block, so a comparison
   table or an image can still use the full column width while a paragraph
   stays at ~68 characters. Capping the block instead would force every policy
   table into a horizontal scroll even on a 1440px screen. */
.sik-prose > p,
.sik-prose > ul,
.sik-prose > ol,
.sik-prose > h2,
.sik-prose > h3,
.sik-prose > h4,
.sik-prose > blockquote,
.sik-prose > pre { max-width: 68ch; }
.sik-prose > * + * { margin-top: 0; }
.sik-prose h2, .sik-prose h3, .sik-prose h4 {
    color: var(--sik-ink);
    line-height: var(--lh-snug);
    letter-spacing: var(--ls-tight);
    /* Anchored headings must clear the sticky header when jumped to. */
    scroll-margin-top: calc(var(--sik-header-h) + var(--sp-5));
}
.sik-prose h2 {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-bold);
    margin: var(--sp-8) 0 var(--sp-3);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-border);
}
/* The rule is a separator between sections, so the first section does not
   need one hanging above it. */
.sik-prose > h2:first-child { border-top: 0; padding-top: 0; }
.sik-prose h3 {
    font-size: var(--fs-lg);
    font-weight: var(--fw-semibold);
    margin: var(--sp-6) 0 var(--sp-2);
}
.sik-prose h4 {
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    margin: var(--sp-5) 0 var(--sp-2);
    letter-spacing: var(--ls-normal);
}
.sik-prose > :first-child { margin-top: 0; }
.sik-prose p { margin-bottom: var(--sp-4); }
/* The last-updated stamp a policy row opens with. */
.sik-prose .sik-doc__stamp {
    display: inline-block;
    margin-bottom: var(--sp-5);
    padding: var(--sp-1) var(--sp-3);
    border: 1px solid var(--sik-border);
    border-radius: 999px;
    background: var(--sik-soft);
    font-size: var(--fs-xs);
    font-weight: var(--fw-semibold);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    color: var(--sik-muted);
}
.sik-prose ul, .sik-prose ol { margin: 0 0 var(--sp-5) var(--sp-6); }
.sik-prose ul { list-style: disc; }
.sik-prose ol { list-style: decimal; }
.sik-prose li { margin-bottom: var(--sp-2); }
.sik-prose li::marker { color: var(--sik-primary); }
.sik-prose strong { color: var(--sik-ink); font-weight: var(--fw-bold); }
/* The raw brand orange is 3.48:1 on white — too low for 15px links. */
.sik-prose a {
    color: var(--sik-primary-ink);
    text-decoration: underline;
    text-underline-offset: 2px;
}
.sik-prose a:hover { text-decoration-thickness: 2px; }
.sik-prose blockquote {
    margin: var(--sp-6) 0;
    padding: var(--sp-1) 0 var(--sp-1) var(--sp-5);
    border-left: 3px solid var(--sik-primary);
    color: var(--sik-muted);
    font-style: italic;
}
.sik-prose hr { border: 0; border-top: 1px solid var(--sik-border); margin: var(--sp-7) 0; }
.sik-prose code {
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: .9em;
    background: var(--sik-soft);
    border: 1px solid var(--sik-border);
    border-radius: 5px;
    padding: 1px var(--sp-1);
}
/* A video or map embed. The author pastes markup with a fixed 560x315 on it;
   sanitize_html() drops those attributes and content_prose() wraps what is
   left in this box, so the frame is always exactly as wide as the column it
   sits in - which is the whole of the responsive behaviour. A span, not a
   div, because editors habitually leave the embed inside a <p> and a div
   there would close the paragraph out from under it. */
.sik-embed {
    display: block;
    position: relative;
    width: 100%;
    /* The 68ch measure .sik-prose puts on paragraphs is set on the text
       elements, and a wrapper span is not one of them - so without this an
       embed pasted between two paragraphs runs the full 1280px container
       while the copy around it stops at 68ch. Matching the measure also
       makes the two cases agree, since an embed left inside a <p> is
       already capped by that paragraph. */
    max-width: min(100%, 68ch);
    margin: var(--sp-6) 0;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-soft);
}
/* A map is read, not watched: 16:9 leaves it a letterbox slot on a phone. */
.sik-embed--map { aspect-ratio: 4 / 3; }
@media (min-width: 640px) { .sik-embed--map { aspect-ratio: 16 / 9; } }
.sik-embed > iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
}

/* A policy table with five columns cannot fit a 320px phone. content_prose()
   wraps every table in a focusable .sik-scroll-x; the min-width below is what
   makes that wrapper actually scroll instead of squashing the columns, and the
   wrapper is what keeps the *page* from scrolling sideways with it. */
.sik-prose .sik-scroll-x {
    margin-bottom: var(--sp-5);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius-sm);
    background:
        linear-gradient(90deg, var(--sik-surface) 30%, transparent) left / 28px 100% no-repeat,
        linear-gradient(270deg, var(--sik-surface) 30%, transparent) right / 28px 100% no-repeat,
        radial-gradient(farthest-side at 0 50%, rgba(15, 33, 67, .14), transparent) left / 12px 100% no-repeat,
        radial-gradient(farthest-side at 100% 50%, rgba(15, 33, 67, .14), transparent) right / 12px 100% no-repeat;
    background-attachment: local, local, scroll, scroll;
}
/* The wrapper is focusable so a keyboard user can scroll a wide table; the
   global :focus-visible outline supplies the ring, this only pulls it inside
   the rounded border. */
.sik-prose .sik-scroll-x:focus-visible { outline-offset: -2px; }
.sik-prose table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 0;
    font-size: var(--fs-base);
    line-height: var(--lh-normal);
    min-width: 520px;
    background: transparent;
}
.sik-prose th, .sik-prose td {
    border-bottom: 1px solid var(--sik-border);
    padding: var(--sp-3) var(--sp-4);
    text-align: left;
    vertical-align: top;
}
.sik-prose th {
    background: var(--sik-soft);
    font-weight: var(--fw-bold);
    color: var(--sik-ink);
    font-size: var(--fs-xs);
    letter-spacing: var(--ls-wide);
    text-transform: uppercase;
    white-space: nowrap;
}
.sik-prose tbody tr:last-child th, .sik-prose tbody tr:last-child td { border-bottom: 0; }
.sik-prose tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--sik-text) 3%, transparent); }
/* 14px table copy needs the -ink tone, not the bright brand orange. */
.sik-prose td a, .sik-prose th a { color: var(--sik-primary-ink); }
.sik-prose img { border-radius: var(--sik-radius-sm); margin-block: var(--sp-4); max-width: 100%; height: auto; }

/* --------------------------------------------------------------------------
   Long-form document: prose body + its own in-page contents list.
   Used by every policy page and by anything else rendered through
   cms_page_document(). One <details> serves both layouts, so there is no
   second copy of the list and no JavaScript.
   -------------------------------------------------------------------------- */
.sik-doc {
    display: grid;
    /* minmax(0, 1fr): a bare 1fr takes its minimum from the widest unbreakable
       child — one long table cell would push the whole page sideways. */
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-6);
    align-items: start;
}
.sik-doc__body, .sik-doc__aside { min-width: 0; }

.sik-toc {
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius);
    background: var(--sik-surface, #fff);
    overflow: hidden;
}
.sik-toc__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: var(--tap);
    padding: var(--sp-3) var(--sp-4);
    cursor: pointer;
    list-style: none;
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-ink);
}
.sik-toc__head::-webkit-details-marker { display: none; }
.sik-toc__head:focus-visible { outline-offset: -3px; }
.sik-toc__title { flex: 1 1 auto; min-width: 0; }
.sik-toc__count {
    flex: none;
    min-width: 22px;
    padding: 0 var(--sp-2);
    border-radius: 999px;
    background: var(--sik-soft);
    color: var(--sik-muted);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    line-height: 20px;
    text-align: center;
}
.sik-toc__chev {
    flex: none;
    width: var(--icon-sm); height: var(--icon-sm);
    color: var(--sik-muted);
    transition: transform var(--dur-base) var(--ease-out);
}
.sik-toc[open] .sik-toc__chev { transform: rotate(180deg); }
.sik-toc__nav { padding: 0 var(--sp-2) var(--sp-2); }
.sik-toc__list { list-style: none; margin: 0; padding: 0; counter-reset: none; }
.sik-toc__item { margin: 0; }
.sik-toc__link {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: var(--tap);
    padding: var(--sp-2) var(--sp-3);
    border-radius: var(--sik-radius-sm);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    color: var(--sik-text);
    text-decoration: none;
    transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}
.sik-toc__link:hover { background: var(--sik-soft); color: var(--sik-primary-ink); }
.sik-toc__link:focus-visible { outline-offset: -2px; }
/* Long headings wrap rather than force the rail wider. */
.sik-toc__label { min-width: 0; overflow-wrap: anywhere; }

@media (min-width: 1024px) {
    .sik-doc {
        grid-template-columns: minmax(0, 1fr) 250px;
        gap: var(--sp-8);
    }
    /* Explicit placement, so the rail sits on the right while the disclosure
       still comes first in the DOM for a phone. */
    .sik-doc__body  { grid-column: 1; grid-row: 1; }
    .sik-doc__aside { grid-column: 2; grid-row: 1; }
    .sik-doc__aside .sik-toc {
        position: sticky;
        top: calc(var(--sik-header-h) + var(--sp-4));
        max-height: calc(100vh - var(--sik-header-h) - var(--sp-9));
        overflow-y: auto;
        overscroll-behavior: contain;
    }
    .sik-toc__link { min-height: 0; padding-block: var(--sp-2); }
}

@media print {
    /* A policy page is a document people file. Print it as one: no rail, no
       scroll boxes, and every link resolved so a paper copy still leads
       somewhere. */
    .sik-doc { display: block; }
    .sik-doc__aside { display: none; }
    .sik-prose { font-size: 11pt; line-height: var(--lh-normal); color: #000; }
    .sik-prose > p, .sik-prose > ul, .sik-prose > ol,
    .sik-prose > h2, .sik-prose > h3, .sik-prose > h4,
    .sik-prose > blockquote { max-width: none; }
    .sik-prose h2 { font-size: 14pt; page-break-after: avoid; break-after: avoid; }
    .sik-prose h3 { font-size: 12pt; page-break-after: avoid; break-after: avoid; }
    .sik-prose .sik-scroll-x { overflow: visible; background: none; border: 0; }
    .sik-prose table { min-width: 0; font-size: 9.5pt; page-break-inside: avoid; break-inside: avoid; }
    .sik-prose th { background: none; border-bottom: 1px solid #000; }
    .sik-prose tbody tr:nth-child(even) td { background: none; }
    .sik-prose a { color: #000; text-decoration: underline; }
    .sik-prose a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 8.5pt; word-break: break-all; }
    .sik-prose li, .sik-prose p { page-break-inside: avoid; break-inside: avoid; }
}

/* Accordion (FAQ, specs on mobile) */
.sik-acc__item { border: 1px solid var(--sik-border); border-radius: var(--sik-radius-sm); margin-bottom: var(--sp-3); overflow: hidden; background:  var(--sik-surface); }
.sik-acc__head {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    padding: var(--sp-4) var(--sp-5);
    background: none;
    border: 0;
    text-align: left;
    font-size: var(--fs-md);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    color: var(--sik-text);
}
.sik-acc__head svg { flex: none; color: var(--sik-muted); transition: transform var(--dur-base) var(--ease-out); }
.sik-acc__item.is-open .sik-acc__head { color: var(--sik-primary-ink); }
.sik-acc__item.is-open .sik-acc__head svg { transform: rotate(180deg); }
.sik-acc__body { display: none; padding: 0 var(--sp-5) var(--sp-4); font-size: var(--fs-base); color: var(--sik-muted); line-height: var(--lh-relaxed); }
.sik-acc__item.is-open .sik-acc__body { display: block; }

/* --------------------------------------------------------------------------
   38. Account: orders, purchase history, notification centre, preferences

   Everything below belongs to the signed-in account area. Nothing here
   redefines a shared component; where a shared class needs to behave
   differently inside one of these screens the rule is scoped to the screen.
   -------------------------------------------------------------------------- */

/* Filter tab strip with a count chip. Extends .sik-tabs, does not replace it. */
.sik-tabs--filters { margin-bottom: var(--sp-5); gap: var(--sp-1); }
.sik-tabs--filters .sik-tab {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--tap);
    padding-inline: var(--sp-4);
    font-size: var(--fs-base);
}
.sik-tab__count {
    padding: 1px var(--sp-2);
    border-radius: 999px;
    background: var(--sik-soft);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    color: var(--sik-muted);
}
.sik-tab.is-active .sik-tab__count { background: var(--sik-primary-soft); color: var(--sik-primary-ink); }

/* --- order cards --------------------------------------------------------- */
.sik-orderlist { display: grid; gap: var(--sp-4); }

.sik-ordercard__head { align-items: flex-start; gap: var(--sp-3); flex-wrap: wrap; }
.sik-ordercard__ident { min-width: 0; flex: 1 1 200px; }
.sik-ordercard__number {
    display: inline-block;
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    color: var(--sik-ink);
    font-variant-numeric: tabular-nums;
    word-break: break-all;
}
.sik-ordercard__number:hover { color: var(--sik-primary-ink); }
.sik-ordercard__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    margin-top: var(--sp-1);
    font-size: var(--fs-xs);
    color: var(--sik-muted);
}
.sik-ordercard__meta strong { color: var(--sik-text); font-weight: var(--fw-bold); }
.sik-ordercard__body { display: grid; gap: var(--sp-4); }

.sik-ordercard__items { display: grid; gap: var(--sp-3); }
.sik-ordercard__item { display: flex; gap: var(--sp-3); align-items: center; }
.sik-ordercard__thumb {
    width: 52px; height: 52px;
    flex: none;
    object-fit: contain;
    padding: var(--sp-1);
    background:  var(--sik-surface);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius-sm);
}
/* minmax(0,…) in spirit: a long unbroken product name must not push the row
   wider than the card, which is what a bare flex child would do. */
.sik-ordercard__lines { min-width: 0; flex: 1; display: grid; gap: 2px; }
.sik-ordercard__name {
    font-size: var(--fs-sm);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-text);
    overflow-wrap: anywhere;
}
a.sik-ordercard__name:hover { color: var(--sik-primary-ink); }
.sik-ordercard__sub { display: flex; flex-wrap: wrap; gap: var(--sp-1); font-size: var(--fs-xs); color: var(--sik-muted); }
.sik-ordercard__more { font-size: var(--fs-xs); font-weight: var(--fw-semibold); color: var(--sik-primary-ink); }
.sik-ordercard__more:hover { text-decoration: underline; }

.sik-ordercard__tracking {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-3);
    border: 1px dashed var(--sik-border);
    border-radius: var(--sik-radius-sm);
    background: var(--sik-soft);
    font-size: var(--fs-xs);
    color: var(--sik-muted);
}
.sik-ordercard__tracking > span { min-width: 0; overflow-wrap: anywhere; }
.sik-ordercard__tracking strong { color: var(--sik-text); font-variant-numeric: tabular-nums; }
.sik-ordercard__ticon { width: var(--icon-sm); height: var(--icon-sm); flex: none; color: var(--sik-primary-ink); }
.sik-ordercard__copy { margin-left: auto; flex: none; }
.sik-ordercard__note { font-size: var(--fs-xs); color: var(--sik-danger-ink); overflow-wrap: anywhere; }
.sik-ordercard__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); }
.sik-ordercard__hint { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-ordercard__hint a { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }
.sik-ordercard__hint a:hover { text-decoration: underline; }

/* --- compact progress rail ----------------------------------------------- */
.sik-track { display: grid; gap: var(--sp-2); }
.sik-track__rail { display: flex; align-items: center; gap: 0; list-style: none; margin: 0; padding: 0; }
.sik-track__step { position: relative; flex: 1 1 0; min-width: 0; display: flex; justify-content: center; }
/* The connector is drawn behind each dot except the first, so the rail keeps
   its shape whatever number of steps the timeline returns. */
.sik-track__step + .sik-track__step::before {
    content: "";
    position: absolute;
    top: 50%;
    right: 50%;
    left: -50%;
    height: 2px;
    transform: translateY(-50%);
    background: var(--sik-border);
}
.sik-track__step.is-done + .sik-track__step.is-done::before,
.sik-track__step.is-done + .sik-track__step.is-current::before { background: var(--sik-success-ink); }
.sik-track__dot {
    position: relative;
    z-index: 1;
    width: 18px; height: 18px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--sik-surface);
    border: 2px solid var(--sik-border);
    color: transparent;
    transition: background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.sik-track__tick { width: 10px; height: 10px; }
.sik-track__step.is-done .sik-track__dot {
    background: var(--sik-success-ink);
    border-color: var(--sik-success-ink);
    color: #fff;
}
.sik-track__step.is-current .sik-track__dot {
    background: var(--sik-primary);
    border-color: var(--sik-primary);
    color: #fff;
    box-shadow: 0 0 0 4px var(--sik-primary-soft);
}
.sik-track__caption {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    color: var(--sik-muted);
}
.sik-track__caption strong { color: var(--sik-text); font-weight: var(--fw-bold); }
.sik-track__step-of {
    padding: 1px var(--sp-2);
    border-radius: 999px;
    background: var(--sik-soft);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
}
.sik-track__at { margin-left: auto; }

/* --- notification centre -------------------------------------------------- */
.sik-feed { padding: var(--sp-2); }
.sik-feed__list { display: grid; gap: var(--sp-1); list-style: none; margin: 0; padding: 0; }
.sik-feed__foot:not(:empty) { padding: var(--sp-3) var(--sp-2) var(--sp-1); }

.sik-feeditem {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: var(--sp-1);
    border-radius: var(--sik-radius-sm);
}
.sik-feeditem + .sik-feeditem { border-top: 1px solid var(--sik-border); }
.sik-feeditem .sik-notif { flex: 1 1 auto; min-width: 0; }
/* A full-bleed tint across a wide row is heavy. On this screen unread reads as
   an accent edge instead, which still survives a high-contrast setting. */
.sik-feeditem .sik-notif.is-unread { background: none; }
.sik-feeditem.is-unread { box-shadow: inset 3px 0 0 var(--sik-primary); }
.sik-feeditem.is-unread .sik-notif__title { font-weight: var(--fw-bold); color: var(--sik-ink); }
.sik-feeditem .sik-notif__dot { top: var(--sp-4); right: var(--sp-2); }

.sik-feeditem__tools {
    display: flex;
    flex: none;
    align-items: center;
    gap: 2px;
    padding-top: var(--sp-2);
}
.sik-feeditem__btn { width: var(--tap); height: var(--tap); color: var(--sik-muted); }
.sik-feeditem__btn:hover { color: var(--sik-primary-ink); background: var(--sik-soft); }
.sik-feeditem__btn--danger:hover { color: var(--sik-danger-ink); background: color-mix(in srgb, var(--sik-danger) 10%, var(--sik-surface)); }
@media (max-width: 479px) {
    /* Two 44px targets plus the row leaves nothing for the text on a 320px
       screen, so the tools sit under the row rather than beside it. */
    .sik-feeditem { flex-wrap: wrap; }
    .sik-feeditem .sik-notif { flex-basis: 100%; }
    .sik-feeditem__tools { margin-left: auto; padding-top: 0; padding-bottom: var(--sp-1); }
}

/* --- purchase history ----------------------------------------------------- */
/* Not .sik-grid: that component holds three columns all the way down to 320px,
   where a third of the width cannot fit a rupee total and the amount broke
   across two lines mid-number. Below 480px each stat is a label/value row
   instead, which never truncates and never wraps a figure. */
.sik-history__stats {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
}
@media (min-width: 480px) {
    .sik-history__stats { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--sp-3); }
}
.sik-statcard {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius);
    background: var(--sik-surface);
    min-width: 0;
}
@media (min-width: 480px) { .sik-statcard { display: block; padding: var(--sp-4); } }
.sik-statcard__label {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--ls-wider);
    text-transform: uppercase;
    color: var(--sik-muted);
}
/* nowrap, never overflow-wrap: a currency total must not break between digits. */
.sik-statcard__value {
    font-size: var(--fs-lg);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    color: var(--sik-ink);
    white-space: nowrap;
}
@media (min-width: 480px) {
    .sik-statcard__label { display: block; }
    .sik-statcard__value { display: block; margin-top: var(--sp-1); font-size: var(--fs-xl); }
}

.sik-historyfilter { margin-bottom: var(--sp-5); }
.sik-historyfilter__body {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 0 var(--sp-4);
}
@media (min-width: 640px) {
    .sik-historyfilter__body { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .sik-historyfilter__search { grid-column: 1 / -1; }
}
@media (min-width: 1024px) {
    .sik-historyfilter__body { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr) auto; }
    .sik-historyfilter__search { grid-column: auto; }
}
.sik-historyfilter__go { display: flex; align-items: flex-end; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.sik-historyfilter__foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-5);
    border-top: 1px solid var(--sik-border);
    background: var(--sik-soft);
}
.sik-historyfilter__view .sik-pill { display: inline-flex; align-items: center; gap: var(--sp-2); min-height: var(--tap); }
.sik-historyfilter__view .sik-pill svg { width: var(--icon-xs); height: var(--icon-xs); }
.sik-historyfilter__presets .sik-pill { min-height: var(--tap); }

.sik-buylist { list-style: none; margin: 0; padding: 0; }
.sik-buylist__row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
    align-items: flex-start;
    padding: var(--sp-4) var(--sp-5);
}
.sik-buylist__row + .sik-buylist__row { border-top: 1px solid var(--sik-border); }
.sik-buylist__thumb {
    width: 56px; height: 56px;
    flex: none;
    object-fit: contain;
    padding: var(--sp-1);
    background:  var(--sik-surface);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius-sm);
}
.sik-buylist__main { flex: 1 1 200px; min-width: 0; display: grid; gap: var(--sp-1); }
.sik-buylist__name {
    font-size: var(--fs-base);
    font-weight: var(--fw-semibold);
    line-height: var(--lh-snug);
    color: var(--sik-text);
    overflow-wrap: anywhere;
}
a.sik-buylist__name:hover { color: var(--sik-primary-ink); }
.sik-buylist__meta,
.sik-buylist__order {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    color: var(--sik-muted);
}
.sik-buylist__order a { font-weight: var(--fw-semibold); color: var(--sik-ink); font-variant-numeric: tabular-nums; }
.sik-buylist__order a:hover { color: var(--sik-primary-ink); }
.sik-buylist__side { display: grid; gap: var(--sp-2); justify-items: flex-start; flex: none; }
@media (min-width: 640px) { .sik-buylist__side { justify-items: flex-end; text-align: right; } }
.sik-buylist__total { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--sik-ink); }

/* --- preferences ---------------------------------------------------------- */
.sik-prefs { display: grid; gap: var(--sp-4); }
/* .sik-panel__head wraps by default, which is right when its two children are a
   title and an action. Here the second child is the title, so wrapping dropped
   the icon onto its own line for the one group whose hint happened to be long
   and kept it inline for the rest. The pair always stays on one row. */
.sik-prefgroup__head { align-items: flex-start; justify-content: flex-start; gap: var(--sp-3); flex-wrap: nowrap; }
.sik-prefgroup__icon {
    width: 36px; height: 36px;
    flex: none;
    display: grid;
    place-items: center;
    border-radius: var(--sik-radius-sm);
    background: var(--sik-primary-soft);
    color: var(--sik-primary-ink);
}
.sik-prefgroup__intro { min-width: 0; }
.sik-prefgroup__hint { margin-top: 2px; font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }
.sik-prefgroup__body { display: grid; gap: var(--sp-1); }

.sik-pref {
    display: flex;
    align-items: center;
    gap: var(--sp-4);
    padding-block: var(--sp-3);
    min-height: var(--tap);
}
.sik-pref + .sik-pref { border-top: 1px solid var(--sik-border); }
.sik-pref--stacked { display: block; }
.sik-pref--stacked .sik-select,
.sik-pref--stacked .sik-input { max-width: 380px; }
.sik-pref__text { flex: 1; min-width: 0; display: grid; gap: 2px; }
.sik-pref__label { font-size: var(--fs-base); font-weight: var(--fw-semibold); color: var(--sik-text); cursor: pointer; }
.sik-pref__note { font-size: var(--fs-xs); line-height: var(--lh-normal); color: var(--sik-muted); }

.sik-switch { position: relative; flex: none; display: inline-flex; align-items: center; }
.sik-switch__input {
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--tap);
    height: var(--tap);
    transform: translate(-50%, -50%);
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.sik-switch__track {
    width: 46px;
    height: 26px;
    border-radius: 999px;
    background: var(--sik-line-strong);
    padding: 3px;
    display: block;
    transition: background var(--dur-base) var(--ease-out);
}
.sik-switch__thumb {
    display: block;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background:  var(--sik-surface);
    box-shadow: var(--sik-shadow-soft);
    transition: transform var(--dur-base) var(--ease-spring);
}
.sik-switch__input:checked + .sik-switch__track { background: var(--sik-success-ink); }
.sik-switch__input:checked + .sik-switch__track .sik-switch__thumb { transform: translateX(20px); }
/* --sik-focus-ring is a box-shadow value, not an outline one. */
.sik-switch__input:focus-visible + .sik-switch__track { box-shadow: var(--sik-focus-ring); }
.sik-switch__input:disabled + .sik-switch__track { opacity: .5; cursor: not-allowed; }

.sik-prefs__save {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4);
    border: 1px solid var(--sik-border);
    border-radius: var(--sik-radius);
    background: var(--sik-soft);
}
.sik-prefs__state { font-size: var(--fs-xs); color: var(--sik-muted); }
/* --sik-warning is a fill tone (~2.2:1 on white); 12px text takes an ink one. */
.sik-prefs.is-dirty .sik-prefs__state { color: var(--sik-primary-ink); font-weight: var(--fw-semibold); }
.sik-prefs__foot { margin-top: var(--sp-4); }

/* ==========================================================================
   Listing header
   --------------------------------------------------------------------------
   Was three inline style attributes on the h1 and the subtitle:
   `clamp(20px, 4vw, 27px)` at weight 800. Both were off the type scale — 27px
   sits between --fs-2xl and --fs-3xl, and 800 is heavier than any weight token
   the rest of the site uses, so the shop's own title was the boldest text on
   the page while the products under it set in 15px medium.
   ========================================================================== */
.sik-listing__heading { min-width: 0; }

.sik-listing__title {
    /* --fs-2xl -> --fs-3xl across the same range the clamp covered, but on the
       scale at both ends. */
    font-size: clamp(var(--fs-2xl), 3.2vw, var(--fs-3xl));
    font-weight: var(--fw-semibold);
    line-height: var(--lh-tight);
    color: var(--sik-ink);
    letter-spacing: -0.01em;   /* display sizes tighten; body text does not */
}

.sik-listing__sub {
    margin-top: var(--sp-2);
    max-width: 72ch;
    font-size: var(--fs-base);
    line-height: var(--lh-relaxed, 1.6);
    color: var(--sik-muted);
}

/* --------------------------------------------------------------------------
   39. Sign in, register, password reset

   One centred card on a softly lit page. It used to be an inline
   `background:#fff` with an inline navy shadow, which no theme could
   re-point - in dark mode it rendered a white box with near-white type on it
   and the "Back to store" link in navy on navy. Everything here reads role
   tokens instead, so the card follows the palette like every other surface.
   -------------------------------------------------------------------------- */
.sik-auth {
    display: flex;
    align-items: center;
    min-height: 66vh;
    padding-block: var(--section-y);
    /* The brand wash from the token layer, so the sign-in page belongs to the
       same store as the hero rather than being a bare grey field. */
    background: var(--sik-grad-soft);
}
.sik-auth__inner { width: 100%; }

.sik-auth__card {
    position: relative;
    padding: var(--sp-6) var(--sp-5);
    background: var(--sik-elevated);
    border: 1px solid var(--sik-line);
    border-radius: var(--sik-radius-lg);
    box-shadow: var(--sik-shadow-lift);
}
@media (min-width: 768px) { .sik-auth__card { padding: var(--sp-8); } }

/* A gradient hairline along the top edge: the same signature the hero panel
   and the stuck header carry. */
.sik-auth__card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 3px;
    border-radius: var(--sik-radius-lg) var(--sik-radius-lg) 0 0;
    background: var(--sik-grad-brand);
}

.sik-auth__logo {
    display: flex;
    justify-content: center;
    margin-bottom: var(--sp-5);
}
.sik-auth__logo img { height: 34px; width: auto; max-width: 100%; }

.sik-auth__title {
    font-size: var(--fs-2xl);
    font-weight: var(--fw-black);
    letter-spacing: var(--ls-tight);
    text-align: center;
    color: var(--sik-ink);
    margin: 0 0 var(--sp-2);
}
.sik-auth__sub {
    text-align: center;
    color: var(--sik-muted);
    font-size: var(--fs-sm);
    line-height: var(--lh-normal);
    margin: 0 0 var(--sp-6);
    text-wrap: pretty;
}

.sik-auth__rule { margin: var(--sp-6) 0 var(--sp-4); }

.sik-auth__alt {
    text-align: center;
    font-size: var(--fs-sm);
    color: var(--sik-muted);
    margin: 0;
}
.sik-auth__alt a {
    color: var(--sik-primary-ink);
    font-weight: var(--fw-bold);
}
.sik-auth__alt a:hover { text-decoration: underline; text-underline-offset: 3px; }

.sik-auth__back {
    text-align: center;
    font-size: var(--fs-xs);
    margin-top: var(--sp-5);
}
.sik-auth__back a {
    color: var(--sik-ink-2);
    font-weight: var(--fw-semibold);
}
.sik-auth__back a:hover { color: var(--sik-primary-ink); }

/* --- The coupon box on checkout -------------------------------------------
   The cart's coupon markup is reused verbatim so the two read as the same
   control; only the surrounding box is new, because on checkout it sits inside
   the order summary rather than in a card of its own. */
.sik-couponbox {
    padding: var(--sp-4) 0;
    margin-bottom: var(--sp-2);
    border-top: 1px solid var(--sik-line);
    border-bottom: 1px solid var(--sik-line);
}
.sik-couponbox__msg {
    margin: var(--sp-2) 0 0;
    font-size: var(--fs-xs);
    color: var(--sik-success-ink);
}
.sik-couponbox__msg[hidden] { display: none; }
/* A rejected code is not an error state of the page, just of the code - so it
   is coloured, not boxed or iconed. */
.sik-couponbox__msg.is-error { color: var(--sik-danger-ink, #B91C1C); }

/* --- Checkout order summary, unframed -------------------------------------
   The summary sat in a bordered, tinted card beside the form's own bordered
   cards, so the page read as two competing stacks of boxes. Flattening it
   lets the form be the only framed thing on the page and the summary read as
   the column it actually is.

   Scoped to the checkout aside on purpose: .sik-summarycard is also the cart's
   totals panel, where it stands alone beside an unframed list and the frame is
   what separates it from the page. */
.sik-checkout__aside .sik-summarycard {
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
}
/* The card's padding was doing the work of separating the head from the page
   edge; without it the title needs its own breathing room. */
.sik-checkout__aside .sik-summarycard__head {
    padding-bottom: var(--sp-3);
    border-bottom: 1px solid var(--sik-line);
}
/* The rows below used to sit inside a tinted panel. On the page background
   they need a rule of their own to stay grouped. */
.sik-checkout__aside .sik-summary {
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-line);
}
/* The coupon box already draws a rule above and below itself. Against the
   summary's new top rule that stacks two lines in a row, so it drops its own. */
.sik-checkout__aside .sik-couponbox { border-bottom: 0; padding-bottom: var(--sp-2); }

/* On a narrow screen the summary stacks under the form with nothing between
   them, so it keeps a divider where the frame used to be. */
@media (max-width: 1023px) {
    .sik-checkout__aside .sik-summarycard {
        padding-top: var(--sp-5);
        border-top: 1px solid var(--sik-line);
    }
}

/* --- Phone field with a country selector ----------------------------------
   One control to the eye: the select and the number share a border and a
   focus ring, so they read as a single field rather than two that happen to
   sit together.

   The select is capped rather than sized to its longest option - "United Arab
   Emirates" would otherwise push the number box to nothing on a phone. What
   stays visible is the flag, the ISO code and the dial code, which is the part
   anyone needs while typing; the full name is there when the list is open. */
.sik-phonegroup {
    display: flex;
    align-items: stretch;
    gap: 0;
    min-width: 0;
}
.sik-phonegroup__country {
    flex: 0 0 auto;
    width: 8.5rem;
    min-width: 0;
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
    border-right-color: transparent;
    /* The option text is long; the closed control shows only its head. */
    text-overflow: ellipsis;
}
.sik-phonegroup__number {
    flex: 1 1 auto;
    min-width: 0;
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
}
/* Whichever half has focus paints above the other, so the ring is never cut
   in half by its neighbour's border. */
.sik-phonegroup__country:focus,
.sik-phonegroup__number:focus,
.sik-phonegroup__country:focus-visible,
.sik-phonegroup__number:focus-visible { position: relative; z-index: 1; }

@media (max-width: 400px) {
    /* Below this the two halves leave the number too little room to read back
       what was typed, so they stack and each takes the full width. */
    .sik-phonegroup { flex-wrap: wrap; }
    .sik-phonegroup__country,
    .sik-phonegroup__number {
        flex: 1 1 100%;
        width: 100%;
        border-radius: var(--sik-radius-sm);
        border-right-color: var(--sik-line);
    }
    .sik-phonegroup__number { margin-top: var(--sp-2); }
}

/* ==========================================================================
   Consent banner  (includes/consent.php + assets/js/consent.js)

   A fixed bottom sheet, which is the whole point: it is out of flow, so it
   pushes nothing and its layout-shift contribution is 0 by construction rather
   than by measurement. It sits above the mobile bottom nav, below toasts.

   The three answers share ONE button rule. No primary/secondary split, no
   "Reject" hidden behind "Choose", no greyed-out refusal: the visual weight of
   accepting and refusing is identical, because that is the part of a consent
   banner a regulator actually reads.
   ========================================================================== */
.sik-consent {
    position: fixed;
    left: 0;
    right: 0;
    /* Clears the phone bottom nav; the token is 0 above 1024px. */
    bottom: calc(var(--sik-bottomnav-h, 0px) + env(safe-area-inset-bottom, 0px));
    /* Under --z-toast (200) so a toast still reads over it, over everything
       else on the page including drawers and modals: an unanswered banner must
       not end up behind the thing it is asking about. */
    z-index: 190;
    max-height: 80vh;
    overflow-y: auto;
    padding: var(--sp-5) var(--sp-4);
    background: var(--sik-elevated);
    border-top: 1px solid var(--sik-line);
    box-shadow: 0 -10px 40px -18px rgba(9, 11, 16, .45);
    color: var(--sik-ink);
    /* Revealed by JS, so it starts translated and settles. Transform only - no
       height, no margin - so nothing around it moves. */
    transform: translate3d(0, 100%, 0);
    transition: transform var(--dur-slow) var(--ease-out);
}
.sik-consent[hidden] { display: none; }
.sik-consent.is-open { transform: none; }
/* consent.js moves focus here when the banner appears, so a screen reader
   lands on it instead of having to find it at the end of the document. The
   container is not a control, though, and Chrome treats that programmatic
   focus as focus-visible - which painted a 2px ring around the whole sheet on
   every first page load. The ring belongs on the buttons inside, which have
   their own, so this one is suppressed in both states. */
.sik-consent:focus,
.sik-consent:focus-visible { outline: none; }

.sik-consent__inner {
    max-width: var(--sik-container, 1280px);
    margin: 0 auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-4) var(--sp-6);
}
.sik-consent__copy { flex: 1 1 320px; min-width: 0; }

.sik-consent__title {
    margin: 0 0 var(--sp-1);
    font-size: var(--fs-md);
    font-weight: 700;
    line-height: 1.3;
}
.sik-consent__text,
.sik-consent__meta,
.sik-consent__signal {
    margin: 0;
    font-size: var(--fs-sm);
    line-height: 1.55;
    color: var(--sik-ink-2);
}
.sik-consent__meta { margin-top: var(--sp-2); color: var(--sik-muted); font-size: var(--fs-xs); }
.sik-consent__meta a { text-decoration: underline; }

/* Only shown when the browser is sending DNT/GPC. */
.sik-consent__signal { display: none; margin-top: var(--sp-2); }
.sik-consent.is-signal .sik-consent__signal { display: block; }
.sik-consent.is-signal .sik-consent__text { display: none; }

/* --- the answers ---------------------------------------------------------
   Equal prominence is enforced by there being one rule: same background, same
   border, same size, same order every time. Do not add a --primary modifier to
   "Accept all" here; that is the dark pattern this component exists to avoid.
   ------------------------------------------------------------------------- */
.sik-consent__actions {
    flex: 0 1 auto;
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}
.sik-consent__actions--signal,
.sik-consent.is-signal .sik-consent__actions { display: none; }
.sik-consent.is-signal .sik-consent__actions--signal { display: flex; }

.sik-consent__btn {
    flex: 1 1 auto;
    min-height: var(--tap, 44px);
    min-width: 128px;
    padding: 0 var(--sp-5);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--sik-ink);
    background: var(--sik-surface-2);
    border: 1px solid var(--sik-line-strong);
    border-radius: var(--sik-radius-sm, 10px);
    cursor: pointer;
    transition: background var(--dur-fast) linear, border-color var(--dur-fast) linear;
}
.sik-consent__btn:hover { background: var(--sik-hover); }
.sik-consent__btn:active { background: var(--sik-press); }
.sik-consent__btn:focus-visible {
    outline: 2px solid var(--sik-primary);
    outline-offset: 2px;
}

/* --- per-category choices ------------------------------------------------- */
.sik-consent__choices {
    max-width: var(--sik-container, 1280px);
    margin: var(--sp-4) auto 0;
    padding-top: var(--sp-4);
    border-top: 1px solid var(--sik-line);
}
.sik-consent__choices[hidden] { display: none; }

.sik-consent__list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-3); }
.sik-consent__row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-4);
}
.sik-consent__rowtext { min-width: 0; display: grid; gap: 2px; }
.sik-consent__rowname { font-size: var(--fs-sm); font-weight: 600; color: var(--sik-ink); }
.sik-consent__rowhelp { font-size: var(--fs-xs); color: var(--sik-muted); line-height: 1.5; }
.sik-consent__always { flex: none; font-size: var(--fs-xs); color: var(--sik-muted); padding-top: 2px; }

/* The switch. The checkbox itself stays in the layer and keeps its own focus
   ring, so it is reachable and operable by keyboard exactly like any checkbox;
   only its painting is replaced. */
.sik-consent__toggle { flex: none; position: relative; display: inline-flex; align-items: center; }
.sik-consent__toggle input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    opacity: 0;
    cursor: pointer;
}
.sik-consent__track {
    width: 44px;
    height: 26px;
    border-radius: 999px;
    background: var(--sik-line-strong);
    position: relative;
    transition: background var(--dur-fast) linear;
}
.sik-consent__track::after {
    content: "";
    position: absolute;
    top: 3px;
    left: 3px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .3);
    transition: transform var(--dur-fast) var(--ease-out);
}
.sik-consent__toggle input:checked + .sik-consent__track { background: var(--sik-success, #16A34A); }
.sik-consent__toggle input:checked + .sik-consent__track::after { transform: translateX(18px); }
.sik-consent__toggle input:focus-visible + .sik-consent__track {
    outline: 2px solid var(--sik-primary);
    outline-offset: 2px;
}

.sik-consent__actions--save { margin-top: var(--sp-4); justify-content: flex-start; }
.sik-consent__actions--save .sik-consent__btn { flex: 0 0 auto; }

/* The footer's "Cookie preferences" control: a button, because it opens
   something here, painted exactly like the policy links beside it. */
.sik-consent-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--tap);
    padding: 0;
    font: inherit;
    font-size: var(--fs-xs);
    color: inherit;
    background: none;
    border: 0;
    cursor: pointer;
    text-align: left;
}
.sik-consent-link:hover { text-decoration: underline; }
@media (min-width: 1024px) { .sik-consent-link { min-height: 28px; } }

@media (max-width: 639px) {
    .sik-consent { padding: var(--sp-4); }
    .sik-consent__inner { gap: var(--sp-3); }
    .sik-consent__actions { width: 100%; }
    .sik-consent__btn { min-width: 0; flex: 1 1 100%; }
}

/* Motion off: both switches, matching the rest of the storefront. The banner
   still appears - it just appears rather than travels. */
@media (prefers-reduced-motion: reduce) {
    .sik-consent { transition: none; transform: none; }
    .sik-consent__track, .sik-consent__track::after { transition: none; }
}
body.sik-noanim .sik-consent { transition: none; transform: none; }

/* A category with no picture shows its own glyph inside the circle, where the
   single capital letter used to be. The letter was there to avoid an empty
   well; it never said anything about the range, and with three empty
   categories on the front page it said nothing three times.

   Sized against the circle rather than in px, because the circle itself is
   sized by --cat-size, which the renderer sets from how many tiles there are:
   a fixed 32px glyph would be lost in a 104px feature tile and crowd a 72px
   one. The animations live with the glyph set (sik-caticon), including both of
   the motion switches. */
.sik-catrow__glyph {
    width: 46%;
    height: 46%;
    color: var(--sik-primary-ink);
}
.sik-catrow__ring--contain .sik-catrow__glyph { padding: 0; }
