/**
 * Plenum storefront theme.
 *
 * Loaded on every shop page via the `bagisto.shop.layout.head.after` render event
 * (App\Providers\AppServiceProvider). Hand-written rather than generated because this
 * install has no node_modules, so Tailwind cannot be recompiled -- everything here is
 * plain CSS layered over the built Tailwind sheet.
 *
 * The visual language is the luxury Ayurveda register the client asked for: warm ivory
 * grounds, a single gold accent, high contrast serif headings against wide tracked
 * uppercase sans labels, and generous whitespace. Bagisto's stock palette is navy and
 * rounded; the overrides below replace that everywhere it shows on the storefront.
 */

:root {
    --plenum-ivory: #faf8f3;
    --plenum-cream: #f6f2eb;
    --plenum-sand: #f1eadf;
    --plenum-blush: #ebd1c4;
    --plenum-gold: #d2ab67;
    --plenum-gold-deep: #c7a05a;
    /*
     * Gold for text that has to be *read* rather than noticed.
     *
     * Neither gold above can carry small type on the cream: #d2ab67 is 1.93:1 on
     * --plenum-cream and #c7a05a only 2.19:1, both far under the 4.5:1 that WCAG AA
     * asks of body-sized text. They stay as they are -- they are accent fills and
     * hover states, where the requirement is 3:1 against adjacent colour, not text
     * contrast -- and anything set as small gold words uses this instead, at 4.89:1.
     *
     * Note this is darker than the 4.5:1 line rather than sitting on it. #8a6a2f,
     * the obvious rounder value, computes to 4.499:1 and fails by a rounding error.
     */
    --plenum-gold-contrast: #856428;
    /*
     * Body copy sits at the same ink as the headings and the division nav. At #484848 it
     * read as grey against the ivory ground; content is meant to be the darkest thing on
     * the page, with hierarchy carried by size and weight rather than by fading the text.
     */
    --plenum-charcoal: #2f2f2f;
    --plenum-ink: #2f2f2f;
    /* Secondary micro-text only -- pack sizes, rating counts, placeholders. Kept a step
     * lighter than the body ink so it still reads as subordinate, but no longer washes
     * out at 11-12px. */
    --plenum-muted: #5a5a5a;
    /*
     * Text sitting on a gold fill. White reaches only 2.15:1 there -- the label washes
     * straight out -- so anything on gold is set in near-black instead: 8.09:1 on gold
     * and 7.13:1 on the hover shade.
     */
    --plenum-on-gold: #1a1a1a;
    --plenum-hairline: #e4dccd;
    /*
     * The rating stars. Black and grey on purpose, not the site's gold: the gold is the
     * accent the page uses for things you can act on -- buttons, links, the offer strip --
     * and a rating is a fact, not an invitation. Black also holds its shape at 14px on
     * both the white card and the cream panel behind it, where gold at that size reads as
     * decoration. #000 on the card's #fff is 21:1.
     *
     * These three are the whole colour surface of the rating. Change them here and the
     * card, the product page and the review form all follow.
     */
    --plenum-star-filled: #000000;
    --plenum-star-empty: #d9d9d9;
    --plenum-star-count: #777777;

    /* Card size; the product page overrides it locally. */
    --plenum-star-size: 14px;

    --plenum-serif: "Cormorant Garamond", "DM Serif Display", Georgia, serif;
    --plenum-sans: "Montserrat", -apple-system, "Segoe UI", sans-serif;

    /*
     * Headings only -- Playfair Display Bold.
     *
     * A high-contrast transitional serif: the thick-to-thin swing is what makes it read
     * as considered rather than merely large, which is the register a pharmaceutical
     * brand wants. It is already on the page for the category rail, so the site gains a
     * face rather than adding one.
     *
     * Cormorant Garamond is the other candidate and a one-line swap -- it is quieter and
     * more bookish, and it is already loaded at every weight this needs. Whichever is
     * named here must be fetched at 700 in theme-css.blade.php, or the browser
     * synthesises the bold and thickens the hairlines the face is built on.
     *
     * Kept apart from --plenum-serif deliberately. That token carries the product names
     * as well (see the product-name rule below), and a heading face set on it would put
     * every 19px product name into a display cut.
     *
     * Cormorant Garamond stays first in the fallback chain, so a failed font fetch lands
     * the headings on a serif of roughly the right colour rather than on Times.
     */
    --plenum-display-face: "Playfair Display", "Cormorant Garamond", Georgia, serif;

    /*
     * 700. The face is fetched at this weight, so nothing is synthesised; at 500 the
     * headings sat level with the body copy's own SemiBold and stopped reading as
     * headings at all.
     */
    --plenum-display-weight: 700;

    /*
     * Content is set in Montserrat SemiBold. The weight is carried on the token rather
     * than on `body` alone because the universal selector below re-points font-family at
     * zero specificity: anything naming its own weight (the 700 labels, the serif
     * headings) still wins, and everything else inherits SemiBold.
     */
    --plenum-sans-weight: 600;

    /*
     * Small tracked uppercase labels only -- nav, buttons, footer headings, badges and
     * the section eyebrows. Inter holds its shape at 10-12px where Jost's lighter, more
     * geometric forms thin out under wide tracking. Body copy stays on --plenum-sans:
     * only weights 700-900 are loaded, so anything set in this family reads as bold.
     */
    --plenum-label: "Inter", "Jost", -apple-system, "Segoe UI", sans-serif;

    /* The label family is loaded bold-only; 700 is its lightest available weight. */
    --plenum-label-weight: 700;

    /* Wide tracking on small uppercase labels is the signature of the register. */
    --plenum-track: 0.18em;
}

/* ------------------------------------------------------------------ base */

body {
    background-color: var(--plenum-ivory);
    color: var(--plenum-charcoal);
    font-family: var(--plenum-sans);
    font-weight: var(--plenum-sans-weight);
    -webkit-font-smoothing: antialiased;
}

/*
 * The layout paints `main` with Tailwind's `bg-white`, which sits on top of the body
 * ground and puts a pure-white sheet over every page. Matching it as `main#main` clears
 * the utility on specificity without touching `.bg-white` anywhere else -- product
 * cards, dropdowns and modals still want a true white.
 */
main#main,
main#main.bg-white {
    background-color: var(--plenum-ivory);
}

/*
 * The built sheet opens with `* { font-family: Poppins }`. Matching that universal
 * selector from this later sheet re-points the default at zero specificity, so
 * headings and the per-section stylesheets still override it -- raising the
 * specificity here instead would silently flatten every serif heading on the site.
 * Icon classes name their own family at class specificity and are unaffected.
 */
* {
    font-family: var(--plenum-sans);
}

h1, h2, h3, h4,
.plenum-display {
    font-family: var(--plenum-display-face);
    color: var(--plenum-ink);
    font-weight: var(--plenum-display-weight);
    /*
     * Playfair Bold sets tightly by default, and its hairlines need room either side or
     * the thick strokes close up on them at display sizes. 0.06em is the difference
     * between a headline that reads and one that has to be parsed.
     */
    letter-spacing: 0.06em;
}

/*
 * The two headings the home page opens with, set in the display face and scaled up
 * alongside the rest.
 *
 * They are CMS blocks -- the intro band and Shop By Concern are rows in cms_pages,
 * each carrying its own <style> -- and that stylesheet names --plenum-serif on
 * `.plenum-intro-title` and `.plenum-concern-heading` directly. A class beats the bare
 * h1-h4 rule above, and the block's <style> lands later in the document than this
 * sheet, so both the face and the size have to be taken back here at two classes of
 * specificity rather than one.
 *
 * Done from this sheet rather than by editing the CMS rows because the live database is
 * not this one and content does not travel with a deploy: a row edited here would look
 * right locally and unchanged in production. This file is in the repo, so it ships.
 *
 * Sizes track the block's own breakpoints exactly -- 991/768/525 for the intro,
 * 1024/768/525 for the concern grid -- so overriding the top number alone would leave
 * the original cascade to undo it at every width below the first breakpoint.
 */
.plenum-intro .plenum-intro-title,
.plenum-concern .plenum-concern-heading {
    font-family: var(--plenum-display-face);
    font-weight: var(--plenum-display-weight);
}

.plenum-intro .plenum-intro-title {
    font-size: 62px;
}

.plenum-concern .plenum-concern-heading {
    font-size: 54px;
}

@media (max-width: 1024px) {
    .plenum-concern .plenum-concern-heading {
        font-size: 42px;
    }
}

@media (max-width: 991px) {
    .plenum-intro .plenum-intro-title {
        font-size: 46px;
    }
}

@media (max-width: 768px) {
    .plenum-intro .plenum-intro-title {
        font-size: 36px;
    }

    .plenum-concern .plenum-concern-heading {
        font-size: 34px;
    }
}

@media (max-width: 525px) {
    .plenum-intro .plenum-intro-title {
        font-size: 29px;
    }

    .plenum-concern .plenum-concern-heading {
        font-size: 27px;
    }
}

/* The stock theme paints headings and links navy; gold is the accent here. */
.text-navyBlue {
    color: var(--plenum-ink) !important;
}

a:hover .text-navyBlue,
a:hover.text-navyBlue {
    color: var(--plenum-gold-deep) !important;
}

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

header {
    background-color: var(--plenum-ivory);
    border-bottom: 1px solid var(--plenum-hairline);
}

/*
 * Division nav: small, wide tracked, uppercase, gold on hover.
 *
 * Set in ink rather than charcoal, at 500: at 12px under 0.18em of tracking the lighter
 * grey thinned out against the ivory ground and read as disabled rather than as the
 * primary navigation.
 */
header nav a,
header .grid a[href] {
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-ink);
    transition: color 220ms ease;
}

header nav a:hover,
header .grid a[href]:hover {
    color: var(--plenum-gold-deep);
}

/* -------------------------------------------------------- product cards */

/* Image sits on a warm ground, not Bagisto's cold zinc grey. */
.bg-zinc-100 {
    background-color: var(--plenum-cream) !important;
}

/* Square off the stock rounded card and drop its hover shadow for a flat, airy grid. */
.group.w-full.rounded-md,
.group.w-full.rounded-md:hover {
    border-radius: 0;
    box-shadow: none !important;
}

/*
 * The card name sits in a bare <p> inside the card link, so with no colour class of
 * its own it falls back to the browser's link blue. Colour is set on the link so the
 * name, price and any other inherited text in the card all follow it.
 */
.group a,
.group a:hover,
.group a:visited {
    color: var(--plenum-ink);
}

/* Product name: serif, roomy, never shouty. */
.group .break-words.text-base.font-medium {
    font-family: var(--plenum-serif);
    font-size: 19px;
    font-weight: 500;
    line-height: 1.35;
    color: var(--plenum-ink);
    letter-spacing: 0.01em;
}

/* Price: small, tracked, sans -- deliberately quieter than the name. */
.group .text-lg.font-semibold {
    font-family: var(--plenum-sans) !important;
    font-size: 14px !important;
    font-weight: 500 !important;
    letter-spacing: 0.06em;
    color: var(--plenum-charcoal);
}

/* Card body sits on the page ground rather than a white plate. */
.group .bg-white {
    background-color: transparent;
}

/* ---------------------------------------------------------------- buttons */

/*
 * One button language across the storefront: a hairline gold outline that fills on
 * hover. `secondary-button` is the add-to-cart on product cards; `primary-button` is
 * the filled call to action on product and checkout pages.
 *
 * The card's add-to-cart is the one exception -- see .plenum-card-button, which repaints
 * it as the filled half. The outline is right for a button sitting in a page beside
 * other content, but inside a card it was the palest element in the frame.
 */
.secondary-button {
    display: inline-block;
    border: 1px solid var(--plenum-gold) !important;
    border-radius: 0 !important;
    background-color: transparent !important;
    color: var(--plenum-gold-deep) !important;
    font-family: var(--plenum-label);
    font-size: 12px !important;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    transition: background-color 250ms ease, color 250ms ease;
}

.secondary-button:hover {
    background-color: var(--plenum-gold) !important;
    color: var(--plenum-on-gold) !important;
}

.primary-button {
    border: 1px solid var(--plenum-gold) !important;
    border-radius: 0 !important;
    background-color: var(--plenum-gold) !important;
    color: var(--plenum-on-gold) !important;
    font-family: var(--plenum-label);
    font-size: 12px !important;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    transition: background-color 250ms ease, border-color 250ms ease;
}

.primary-button:hover {
    background-color: var(--plenum-gold-deep) !important;
    border-color: var(--plenum-gold-deep) !important;
}

/* The stock theme's navy chips and badges. */
.bg-navyBlue {
    background-color: var(--plenum-gold) !important;
}

/*
 * They ship with `text-white`, which was fine on navy and is 2.15:1 on the gold they
 * are now painted. The label darkens with the ground.
 */
.bg-navyBlue,
.bg-navyBlue .text-white {
    color: var(--plenum-on-gold) !important;
}

/* ------------------------------------------------------------ whitespace */

/*
 * The register lives on air. Bagisto's carousels are spaced for a dense catalogue, so
 * the rhythm between storefront sections is opened up.
 */
main .container {
    margin-top: 92px;
}

@media (max-width: 768px) {
    main .container {
        margin-top: 48px;
    }
}

/*
 * The gutter either side of a page's content.
 *
 * Tailwind's container is configured with a flat 90px of side padding and no responsive
 * override -- the only media query on it sets a max-width at 1440px. 90px is right on a
 * desktop and impossible on a phone: 180px of the screen is gutter, so a 360px handset
 * had 180px left to read in, and a table or a long product name simply overflowed. Every
 * page built on `.container` inherited that -- category, cart, checkout, CMS, the tabs on
 * the product page.
 *
 * It cannot be fixed where it comes from. The container's padding is set in
 * tailwind.config.js and baked into the committed bundle, and this project has no npm
 * build to regenerate it, so the correction is made here instead. This file loads after
 * that bundle, so equal specificity resolves in its favour.
 *
 * The steps follow the ones the theme already uses elsewhere -- the header drops to 32px
 * at 1180px, the collections grid to 30px at 1024px and 15px below 520px -- so the page
 * gutter now moves with the furniture around it instead of staying at 90px while
 * everything else narrows.
 *
 * A handful of templates carry their own `max-1180:px-5` beside `.container`, and being
 * later, these win over it. That is intentional: those utilities were each a local patch
 * for this same missing breakpoint, and 40px is close enough to the 20px they asked for
 * that the pages they were protecting are unaffected. Anything that genuinely needs a
 * different gutter still has `!p-0`, which is important and still wins.
 */
@media (max-width: 1180px) {
    .container {
        padding-left: 40px;
        padding-right: 40px;
    }
}

@media (max-width: 1024px) {
    .container {
        padding-left: 30px;
        padding-right: 30px;
    }
}

@media (max-width: 768px) {
    .container {
        padding-left: 20px;
        padding-right: 20px;
    }
}

@media (max-width: 525px) {
    .container {
        padding-left: 16px;
        padding-right: 16px;
    }
}

/* ---------------------------------------------------------------- footer */

footer.bg-lightOrange {
    background-color: var(--plenum-cream);
    border-top: 1px solid var(--plenum-hairline);
}

/*
 * Everything in the footer is centred. The stock layout is `justify-between` with the
 * link columns on one side and the newsletter on the other; the newsletter has since
 * moved out to its own band, which left the links stranded against one edge with the
 * whole other half empty.
 */
footer.bg-lightOrange > div:first-child {
    justify-content: center;
}

/*
 * Named rather than positional. This used to read `> div:first-child`, which meant the
 * links -- until the brand column became the row's first child and quietly inherited
 * the rule while the links lost it. The class says which element is meant.
 */
footer.bg-lightOrange .plenum-footer-links {
    justify-content: center;
}

footer.bg-lightOrange ul {
    justify-items: center;
    text-align: center;
}

/*
 * The gap between the two link columns, as one continuous value instead of a step.
 *
 * The markup asks for 96px, then 24px below 1180px. Nothing about eleven short links
 * needs a quarter of that space at 1179px and four times as much at 1181px, and because
 * the columns are centred the jump moves both of them at once -- drag the window across
 * that pixel and the footer visibly reshuffles, which is what made it look like a
 * different design at different sizes.
 *
 * It now scales with the window between those same two endpoints, so the footer holds
 * its shape at every width and the change is never a jump. The stock utilities stay on
 * the element as the no-CSS fallback; this file loads after the Tailwind bundle, so
 * equal specificity resolves in its favour.
 */
.plenum-footer-links {
    gap: clamp(24px, 7vw, 96px);
}

/* ---------------------------------------------- footer columns / accordions */

/*
 * Two open lists on a wide screen; two accordions under 768px.
 *
 * One markup serves both. The heading is a real <button> at every width and the list is
 * always in the DOM -- above the breakpoint the button is taken out of the flow and the
 * list is forced open, below it the button shows and `aria-expanded` drives the list.
 * Nothing is duplicated and nothing is hidden from a screen reader, which is what the
 * separate phones-only accordion used to cost.
 */
.plenum-footer-col {
    min-width: 0;
}

/*
 * Above 768px the heading is not a control. `display: none` rather than opacity or
 * visibility: it must leave the layout entirely, or the centred columns sit lower than
 * the rest of the footer by the height of a button nobody can see.
 */
.plenum-footer-col-toggle {
    display: none;
}

/* The list is open above the breakpoint whatever aria-expanded says. */
.plenum-footer-col-list {
    display: grid;
}

@media (max-width: 767.98px) {
    /*
     * Full width, one column under the other. The row above wraps for the brand column
     * already; these take the whole line so a heading and its list are never beside a
     * second heading too narrow to read.
     */
    .plenum-footer-col {
        flex: 1 1 100%;
        width: 100%;
        border-top: 1px solid var(--plenum-hairline);
    }

    .plenum-footer-col:last-child {
        border-bottom: 1px solid var(--plenum-hairline);
    }

    .plenum-footer-col-toggle {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        width: 100%;
        padding: 14px 2px;
        background: none;
        border: 0;
        cursor: pointer;

        font-family: var(--plenum-label);
        font-size: 12px;
        font-weight: var(--plenum-label-weight);
        letter-spacing: var(--plenum-track);
        text-transform: uppercase;
        color: var(--plenum-ink);
        text-align: left;
    }

    .plenum-footer-col-toggle:focus-visible {
        outline: 2px solid var(--plenum-gold-contrast);
        outline-offset: -2px;
    }

    .plenum-footer-col-chevron {
        flex: none;
        width: 18px;
        height: 18px;
        transition: transform 200ms ease;
    }

    .plenum-footer-col-toggle[aria-expanded="true"] .plenum-footer-col-chevron {
        transform: rotate(180deg);
    }

    /*
     * Collapsed by default, and `display: none` rather than max-height:0 -- a height
     * animation on a list whose length is unknown either clips the longer column or
     * animates to a height it never reaches. The chevron carries the motion instead.
     *
     * Only lists the markup marked collapsible are hidden: a section with no heading
     * has no button to reopen it, so it stays open rather than becoming unreachable.
     */
    .plenum-footer-col-list[data-collapsible] {
        display: none;
        padding: 0 2px 16px;
    }

    .plenum-footer-col-toggle[aria-expanded="true"] + .plenum-footer-col-list {
        display: grid;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-footer-col-chevron {
        transition: none;
    }
}

/*
 * The brand column.
 *
 * Left-aligned inside a footer whose every other column is centred, on purpose: it is
 * a paragraph of running text, and centred prose over three or four lines makes a
 * ragged left edge the eye has to find again on every line. The column itself still
 * travels with the centred group rather than being pinned to the page edge, so the
 * footer keeps its symmetry.
 *
 * 300px is about 60 characters at this size -- inside the comfortable measure, and
 * narrow enough that the links keep the rest of the row.
 */
.plenum-footer-brand {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 12px;
    flex: 0 1 300px;
    max-width: 300px;
    text-align: left;
}

/*
 * The cap the mark is drawn at. `height: auto` against the stated width/height keeps
 * the aspect ratio when the cap is what binds, rather than squashing it to 160x64.
 */
.plenum-footer-brand-logo {
    display: block;
    max-width: 160px;
}

.plenum-footer-brand-logo img {
    display: block;
    width: 100%;
    max-width: 160px;
    height: auto;
}

.plenum-footer-brand-logo:focus-visible {
    outline: 2px solid var(--plenum-gold-contrast);
    outline-offset: 3px;
}

/* The claim. Body ink at 12:1, a step up in size from the description under it. */
.plenum-footer-brand-tagline {
    margin: 0;
    font-size: 13px;
    line-height: 1.45;
    color: var(--plenum-ink);
}

/* Supporting copy: secondary ink at 6.18:1, both past AA on the cream. */
.plenum-footer-brand-description {
    margin: 0;
    font-size: 12.5px;
    line-height: 1.6;
    color: var(--plenum-muted);
}

/*
 * On phones the links collapse into an accordion, and the brand column takes the full
 * width above it.
 *
 * The row has to be told it may wrap for that to happen. It is a bare `flex` with a
 * `gap-y-8` that implies wrapping was once intended but never enabled, so a basis of
 * 100% was simply compressed instead of pushed onto its own line -- the column came
 * out 168px wide at 390px, with the mark nearly filling it and the text in a gutter.
 */
@media (max-width: 640px) {
    footer.bg-lightOrange > div:first-child {
        flex-wrap: wrap;
    }

    .plenum-footer-brand {
        flex: 1 1 100%;
        max-width: none;
    }
}

/* Column headings: small uppercase sans, matching the nav. */
footer.bg-lightOrange h3,
footer.bg-lightOrange .font-medium {
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-ink);
}

footer.bg-lightOrange ul li a {
    color: var(--plenum-charcoal);
    font-size: 13px;
    letter-spacing: 0.02em;
    transition: color 200ms ease;
}

footer.bg-lightOrange ul li a:hover {
    color: var(--plenum-gold-deep);
}

/* Newsletter heading -- the built CSS paints this navy */
footer.bg-lightOrange .text-navyBlue {
    color: var(--plenum-ink) !important;
    font-family: var(--plenum-display-face);
    font-weight: var(--plenum-display-weight);
    font-style: normal;
}

footer.bg-lightOrange input[type="email"] {
    background-color: #fff;
    border-color: var(--plenum-hairline);
    border-radius: 0;
}

footer.bg-lightOrange input[type="email"]:focus {
    border-color: var(--plenum-gold);
    outline: none;
}

footer.bg-lightOrange button[type="submit"] {
    background-color: var(--plenum-gold);
    border-radius: 0;
    color: var(--plenum-on-gold);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    font-size: 12px;
    transition: background-color 200ms ease;
}

footer.bg-lightOrange button[type="submit"]:hover {
    background-color: var(--plenum-gold-deep);
}

/*
 * Bottom strip: payment methods, then the copyright under them, both centred on the
 * page. They started as left/right columns split by a hairline; centring them stacks the
 * two so the strip reads down the middle like the rest of the footer.
 */
footer.bg-lightOrange .bg-\[\#F1EADF\] {
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 20px;
    padding-block: 30px;
    background-color: var(--plenum-sand);
    border-top: 1px solid var(--plenum-hairline);
}

.plenum-payments {
    text-align: center;
}

.plenum-payments-title,
footer.bg-lightOrange .bg-\[\#F1EADF\] > p {
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-ink);
}

/* Copyright, centred under the badge row. */
footer.bg-lightOrange .bg-\[\#F1EADF\] > p {
    text-transform: none;
    letter-spacing: 0.04em;
    color: var(--plenum-ink);
    text-align: center;
}

/*
 * Wraps, centred, rather than scrolling sideways.
 *
 * It used to be one row that never wrapped, scrolling horizontally when it ran out of
 * width. That held while there were six badges; there are nine now, and a sideways
 * scroll in a footer is a poor place to hide half of them -- there is no scrollbar
 * (it is suppressed) and nothing on screen to say the row continues, so on a phone the
 * last rails simply looked absent. Wrapping shows all of them at every width.
 */
.plenum-payments-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 8px;
    margin-top: 14px;
    padding: 0;
    list-style: none;
}

/*
 * Every badge is a white tile of the same size, so the row reads as one set rather
 * than as marks of assorted proportions.
 *
 * The tiles are equal because the marks are: every file in assets/payments is drawn on
 * a 64x24 viewBox, so at a fixed height of 24px each is 64px wide and the tile around
 * it comes out identical without the CSS sizing each one. A mark with different
 * proportions still fits -- max-width keeps it inside the tile -- it just will not fill
 * it, which is the trade for not hard-coding a width per badge.
 */
.plenum-payment {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 86px;
    height: 40px;
    padding: 0 10px;
    border: 1px solid var(--plenum-hairline);
    background-color: #fff;
    color: var(--plenum-ink);
}

/* 24px tall, natural width, never wider than the tile it sits in. */
.plenum-payment img {
    height: 24px;
    width: auto;
    max-width: 100%;
    display: block;
}

/* Only reached when a badge has no file: the label, set to fill the same tile. */
.plenum-payment-text {
    font-family: var(--plenum-sans);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    white-space: nowrap;
}

/*
 * Tighter below the desktop width. The tiles still wrap rather than shrink to nothing:
 * the mark has to stay legible, and nine badges reading clearly over two lines beats
 * nine unreadable ones on one.
 */
@media (max-width: 1180px) {
    .plenum-payments-list {
        gap: 6px;
    }

    .plenum-payment {
        min-width: 76px;
        height: 36px;
        padding: 0 8px;
    }

    .plenum-payment img {
        height: 21px;
    }
}

@media (max-width: 768px) {
    footer.bg-lightOrange .bg-\[\#F1EADF\] {
        gap: 16px;
        padding-block: 24px;
    }

    .plenum-payments-list {
        margin-top: 12px;
    }
}

/* Mobile accordion */
footer.bg-lightOrange .border-\[\#e9decc\] {
    border-color: var(--plenum-hairline) !important;
}

/* ------------------------------------------------ save & share on images */

/*
 * Save and share rest in the bottom corner of the product image, matching the
 * reference card. Bagisto only reveals its own icons on hover, which puts them out
 * of reach on touch devices; these are always visible.
 *
 * The parent (.relative on the image wrapper) provides the positioning context.
 */
.plenum-image-actions {
    position: absolute;
    bottom: 14px;
    display: flex;
    align-items: center;
    gap: 12px;
    z-index: 2;
}

/* Corner follows text direction, so RTL mirrors correctly. */
[dir="rtl"] .plenum-image-actions { left: 16px; }
[dir="ltr"] .plenum-image-actions,
.plenum-image-actions { right: 16px; }
[dir="rtl"] .plenum-image-actions { right: auto; }

/* Bare glyphs, no chrome -- the reference card carries no button around its heart. */
.plenum-image-action {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--plenum-ink);
    font-size: 20px;
    line-height: 1;
    cursor: pointer;
    background: none;
    border: 0;
    transition: color 200ms ease, transform 200ms ease;
}

.plenum-image-action:hover {
    color: var(--plenum-gold-deep);
    transform: scale(1.12);
}

.plenum-image-action:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 3px;
}

/* Saved: the heart fills gold so the state reads at a glance. */
.plenum-image-action.is-active {
    color: var(--plenum-gold);
}

@media (max-width: 640px) {
    .plenum-image-actions {
        bottom: 10px;
        right: 10px;
        gap: 10px;
    }

    .plenum-image-action {
        font-size: 17px;
    }
}

/* --------------------------------------------------------- image badges */

/*
 * "Sale" and "New" sit square in the top corner as a dark plate with tracked
 * uppercase type, replacing Bagisto's rounded red and navy pills.
 */
.plenum-card-badge,
.group .action-items > p {
    position: absolute;
    top: 0 !important;
    border-radius: 0 !important;
    padding: 6px 12px !important;
    background-color: var(--plenum-ink) !important;
    color: #fff !important;
    font-size: 10px !important;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    line-height: 1.4;
    z-index: 2;
}

[dir="ltr"] .plenum-card-badge,
[dir="ltr"] .group .action-items > p,
.plenum-card-badge,
.group .action-items > p { left: 0 !important; }

[dir="rtl"] .plenum-card-badge { left: auto !important; right: 0 !important; }

/*
 * The saving is the one badge worth a second colour. It is the only one of the three
 * that changes what the visitor pays, and in the site's gold it reads as an offer where
 * the charcoal plate reads as a label.
 *
 * Bestseller and New keep the charcoal above, so a row of cards has at most one coloured
 * mark in it.
 */
.plenum-card-badge.is-save {
    background-color: var(--plenum-gold-deep) !important;
    color: var(--plenum-ink) !important;
    font-weight: 700;
}

/* ------------------------------------------------------ header search */

/*
 * The search sits open, wide enough to read, and the placeholder rotates a product term
 * through it -- see plenum.search-typewriter. An invitation to search only works if it
 * can be seen, and the field was previously a 92px pill whose placeholder was painted
 * transparent until focus: the rotation would have run its whole cycle behind an icon.
 *
 * It was that pill because a wide input crowded out the division links "once there are
 * more than a few". There are four now, not seven, and four leave room -- but only on a
 * wide desktop. Below 1280 the pill comes back, placeholder and all, which is why the
 * transparent-placeholder rule still exists further down instead of being deleted.
 *
 * The icons are positioned against the wrapper div rather than the form, so the wrapper
 * is what has to stop filling the row.
 */
header div:has(> form[role="search"]) {
    position: relative;
    width: auto;
    flex: 0 0 auto;
    margin-inline-start: auto;
}

header form[role="search"] {
    width: 300px;
    max-width: none;
    border-radius: 999px;
    background-color: var(--plenum-sand);
    transition: width 280ms ease, background-color 280ms ease;
}

header form[role="search"]:focus-within {
    width: 340px;
    background-color: #fff;
    box-shadow: inset 0 0 0 1px var(--plenum-hairline);
}

header form[role="search"] input[type="text"] {
    width: 100%;
    padding: 11px 42px !important;
    border: 0 !important;
    border-radius: 999px !important;
    background-color: transparent !important;
    font-size: 12px;
    letter-spacing: 0.04em;
    color: var(--plenum-ink);
}

header form[role="search"] input[type="text"]:focus {
    outline: none;
}

header form[role="search"] input[type="text"]::placeholder {
    color: var(--plenum-muted);
}

/*
 * Narrower desktops keep the old behaviour: the field collapses back to the pill and the
 * placeholder is painted out until focus, because below this width an open search and
 * four division links cannot both have the room.
 *
 * The typed placeholder is therefore a wide-desktop flourish. Between 1024 and 1279 it
 * runs unseen behind the pill, which costs a timer and nothing else; below 1024 the
 * mobile header takes over and its field is full width, so it shows there again.
 */
@media (max-width: 1279px) {
    header form[role="search"] {
        width: 92px;
    }

    header form[role="search"]:focus-within {
        width: 300px;
    }

    header form[role="search"] input[type="text"]::placeholder {
        color: transparent;
    }

    header form[role="search"]:focus-within input[type="text"]::placeholder {
        color: var(--plenum-muted);
    }
}

header .icon-search,
header .icon-camera {
    color: var(--plenum-charcoal);
    font-size: 17px !important;
}

/* ---------------------------------------------------------- division nav */

/*
 * Seven divisions have to share the row, so the links wrap to two lines rather than
 * pushing the search and account icons off the end.
 */
header nav a,
header .grid a[href],
.plenum-mega-item a {
    line-height: 1.3;
}

/*
 * The mega-menu bar is a div, not a <nav>, so the division nav rules above it never
 * reached these links: they fell back to the stock theme's light grey and read as
 * disabled against the ivory ground. Set on the element that actually renders.
 */
.plenum-mega-item a {
    color: var(--plenum-ink);
}

.plenum-mega-item a:hover {
    color: var(--plenum-gold-deep);
}

/* ------------------------------------------------- section headings */

/*
 * Section headers follow the reference: the title sits at the start of the row in
 * tracked uppercase, with VIEW ALL and the carousel arrows closing the line. The stock
 * theme centres nothing and hides VIEW ALL on desktop; the earlier centred treatment
 * here has been replaced because it does not match the reference layout.
 */
.container > .flex:has(> h2) {
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
    padding-bottom: 14px;
    border-bottom: 1px solid var(--plenum-hairline);
    margin-bottom: 34px;
}

.container > .flex > h2 {
    font-family: var(--plenum-display-face);
    font-size: 34px;
    font-weight: var(--plenum-display-weight);
    line-height: 1.2;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--plenum-ink);
}

/* VIEW ALL: small, tracked, gold, sitting on the same baseline as the title. */
.plenum-view-all p {
    font-size: 11px !important;
    font-weight: 500;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--plenum-gold-deep);
    white-space: nowrap;
}

.plenum-view-all .icon-arrow-right {
    font-size: 14px !important;
    vertical-align: middle;
}

.plenum-view-all:hover p {
    color: var(--plenum-ink);
}

@media (max-width: 768px) {
    .container > .flex > h2 {
        font-size: 23px;
        letter-spacing: 0.06em;
    }
}

/* ------------------------------------------------------- product card */

/*
 * The grid card, built to the Plenum reference: a padded cream panel holding the
 * packshot whole, then name / concern / pack size / price / rating stacked beneath it,
 * closed by a permanent full-width Add to Bag.
 *
 * The stock card cropped the shot edge to edge and parked its call to action below an
 * overflow-hidden fold, sliding it up on hover -- unreachable on touch. Markup is in
 * packages/Webkul/Shop/.../components/products/card.blade.php.
 */
/*
 * Stretched to its row, so every card beside it is the same height. Which matters more
 * than it did: the rating row is drawn only for a product that has reviews, so within
 * one row some cards carry it and some do not, and their add-to-cart buttons would
 * otherwise sit at different heights.
 *
 * `align-self: stretch` rather than `height: 100%`. Both containers -- the carousel's
 * flex track and the listing grid -- stretch their children already, and stating a
 * percentage height instead *undoes* that: it is an explicit cross size, and a
 * percentage resolved against a parent whose own height is auto falls back to the
 * content's. The carousel measured 483px and 459px side by side until this changed.
 */
.plenum-card {
    display: flex;
    flex-direction: column;
    align-self: stretch;
    background-color: #fff;
}

/* Square panel, so a row of cards lines up whatever the packshot proportions. */
.plenum-card-media {
    position: relative;
    aspect-ratio: 1 / 1;
    background-color: var(--plenum-cream);
    overflow: hidden;
}

.plenum-card-media-link {
    display: block;
    height: 100%;
    padding: 26px 26px 46px;
}

/*
 * `contain` rather than `cover`: these are packshots, and a bottle cropped at the cap
 * is worse than one sitting small in its panel.
 */
.plenum-card-image {
    width: 100%;
    height: 100%;
    object-fit: contain;
    object-position: center;
    transition: transform 400ms ease;
}

.plenum-card:hover .plenum-card-image {
    transform: scale(1.04);
}

/*
 * Inset on all four sides, not just the top. The media panel above is a full-bleed
 * square, so text running to the card's own edge read as a layout fault rather than a
 * choice -- particularly the add-to-cart, which sat flush against the left rule.
 */
.plenum-card-body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 7px;
    padding: 18px 18px 22px;
    /* Takes the height the media panel leaves, so the button below can be pushed to it. */
    flex: 1 1 auto;
}

/*
 * The product name is the line the eye should land on first, so it carries the card's
 * heaviest weight. At 500 it sat under the body's own 600 and read as a caption for the
 * image above it rather than as the product.
 *
 * The colour is stated again as a literal rather than through --plenum-ink. It is the
 * same value, but the name is the one place on the card where an anchor is the styled
 * element, and a link that falls back to the UA's blue is a conspicuous failure; a
 * literal cannot be undone by a missing custom property.
 */
.plenum-card-name {
    font-family: var(--plenum-sans);
    font-size: 14px;
    font-weight: 700;
    line-height: 1.45;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #2f2f2f;
    /* Two lines, so an eight-word product name cannot push the price out of line. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.plenum-card-name:hover {
    color: var(--plenum-gold-deep);
}

.plenum-card-concern {
    font-size: 12px;
    line-height: 1.4;
    color: var(--plenum-charcoal);
}

.plenum-card-pack {
    font-size: 12px;
    line-height: 1.4;
    color: var(--plenum-muted);
}

.plenum-card-price {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-top: 3px;
    font-size: 15px;
    font-weight: 600;
    color: var(--plenum-ink);
}

/*
 * The compact rating: one gold star, the average, the count. "4.6 (787)".
 *
 * A single figure rather than five stars to decode -- and five stars only ever carried
 * a rounded value anyway, so this says more in less room. It sits under the title and
 * above the price, where the eye is already travelling.
 *
 * `nowrap` because the row is three short pieces that mean nothing apart: a count that
 * drops to its own line at 360px reads as a stray number under the name.
 */
.plenum-card-rating {
    display: flex;
    align-items: center;
    /* 2px between the stars; the count gets its own 4px from the rule below. */
    gap: 2px;
    margin-top: 1px;
    white-space: nowrap;
}

.plenum-card-rating .plenum-card-rating-count {
    margin-left: 2px;
}

/*
 * The assurance line, standing in for the rating on a product nobody has reviewed yet.
 *
 * Sized and spaced to occupy the same line the stars would, so a row of cards holds one
 * height whichever of the two it is showing. Quieter than the product name above it --
 * it is a reassurance, not the thing being sold -- and in the label face with the wide
 * tracking the rest of the site uses for small caps.
 */
.plenum-card-assurance {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-top: 2px;
    font-family: var(--plenum-label);
    font-size: 9.5px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.12em;
    line-height: 1;
    text-transform: uppercase;
    color: var(--plenum-muted);
    white-space: nowrap;
}

/*
 * The tick carries the colour, the words stay quiet. Gold here rather than black: this
 * is the one place on the card where the mark is a claim about quality, which is what
 * the gold is for everywhere else on the site -- and it keeps the line from reading as
 * a second, greyer rating.
 */
.plenum-card-assurance-tick {
    flex: 0 0 auto;
    width: 11px;
    height: 11px;
    color: var(--plenum-gold-deep);
}

/*
 * A star: the grey one underneath, the black one clipped over it.
 *
 * The half is a clip, not a gradient or a clipPath element, because either of those has
 * to be referenced by `id` -- and this star renders five times a card across dozens of
 * cards from a cloned Vue template, so the ids would collide and every half star after
 * the first would take the first one's fill. `clip-path: inset()` needs no id at all.
 *
 * Both layers are absolutely placed in a box the span sizes, so they cannot drift apart.
 */
.plenum-star {
    position: relative;
    flex: 0 0 auto;
    display: inline-block;
    width: var(--plenum-star-size);
    height: var(--plenum-star-size);
    line-height: 0;
}

.plenum-star-base,
.plenum-star-fill {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
}

.plenum-star-base {
    fill: var(--plenum-star-empty);
}

/*
 * Clipped away entirely by default, so an unrated star is the grey one alone. The two
 * modifiers below open it to half and to full.
 */
.plenum-star-fill {
    fill: var(--plenum-star-filled);
    clip-path: inset(0 100% 0 0);
}

.plenum-star.is-half .plenum-star-fill {
    clip-path: inset(0 50% 0 0);
}

.plenum-star.is-on .plenum-star-fill {
    clip-path: inset(0 0 0 0);
}

[dir="rtl"] .plenum-star-fill {
    clip-path: inset(0 0 0 100%);
}

[dir="rtl"] .plenum-star.is-half .plenum-star-fill {
    clip-path: inset(0 0 0 50%);
}

[dir="rtl"] .plenum-star.is-on .plenum-star-fill {
    clip-path: inset(0 0 0 0);
}

/*
 * The icon-font stars that stay: the review form's input, and the score on each posted
 * review. Those are whole stars only -- you cannot leave half a review -- so they have
 * no need of the two-layer SVG, and they keep the package's glyph and take the palette
 * instead. They were amber; nothing in a rating is gold any more.
 */
.plenum-star-glyph-on {
    color: var(--plenum-star-filled);
}

.plenum-star-glyph-off {
    color: var(--plenum-star-empty);
}

/* Quieter than the stars, which are the thing being read. */
.plenum-card-rating-count {
    font-family: var(--plenum-sans);
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1;
    color: var(--plenum-star-count);
}

/*
 * The same rating beside the product page's title, where it is a control: clicking it
 * takes you to the reviews. A <button> rather than a div with a click handler, so it is
 * reachable by keyboard and announced as something that does something -- the stock
 * markup was a div with role="button" and a tabindex, which is the same thing rebuilt by
 * hand and missing the Enter key.
 */
.plenum-pdp-rating {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    margin-top: 8px;
    padding: 0;
    border: 0;
    background: none;
    white-space: nowrap;
    cursor: pointer;
    /* Larger here than on the card, where the row is one line among five. */
    --plenum-star-size: 18px;
}

.plenum-pdp-rating .plenum-card-rating-count {
    margin-left: 4px;
    font-size: 13px;
}

.plenum-pdp-rating:hover .plenum-card-rating-count {
    color: var(--plenum-star-filled);
}

.plenum-pdp-rating:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 3px;
}

/*
 * Buying is the card's whole purpose, so its button is the filled half of the language
 * rather than the outlined half the markup asks for. As a `secondary-button` it was a
 * hairline outline with gold text on cream -- the palest thing in the frame, and close
 * enough to the disabled state that it read as one.
 *
 * These rules sit after .secondary-button and repeat its !important because the stock
 * theme's own utilities are !important; without it the outline would win regardless of
 * order.
 */
/*
 * Pinned to the foot of the card. `auto` absorbs whatever height the card was given
 * beyond its content, so the buttons in a row line up across cards with two-line names
 * and one, with a rating row and without.
 *
 * It replaces a fixed 11px. Where a card has spare height the gap is now larger than
 * that and the button sits on the bottom edge; on the single tallest card in a row --
 * the one setting the height, with no spare -- the gap falls back to the body's own 7px.
 */
.plenum-card-button {
    width: 100%;
    margin-top: auto;
    padding: 14px 10px;
    border: 1px solid var(--plenum-gold) !important;
    background-color: var(--plenum-gold) !important;
    color: var(--plenum-on-gold) !important;
    font-weight: 700 !important;
    text-align: center;
    cursor: pointer;
    transition: background-color 220ms ease, border-color 220ms ease,
        transform 220ms ease, box-shadow 220ms ease;
}

/*
 * A card is a flat surface, so the lift is what tells you the control is live -- a
 * colour shift alone is easy to miss on a button that is already filled. Guarded on
 * :not(:disabled) so a sold-out card does not animate under the cursor.
 */
.plenum-card-button:hover:not(:disabled) {
    background-color: var(--plenum-gold-deep) !important;
    border-color: var(--plenum-gold-deep) !important;
    transform: translateY(-1px);
    box-shadow: 0 6px 16px rgba(47, 47, 47, 0.16);
}

/* Press puts it back down, so the click has somewhere to land. */
.plenum-card-button:active:not(:disabled) {
    transform: translateY(0);
    box-shadow: none;
}

.plenum-card-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* Compare is a secondary action, so it reads as a small link rather than a second button. */
.plenum-card-compare {
    margin-top: 4px;
    font-family: var(--plenum-label);
    font-size: 10px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-muted);
    cursor: pointer;
}

.plenum-card-compare:hover {
    color: var(--plenum-ink);
}

@media (max-width: 640px) {
    .plenum-card-media-link {
        padding: 16px 16px 34px;
    }

    /* Tighter inset on narrow screens, where 18px each side eats the name's line length. */
    .plenum-card-body {
        padding: 14px 12px 16px;
    }

    /* One step down from the desktop 14px, as it was one step down from 13px before. */
    .plenum-card-name {
        font-size: 13px;
    }

    .plenum-card-button {
        padding: 11px 8px;
    }
}

/* ------------------------------------------------- packshots elsewhere */

/*
 * The same treatment on every other surface a packshot appears: the list card, the
 * product page gallery, and the cart / wishlist thumbnails. The stock theme pins these
 * to fixed boxes with no object-fit, so a tall bottle in a square thumbnail comes out
 * squashed, and the gallery crops the shot to its frame.
 *
 * `contain` on a cream ground shows the whole product at every size.
 */
.plenum-product-thumb {
    object-fit: contain;
    background-color: var(--plenum-cream);
    padding: 6px;
}

/* Product page, main image. */
.plenum-pdp-media {
    background-color: var(--plenum-cream);
    overflow: hidden;
}

.plenum-pdp-image {
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: contain;
    object-position: center;
    padding: 40px 40px 56px;
    border-radius: 0;
}

/* Product page, the filmstrip beside it. */
.plenum-pdp-thumb {
    aspect-ratio: 1 / 1;
    object-fit: contain;
    background-color: var(--plenum-cream);
    padding: 6px;
    border-radius: 0 !important;
}

/* Product page on phones, where the gallery is a full-width swiper. */
.plenum-pdp-slide-image {
    object-fit: contain;
    background-color: var(--plenum-cream);
    padding: 24px 24px 40px;
}

/*
 * The same swiper between phone and desktop.
 *
 * The stock slide is `w-screen` -- one viewport wide -- which is right on the phone it
 * was written for and wrong on everything else. The desktop gallery hides below 1180px,
 * so tablets and small laptops fall to this swiper, and `w-screen` against the image's
 * `aspect-square` drew a 1024x1024 packshot on a 1024px window: taller than the screen,
 * cropped at the fold, with the price and the add-to-cart pushed a screen and a half
 * down the page.
 *
 * Capped at the 560px the desktop gallery uses, so the packshot is the same size either
 * side of the breakpoint, and centred in whatever space is left.
 *
 * `100vw` counts the scrollbar, so it is not used for the frame -- that is what put a
 * horizontal scrollbar on the page. It stays on the slide only as the value before the
 * component measures the frame and sets a real width inline; the slider translates by
 * exactly one slide per swipe, so the two have to agree.
 */
.plenum-pdp-gallery {
    width: 100%;
    max-width: 560px;
    margin-inline: auto;
}

.plenum-pdp-slide {
    width: min(100vw, 560px);
}

/*
 * Packshot beside the details, held down to 1024px instead of 1180px.
 *
 * The desktop gallery is built from fixed widths -- a 100px filmstrip, a 32px gap and a
 * 560px image -- and sits next to a 590px details column, so the row demanded 1318px
 * before the page gutter. That is the only reason it was hidden below 1180px, and why
 * every laptop under that width dropped to the stacked phone swiper with the price a
 * scroll below the image.
 *
 * Below 1180 both columns become proportional instead: the gallery gives up its fixed
 * width and the pair share the row. `min-width: 0` on each is what actually allows the
 * shrink -- a flex item defaults to min-width:auto and will not go below its content,
 * which here is a 560px image.
 *
 * It stops at 1024 rather than going further. Under that the two columns leave the
 * details around 280px, and the quantity stepper and the add-to-cart button sit on one
 * row that cannot hold at that width -- so the layout stacks there, and the swiper
 * (capped above) takes over.
 */
@media (max-width: 1180px) {
    .plenum-pdp-figure,
    .plenum-pdp-details {
        flex: 1 1 0;
        min-width: 0;
    }

    .plenum-pdp-figure .plenum-pdp-media,
    .plenum-pdp-figure .plenum-pdp-media-shimmer {
        width: auto;
        min-width: 0;
        max-width: none;
        flex: 1 1 0;
    }

    /*
     * The shimmer is a 560x607 block. Left alone it would stand a head taller than the
     * image that replaces it and drop the page by the difference on every load.
     */
    .plenum-pdp-figure .plenum-pdp-media-shimmer > div {
        min-width: 0;
        min-height: 0;
        aspect-ratio: 1 / 1;
    }
}

/*
 * Stacked. The row's own gutter is dropped so the swiper runs edge to edge as before,
 * and the details column carries its own, since it is no longer sharing the container's.
 */
@media (max-width: 1024px) {
    .plenum-pdp-container {
        padding-left: 0;
        padding-right: 0;
    }

    .plenum-pdp-details {
        flex: 1 1 100%;
        width: 100%;
        max-width: 100%;
        padding-left: 20px;
        padding-right: 20px;
    }
}

@media (max-width: 525px) {
    .plenum-pdp-details {
        padding-left: 16px;
        padding-right: 16px;
    }
}

/*
 * Category page hero banner. A fixed frame is needed so the shimmer has a size before
 * the image loads, but it covers rather than stretches, so the frame has to match the
 * artwork or cover trims the banner to fit. The artwork is not one ratio -- Ayurveda and
 * Weight Management are 2.74:1, Immunity & Nutrition is 2:1 -- so categories.view.blade
 * measures each file and sends the real ratio through as an inline aspect-ratio.
 *
 * The value below is only the fallback for when that measurement fails.
 */
.plenum-category-banner {
    display: block;
    width: 100%;
    aspect-ratio: 1600 / 584;
    object-fit: cover;
    object-position: center;
    border-radius: 0;
}

/*
 * Category carousel tiles. The stock markup gives the image `w-full` with no height and
 * no object-fit inside a round mask, so any logo that is not already square is stretched
 * into the circle -- the three division logos are 2.74:1 banners. Filling the mask and
 * cropping to it keeps every tile round and undistorted.
 */
.plenum-category-tile > img,
.plenum-category-tile > .shimmer {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/*
 * Five circles to a row; the remaining two are reached by scrolling.
 *
 * The cell is a fraction of the visible track rather than a fixed pixel width, so the
 * count holds at every window size -- five cells plus the four 40px gaps between them,
 * less an 80px allowance that leaves the sixth circle peeking past the edge. That peek
 * is the cue that the row scrolls at all; without it the track looks like a static row
 * again, which is what made the arrows read as dead controls before. A fixed width
 * cannot do this: it gives five circles at one window size and four and a half at the
 * next.
 *
 * The circle fills its cell up to 210px and centres in whatever slack is left, so it
 * never outgrows a narrow cell and never becomes a dinner plate on a wide monitor. On a
 * 1036px track that lands at roughly 159px -- as large as five across can be.
 *
 * These sizes live here rather than on the markup because the project has no npm build
 * -- Tailwind's stylesheet is prebuilt and committed, so a fresh arbitrary class like
 * h-[210px] would never be generated and would silently do nothing. The stock
 * h-[110px] / min-w-[120px] stay on the elements as the no-CSS fallback; this file
 * loads after the Tailwind bundle, so equal specificity resolves in its favour.
 *
 * Scoped to lg and up because gap-10 drops to max-lg:gap-4 below 1024px, which would
 * throw the arithmetic out. Under that width the stock 120px and 80px cells overflow on
 * their own, so the row still scrolls there.
 */
@media (min-width: 1024px) {
    .plenum-category-carousel {
        container-type: inline-size;
    }

    .plenum-category-cell {
        min-width: calc((100% - 240px) / 5);
        max-width: calc((100% - 240px) / 5);
    }

    .plenum-category-tile {
        width: 100%;
        max-width: 210px;
        height: auto;
        aspect-ratio: 1 / 1;
    }

    /*
     * Keep the arrows centred on a circle whose height now moves with the window. The
     * static value is the fallback -- correct at the ~1036px track this was measured on
     * -- and the calc repeats the cell formula in container units, because a percentage
     * in `top` would resolve against the wrapper's height rather than its width. If a
     * browser cannot do either, the declaration is dropped and Tailwind's own top-9
     * still puts them in a sensible place.
     */
    .plenum-category-carousel .icon-arrow-left-stylish,
    .plenum-category-carousel .icon-arrow-right-stylish {
        top: 55px;
        top: calc(min((100cqw - 240px) / 5, 210px) / 2 - 25px);
    }
}

/* ------------------------------------------------- announcement strip */

/*
 * The rotating announcement strip. Markup and behaviour in
 * resources/views/plenum/announcement.blade.php; the messages themselves are
 * config/plenum.php.
 *
 * Sticky above everything, at a fixed height. The height is stated rather than left to
 * the content because the messages are stacked on top of each other -- the tallest one
 * would otherwise decide the height of the bar, and a different one is tallest at
 * different widths.
 */
.plenum-announcement {
    position: sticky;
    top: 0;
    z-index: 70;
    background-color: var(--plenum-ink);
    color: #fff;
}

.plenum-announcement-inner {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    max-width: 1320px;
    height: 38px;
    margin: 0 auto;
    padding: 0 16px;
}

/*
 * The track holds every message in the same square inch. `grid` rather than absolute
 * positioning: each slide is placed in the single cell, so the track is as wide as it
 * needs to be and as tall as the row, and no slide is taken out of flow.
 */
.plenum-announcement-track {
    display: grid;
    flex: 1 1 auto;
    min-width: 0;
    place-items: center;
}

.plenum-announcement-slide {
    display: flex;
    grid-area: 1 / 1;
    align-items: center;
    justify-content: center;
    gap: 10px;
    width: 100%;
    min-width: 0;
    color: #fff;
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-decoration: none;
    opacity: 0;
    visibility: hidden;
    transition: opacity 420ms ease;
}

.plenum-announcement-slide.is-current {
    opacity: 1;
    visibility: visible;
}

/*
 * One line, ever. A message too long for the bar is cut with an ellipsis rather than
 * wrapped -- a second line would change the height of a strip the whole page is sitting
 * under.
 */
.plenum-announcement-text {
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

a.plenum-announcement-slide:hover .plenum-announcement-text {
    color: var(--plenum-gold);
}

/* The short wording is a phone thing; the full one is everywhere else. */
.plenum-announcement-text.is-short {
    display: none;
}

.plenum-announcement-copy {
    flex: 0 0 auto;
    padding: 3px 8px;
    border: 1px solid var(--plenum-gold);
    border-radius: 999px;
    color: var(--plenum-gold);
    font-size: 9.5px;
    letter-spacing: 0.12em;
    cursor: pointer;
    transition: background-color 200ms ease, color 200ms ease;
}

.plenum-announcement-copy:hover,
.plenum-announcement-copy:focus-visible {
    background-color: var(--plenum-gold);
    color: var(--plenum-ink);
}

/*
 * The arrows, the pause control and the dismiss button were all dropped from the strip --
 * it is the message and nothing else now. Their rules went with them rather than being
 * left as dead selectors nobody can match, which is also why nothing here needs to hide
 * them at a breakpoint any more.
 */

/* Keyboard focus has to be visible on a dark ground; the gold ring is the site's own. */
.plenum-announcement-copy:focus-visible,
.plenum-announcement-slide:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 2px;
}

@media (max-width: 768px) {
    .plenum-announcement-inner {
        height: 34px;
        gap: 8px;
        padding: 0 10px;
    }

    .plenum-announcement-slide {
        font-size: 10.5px;
        letter-spacing: 0.06em;
        gap: 6px;
    }

    .plenum-announcement-copy {
        padding: 2px 6px;
        font-size: 9px;
        letter-spacing: 0.08em;
    }

    .plenum-announcement-text.is-long {
        display: none;
    }

    .plenum-announcement-text.is-short {
        display: block;
        overflow: hidden;
        white-space: nowrap;
        text-overflow: ellipsis;
    }
}

/*
 * Reduced motion: no fade between messages. The script has already stopped rotating by
 * this point, so this is only about the slide that is shown arriving without animation.
 */
@media (prefers-reduced-motion: reduce) {
    .plenum-announcement-slide {
        transition: none;
    }
}

/* Duplicate of the header VIEW ALL, which now shows at every width. */
.plenum-view-all-foot {
    display: none !important;
}

/* ------------------------------------------------------ division nav */

/*
 * This is what actually sets the division links' padding -- it carries !important and
 * overrides whatever px-* class the markup names, so changing the class alone does
 * nothing. Written when seven divisions shared the row and the longer names wrapped to
 * two lines ("WEIGHT MANAGEMENT", "IMMUNITY & NUTRITION"), which is why it was 20px a
 * side with room between wrapped lines.
 *
 * Four divisions now, none of them wrapping. 20px a side put 52px between one name and
 * the next once the row's gap was counted, and the menu read as four separate links
 * rather than one. 10px gives 26px between names, which groups them.
 */
header a.inline-block.uppercase {
    padding-inline: 10px !important;
    line-height: 1.6;
    text-align: center;
}

@media (max-width: 1280px) {
    header a.inline-block.uppercase {
        /*
         * Tighter again below 1280, but the size no longer drops with it. This used to
         * fall to 11px so that seven names could fit; four fit at full size, helped by
         * the search collapsing to its pill at this width.
         */
        padding-inline: 9px !important;
    }
}

/* ------------------------------------------------------ category page */

/*
 * The listing container already carries 60px of side padding from the theme, so what it
 * lacked was vertical room: the toolbar sat hard under the header and the grid ran
 * straight into the footer.
 */
main .container > .flex.items-start {
    padding-top: 30px;
    padding-bottom: 70px;
}

/* Toolbar row -- sort, per-page, layout switches. */
main .container select,
main .container .icon-filter-1 {
    font-size: 13px;
    letter-spacing: 0.03em;
}

/* Sidebar filters: space the groups so they read as a list, not a block. */
main .container aside > div,
main .container .filter-group {
    padding-block: 6px;
}

/* Empty category message, so it is not pinned to the top of a tall blank area. */
main .container .flex.items-start .grid {
    row-gap: 34px;
}

@media (max-width: 768px) {
    main .container > .flex.items-start {
        padding-top: 18px;
        padding-bottom: 44px;
    }
}

/* --------------------------------------------------- newsletter band */

/*
 * A full-bleed band between the services strip and the footer. It carries the whole
 * subscribe form now, so the footer's own newsletter column has been removed from
 * components/layouts/footer/index.blade.php -- two subscribe boxes a screen apart read
 * as a bug.
 *
 * Sand against the cream footer separates the two without a rule between them; the
 * footer's own top margin is dropped so they meet flush.
 */
.plenum-newsletter {
    margin-top: 92px;
    padding: 74px 30px 78px;
    background-color: var(--plenum-sand);
    border-top: 1px solid var(--plenum-hairline);
    text-align: center;
}

footer.bg-lightOrange {
    margin-top: 0;
}

.plenum-newsletter-inner {
    max-width: 660px;
    margin: 0 auto;
}

/* Matches the storefront section headings: serif, tracked, uppercase. */
.plenum-newsletter-title {
    font-family: var(--plenum-display-face);
    font-size: 38px;
    font-weight: var(--plenum-display-weight);
    line-height: 1.2;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--plenum-ink);
}

/*
 * Ink rather than muted: the band sits on sand, where #6c6c6c lands at 4.39:1 and
 * misses the 4.5:1 floor for body text. Ink reads at 11.2:1 on the same ground.
 */
.plenum-newsletter-copy {
    max-width: 560px;
    margin: 18px auto 0;
    font-size: 13px;
    line-height: 1.85;
    color: var(--plenum-ink);
}

.plenum-newsletter-form {
    margin-top: 30px;
}

/*
 * The button sits inside the field rather than beside it, so the pair reads as one
 * control at every width. Right padding on the input keeps typed text clear of it.
 */
.plenum-newsletter-field {
    position: relative;
    max-width: 560px;
    margin: 0 auto;
}

.plenum-newsletter-input {
    width: 100%;
    padding: 19px 168px 19px 24px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 0;
    background-color: #fff;
    font-family: var(--plenum-sans);
    font-size: 12px;
    letter-spacing: 0.08em;
    color: var(--plenum-ink);
}

.plenum-newsletter-input::placeholder {
    font-family: var(--plenum-label);
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-charcoal);
}

.plenum-newsletter-input:focus {
    border-color: var(--plenum-gold);
    outline: none;
}

/* The filled half of the theme's button language -- see .primary-button. */
.plenum-newsletter-button {
    position: absolute;
    top: 5px;
    bottom: 5px;
    right: 5px;
    padding: 0 34px;
    border: 1px solid var(--plenum-gold);
    background-color: var(--plenum-gold);
    color: var(--plenum-on-gold);
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color 250ms ease, border-color 250ms ease;
}

.plenum-newsletter-button:hover {
    background-color: var(--plenum-gold-deep);
    border-color: var(--plenum-gold-deep);
}

.plenum-newsletter-consent {
    display: flex;
    align-items: flex-start;
    justify-content: center;
    gap: 10px;
    margin: 18px auto 0;
    max-width: 560px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--plenum-ink);
    text-align: left;
    cursor: pointer;
}

.plenum-newsletter-consent input {
    flex: none;
    width: 14px;
    height: 14px;
    margin-top: 2px;
    accent-color: var(--plenum-gold);
    cursor: pointer;
}

.plenum-newsletter-consent a {
    color: var(--plenum-ink);
    text-decoration: underline;
    text-underline-offset: 3px;
}

/*
 * Gold is the hover colour elsewhere, but it only reaches 2:1 on sand -- hovering
 * would fade the link out. The gold moves to the underline instead, so the hover still
 * reads while the text stays at full contrast.
 */
.plenum-newsletter-consent a:hover {
    text-decoration-color: var(--plenum-gold);
    text-decoration-thickness: 2px;
}

.plenum-newsletter-contact {
    display: inline-flex;
    align-items: center;
    gap: 10px;
    margin-top: 40px;
    font-family: var(--plenum-serif);
    font-size: 26px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--plenum-ink);
    transition: color 200ms ease;
}

.plenum-newsletter-contact:hover {
    color: var(--plenum-gold-deep);
}

.plenum-newsletter-contact-icon {
    width: 21px;
    height: 21px;
}

/* Label kept for screen readers -- the placeholder alone is not an accessible name. */
.plenum-sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

@media (max-width: 768px) {
    .plenum-newsletter {
        margin-top: 48px;
        padding: 44px 20px 48px;
    }

    .plenum-newsletter-title {
        font-size: 26px;
        letter-spacing: 0.06em;
    }

    /* The button drops below the field: 168px of inset leaves no room to type. */
    .plenum-newsletter-input {
        padding: 15px 18px;
    }

    .plenum-newsletter-button {
        position: static;
        width: 100%;
        margin-top: 10px;
        padding: 15px 0;
    }

    .plenum-newsletter-contact {
        margin-top: 30px;
        font-size: 18px;
    }
}

/* ---------------------------------------------------------------------------
   Home promotional strip
   ---------------------------------------------------------------------------
   Moved out of the database (the "Offer Headline" theme section) so that a code
   deploy carries the offer. Markup is in resources/views/plenum/offer-strip.blade.php.

   The superseded section is still present in the theme_customizations table on
   environments that were seeded before the move, and Bagisto will happily render
   it alongside this one. Rather than requiring a database edit on every such
   environment -- the exact manual step this move exists to remove -- the old
   section is retired here, in the deployed stylesheet. It can be deleted from the
   table whenever convenient; until then it is simply not drawn.

   Scoped to the class the database section carries, which is why this strip uses
   plenum-promo-* names and not the plenum-offer-* ones it replaces. */
.plenum-offer-band {
    display: none !important;
}

/* ---------------------------------------------------------------------------
   The combo offer strip

   Type, not artwork, and several offers rather than one. The history is in
   plenum.offer-strip; the short version is that the bitmap stopped reflowing by
   becoming unreadable -- about 6px of qualifier at 768px -- and five offers cannot
   each be a bitmap anyway.

   So the reflow is handled here instead of avoided. Three things do it: the band
   is allowed to stack on a narrow screen rather than being forced onto one line,
   each offer carries a shorter wording that is swapped in before the stack is
   needed, and the type is sized in clamp() so it shrinks with the viewport but
   bottoms out somewhere still readable.

   The old .plenum-promo-* rules are gone with the artwork they styled. The
   .plenum-offer-band rule above stays: it retires the superseded database
   section, which is unrelated to this band and still present on older
   environments. */

/* The band is the window; the track is wider than it and slides through.
   overflow hidden is what makes it a window rather than a page-wide scrollbar. */
.plenum-combo-strip {
    position: relative;
    overflow: hidden;
    background-color: var(--plenum-ink);
    padding: clamp(0.7rem, 2vw, 1.15rem) 0;
}

/* max-content, so the track is as wide as its two sets laid end to end rather than
   being squeezed to the band's width -- without it there is nothing to slide.

   The offers are rendered twice and the track travels exactly half its own width,
   at which point the second set is standing where the first set started. Restarting
   there is invisible, which is what makes the loop seamless rather than a rewind. */
.plenum-combo-track {
    display: flex;
    width: max-content;
    animation: plenum-combo-glide var(--plenum-combo-duration, 40s) linear infinite;
}

.plenum-combo-strip.is-paused .plenum-combo-track {
    animation-play-state: paused;
}

@keyframes plenum-combo-glide {
    from { transform: translateX(0); }
    to   { transform: translateX(-50%); }
}

.plenum-combo-set {
    display: flex;
    align-items: center;
    flex: 0 0 auto;
}

.plenum-combo-offer {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    white-space: nowrap;
    text-decoration: none;
}

.plenum-combo-separator {
    color: var(--plenum-gold);
    padding: 0 clamp(1.25rem, 3vw, 2.75rem);
    font-size: 0.7rem;
}

/* The gold tag. Kept on one line at every width: it is the half of the offer that
   has to survive, so it is the half that never wraps. */
.plenum-combo-badge {
    flex: 0 0 auto;
    background-color: var(--plenum-gold-deep);
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-weight: 700;
    font-size: clamp(0.7rem, 1.6vw, 0.95rem);
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    padding: 0.35em 0.85em;
    border-radius: 2px;
}

/* No clamp on the lower bound worth worrying about: nothing has to fit, so the
   type is sized to be read rather than to survive a narrow band. */
.plenum-combo-text {
    color: var(--plenum-cream);
    font-family: var(--plenum-serif);
    font-size: clamp(0.95rem, 1.6vw, 1.35rem);
    line-height: 1.3;
}

/* Pinned over the band's right edge, on a short gradient so the offers fade out
   behind it rather than colliding with the glyph as they pass under. */
.plenum-combo-toggle {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    z-index: 1;
    padding: 0 0.9rem 0 2.5rem;
    border: 0;
    background: linear-gradient(to right, transparent, var(--plenum-ink) 45%);
    color: var(--plenum-muted);
    font-size: 0.6rem;
    line-height: 1;
    cursor: pointer;
}

.plenum-combo-toggle:hover {
    color: var(--plenum-gold);
}

.plenum-combo-toggle:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: -2px;
}

/*
 * Motion that starts on its own is what this setting asks to be spared, so the
 * glide does not run at all -- the same bail the rails make in plenum.auto-scroll.
 *
 * A stopped marquee would leave whatever happened to be off-screen unreadable, so
 * the track stops being a track: it wraps, centres and shows every offer at once,
 * and the duplicate set is dropped so nothing is listed twice.
 */
@media (prefers-reduced-motion: reduce) {
    .plenum-combo-track {
        animation: none;
        width: auto;
        flex-wrap: wrap;
        justify-content: center;
    }

    .plenum-combo-set {
        flex-wrap: wrap;
        justify-content: center;
        gap: 0.5rem 0;
    }

    .plenum-combo-set[aria-hidden="true"] {
        display: none;
    }

    .plenum-combo-offer {
        white-space: normal;
    }
}

/* The band runs edge to edge, the full width of the page. It was an inset card
   until it was asked to run full screen; the gutter and the 1440px cap went with
   that, and the corners were squared to match -- run to the edges, four clipped
   corners read as a mistake rather than as a card. Nothing here reimposes a
   width, so that holds for the type band too. */

/* ---------------------------------------------------------------------------
   Hero banner: where a wide banner gets cropped on a phone

   The hero frame is 2.743:1 on a desktop and 16/10 on a phone, and every banner fills
   it with `object-fit: cover`. A 2.74:1 banner in a 1.6:1 frame therefore loses about
   42% of its width, and `object-position: center` takes that out of both ends at once.

   For a photograph that is the right call, which is why the carousel's own rule stays
   as it is. For a banner whose type all sits down one side it is the wrong one: the
   Business Unlimited artwork carries its headline and its call to action on the left,
   so a centred crop showed a phone the middle of the picture and cut both of them off.
   Anchoring the crop left keeps the headline, EXCLUSIVE ACCESS and EXPLORE NOW whole,
   and spends the loss on the far end of the frame, which is background and decoration.

   Matched on the file rather than on the slide's title, because the title is content --
   somebody renaming the slide in the admin should not silently change how it crops --
   whereas the filename is the artwork this rule is about. A class, an element and an
   attribute outrank the one class and element the carousel sets, so this wins without
   !important.

   Matched on `data-src` and `srcset`, and deliberately not on `src`: these banners are
   rendered by the shimmer component, which hands the browser a `srcset` plus a `data-src`
   and never sets a plain `src` at all. A `src*=` selector looks right, matches nothing,
   and fails silently -- which is exactly how this rule behaved on its first outing.
   --------------------------------------------------------------------------- */
@media (max-width: 768px) {
    .plenum-hero-slide img[data-src*="plenum-business-unlimited"],
    .plenum-hero-slide img[srcset*="plenum-business-unlimited"] {
        object-position: left center;
    }
}

/* ---------------------------------------------------------------------------
   Composed hero slide: "Buy 2, Get 1 Free"

   The slide this replaces was a flattened 1920px bitmap whose packshots overlapped
   each other and ran off the right edge. Laying the same content out in CSS is what
   makes that class of fault impossible: the packshots are a flex row, so they are
   spaced at render time against whatever width the frame actually has.

   Everything here is scoped under .plenum-offer-slide, which only ever exists inside
   the hero frame. The frame supplies the height; this fills it.
   --------------------------------------------------------------------------- */
/*
 * Every layer of atmosphere lives here, in the slide's own `background`, and not in a
 * pseudo-element on the stage. A pseudo-element would paint after the packshots in tree
 * order and cover them, and the z-index that would push it back under them is exactly the
 * thing the rest of this block spends its comments avoiding. A background layer is painted
 * beneath everything by definition, so it cannot get into that argument at all.
 *
 * Read front to back. The order is doing real work: the pale bloom sits in FRONT of the
 * two gold washes so it can hold the product band near-white. That matters because the
 * packshots multiply -- against a mid-tone ground the white surround still vanishes, but
 * the products themselves go muddy, so the light has to stay where the products stand and
 * the colour has to stay out at the edges.
 */
.plenum-offer-slide {
    position: relative;
    display: flex;
    align-items: center;
    gap: 40px;
    width: 100%;
    height: 100%;
    padding: 0 clamp(28px, 5vw, 96px);
    overflow: hidden;
    background:
        /* the floor the products stand on -- a pool of warmth, not a drawn line */
        radial-gradient(ellipse 42% 22% at 72% 92%, rgba(199, 160, 90, .20) 0%, rgba(199, 160, 90, 0) 72%),
        /* the bloom that keeps the product band light enough to multiply cleanly */
        radial-gradient(ellipse 38% 52% at 71% 54%, rgba(255, 253, 248, .92) 0%, rgba(255, 253, 248, 0) 74%),
        /* gold falling in from the top-right, behind the badge */
        radial-gradient(ellipse 58% 66% at 97% 2%, rgba(210, 171, 103, .17) 0%, rgba(210, 171, 103, 0) 66%),
        /* and a deeper corner bottom-left, so the slide has a near side and a far side */
        radial-gradient(ellipse 46% 58% at 0% 100%, rgba(199, 160, 90, .14) 0%, rgba(199, 160, 90, 0) 68%),
        linear-gradient(105deg, var(--plenum-ivory) 0%, var(--plenum-cream) 55%, var(--plenum-sand) 100%);
}

/*
 * Copy takes the larger share and the packshots the rest, rather than both taking
 * half: the headline is the reason the slide exists, and a 1:1 split left FREE
 * wrapping at the sizes below.
 */
.plenum-offer-slide-copy {
    position: relative;
    z-index: 2;
    flex: 1 1 38%;
    min-width: 0;
}

/*
 * Deliberately without a z-index. `position: relative` alone leaves the stage in its
 * parent's stacking context, which is what lets the packshots inside it multiply against
 * the slide's own background. Give it a z-index and it becomes a stacking context of its
 * own, the blend then has nothing but the stage's transparent backdrop to work against,
 * and every packshot goes back to sitting in a white rectangle. The copy is lifted with
 * z-index instead, and the orbs pushed behind, so the stage needs no stacking of its own.
 */
.plenum-offer-slide-stage {
    position: relative;
    flex: 1 1 62%;
    align-self: stretch;
    min-width: 0;
    /*
     * Vertical padding keeps the row clear of the frame edge at every width, which is
     * the clipping the old bitmap suffered along its bottom. Kept tight: every point of
     * padding here comes straight off the size of the packshots, and the room at the top
     * only has to clear the badge.
     */
    padding: 6% 4% 3% 0;
}

/*
 * A warm pool of light behind the row. The packshots are photographed on white and then
 * multiplied onto the cream, which leaves them sitting flat on the background; this puts
 * something behind them to sit on. Drawn on the stage rather than the row so it stays put
 * as the row's own height changes, and kept as a background-image because a pseudo-element
 * with a z-index would isolate the blend above it.
 */
.plenum-offer-slide-stage::before {
    content: "";
    position: absolute;
    inset: 12% 2% 0 0;
    background: radial-gradient(ellipse at 50% 62%, rgba(210, 171, 103, .22) 0%, rgba(210, 171, 103, .07) 45%, rgba(210, 171, 103, 0) 72%);
    pointer-events: none;
}

/* ---- copy ---- */

/* Same gold, given a top-lit face and a shadow, so the label sits on the slide rather
   than looking like a rectangle of fill dropped behind the words. */
.plenum-offer-slide-eyebrow {
    display: inline-block;
    padding: 7px 14px;
    background: linear-gradient(160deg, #e2c084 0%, var(--plenum-gold) 52%, #bd9450 100%);
    color: var(--plenum-on-gold);
    font-family: var(--plenum-sans);
    font-size: clamp(9px, 0.72vw, 13px);
    font-weight: 800;
    letter-spacing: .18em;
    line-height: 1;
    text-transform: uppercase;
    box-shadow: 0 6px 14px -8px rgba(148, 113, 52, .75);
}

/*
 * Headline and payoff are set in vw so they hold their proportion against the frame,
 * which is itself proportional (2.743:1). Clamped at both ends: without the floor the
 * offer is unreadable on a small laptop, without the ceiling it overruns the packshots
 * on a wide monitor.
 */
.plenum-offer-slide-headline {
    margin: 0.5em 0 0;
    font-family: var(--plenum-sans);
    font-size: clamp(28px, 3.1vw, 60px);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -.01em;
    text-transform: uppercase;
    color: var(--plenum-ink);
}

/*
 * The one word the slide is selling, so it is the one word given a gradient: flat gold at
 * this size reads as a large flat shape, while a raking light down the letterforms reads
 * as leaf. `background-clip: text` needs a transparent fill to show through, and the
 * `color` above it is the fallback any browser that cannot clip will use instead -- so the
 * word is never invisible, just flat, which is where it started.
 */
.plenum-offer-slide-lead {
    margin: 0.06em 0 0;
    font-family: var(--plenum-sans);
    font-size: clamp(44px, 5.2vw, 100px);
    font-weight: 800;
    line-height: .92;
    letter-spacing: -.015em;
    text-transform: uppercase;
    color: var(--plenum-gold-deep);
    background: linear-gradient(168deg, #e0bd7d 0%, var(--plenum-gold) 26%, #b98c3f 62%, #d7ab63 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
}

/* The one italic serif line, as in the artwork -- it is what stops the block reading
   as three weights of the same sans stacked up. */
.plenum-offer-slide-tail {
    margin: 0.18em 0 0;
    font-family: var(--plenum-serif);
    font-size: clamp(18px, 1.85vw, 36px);
    font-style: italic;
    font-weight: 500;
    line-height: 1.1;
    color: var(--plenum-gold-deep);
}

.plenum-offer-slide-note {
    max-width: 30em;
    margin: 1.1em 0 0;
    font-family: var(--plenum-sans);
    font-size: clamp(11px, 0.86vw, 16px);
    font-weight: 600;
    line-height: 1.5;
    color: var(--plenum-ink);
}

/* Underlined rather than boxed: the slide already carries a gold fill on the eyebrow,
   and a second one here would compete with it for the eye. */
.plenum-offer-slide-cta {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    margin-top: 1.3em;
    padding-bottom: 7px;
    border-bottom: 2px solid rgba(47, 47, 47, .28);
    font-family: var(--plenum-sans);
    font-size: clamp(11px, 0.86vw, 16px);
    font-weight: 800;
    letter-spacing: .16em;
    line-height: 1;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--plenum-ink);
    transition: color 220ms ease;
}

/*
 * The gold rule that wipes across the resting one on hover, rather than both simply
 * changing colour at once. `position: absolute` is load-bearing and not just placement:
 * the anchor is an inline-flex container, so an in-flow pseudo-element would become a
 * third flex item and sit between the label and the chevron.
 */
.plenum-offer-slide-cta::after {
    content: "";
    position: absolute;
    right: 0;
    bottom: -2px;
    left: 0;
    height: 2px;
    background: linear-gradient(90deg, var(--plenum-gold) 0%, var(--plenum-gold-deep) 100%);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 320ms cubic-bezier(.22, .61, .36, 1);
}

.plenum-offer-slide-cta:hover {
    color: var(--plenum-gold-deep);
}

.plenum-offer-slide-cta:hover::after {
    transform: scaleX(1);
}

/* Nobody asked for the sweep if they have asked the OS for less movement. */
@media (prefers-reduced-motion: reduce) {
    .plenum-offer-slide-cta::after {
        transition: none;
    }
}

.plenum-offer-slide-chevron {
    font-size: 1.5em;
    line-height: 0;
    transition: transform 220ms ease;
}

.plenum-offer-slide-cta:hover .plenum-offer-slide-chevron {
    transform: translateX(3px);
}

/* ---- packshots ---- */

/*
 * The row is the whole fix. `flex: 1 1 0` gives every packshot an equal share of the
 * stage no matter what its own photograph measures, and the gap is a real gap rather
 * than a hoped-for margin -- so three products cannot touch, let alone overlap.
 *
 * The gap has to be read against what is *inside* a shot, not against zero. Each file is
 * a pair -- carton and jar photographed together, separated by about 4% of the file's own
 * width -- so a gap near that figure makes the seam between two shots look exactly like
 * the seam within one, and the eye gives up and reads all four products as a single row.
 * Sized well clear of it instead: the space between two shots now runs several times the
 * space inside one, which is what makes each pair group itself.
 */
.plenum-offer-slide-row {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    gap: clamp(18px, 3.4vw, 64px);
    width: 100%;
    height: 100%;
}

/*
 * One plinth per packshot, each taking an equal share of the row. The share is what
 * guarantees the packshots never touch: they are given the space, they do not take it.
 *
 * No z-index, transform or filter here -- any of the three makes this a stacking context
 * and the image inside it loses the multiply that removes its white background.
 */
.plenum-offer-slide-plinth {
    position: relative;
    display: flex;
    flex: 1 1 0;
    align-items: flex-end;
    justify-content: center;
    min-width: 0;
    height: 100%;
}

/*
 * The shadow the product stands on: a soft ellipse on the floor rather than a drop shadow
 * around the silhouette, which is what a packshot lit in a studio actually casts. It sits
 * under the image, so it is drawn first and the image multiplies over it.
 *
 * Measured against the shot rather than the plinth: the shot is inset to 86%, so the same
 * ellipse spread across the full plinth would reach out past the product on both sides and
 * read as a shadow cast by nothing. These figures are the old ones scaled by that 86%.
 */
.plenum-offer-slide-plinth::after {
    content: "";
    position: absolute;
    bottom: 1%;
    left: 14%;
    width: 72%;
    height: 7%;
    border-radius: 50%;
    background: radial-gradient(ellipse at 50% 50%, rgba(47, 47, 47, .2) 0%, rgba(47, 47, 47, .07) 55%, rgba(47, 47, 47, 0) 78%);
    pointer-events: none;
}

/*
 * Selector carries the parent deliberately. The carousel styles every image inside a
 * slide as a full-bleed banner -- `.plenum-hero-slide img { width:100%; height:100%;
 * object-fit:cover }` -- which is right for artwork that fills the frame and wrong for
 * a packshot: it stretched each one to the height of the row and then cropped the sides
 * off the carton. Two classes outrank that one class plus element, so these win.
 *
 * `contain` and not `cover`, for the same reason: cropping a packshot to fill a box
 * cuts the top off the carton.
 *
 * Each is shot on white. `multiply` against the slide's cream ground leaves the product
 * untouched and turns the white surround to the colour behind it, so the shot sits on
 * the slide instead of in a white rectangle -- no cut-out needed. A `filter` here would
 * undo that: it makes the element its own stacking context, and the blend then has the
 * element's own transparent backdrop to work against rather than the slide's.
 *
 * Short of the full share on purpose. The row's gap alone separates the boxes the shots
 * sit in, but a packshot is drawn to the edge of its own file, so flush-width shots put
 * the products themselves hard against that gap and the pairs still read as one run.
 * Holding each back to 86% keeps the remaining 14% as margin the artwork cannot spend,
 * which lands on both sides of every gap and roughly doubles the visible separation.
 */
.plenum-offer-slide .plenum-offer-slide-shot {
    width: 86%;
    height: auto;
    max-height: 100%;
    object-fit: contain;
    object-position: bottom center;
    mix-blend-mode: multiply;
}

/* ---- badge ---- */

/*
 * A stamp pressed onto the slide, rather than a circle parked in the corner. Three things
 * do that and none of them is the shape: the tilt, which stops it sitting square to a
 * layout where everything else is; the double ring, an outer hairline set off the rim by a
 * gap so the gold reads as struck metal rather than as a border; and the cast shadow,
 * which is what lifts it off the cream instead of letting it lie flat on it.
 *
 * The transform is safe here in a way it would not be three rules further up: the badge is
 * already its own stacking context by way of `z-index: 3`, and it is a sibling of the row
 * rather than an ancestor of any packshot, so isolating it costs the multiply nothing.
 */
/*
 * Inset from the corner rather than pinned to it. At `top: 0; right: 0` the ring and the
 * cast shadow have nowhere to go: they are drawn outside the badge's own box, so the frame
 * clipped the top of the stamp and the promo strip covered what survived. The inset is
 * sized to clear both, and it also puts the badge in the empty band above the row, which
 * had nothing in it.
 */
.plenum-offer-slide-badge {
    position: absolute;
    top: 13%;
    right: 3%;
    z-index: 3;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    width: clamp(62px, 7.2vw, 132px);
    aspect-ratio: 1;
    border: 2px solid var(--plenum-gold);
    border-radius: 50%;
    background:
        radial-gradient(circle at 34% 24%, rgba(255, 255, 255, .16) 0%, rgba(255, 255, 255, 0) 58%),
        linear-gradient(150deg, #3d3a34 0%, var(--plenum-ink) 48%, #232323 100%);
    color: var(--plenum-ivory);
    text-align: center;
    transform: rotate(-9deg);
    box-shadow:
        0 0 0 1px rgba(210, 171, 103, .30),
        0 0 0 5px rgba(246, 242, 235, .55),
        0 0 0 6px rgba(210, 171, 103, .38),
        0 16px 30px -12px rgba(66, 50, 22, .55);
}

.plenum-offer-slide-badge-figure {
    font-family: var(--plenum-sans);
    font-size: clamp(26px, 3.1vw, 58px);
    font-weight: 800;
    line-height: .9;
    color: var(--plenum-gold);
}

.plenum-offer-slide-badge-caption {
    margin-top: .15em;
    font-family: var(--plenum-sans);
    font-size: clamp(8px, 0.72vw, 14px);
    font-weight: 700;
    letter-spacing: .14em;
    line-height: 1;
    text-transform: uppercase;
}

/* ---- decoration ---- */

/*
 * The spheres from the original artwork, as three circles. They sit behind everything
 * and are never the only thing carrying a meaning, so they can be dropped entirely on
 * a small screen without losing anything.
 *
 * Lit rather than filled: the highlight sits off-centre at 30%/26% and the tone runs all
 * the way to a shadowed edge, which is what reads as a sphere instead of a dot. The ring
 * is a hair of reflected light around the rim, and the outer shadow sets it off the ground
 * it floats over.
 */
.plenum-offer-slide-orb {
    position: absolute;
    z-index: 0;
    border-radius: 50%;
    background: radial-gradient(circle at 30% 26%, #fffdf7 0%, #f7ecd6 38%, #e7d0a6 72%, #d3b485 100%);
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, .55),
        0 10px 22px -10px rgba(148, 113, 52, .45);
    pointer-events: none;
}

/*
 * Positions are the whole point of this block, so they get the explanation.
 *
 * All three used to sit between 40% and 49% from the left, on the seam where the copy ends
 * and the packshot row begins -- which put them directly behind the first product. Behind
 * is not harmless here: the packshots multiply, so an orb under one did not hide, it came
 * through the carton as a beige disc floating over the label.
 *
 * They now sit in the band above the row, which is the one part of the stage that was
 * genuinely empty, so they fill dead space instead of printing through the goods.
 */
.plenum-offer-slide-orb.is-one {
    top: 26%;
    left: 50%;
    width: 2.6vw;
    max-width: 36px;
    aspect-ratio: 1;
}

.plenum-offer-slide-orb.is-two {
    top: 16%;
    left: 57%;
    width: 5.2vw;
    max-width: 84px;
    aspect-ratio: 1;
}

.plenum-offer-slide-orb.is-three {
    top: 42%;
    left: 66%;
    width: 1.9vw;
    max-width: 26px;
    aspect-ratio: 1;
}

/*
 * Below 1024 the copy needs the room more than the third packshot does, so the row
 * drops to two. Hiding the third rather than shrinking all three keeps the remaining
 * two legible instead of making a row of thumbnails.
 */
/*
 * Below 1280 the row drops to two packshots. Three would still fit without touching --
 * the row divides itself, so they always fit -- but each would be small enough that the
 * product stops being legible, and two large ones sell the range better than three
 * thumbnails of it.
 */
@media (max-width: 1280px) {
    .plenum-offer-slide-plinth:nth-child(3) {
        display: none;
    }
}

@media (max-width: 1024px) {
    .plenum-offer-slide {
        gap: 20px;
        padding: 0 24px;
    }

    .plenum-offer-slide-copy {
        flex-basis: 46%;
    }

    .plenum-offer-slide-stage {
        flex-basis: 54%;
        padding: 8% 3% 3% 0;
    }

    .plenum-offer-slide-orb {
        display: none;
    }
}

/*
 * On a phone the frame turns to 16:10 -- about 244px tall at 390px wide -- and a
 * two-column split leaves neither column usable, so the slide stacks: the offer reads
 * first, the packshots sit under it as evidence.
 *
 * That height is the whole constraint here. Every size below is set against it rather
 * than scaled down from the desktop figure, because the desktop block overflowed the
 * frame and `overflow: hidden` then cropped the headline clean off the top, leaving a
 * slide that opened on the call to action. The note and the badge go entirely: the note
 * is detail nobody reads at a glance, and "3 for 2" only restates the headline.
 */
@media (max-width: 768px) {
    .plenum-offer-slide {
        flex-direction: column;
        align-items: flex-start;
        justify-content: center;
        gap: 10px;
        padding: 16px 20px;
    }

    .plenum-offer-slide-copy,
    .plenum-offer-slide-stage {
        flex: 0 0 auto;
        width: 100%;
    }

    .plenum-offer-slide-stage {
        padding: 0;
    }

    .plenum-offer-slide-eyebrow {
        padding: 5px 10px;
        font-size: 9px;
    }

    .plenum-offer-slide-headline {
        margin-top: 0.45em;
        font-size: 21px;
    }

    .plenum-offer-slide-lead {
        font-size: 35px;
    }

    .plenum-offer-slide-tail {
        font-size: 15px;
    }

    .plenum-offer-slide-cta {
        margin-top: 0.9em;
        padding-bottom: 5px;
        font-size: 10px;
    }

    .plenum-offer-slide-note,
    .plenum-offer-slide-badge {
        display: none;
    }

    /*
     * The pairs still have to read apart at this size, and there is room: three plinths
     * claim 24% each, so better than a quarter of the row is going spare for the two gaps
     * regardless. Spending some of it here costs the packshots nothing.
     */
    .plenum-offer-slide-row {
        height: auto;
        gap: clamp(14px, 5vw, 26px);
        justify-content: flex-start;
        align-items: flex-end;
    }

    /*
     * Back to a share of the row rather than an equal division of it, so three packshots
     * occupy the left three-quarters and the row reads as a sample of the range rather
     * than as a full-width band of thumbnails. The third comes back here: on a stacked
     * layout the row has the whole slide width, which is room enough for all three.
     */
    .plenum-offer-slide-plinth {
        flex: 0 1 24%;
        height: 62px;
    }

    .plenum-offer-slide-plinth:nth-child(3) {
        display: flex;
    }

    /* The floor shadow is a few pixels tall at this size and reads as a smudge. */
    .plenum-offer-slide-plinth::after {
        display: none;
    }
}

/*
 * Under 420px the serif line goes too: a headline, a payoff, a call to action and the
 * packshots is all that fits without setting any of them at a size nobody reads.
 */
@media (max-width: 420px) {
    .plenum-offer-slide-tail {
        display: none;
    }

    .plenum-offer-slide-plinth {
        height: 56px;
    }
}

/* ---------------------------------------------------------------------------
   Retired: "Nature Meets Science"

   A full-width banner linking to About Us, drawn between Shop By Concern and the
   division ranges. Removed from the home page on request.

   The section is a row in theme_customizations -- markup, styles and all -- so
   deleting it there removes it only from the database it was deleted in. It is
   switched off in database/content/theme-customizations.json and in the seeder,
   which covers anything seeded from here on; every environment already carrying
   the row would keep drawing the banner until somebody re-seeded it by hand, and
   production has not been re-seeded in months.

   So it is retired here as well, the way the old offer band above is: scoped to
   the class the database section carries, and !important because that section
   ships its own stylesheet and this has to outrank it wherever it lands. The row
   can be deleted whenever convenient; until then it is simply not drawn.
   --------------------------------------------------------------------------- */
.plenum-science {
    display: none !important;
}

/* ---------------------------------------------------------------------------
   Stopgap: the Spa Ruca heading, corrected from the stylesheet

   TEMPORARY. Delete this once production clears its caches -- see below.

   The heading should read "Spa Ruca - Skin & Hair Care", and it does: the
   wording is in the section data, in the seeder, and rewritten in
   components/products/carousel.blade.php for environments carrying the old row.
   Production renders none of it, because that server is serving stale compiled
   PHP -- its copy of plenum-theme.css updates within a minute of a merge, while
   Blade output does not (the same freeze leaves the stylesheet link stuck at
   ?v=37). Nothing that can be pushed reaches HTML until somebody runs
   `php artisan optimize:clear` there and restarts PHP.

   So the correction is made here instead, where a deploy does land. The heading
   is matched through the section's own "View All" link, whose aria-label is the
   same stale title -- the heading itself carries no attribute to match on, and
   CSS cannot match text. Hidden rather than collapsed, with the replacement
   painted over it, so the h2 keeps its box and the line does not jump.

   It retires itself: the moment that server renders the corrected title, the
   aria-label changes with it, this selector stops matching, and the real
   heading shows through. It can then be deleted.
   --------------------------------------------------------------------------- */
.container:has(> a.plenum-view-all-foot[aria-label="Spa Ruca Skin & Hair Care"]) > div > h2 {
    position: relative;
    visibility: hidden;
}

.container:has(> a.plenum-view-all-foot[aria-label="Spa Ruca Skin & Hair Care"]) > div > h2::after {
    content: "Spa Ruca - Skin & Hair Care";
    position: absolute;
    inset-inline-start: 0;
    top: 0;
    visibility: visible;
}

/* ---------------------------------------------------------------------------
   Stopgap: the header logo, swapped from the stylesheet

   TEMPORARY. Delete this once production carries the new logo itself.

   The channel logo is a database field pointing at an uploaded file, and the
   file production points at was uploaded straight to that server -- it is not
   in this repo, so there is nothing here to replace. Adding a file at its path
   would be worse than useless: git refuses to overwrite an untracked file on
   checkout, so the pull that deploys this site would start failing outright.

   The new artwork therefore ships under its own name, and the old <img> is
   pointed at it here: `content` on an <img> replaces the image it renders.
   Nothing else is set. The two files are the same 1600x638 frame -- the change
   is GROUP OF COMPANY becoming GROUP OF COMPANIES -- so whatever box the header
   gives the old mark fits the new one exactly, and a size written here could
   only disagree with it.

   Matched on the uploaded file's name, so it retires itself: upload the new
   logo in admin (Settings > Channels) and the src changes, this stops matching,
   and the real logo shows through. That upload is the proper fix -- the logo is
   data, so unlike a code change it takes effect on that server immediately.
   --------------------------------------------------------------------------- */
img[src*="zIJjoXhQspsqwMgOZlDzLsCdAmGdWHMUH5AhJgVz"] {
    content: url("/storage/channel/1/plenum-group-of-companies.webp");
}

/* ---------------------------------------------------------------------------
   Division page product grid

   The division pages lay their sections out in full instead of sliding four cards
   past the reader -- see resources/views/plenum/product-grid.blade.php for why.
   The home page keeps the carousel, so this is scoped to the grid's own class and
   nothing here touches the sliding row.

   Tracks are sized, not counted: `auto-fill` with a floor wide enough for the card's
   square panel plus its name and price gives four across a desktop, three on a
   laptop and two on a phone without a breakpoint for each. The gap is the carousel's
   own 32px, so a division page and the home page keep the same rhythm.

   The cards carry min-widths from the carousel (`min-w-[291px]`) only where that
   component sets them; here the card is left to fill its track, which is what
   `.plenum-card { width: 100% }` below guarantees against a stray utility.
   --------------------------------------------------------------------------- */
.plenum-product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
    gap: 32px;
    margin-top: 40px;
}

/*
 * The division sections have no heading row, so the grid opens its section. The 40px
 * that separated it from a title would then be stacked on the section's own top margin
 * and read as a gap nobody put there.
 */
.plenum-product-grid:first-child {
    margin-top: 0;
}

.plenum-product-grid > * {
    width: 100%;
    min-width: 0;
    max-width: none;
}

@media (max-width: 768px) {
    .plenum-product-grid {
        grid-template-columns: repeat(auto-fill, minmax(168px, 1fr));
        gap: 20px;
        margin-top: 20px;
    }
}

/* Two to a row is the floor: one card a row reads as a list, not a range. */
@media (max-width: 400px) {
    .plenum-product-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 14px;
    }
}

/* ----------------------------------------------------------- trust strip */

/*
 * The four promises. One component, three placements -- see
 * resources/views/plenum/trust-strip.blade.php.
 *
 * A full-bleed band in the site's cream with a hairline top and bottom, so it separates
 * the products above from the newsletter below without needing a heading.
 */
.plenum-trust {
    /*
     * The strip's type, in one place. Scoped to .plenum-trust rather than :root so the
     * mobile block below can redefine sizes for the whole component by overriding two
     * variables, and so nothing outside the strip can be reached by accident.
     *
     * Every rule below reads these, and every rule names .plenum-trust, which is what
     * keeps the built theme's own body colour and weights from winning: the stock sheet
     * styles `p` and `span` at element specificity, a single class beats it, and none of
     * these values is left to inherit.
     */
    --trust-title-weight: 700;
    --trust-title-size: 16px;
    --trust-title-color: #000000;
    --trust-title-tracking: 0.01em;

    --trust-desc-weight: 500;
    --trust-desc-size: 14px;
    /*
     * #333 on the cream ground is 11.3:1. What it replaces, --plenum-muted at #5a5a5a,
     * measured 6.18:1 -- under the 7:1 this strip is held to, and it read as washed out
     * on a phone in daylight, which is where the strip does most of its work.
     */
    --trust-desc-color: #333333;

    --trust-icon-size: 32px;
    --trust-icon-stroke: 2;

    margin-top: 72px;
    /* 28px, up from 20px: the larger type needs the room. */
    padding: 28px 20px;
    border-top: 1px solid var(--plenum-hairline);
    border-bottom: 1px solid var(--plenum-hairline);
    background-color: var(--plenum-cream);
}

.plenum-trust-list {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 20px;
    max-width: 1320px;
    margin: 0 auto;
    padding: 0;
    list-style: none;
}

.plenum-trust-item {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    min-width: 0;
}

/*
 * One size and one stroke for all four, set here rather than on each icon so they
 * cannot drift apart. Black to match the rating stars -- the strip is information, and
 * the gold is reserved for things you can act on.
 *
 * `stroke-width` is set in CSS even though the markup carries the attribute: a CSS
 * declaration beats an SVG presentation attribute, so the weight lives with the rest of
 * the strip's type rather than half here and half in the Blade.
 */
.plenum-trust .plenum-trust-icon {
    flex: 0 0 auto;
    width: var(--trust-icon-size);
    height: var(--trust-icon-size);
    color: var(--trust-title-color);
    stroke-width: var(--trust-icon-stroke);
}

.plenum-trust .plenum-trust-text {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: 0;
}

/*
 * Bold, black, and barely tracked. Wide tracking on bold text opens the counters and
 * makes it read lighter, which is the opposite of what this change is for -- 0.01em is
 * enough to stop the letters touching and no more.
 *
 * Playfair Display is fetched at 700 (see theme-css.blade.php), so this is a real bold
 * file. Without it the browser would smear the 500 into a fake one, which thickens the
 * hairlines this face is drawn around and reads muddy rather than strong.
 */
.plenum-trust .plenum-trust-title {
    font-family: var(--plenum-display-face);
    font-size: var(--trust-title-size);
    font-weight: var(--trust-title-weight);
    line-height: 1.3;
    letter-spacing: var(--trust-title-tracking);
    color: var(--trust-title-color);
}

/* Montserrat is fetched at 500, so Medium here is a real cut too. */
.plenum-trust .plenum-trust-desc {
    font-family: var(--plenum-sans);
    font-size: var(--trust-desc-size);
    font-weight: var(--trust-desc-weight);
    line-height: 1.5;
    color: var(--trust-desc-color);
}

/*
 * The compact form, on the product and cart pages. No band and no borders -- it sits
 * inside a column that already has its own edges -- and the icons come down to the size
 * of the text beside them.
 */
.plenum-trust.is-compact {
    margin-top: 22px;
    padding: 16px 0 0;
    border-bottom: 0;
    background: none;
}

.plenum-trust.is-compact .plenum-trust-list {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 14px 18px;
}

/*
 * The compact form takes the same weights and colours -- it is the same promise in a
 * tighter space -- and only the sizes come down. Set through the variables, so the rules
 * above stay the single definition of how this strip is typeset.
 */
.plenum-trust.is-compact {
    --trust-icon-size: 22px;
    --trust-title-size: 14px;
}

@media (max-width: 768px) {
    .plenum-trust {
        --trust-title-size: 15px;
        --trust-desc-size: 13px;
        --trust-icon-size: 28px;

        margin-top: 44px;
        padding: 24px 14px;
    }

    .plenum-trust.is-compact {
        --trust-icon-size: 20px;
        --trust-title-size: 13.5px;
    }

    /*
     * 2x2 on a phone, and the item stacks so a long title has the column's whole width
     * rather than what is left beside a 32px icon. At 360px that is the difference
     * between "Cash on Delivery" on one line and on three.
     */
    .plenum-trust-list {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 18px 12px;
    }

    .plenum-trust-item {
        flex-direction: column;
        align-items: center;
        gap: 8px;
        text-align: center;
    }

    .plenum-trust.is-compact .plenum-trust-item {
        flex-direction: row;
        text-align: left;
    }
}

/* ------------------------------------------------ licences in the footer */

/*
 * Renders only when config('plenum.legal') has values in it -- see
 * resources/views/plenum/legal-block.blade.php, which draws nothing at all while the
 * numbers are blank.
 */
.plenum-legal {
    width: 100%;
    margin-bottom: 18px;
    padding-bottom: 16px;
    border-bottom: 1px solid rgba(228, 220, 205, 0.35);
}

/*
 * There are several of these now -- licences, customer care, the grievance officer --
 * and each one drew a rule under itself. The last of them sat directly above the rule
 * the payment strip already draws, which read as a double line rather than a divider.
 * The separators between the blocks are the useful ones; the trailing one is not.
 */
.plenum-legal:last-of-type {
    border-bottom: 0;
    padding-bottom: 0;
}

/*
 * The heading is 10px uppercase, so it is small text and needs the full 4.5:1 --
 * the 3:1 allowance is for large text (24px, or 18.66px bold) and is a question of
 * rendered size, not of whether an element is a heading. --plenum-gold reached
 * 1.93:1 here; the contrast gold is 4.89:1.
 */
.plenum-legal-title {
    margin: 0 0 10px;
    font-family: var(--plenum-label);
    font-size: 10px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
}

.plenum-legal-list {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 32px;
    margin: 0;
}

.plenum-legal-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    min-width: 0;
}

/*
 * The three rules below were written in white at partial alpha, for a dark footer
 * this block never shipped into: it renders on --plenum-cream, where white at 0.78
 * composites to #fdfcfb and the registered address came out at 1.09:1 -- legally
 * required text that could not be read. They are opaque ink now, on the same two
 * tokens the rest of the storefront uses for body and secondary copy.
 *
 * Label stays a step lighter than the value so the pairing still reads as
 * label-then-fact, but the step is --plenum-muted at 6.18:1, not transparency.
 */
.plenum-legal-label {
    margin: 0;
    font-size: 12px;
    color: var(--plenum-muted);
}

.plenum-legal-label::after {
    content: ":";
}

/* The registered address, GSTIN and licence numbers: 12:1. */
.plenum-legal-value {
    margin: 0;
    font-size: 12px;
    color: var(--plenum-ink);
    word-break: break-word;
}

/*
 * Phone, WhatsApp and email are the things in this block a customer is meant to
 * *act* on, so they carry an underline rather than colour alone -- colour on its own
 * is not a distinguishing mark under WCAG 1.4.1, and at 12px on cream it is barely a
 * signal at all. Ink at 12:1 for the resting state, contrast gold at 4.89:1 on hover
 * and focus, both past AA.
 */
.plenum-legal-link {
    color: var(--plenum-ink);
    text-decoration: underline;
    text-underline-offset: 2px;
    text-decoration-thickness: 1px;
    text-decoration-color: rgba(47, 47, 47, 0.4);
    transition: color 200ms ease, text-decoration-color 200ms ease;
}

.plenum-legal-link:hover,
.plenum-legal-link:focus-visible {
    color: var(--plenum-gold-contrast);
    text-decoration-color: currentColor;
}

.plenum-legal-link:focus-visible {
    outline: 2px solid var(--plenum-gold-contrast);
    outline-offset: 2px;
}

/*
 * The grievance timeline. It belongs to the block above it rather than to the page,
 * so it is indented to the list and set at the secondary ink -- a promise about
 * service, not another labelled fact.
 */
.plenum-legal-note {
    margin: 8px 0 0;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--plenum-muted);
}

/* Health disclaimer: 11.5px, so small text -- 6.18:1. */
.plenum-legal-disclaimer {
    width: 100%;
    margin: 0 0 14px;
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--plenum-muted);
}

@media (max-width: 768px) {
    .plenum-legal-list {
        grid-template-columns: minmax(0, 1fr);
    }
}

/* --------------------------------------------------- card image slideshow */

/*
 * Every photograph occupies the same square, stacked on top of the others and
 * cross-faded. Absolute rather than a sliding track, so the panel's height is the
 * panel's height whatever is showing and a card can never shift the row it sits in.
 *
 * .plenum-card-media already carries aspect-ratio 1/1 and the image object-fit: contain,
 * so nothing here has to restate the shape.
 */
.plenum-card-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    transition: opacity 500ms ease;
}

.plenum-card-slide.is-current {
    opacity: 1;
}

/*
 * The panel's padding lives on the link, so the stacked images need it restated or they
 * would run to the edges the single image never reached.
 */
.plenum-card-media-link {
    position: relative;
}

/* Dots: one per photograph, black for the one showing. */
.plenum-card-dots {
    position: absolute;
    bottom: 10px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 2;
    display: flex;
    gap: 5px;
}

.plenum-card-dot {
    width: 6px;
    height: 6px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background-color: #d9d9d9;
    cursor: pointer;
    transition: background-color 250ms ease;
}

.plenum-card-dot.is-on {
    background-color: #000000;
}

.plenum-card-dot:focus-visible,
.plenum-card-arrow:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 2px;
}

/*
 * Arrows appear on hover and on keyboard focus. Focus matters more than it looks: they
 * are the only way to reach the other photographs without a pointer, so a rule keyed on
 * hover alone would hide them from exactly the people who need them.
 */
.plenum-card-arrow {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    padding: 0 0 2px;
    border: 0;
    border-radius: 50%;
    background-color: rgba(255, 255, 255, 0.82);
    color: var(--plenum-ink);
    font-size: 17px;
    line-height: 1;
    cursor: pointer;
    opacity: 0;
    transition: opacity 250ms ease, background-color 250ms ease;
}

.plenum-card-arrow.is-prev { left: 8px; }
.plenum-card-arrow.is-next { right: 8px; }

.plenum-card-media:hover .plenum-card-arrow,
.plenum-card-arrow:focus-visible {
    opacity: 1;
}

.plenum-card-arrow:hover {
    background-color: #fff;
}

/* A touch screen has no hover, and an arrow that only appears on one is unreachable. */
@media (hover: none) {
    .plenum-card-arrow {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-card-slide {
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   Category icons

   One glyph per category, on every surface that lists the catalogue as text:
   the desktop mega menu, the mobile menu, and the sidebar rail beside the
   products. Not the home page's round category rail -- that one draws each
   division's own photograph, and a line glyph there would replace real artwork
   with less.

   ---------------------------------------------------------------------------
   Why the mapping lives here, keyed on the slug
   ---------------------------------------------------------------------------

   `categories.logo_path` is a column, and columns do not travel with a deploy --
   the lesson `superseded_banners` and the hero slides already learned. An icon
   set chosen in the admin would appear on one machine. There is also nothing
   there to read: every sub-category's logo_path is NULL.

   Keying on `[data-category="<slug>"]` means one mechanism serves both a Blade
   template and a Vue one, with no second copy of the map in JavaScript. The
   category objects the menus iterate already carry `slug` -- see
   CategoryTreeResource -- so each call site is a single span.

   A slug with no rule draws nothing rather than breaking: mask-image is unset,
   so the span collapses. A category added tomorrow costs a missing glyph, not a
   broken layout. Add its rule when you add the category.

   ---------------------------------------------------------------------------
   Why masks rather than <img> or inline SVG
   ---------------------------------------------------------------------------

   mask-image with `background-color: currentColor` means each glyph takes the
   colour of the text beside it, so hover, the dark mega menu panel and the light
   sidebar all come out right from one definition, with no second palette to keep
   in step.

   Inline SVG would want an id per clip or gradient to be reusable, and these are
   drawn dozens of times per page inside cloned Vue templates -- duplicate ids
   resolve to whichever the browser saw first, the bug .plenum-star documents at
   length. A mask has no ids at all.

   The glyphs are plain line drawings on a common 24x24 box at 1.6 stroke, so a
   column of them reads as one set rather than as assorted clip art. */

/* Sized in em, so the glyph tracks the type it sits beside -- a division heading
   in the mega menu gets a larger one than a third-level link without either
   being set twice. One variable, so the whole set is tuned in one place. */
.plenum-cat-icon {
    --plenum-cat-icon-size: 1.65em;

    display: inline-block;
    flex: 0 0 auto;
    width: var(--plenum-cat-icon-size);
    height: var(--plenum-cat-icon-size);
    background-color: currentColor;
    opacity: 0.8;
    -webkit-mask-image: var(--plenum-cat-glyph);
            mask-image: var(--plenum-cat-glyph);
    -webkit-mask-repeat: no-repeat;
            mask-repeat: no-repeat;
    -webkit-mask-position: center;
            mask-position: center;
    -webkit-mask-size: contain;
            mask-size: contain;
}

/* The glyph strengthens with the link it sits beside, so the row the reader is
   on reads as one thing rather than as text plus a mark. */
a:hover > .plenum-cat-icon,
a.is-active > .plenum-cat-icon {
    opacity: 1;
}

/* ---- divisions ---- */

/* Leaf. */
.plenum-cat-icon[data-category="ayurveda"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 4C20 13 15 19 8 20 8 11 13 5 20 4Z'/%3E%3Cpath d='M8 20c1-5 4-9 8-11'/%3E%3C/svg%3E");
}

/* Droplet. */
.plenum-cat-icon[data-category="cosmetic"],
.plenum-cat-icon[data-category="cosmetic-body-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3c4 5 6 8 6 11a6 6 0 0 1-12 0c0-3 2-6 6-11Z'/%3E%3C/svg%3E");
}

/* Sparkle: skin being looked after rather than treated. */
.plenum-cat-icon[data-category="derma-skincare"],
.plenum-cat-icon[data-category="derma-skin-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M10.5 3l1.7 4.3L16.5 9l-4.3 1.7L10.5 15 8.8 10.7 4.5 9l4.3-1.7Z'/%3E%3Cpath d='M17.5 14l.8 2.2 2.2.8-2.2.8-.8 2.2-.8-2.2-2.2-.8 2.2-.8Z'/%3E%3C/svg%3E");
}

/* Paw. */
.plenum-cat-icon[data-category="veterinary"],
.plenum-cat-icon[data-category="veterinary-pet-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='7' cy='9.5' r='1.9'/%3E%3Ccircle cx='11' cy='6.8' r='1.9'/%3E%3Ccircle cx='15.4' cy='7.6' r='1.9'/%3E%3Ccircle cx='18' cy='11.6' r='1.9'/%3E%3Cpath d='M8.6 15c2-2 5.2-2 7.2 0 1.7 1.7 1 4.3-1.6 4.3h-4c-2.6 0-3.3-2.6-1.6-4.3Z'/%3E%3C/svg%3E");
}

/* Balance scale. */
.plenum-cat-icon[data-category="weight-management"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 5v14'/%3E%3Cpath d='M8 19h8'/%3E%3Cpath d='M4 9h16'/%3E%3Cpath d='M4 9l-2.5 4.5h5Z'/%3E%3Cpath d='M20 9l-2.5 4.5h5Z'/%3E%3C/svg%3E");
}

/* Shield. */
.plenum-cat-icon[data-category="immunity-nutrition"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l7 3v5.5c0 4.8-2.9 7.9-7 9.5-4.1-1.6-7-4.7-7-9.5V6Z'/%3E%3C/svg%3E");
}

/* Flask: the liver and digestive shelves, and the tonics under them. */
.plenum-cat-icon[data-category="digestive-liver-care"],
.plenum-cat-icon[data-category="ayurveda-liver-digestive-care"],
.plenum-cat-icon[data-category="digestive-liver-tonics"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M9 3h6'/%3E%3Cpath d='M10 3v5l-4.4 8.3A2 2 0 0 0 7.4 19.4h9.2a2 2 0 0 0 1.8-3.1L14 8V3'/%3E%3Cpath d='M7.6 14.6h8.8'/%3E%3C/svg%3E");
}

/* ---- sub-categories ---- */

/* A joint: two ends and the shaft between them. */
.plenum-cat-icon[data-category="ayurveda-ortho-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='7' cy='17' r='2.4'/%3E%3Ccircle cx='17' cy='7' r='2.4'/%3E%3Cpath d='M8.7 15.3 15.3 8.7'/%3E%3C/svg%3E");
}

/* A nerve signal. */
.plenum-cat-icon[data-category="ayurveda-nerve-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M13 3 6 13h5l-1 8 7-10h-5Z'/%3E%3C/svg%3E");
}

/* Flame: acidity, and the fat that is meant to go. */
.plenum-cat-icon[data-category="ayurveda-gastric-care"],
.plenum-cat-icon[data-category="weight-fat-burners"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3s4.5 4.4 4.5 8.6a4.5 4.5 0 0 1-9 0c0-2.2 1.1-3.4 2.2-4.4 0 1.2.7 1.9 1.4 1.9.9 0 1.4-1.2.9-6.1Z'/%3E%3C/svg%3E");
}

/* Heart. */
.plenum-cat-icon[data-category="ayurveda-general-wellness"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 20S5 15.6 5 11a4 4 0 0 1 7-2.6A4 4 0 0 1 19 11c0 4.6-7 9-7 9Z'/%3E%3C/svg%3E");
}

/* A face. */
.plenum-cat-icon[data-category="cosmetic-face-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='8.4'/%3E%3Cpath d='M9.4 10.2h.01'/%3E%3Cpath d='M14.6 10.2h.01'/%3E%3Cpath d='M9 15c1.8 1.4 4.2 1.4 6 0'/%3E%3C/svg%3E");
}

/* Strands. */
.plenum-cat-icon[data-category="cosmetic-hair-care"],
.plenum-cat-icon[data-category="derma-hair-care"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 4c2.2 4.6 2.2 11.4 0 16'/%3E%3Cpath d='M12 4c2.2 4.6 2.2 11.4 0 16'/%3E%3Cpath d='M17 4c2.2 4.6 2.2 11.4 0 16'/%3E%3C/svg%3E");
}

/* A cow's head. The horns are what make it read as cattle. */
.plenum-cat-icon[data-category="veterinary-cattle"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5.5 9.5c0-1.1.9-2 2-2h9c1.1 0 2 .9 2 2v2.5a6.5 6.5 0 0 1-13 0Z'/%3E%3Cpath d='M5.5 9.5C4 8.6 3.4 6.7 3.9 5.5 5.4 5.5 6.5 6.6 7 7.6'/%3E%3Cpath d='M18.5 9.5c1.5-.9 2.1-2.8 1.6-4-1.5 0-2.6 1.1-3.1 2.1'/%3E%3Cpath d='M10.2 14.2h.01'/%3E%3Cpath d='M13.8 14.2h.01'/%3E%3C/svg%3E");
}

/* A bird. */
.plenum-cat-icon[data-category="veterinary-poultry"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 10.5a4.2 4.2 0 0 1 8.4 0c0 3.1-2.1 4.2-2.1 7.4H10c0-3.2-2-4.3-2-7.4Z'/%3E%3Cpath d='M16.4 9.4 19.6 10.5 16.4 11.6'/%3E%3Cpath d='M11.4 8.6h.01'/%3E%3Cpath d='M11 17.9v2.5'/%3E%3Cpath d='M13.6 17.9v2.5'/%3E%3C/svg%3E");
}

/* A bowl. */
.plenum-cat-icon[data-category="weight-meal-support"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3.5 11.5h17c0 4.4-3.6 8-8 8h-1c-4.4 0-8-3.6-8-8Z'/%3E%3Cpath d='M12 11.5V5.5'/%3E%3Cpath d='M9.2 8.2c0-1.9 1.3-3 2.8-3s2.8 1.1 2.8 3'/%3E%3C/svg%3E");
}

/* Shield with a check: the division's shield, plus what a booster is for. */
.plenum-cat-icon[data-category="immunity-boosters"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 3l7 3v5.5c0 4.8-2.9 7.9-7 9.5-4.1-1.6-7-4.7-7-9.5V6Z'/%3E%3Cpath d='M9 12l2.2 2.2L15 10.4'/%3E%3C/svg%3E");
}

/* A capsule, split across its short axis. */
.plenum-cat-icon[data-category="immunity-vitamins-minerals"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6.2 17.8a5 5 0 0 1 0-7.1l4.5-4.5a5 5 0 0 1 7.1 7.1l-4.5 4.5a5 5 0 0 1-7.1 0Z'/%3E%3Cpath d='M10.5 13.5 13.5 10.5'/%3E%3C/svg%3E");
}

/* A glass, fizzing. */
.plenum-cat-icon[data-category="digestive-antacids"] {
    --plenum-cat-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M7 8.5h10l-1 10.4a2 2 0 0 1-2 1.8h-4a2 2 0 0 1-2-1.8Z'/%3E%3Ccircle cx='10' cy='5' r='1'/%3E%3Ccircle cx='13.6' cy='3.4' r='.8'/%3E%3Ccircle cx='15.2' cy='6' r='.6'/%3E%3C/svg%3E");
}

/* Alignment for a link that carries a glyph. A separate class rather than
   `display` being changed on .plenum-mega-heading / .plenum-mega-link / the
   sidebar link themselves, so any of those without a glyph -- a category whose
   slug has no rule above, or a surface that never got one -- lays out exactly as
   it did before. */
.plenum-with-icon {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

/* The sidebar rail already spends its left column on the chevron, so the glyph
   sits between that and the name and the row keeps one indent. */
.plenum-category-nav-link .plenum-cat-icon {
    margin-right: 0.15rem;
}

/* ------------------------------------------------- floating corner widget */

/*
 * Keeps a fixed widget in the top-right corner fully on screen, 16px clear of both
 * edges, however wide it is.
 *
 * NOT YET APPLIED TO ANYTHING. Nothing in this stylesheet or on the live page uses
 * `position: fixed` in that corner -- the only sticky element is the header -- so the
 * widget being cut off is not something this theme draws. It is most likely injected
 * by a third-party script, a browser extension, or a page not checked here. Add its
 * selector to the list below and the rule takes effect; nothing else needs to change.
 *
 * Three things cause a corner widget to be clipped, and the rule answers all of them:
 *
 *   right/top    an element positioned with `right: 0` sits flush against the edge, and
 *                any shadow or ring on it is cut. 16px gives it the margin asked for.
 *
 *   max-width    a widget wider than the viewport is clipped no matter where it is
 *                pinned. 100vw minus both margins is the widest it can be and still
 *                clear both sides; `width: auto` lets it stay its natural size below
 *                that rather than stretching to fill.
 *
 *   translate    a widget that opens leftward with a transform can still reach past the
 *                left edge. `right` anchoring plus the max-width above keeps the whole
 *                box inside regardless of which way its panel expands.
 *
 * The safe-area insets matter on notched phones in landscape, where the usable viewport
 * is narrower than 100vw; on every other device they resolve to 0 and the rule reads as
 * a plain 16px.
 */
.plenum-corner-widget {
    position: fixed;
    top: calc(16px + env(safe-area-inset-top, 0px));
    right: calc(16px + env(safe-area-inset-right, 0px));
    left: auto;
    bottom: auto;

    width: auto;
    max-width: calc(
        100vw - 32px - env(safe-area-inset-left, 0px) - env(safe-area-inset-right, 0px)
    );
    max-height: calc(100vh - 32px - env(safe-area-inset-top, 0px));
    overflow: auto;

    /* Above the sticky header, which sits at z-index 10. */
    z-index: 60;
}

/*
 * Under the header's own breakpoint the header is full width and a widget in that
 * corner overlaps the cart and account icons. Dropping it below the header keeps both
 * usable; raise the offset if the header grows.
 */
@media (max-width: 767.98px) {
    .plenum-corner-widget {
        top: calc(72px + env(safe-area-inset-top, 0px));
    }
}

/* ------------------------------------------------------- social profiles */

/*
 * A row of marks under the brand column. Left-aligned with the copy above them rather
 * than centred, since the whole column is.
 *
 * Nothing renders until a profile in config('plenum.social') carries a URL, so on a
 * store that has not filled any in there is no empty row to style around.
 */
.plenum-social {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 14px;
    margin: 4px 0 0;
    padding: 0;
    list-style: none;
}

/*
 * 40px of hit area around an 18px mark. The glyph is the size the row wants; the target
 * is the size a thumb wants, and WCAG 2.5.8 asks for 24px minimum -- negative margin
 * keeps the larger target from spacing the marks apart.
 */
.plenum-social-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin: -11px;
    color: var(--plenum-ink);
    transition: color 200ms ease;
}

.plenum-social-link svg {
    width: 18px;
    height: 18px;
    display: block;
}

.plenum-social-link:hover {
    color: var(--plenum-gold-contrast);
}

.plenum-social-link:focus-visible {
    outline: 2px solid var(--plenum-gold-contrast);
    outline-offset: -8px;
    border-radius: 50%;
}

@media (prefers-reduced-motion: reduce) {
    .plenum-social-link {
        transition: none;
    }
}

/* ------------------------------------------- category listing product grid */

/*
 * The grid on a category page (/immunity-nutrition, /ayurveda-immunity and the rest).
 *
 * Not the same grid as a division page's. That one is `.plenum-product-grid`, sized by
 * track with auto-fill, and it already lays four across; this one is the Shop package's
 * own markup in categories/view.blade.php, which counts columns in Tailwind:
 *
 *     grid grid-cols-3 gap-8 max-1060:grid-cols-2 ...
 *
 * Three was its ceiling, so a 1320px row beside a 240px rail had a column's worth of
 * space left over on the right, and under 1060px -- which is most laptops once a
 * browser's own chrome is taken off -- it dropped to two and the page ran to a scroll
 * fifty products long.
 *
 * Overridden here rather than in that Blade so a Bagisto upgrade cannot take it back.
 * This sheet loads after the built Tailwind bundle and every selector below carries
 * three classes against Tailwind's one, so it wins on specificity without !important
 * and without the utilities being edited.
 *
 * Scoped to `.plenum-section-row` -- the row that holds the rail and the grid -- so it
 * reaches the category listing and nothing else that happens to use the same utilities.
 */
.plenum-section-row .grid-cols-3.gap-8 {
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 24px;
}

/* Laptop. Three across while the rail still holds its 240px. */
@media (max-width: 1199.98px) {
    .plenum-section-row .grid-cols-3.gap-8 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 24px;
    }
}

/*
 * Tablet. Still three, at a tighter gap -- the rail has stopped being a column by here
 * and lays its links out as a row above the products, so the grid has the full width.
 */
@media (max-width: 991.98px) {
    .plenum-section-row .grid-cols-3.gap-8 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 16px;
    }
}

/*
 * Phone. Two, never one: a single card per row reads as a list rather than a range,
 * and doubles the scroll the change is here to shorten.
 *
 * `justify-items` is reset because the stock markup sets `max-md:justify-items-center`,
 * which centres each card inside a track wider than it and leaves the row looking
 * unevenly spaced once the cards are stretched to fill.
 */
@media (max-width: 767.98px) {
    .plenum-section-row .grid-cols-3.gap-8 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 12px;
        justify-items: stretch;
    }
}

/*
 * The shimmer placeholder is capped at 291px by the Shop package
 * (shimmer/products/cards/grid.blade.php). Left alone it makes the loading state a
 * narrower grid than the one that replaces it, so the page visibly reflows on load.
 */
.plenum-section-row .grid-cols-3.gap-8 > .max-w-\[291px\] {
    max-width: none;
}

/*
 * The row itself. `min-w-0` on the grid's column is what stops a long product name
 * from widening a track past its share and pushing the row into a horizontal scroll;
 * the utility is already on the markup, and this is here so it survives if it is not.
 */
.plenum-section-row > .flex-1 {
    min-width: 0;
}

/* ----------------------------------------- category pagination / load more */

/*
 * The Load More button Bagisto already ships on a category page, restyled, plus the
 * counter and the crawlable page links injected above it.
 *
 * The button's markup is the Shop package's and is not edited; these selectors are
 * `.secondary-button` scoped to the category row, which this sheet -- loading after the
 * built Tailwind bundle -- wins on specificity.
 */
.plenum-pagination {
    margin-top: 40px;
    text-align: center;
}

.plenum-pagination-count {
    margin: 0;
    font-family: var(--plenum-sans);
    font-size: 13px;
    letter-spacing: 0.02em;
    color: var(--plenum-muted);
}

/*
 * The numbered links: present in the HTML and reachable by keyboard, but not drawn.
 *
 * Clipped rather than `display: none`. A crawler follows either, but display:none also
 * takes the links out of the accessibility tree, and a keyboard visitor who has tabbed
 * to one should be able to use it -- which is what the :focus-within rule restores.
 */
.plenum-pagination-crawl {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
}

.plenum-pagination-crawl:focus-within {
    position: static;
    width: auto;
    height: auto;
    margin: 12px 0 0;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
}

.plenum-pagination-crawl:focus-within ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    list-style: none;
    padding: 0;
    margin: 0;
    font-size: 13px;
}

.plenum-pagination-crawl a {
    color: var(--plenum-ink);
    text-decoration: underline;
}

.plenum-pagination-crawl a[aria-current="page"] {
    font-weight: 600;
    text-decoration: none;
}

/*
 * The button. Brand gold with near-black on it -- white reaches only 2.15:1 on this
 * fill, which is why --plenum-on-gold exists.
 *
 * 40px above comes from .plenum-pagination's own margin when the counter is there, so
 * the button's top margin is reset and the spacing is stated once rather than stacking
 * the two.
 */
.plenum-section-row .secondary-button {
    display: block;
    min-width: 220px;
    height: 48px;
    margin: 16px auto 40px;
    padding: 0 28px;
    border: 0;
    border-radius: 10px;

    background-color: var(--plenum-gold);
    color: var(--plenum-on-gold);

    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;

    cursor: pointer;
    transition: background-color 200ms ease;
}

.plenum-section-row .secondary-button:hover {
    background-color: var(--plenum-gold-deep);
}

.plenum-section-row .secondary-button:focus-visible {
    outline: 2px solid var(--plenum-ink);
    outline-offset: 3px;
}

/*
 * The spinner state is a second button in the stock markup with no click handler, so a
 * second click cannot fire. It is still marked unavailable for anyone not using a
 * mouse, and dimmed so the state reads.
 */
.plenum-section-row .secondary-button[disabled],
.plenum-section-row .secondary-button:disabled {
    opacity: 0.65;
    cursor: default;
}

.plenum-section-row .secondary-button img {
    display: block;
    margin: 0 auto;
    height: 20px;
    width: 20px;
}

@media (max-width: 767.98px) {
    .plenum-section-row .secondary-button {
        min-width: 180px;
        margin: 14px auto 32px;
    }

    .plenum-pagination {
        margin-top: 32px;
    }
}

/* ------------------------------------------------ category rail, redesigned */

/*
 * The rail becomes a card: 260px, white ground, hairline, 12px corners.
 *
 * This reverses the quiet-column treatment it had -- no border, no fill, the gap doing
 * the separating. That was right when the rail was five short links; it is a panel with
 * counts, branches and a help box now, and a panel with no edge floats.
 */
.plenum-category-nav {
    flex: 0 0 260px;
    max-width: 260px;
    padding: 8px;
    background-color: #fff;
    border: 1px solid var(--plenum-hairline);
    border-radius: 12px;

    /*
     * Sticky below the header, which is `sticky top-0` and around 96px tall. Its own
     * scroller, so a long catalogue does not push the foot of the card past the
     * viewport with no way to reach it.
     */
    position: sticky;
    top: 112px;
    max-height: calc(100vh - 132px);
    overflow-y: auto;
    overscroll-behavior: contain;
}

.plenum-category-nav-title {
    margin: 0;
    padding: 10px 12px 8px;
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
}

.plenum-category-nav-list,
.plenum-category-nav-children {
    margin: 0;
    padding: 0;
    list-style: none;
}

.plenum-category-nav-branch {
    position: relative;
}

/*
 * 44px tall, 12px in. The gold left border on the active row is a border that is
 * transparent the rest of the time rather than one added on the active state, so the
 * label does not shift sideways by 3px when you land on it.
 */
.plenum-category-nav-link {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
    padding: 0 12px;
    border-left: 3px solid transparent;
    border-radius: 8px;

    font-size: 15px;
    line-height: 1.3;
    color: var(--plenum-ink);
    text-decoration: none;

    transition: background-color 160ms ease, color 160ms ease;
}

/*
 * Light gold, not the accent itself: a full-strength fill behind 15px text drops it to
 * 1.9:1 and the row becomes a block of colour with a word lost in it.
 */
.plenum-category-nav-link:hover {
    background-color: rgba(210, 171, 103, 0.14);
}

.plenum-category-nav-link:focus-visible {
    outline: 2px solid var(--plenum-gold-contrast);
    outline-offset: -2px;
}

.plenum-category-nav-link-active {
    border-left-color: var(--plenum-gold);
    background-color: rgba(210, 171, 103, 0.14);
    font-weight: 600;
}

.plenum-category-nav-link-active .plenum-cat-icon {
    color: var(--plenum-gold-contrast);
}

.plenum-category-nav-label {
    flex: 1 1 auto;
    min-width: 0;
}

/* 6.18:1 -- subordinate to the name, still readable. */
.plenum-category-nav-count {
    flex: none;
    font-size: 13px;
    color: var(--plenum-muted);
    font-variant-numeric: tabular-nums;
}

.plenum-cat-icon {
    flex: none;
    width: 32px;
    height: 32px;
    color: var(--plenum-ink);
}

/*
 * The branch toggle sits over the right-hand end of the parent row. Absolute so it does
 * not divide the link's hit area -- the whole row stays tappable as the link, and only
 * the chevron opens the level below.
 */
.plenum-category-nav-toggle {
    position: absolute;
    top: 0;
    right: 2px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 44px;
    background: none;
    border: 0;
    color: var(--plenum-muted);
    cursor: pointer;
}

.plenum-category-nav-toggle svg {
    width: 18px;
    height: 18px;
    transition: transform 200ms ease;
}

.plenum-category-nav-toggle[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

.plenum-category-nav-toggle:focus-visible {
    outline: 2px solid var(--plenum-gold-contrast);
    outline-offset: -4px;
    border-radius: 8px;
}

/* Children indent to the parent's label rather than its icon, so the names line up. */
.plenum-category-nav-children .plenum-category-nav-link {
    min-height: 38px;
    padding-left: 54px;
    font-size: 14px;
}

/* ---------------------------------------------------- promo and help boxes */

.plenum-rail-promo {
    display: block;
    margin: 12px 4px 0;
    padding: 14px;
    background-color: var(--plenum-cream);
    border-radius: 10px;
    text-decoration: none;
    color: var(--plenum-ink);
}

.plenum-rail-promo img {
    display: block;
    width: 100%;
    height: auto;
    border-radius: 6px;
    margin-bottom: 10px;
}

.plenum-rail-promo-eyebrow {
    display: block;
    font-family: var(--plenum-label);
    font-size: 10px;
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
}

.plenum-rail-promo-title {
    display: block;
    margin-top: 4px;
    font-size: 14px;
    line-height: 1.4;
}

.plenum-rail-promo-cta {
    display: inline-block;
    margin-top: 8px;
    font-size: 13px;
    text-decoration: underline;
    color: var(--plenum-gold-contrast);
}

.plenum-rail-help {
    margin: 12px 4px 4px;
    padding: 14px;
    border-top: 1px solid var(--plenum-hairline);
}

.plenum-rail-help-title {
    margin: 0 0 8px;
    font-size: 14px;
    color: var(--plenum-ink);
}

.plenum-rail-help-link {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 14px;
    color: var(--plenum-gold-contrast);
    text-decoration: underline;
}

.plenum-rail-help-link svg {
    width: 18px;
    height: 18px;
    flex: none;
}

/* -------------------------------------------------- the chip bar (below 992px) */

/*
 * Under 992px the card stops being a column. It loses its chrome and its sticky
 * position and lays the same links out as a scrollable row above the products -- the
 * markup is unchanged, so there is one rail in the DOM rather than a second copy drawn
 * for small screens.
 */
@media (max-width: 991.98px) {
    .plenum-category-nav {
        flex: 1 1 100%;
        max-width: none;
        position: static;
        max-height: none;
        overflow: visible;
        padding: 0;
        background: none;
        border: 0;
        border-radius: 0;
    }

    .plenum-category-nav-title {
        padding: 0 0 10px;
    }

    .plenum-category-nav-list {
        display: flex;
        gap: 8px;
        overflow-x: auto;
        scrollbar-width: none;
        padding-bottom: 4px;
        -webkit-overflow-scrolling: touch;
    }

    .plenum-category-nav-list::-webkit-scrollbar {
        display: none;
    }

    .plenum-category-nav-list > li {
        flex: none;
    }

    .plenum-category-nav-link {
        min-height: 38px;
        padding: 0 14px;
        border: 1px solid var(--plenum-hairline);
        border-radius: 999px;
        white-space: nowrap;
        font-size: 14px;
        background-color: #fff;
    }

    .plenum-category-nav-link-active {
        border-color: var(--plenum-gold);
    }

    .plenum-cat-icon {
        width: 20px;
        height: 20px;
    }

    /*
     * Branches, their toggles and the two boxes are desktop furniture. A chip row
     * cannot hold an accordion, and a promo card in the middle of it would break the
     * scroll.
     */
    .plenum-category-nav-toggle,
    .plenum-category-nav-children,
    .plenum-rail-promo,
    .plenum-rail-help {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-category-nav-link,
    .plenum-category-nav-toggle svg {
        transition: none;
    }
}

/* ============================================================ product page */

/*
 * The buy box, the sections under it, and the sticky bar.
 *
 * Mobile-first throughout: the base rules are the phone's and the media query adds to
 * them, rather than the other way round. That ordering is not a style preference here --
 * this store's traffic is mostly phones, and a desktop-first sheet makes the phone pay
 * for rules it then has to override.
 *
 * Everything keys on the tokens at the top of this file, so the page inherits the brand
 * without restating a single hex value.
 */

/* ------------------------------------------------------------ the buttons */

/*
 * Add to Cart was the palest thing in the buy box and read as disabled.
 *
 * It is `.secondary-button`, which is the storefront's outline treatment -- correct for a
 * button sitting in a page beside other content, wrong for the primary action on the one
 * screen whose whole job is that action. Buy Now had the filled treatment, so the two
 * were also the wrong way round: the button most people want was the quieter of the two.
 *
 * So on the product page, and only there, Add to Cart takes the filled gold and Buy Now
 * takes ink. Both are solid, both clear AA on their own ground, and the pair reads as two
 * real choices rather than one button and one ghost.
 *
 * Scoped to `.plenum-pdp-details` so cards, listings and checkout keep the outline
 * language they already share.
 */
.plenum-pdp-details .secondary-button {
    border-color: var(--plenum-gold) !important;
    background-color: var(--plenum-gold) !important;
    /* Near-black, not white: white reaches only 2.15:1 on this gold. */
    color: var(--plenum-on-gold) !important;
    font-size: 13px !important;
    min-height: 48px;
}

.plenum-pdp-details .secondary-button:hover {
    border-color: var(--plenum-gold-deep) !important;
    background-color: var(--plenum-gold-deep) !important;
    color: var(--plenum-on-gold) !important;
}

/*
 * Buy Now, repainted as the darker of the two. Ink on ivory is the strongest contrast
 * pair this palette has, which suits the more committing action.
 */
.plenum-pdp-details .primary-button {
    border-color: var(--plenum-ink) !important;
    background-color: var(--plenum-ink) !important;
    color: #fff !important;
    font-size: 13px !important;
    min-height: 48px;
}

.plenum-pdp-details .primary-button:hover {
    border-color: #000 !important;
    background-color: #000 !important;
}

/*
 * A disabled button must look disabled -- which is the whole complaint that started this,
 * and it would be a poor fix to make the live button solid and leave the out-of-stock one
 * looking identical to it.
 */
.plenum-pdp-details .secondary-button:disabled,
.plenum-pdp-details .primary-button:disabled {
    border-color: var(--plenum-hairline) !important;
    background-color: var(--plenum-sand) !important;
    color: var(--plenum-muted) !important;
    cursor: not-allowed;
}

/* -------------------------------------------------------------- the price */

.plenum-price-extras {
    margin-top: 6px;
    display: grid;
    gap: 2px;
}

.plenum-price-saving {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
    margin: 0;
}

/*
 * The discount badge. Gold ground, near-black text, for the contrast reason above.
 *
 * It is not red. A red sale tag is the marketplace convention and it is the one element
 * that would pull this palette apart; the gold reads as an offer here because nothing
 * else in the buy box is filled.
 */
.plenum-discount-badge {
    display: inline-block;
    padding: 3px 9px;
    border-radius: 3px;
    background-color: var(--plenum-gold);
    color: var(--plenum-on-gold);
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    white-space: nowrap;
}

.plenum-price-saving-amount {
    color: var(--plenum-gold-contrast);
    font-family: var(--plenum-sans);
    font-size: 13px;
    font-weight: 600;
}

.plenum-price-tax {
    margin: 0;
    color: var(--plenum-muted);
    font-family: var(--plenum-sans);
    font-size: 12px;
}

/* -------------------------------------------------------------- the stock */

.plenum-stock {
    display: flex;
    align-items: center;
    gap: 7px;
    margin: 14px 0 0;
    font-family: var(--plenum-label);
    font-size: 11.5px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

/*
 * A dot as well as the colour, and wording that differs per state. Colour alone would
 * carry the whole meaning, which fails WCAG 1.4.1 for anyone who cannot separate the
 * greens from the reds.
 */
.plenum-stock-dot {
    flex: 0 0 auto;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background-color: currentColor;
}

.plenum-stock-in {
    color: #2f6d43;
}

.plenum-stock-low {
    color: #9a5b12;
}

.plenum-stock-out {
    color: #8c2f23;
}

/* ----------------------------------------------------------- the benefits */

.plenum-benefits {
    margin-top: 18px;
    padding: 14px 16px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 4px;
    background-color: var(--plenum-ivory);
}

.plenum-benefits-title {
    margin: 0 0 8px;
    color: var(--plenum-ink);
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
}

.plenum-benefits-list {
    display: grid;
    gap: 7px;
    margin: 0;
    padding: 0;
    list-style: none;
}

/*
 * The marker is drawn rather than a list bullet, so it can be gold and sit on the first
 * line of a bullet that wraps. A fixed first column keeps a wrapped second line aligned
 * with the first word instead of tucking under the mark.
 */
.plenum-benefits-list li {
    display: grid;
    grid-template-columns: 14px 1fr;
    align-items: baseline;
    gap: 8px;
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-size: 13.5px;
    line-height: 1.5;
}

.plenum-benefits-list li::before {
    content: "";
    width: 6px;
    height: 6px;
    margin-top: 6px;
    border-radius: 50%;
    background-color: var(--plenum-gold);
}

/* ----------------------------------------------------- the delivery check */

.plenum-delivery {
    margin-top: 18px;
    padding: 14px 16px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 4px;
}

.plenum-delivery-title {
    margin: 0 0 10px;
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: var(--plenum-track);
    text-transform: uppercase;
}

.plenum-delivery-row {
    display: flex;
    align-items: stretch;
    gap: 8px;
}

/* Hidden from the eye, kept for the reader: the input still needs a name. */
.plenum-delivery-label {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.plenum-delivery-input {
    flex: 1 1 auto;
    min-width: 0;
    /* 44px is the comfortable tap target; 16px is what stops iOS zooming on focus. */
    min-height: 44px;
    padding: 0 12px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 3px;
    background-color: #fff;
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-size: 16px;
}

.plenum-delivery-input:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 1px;
}

.plenum-delivery-check {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 18px;
    border: 1px solid var(--plenum-ink);
    border-radius: 3px;
    background-color: transparent;
    color: var(--plenum-ink);
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color 200ms ease, color 200ms ease;
}

.plenum-delivery-check:hover {
    background-color: var(--plenum-ink);
    color: #fff;
}

.plenum-delivery-check:disabled {
    border-color: var(--plenum-hairline);
    color: var(--plenum-muted);
    cursor: wait;
}

.plenum-delivery-result {
    margin: 10px 0 0;
    font-family: var(--plenum-sans);
    font-size: 13px;
    line-height: 1.55;
}

.plenum-delivery-result:empty {
    display: none;
}

.plenum-delivery-eta {
    display: block;
    color: var(--plenum-ink);
    font-weight: 600;
}

.plenum-delivery-note {
    display: block;
    color: var(--plenum-muted);
    font-size: 12.5px;
}

.plenum-delivery-result.is-error {
    color: #8c2f23;
}

/* ------------------------------------------------------- highlights strip */

.plenum-highlights {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin: 16px 0 0;
    padding: 0;
    list-style: none;
}

.plenum-highlight {
    padding: 5px 11px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 999px;
    background-color: var(--plenum-cream);
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-size: 12px;
}

/* --------------------------------------------------------- the disclaimer */

/*
 * Quiet, but never hidden. Smaller than the body copy because it qualifies rather than
 * sells -- and still 12px on a plain ground at 4.6:1, because a legally required sentence
 * set in grey 9px is a sentence the business has decided nobody should read. Do not
 * shrink it further to win vertical space.
 */
.plenum-pdp-disclaimer {
    margin: 16px 0 0;
    padding-top: 12px;
    border-top: 1px solid var(--plenum-hairline);
    color: var(--plenum-muted);
    font-family: var(--plenum-sans);
    font-size: 12px;
    line-height: 1.6;
}

/* ----------------------------------------------- the information sections */

.plenum-pdp-info {
    margin-top: 40px;
    padding: 0 16px;
}

.plenum-pdp-info-inner {
    display: grid;
    gap: 14px;
    max-width: 1320px;
    margin: 0 auto;
}

.plenum-pdp-panel {
    padding: 16px;
    border: 1px solid var(--plenum-hairline);
    border-radius: 4px;
    background-color: #fff;
}

.plenum-pdp-heading {
    margin: 0;
    color: var(--plenum-ink);
    font-family: var(--plenum-display-face);
    font-size: 17px;
    font-weight: 600;
    line-height: 1.3;
}

/*
 * A `<details>` marker has to be removed in two dialects -- the pseudo-element for Blink
 * and WebKit, `list-style` for Gecko -- and a marker of its own put back, or the panel
 * gives no sign that it opens.
 */
.plenum-pdp-details-panel > summary,
.plenum-faq-item > summary {
    position: relative;
    padding-right: 28px;
    list-style: none;
    cursor: pointer;
}

.plenum-pdp-details-panel > summary::-webkit-details-marker,
.plenum-faq-item > summary::-webkit-details-marker {
    display: none;
}

.plenum-pdp-details-panel > summary::after,
.plenum-faq-item > summary::after {
    content: "";
    position: absolute;
    top: 50%;
    right: 4px;
    width: 8px;
    height: 8px;
    border-right: 1.5px solid var(--plenum-gold-deep);
    border-bottom: 1.5px solid var(--plenum-gold-deep);
    transform: translateY(-70%) rotate(45deg);
    transition: transform 200ms ease;
}

.plenum-pdp-details-panel[open] > summary::after,
.plenum-faq-item[open] > summary::after {
    transform: translateY(-30%) rotate(-135deg);
}

.plenum-pdp-details-panel > summary:focus-visible,
.plenum-faq-item > summary:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 3px;
}

.plenum-pdp-prose {
    margin-top: 10px;
    color: var(--plenum-muted);
    font-family: var(--plenum-sans);
    font-size: 13.5px;
    line-height: 1.65;
}

.plenum-pdp-prose p {
    margin: 0 0 8px;
}

.plenum-pdp-prose ul,
.plenum-pdp-prose ol {
    margin: 0 0 8px;
    padding-left: 20px;
}

/* ------------------------------------------------------ the details table */

.plenum-spec-table {
    width: 100%;
    margin-top: 12px;
    border-collapse: collapse;
    font-family: var(--plenum-sans);
    font-size: 13.5px;
}

.plenum-spec-table th,
.plenum-spec-table td {
    padding: 9px 0;
    border-bottom: 1px solid var(--plenum-hairline);
    text-align: left;
    vertical-align: top;
}

.plenum-spec-table tr:last-child th,
.plenum-spec-table tr:last-child td {
    border-bottom: 0;
}

.plenum-spec-table th {
    width: 45%;
    padding-right: 12px;
    color: var(--plenum-muted);
    font-weight: 500;
}

.plenum-spec-table td {
    color: var(--plenum-ink);
    font-weight: 600;
}

/* ---------------------------------------------------------------- the FAQ */

.plenum-faq {
    display: grid;
    margin-top: 12px;
}

.plenum-faq-item {
    border-top: 1px solid var(--plenum-hairline);
}

.plenum-faq-item:first-child {
    border-top: 0;
}

.plenum-faq-question {
    padding: 12px 28px 12px 0;
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-size: 13.5px;
    font-weight: 600;
    line-height: 1.5;
}

.plenum-faq-answer {
    margin: 0;
    padding: 0 28px 14px 0;
    color: var(--plenum-muted);
    font-family: var(--plenum-sans);
    font-size: 13.5px;
    line-height: 1.65;
}

/* --------------------------------------------------------- the sticky bar */

/*
 * Phones only. On a desktop the buy box sits beside the gallery and stays in view as the
 * page scrolls, so a bar pinned to the bottom would cover content to solve a problem that
 * does not exist there.
 */
.plenum-sticky-buy {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    /* The safe area, so the bar clears the home indicator on a notched iPhone. */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    border-top: 1px solid var(--plenum-hairline);
    background-color: #fff;
    box-shadow: 0 -2px 12px rgb(0 0 0 / 8%);
}

.plenum-sticky-buy-price {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: baseline;
    gap: 7px;
}

.plenum-sticky-buy-final {
    color: var(--plenum-ink);
    font-family: var(--plenum-sans);
    font-size: 16px;
    font-weight: 700;
}

.plenum-sticky-buy-mrp {
    color: var(--plenum-muted);
    font-family: var(--plenum-sans);
    font-size: 12.5px;
    text-decoration: line-through;
}

.plenum-sticky-buy-action {
    flex: 0 0 auto;
    min-height: 44px;
    padding: 0 22px;
    border: 0;
    border-radius: 3px;
    background-color: var(--plenum-gold);
    color: var(--plenum-on-gold);
    font-family: var(--plenum-label);
    font-size: 12px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.1em;
    text-transform: uppercase;
    cursor: pointer;
}

.plenum-sticky-buy-action:focus-visible {
    outline: 2px solid var(--plenum-ink);
    outline-offset: 2px;
}

/*
 * The bar is fixed over the page, so the footer needs room or its last line sits under it
 * permanently. The class is put on the body by the script, only while the bar shows.
 */
body.has-plenum-sticky-buy {
    padding-bottom: 72px;
}

/*
 * The trust badges on the product page, enlarged as asked. The strip's markup is
 * untouched -- this is the same compact strip the buy box already included, given room to
 * be read at the point of decision.
 */
.plenum-pdp-details .plenum-trust.is-compact .plenum-trust-icon,
.plenum-pdp-details .plenum-trust.is-compact svg {
    width: 26px;
    height: 26px;
}

@media (min-width: 1024px) {
    .plenum-sticky-buy {
        display: none !important;
    }

    body.has-plenum-sticky-buy {
        padding-bottom: 0;
    }

    .plenum-pdp-info {
        padding: 0 60px;
    }

    .plenum-pdp-panel {
        padding: 22px 26px;
    }

    .plenum-pdp-heading {
        font-size: 20px;
    }

    .plenum-spec-table th {
        width: 30%;
    }

    .plenum-benefits-list li,
    .plenum-pdp-prose,
    .plenum-faq-question,
    .plenum-faq-answer,
    .plenum-spec-table {
        font-size: 14.5px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-pdp-details-panel > summary::after,
    .plenum-faq-item > summary::after,
    .plenum-delivery-check,
    .plenum-pdp-details .secondary-button,
    .plenum-pdp-details .primary-button {
        transition: none;
    }
}

/* ------------------------------------- WhatsApp, in the announcement strip */

/*
 * Positioning context for the mark, and room on the right so a long offer ellipsises
 * before it reaches the glyph rather than running underneath it.
 *
 * The padding is restated here rather than edited into the rule above, because that one
 * is the bar's own geometry and this is a reservation made for a specific child -- keeping
 * them apart means removing the WhatsApp link later is deleting this block, not unpicking
 * a value out of another one.
 */
.plenum-announcement-inner {
    position: relative;
    padding-right: 56px;
}

/*
 * Absolutely positioned rather than a third flex child, so the rotating message stays
 * optically centred in the bar whether the mark is there or not. As a flex item it would
 * push the message left by half its own width, and that offset would appear and disappear
 * with a config value -- the kind of layout that looks broken on one environment and fine
 * on another.
 */
.plenum-announcement-wa {
    position: absolute;
    top: 50%;
    right: 16px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: 7px;
    /*
     * The strip is 38px tall, so this cannot take a 44px tap target without changing the
     * height of a bar the whole page sits under. The padding widens the hit area
     * horizontally instead, which is the dimension a thumb actually misses on a control
     * this narrow.
     */
    min-height: 34px;
    padding: 0 4px;
    /*
     * WhatsApp's own green -- the one colour on this site that is not the brand's, and
     * deliberately so: the mark is a third-party identity and a customer recognises it by
     * colour before reading anything. #25D366 on the ink ground clears 7:1.
     */
    color: #25d366;
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: var(--plenum-label-weight);
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-decoration: none;
    transition: opacity 200ms ease;
}

.plenum-announcement-wa:hover {
    opacity: 0.82;
}

.plenum-announcement-wa:focus-visible {
    outline: 2px solid #25d366;
    outline-offset: 3px;
}

.plenum-announcement-wa svg {
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
}

@media (max-width: 599px) {
    /*
     * The words go on a phone; the mark alone is understood. At 360px "Chat with us" plus
     * the glyph took a third of the bar and cut the offer beside it to about four words.
     */
    .plenum-announcement-wa-text {
        display: none;
    }

    .plenum-announcement-wa {
        right: 10px;
    }

    .plenum-announcement-inner {
        padding-right: 38px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-announcement-wa {
        transition: none;
    }
}

/* ===========================================================================
   The offers page (/offers)

   Driven by plenum/offers-page.blade.php. The ribbon gold and the card chrome are
   the theme's own tokens rather than new values -- an offers page is the one place
   tempted into a louder palette, and a second gold would make the site look like
   two sites.
   =========================================================================== */

.plenum-offers-page {
    max-width: 1120px;
    margin: 0 auto;
    padding: clamp(2rem, 5vw, 3.5rem) 20px clamp(3rem, 7vw, 5rem);
}

.plenum-offers-hero {
    text-align: center;
    margin-bottom: clamp(2rem, 5vw, 3rem);
}

/* The bow. Clamped rather than fixed so it tracks the heading it sits over; at the
   small end it is still large enough to read as a bow rather than a smudge. */
.plenum-offers-bow {
    display: block;
    width: clamp(96px, 13vw, 148px);
    height: auto;
    margin: 0 auto clamp(0.5rem, 1.5vw, 1rem);
}

.plenum-offers-bow-loop {
    fill: var(--plenum-gold);
}

/* The tails a shade deeper, so the bow reads as folded ribbon rather than a flat
   silhouette. Two tones is the whole trick; a third would be a gradient. */
.plenum-offers-bow-tail {
    fill: var(--plenum-gold-deep);
}

.plenum-offers-bow-knot {
    fill: var(--plenum-gold-deep);
}

.plenum-offers-eyebrow {
    font-family: var(--plenum-sans);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
    margin: 0 0 0.5rem;
}

.plenum-offers-title {
    font-family: var(--plenum-display-face);
    font-weight: var(--plenum-display-weight);
    font-size: clamp(2.25rem, 6vw, 3.5rem);
    line-height: 1.1;
    color: var(--plenum-ink);
    margin: 0 0 0.75rem;
}

.plenum-offers-lede {
    font-family: var(--plenum-sans);
    font-size: clamp(0.9rem, 1.5vw, 1rem);
    line-height: 1.65;
    color: var(--plenum-muted);
    max-width: 52ch;
    margin: 0 auto;
}

/* auto-fit rather than a fixed count: five offers do not divide evenly into three
   columns, and auto-fit lets the last row centre itself instead of stranding one
   card against the left edge. */
/* A vertical list, one offer per row, not a grid of columns.

   Held to the banners' own width. Every file is now exactly 600x450, so a wider
   card would upscale all five and soften them -- at 860px each was being stretched
   by 43%. 600px renders them 1:1 on a standard display, which is the sharpest they
   can be and the whole reason for fixing the source size. */
.plenum-offers-grid {
    display: flex;
    flex-direction: column;
    /* Roomier than the grid's old 14-20px. These are full-bleed banners now rather
       than bordered cards with copy, so they need a clear band between them to read
       as five separate offers instead of one long strip of artwork. */
    gap: clamp(28px, 4vw, 44px);
    max-width: 600px;
    margin: 0 auto;
    list-style: none;
    padding: 0;
}

/* Image-only: the banner IS the card, so there is no row to lay out any more.
   overflow:hidden does two jobs here -- it clips the ribbon on a text-only card,
   and it holds the hover zoom inside the card's own edges. */
.plenum-offer-card {
    position: relative;
    display: block;
    background-color: #fff;
    border: 1px solid var(--plenum-hairline);
    overflow: hidden;
    transition: border-color 160ms ease;
}

/* The anchor wrapping the banner. display:block so it has no inline descender
   gap under the image, which otherwise shows as a hairline of card background. */
.plenum-offer-card-media {
    display: block;
}

/* The banner. Full card width at its own proportions -- NOT cropped to a shared
   box: every one of these carries its offer in set type, and object-fit: cover
   would cut the words off the edge of half of them. Uneven card heights are the
   price, and the grid stretches each row to its tallest anyway. */
/* Full width at 4:3. Nothing is cropped: each banner carries its offer in set type,
   so it is fitted inside the 600x450 box and the leftover is filled with the colour
   sampled from that banner's own edge -- object-fit: cover would cut the words off.
   aspect-ratio states the shape so the box is reserved even before the file lands. */
.plenum-offer-card-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 600 / 450;
    transition: transform 320ms ease;
}

/* The slight zoom on hover. Scale rather than width, so it costs no layout. */
.plenum-offer-card-media:hover .plenum-offer-card-image {
    transform: scale(1.03);
}

.plenum-offer-card-media:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: -2px;
}

/* The padding moved off the card and onto this, so the banner can run edge to
   edge while the words keep their margin. */
.plenum-offer-card-body {
    position: relative;
    display: flex;
    flex: 1 1 auto;
    flex-direction: column;
    align-items: flex-start;
    /* Centred against the banner's height: the artwork sets the row's height now,
       and copy pinned to the top of a tall square banner looks dropped. */
    justify-content: center;
    gap: 0.7rem;
    padding: clamp(1.25rem, 3vw, 1.75rem);
    min-width: 0;
}

.plenum-offer-card:hover {
    border-color: var(--plenum-gold);
}

/* The corner ribbon: a square rotated 45deg and pushed mostly out of the card, so
   only the triangle inside the padding box survives the overflow clip. */
.plenum-offer-card-ribbon {
    position: absolute;
    /* Against the body, not the card: with a banner above it, a ribbon pinned to
       the card's own corner would land on the artwork instead of the copy. */
    top: -26px;
    right: -26px;
    width: 52px;
    height: 52px;
    transform: rotate(45deg);
    background-color: var(--plenum-sand);
    pointer-events: none;
}

.plenum-offer-card-badge {
    font-family: var(--plenum-sans);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--plenum-on-gold);
    background-color: var(--plenum-gold);
    padding: 5px 10px;
    margin: 0;
}

.plenum-offer-card-text {
    font-family: var(--plenum-serif);
    font-size: clamp(1.05rem, 2vw, 1.25rem);
    line-height: 1.4;
    color: var(--plenum-ink);
    margin: 0;
}

/* Underlined rather than buttoned: there are up to five of these on the page and
   five gold buttons would each be shouting at the same volume as the badges.

   No `margin-top: auto` any more. That belonged to the column layout, where it
   pushed the link to the foot of each card so five of them lined up across a row.
   In the vertical list the banner sets the row height, so all it did was drive the
   link to the bottom of a tall row and leave a band of dead space under one line
   of copy. The three now sit together as one centred block. */
.plenum-offer-card-link {
    font-family: var(--plenum-sans);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
    text-decoration: underline;
    text-underline-offset: 4px;
    padding-top: 0.35rem;
}

.plenum-offer-card-link:hover {
    color: var(--plenum-ink);
}

/* The one offer with no banner. It is simply a shorter card now the row no longer
   stretches; the gold rule stands in for the artwork so it still reads as one of
   the set rather than as a card that failed to load. */
.plenum-offer-card.is-textonly {
    border-top: 3px solid var(--plenum-gold);
}

/* No breakpoint needed for the cards any more. They were a row of banner-beside-copy
   that had to collapse under 700px; image-only, a card is one full-width banner at
   every size and there is nothing left to rearrange. */

.plenum-offers-note,
.plenum-offers-empty {
    font-family: var(--plenum-sans);
    font-size: 13px;
    line-height: 1.6;
    color: var(--plenum-muted);
    text-align: center;
    max-width: 56ch;
    margin: clamp(1.75rem, 4vw, 2.5rem) auto 0;
}

.plenum-offers-empty {
    margin-top: 0;
}

/* The hover zoom is the only motion on the page, and it is the first thing to go. */
@media (prefers-reduced-motion: reduce) {
    .plenum-offer-card,
    .plenum-offer-card-image {
        transition: none;
    }

    .plenum-offer-card-media:hover .plenum-offer-card-image {
        transform: none;
    }
}

/* ---------------------------------------------------------------------------
   The Offers link at the left-hand end of the announcement strip.

   Mirrors .plenum-announcement-wa opposite it: absolutely positioned so the
   rotating message stays optically centred whether either end is there or not.
   --------------------------------------------------------------------------- */

.plenum-announcement-offers {
    position: absolute;
    top: 50%;
    left: 16px;
    transform: translateY(-50%);
    display: inline-flex;
    align-items: center;
    gap: 7px;
    /* Same reasoning as the WhatsApp mark: the strip is 38px tall, so the hit area
       widens horizontally rather than vertically. */
    min-height: 34px;
    padding: 0 4px;
    color: var(--plenum-gold);
    font-family: var(--plenum-label);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    text-decoration: none;
    transition: color 160ms ease;
}

.plenum-announcement-offers:hover {
    color: #fff;
}

.plenum-announcement-offers:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 2px;
}

.plenum-announcement-offers svg {
    width: 14px;
    height: 14px;
    flex: none;
}

/* Under 600px the rotating message needs the whole bar, so both ends keep their
   mark and drop their word -- the same breakpoint the WhatsApp text uses. */
@media (max-width: 600px) {
    .plenum-announcement-offers-text {
        display: none;
    }
}

@media (prefers-reduced-motion: reduce) {
    .plenum-announcement-offers {
        transition: none;
    }
}

/* ---------------------------------------------------------------------------
   OFFERS in the main header nav.

   Set to match the division links beside it -- same size, weight, tracking and
   caps -- so the row reads as one nav rather than as a button dropped into it.
   The gold is the single difference, because this is the one item that is not a
   division and the colour is what says so.
   --------------------------------------------------------------------------- */

.plenum-header-offers {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: none;
    /* Clears the division links on the left, which Vue draws with its own margins,
       and the search bar on the right -- without the right margin this sits hard
       against the search input with no gap at all. */
    margin-left: 4px;
    margin-right: clamp(16px, 2vw, 28px);
    font-family: var(--plenum-sans);
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    /* The readable gold, not the accent: this is small text on a white header and
       --plenum-gold reaches only 1.9:1 there. See the note on the token. */
    color: var(--plenum-gold-contrast);
    text-decoration: none;
    white-space: nowrap;
    transition: color 160ms ease;
}

.plenum-header-offers:hover {
    color: var(--plenum-ink);
}

.plenum-header-offers:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: 3px;
}

/* The page you are on, marked for the eye as well as for the reader. */
.plenum-header-offers[aria-current="page"] {
    color: var(--plenum-ink);
    text-decoration: underline;
    text-underline-offset: 5px;
}

.plenum-header-offers svg {
    width: 15px;
    height: 15px;
    flex: none;
}

@media (prefers-reduced-motion: reduce) {
    .plenum-header-offers {
        transition: none;
    }
}

/* ===========================================================================
   The offer band on the home page (plenum/home-offers).

   Deliberately not the /offers layout. That page lists the banners vertically at
   their own 600px because they are its subject; here they are one section among
   several, so they go three across and stay inside their own resolution.
   =========================================================================== */

.plenum-home-offers {
    max-width: 1120px;
    margin: 0 auto;
    padding: clamp(2.5rem, 6vw, 4rem) 20px clamp(1.5rem, 4vw, 2.5rem);
    text-align: center;
}

.plenum-home-offers-head {
    margin-bottom: clamp(1.25rem, 3vw, 2rem);
}

.plenum-home-offers-eyebrow {
    font-family: var(--plenum-sans);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
    margin: 0 0 0.4rem;
}

.plenum-home-offers-title {
    font-family: var(--plenum-display-face);
    font-weight: var(--plenum-display-weight);
    font-size: clamp(1.75rem, 4vw, 2.75rem);
    line-height: 1.1;
    color: var(--plenum-ink);
    margin: 0;
}

/* auto-fit with a 300px floor gives three across at 1120px, two on a tablet and one
   on a phone without a media query per step. 300px is also the point below which a
   600x450 banner's set type stops being readable. */
.plenum-home-offers-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: clamp(16px, 2.5vw, 26px);
    list-style: none;
    margin: 0;
    padding: 0;
}

.plenum-home-offer {
    overflow: hidden;
    border: 1px solid var(--plenum-hairline);
    background-color: #fff;
    transition: border-color 160ms ease;
}

.plenum-home-offer:hover {
    border-color: var(--plenum-gold);
}

.plenum-home-offer-media {
    display: block;
}

.plenum-home-offer-image {
    display: block;
    width: 100%;
    height: auto;
    aspect-ratio: 600 / 450;
    transition: transform 320ms ease;
}

.plenum-home-offer-media:hover .plenum-home-offer-image {
    transform: scale(1.03);
}

.plenum-home-offer-media:focus-visible {
    outline: 2px solid var(--plenum-gold);
    outline-offset: -2px;
}

.plenum-home-offers-all {
    display: inline-block;
    margin-top: clamp(1.25rem, 3vw, 2rem);
    font-family: var(--plenum-sans);
    font-size: 12px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--plenum-gold-contrast);
    text-decoration: underline;
    text-underline-offset: 5px;
}

.plenum-home-offers-all:hover {
    color: var(--plenum-ink);
}

@media (prefers-reduced-motion: reduce) {
    .plenum-home-offer,
    .plenum-home-offer-image {
        transition: none;
    }

    .plenum-home-offer-media:hover .plenum-home-offer-image {
        transform: none;
    }
}
