/*
Theme Name: Amaryllis Custom Theme
Author: Your Name
Description: A custom built theme from scratch.
Version: 1.6
*/

/* =========================================
   0. DESIGN TOKENS
   --------------------------------------
   Every number here was read off the Figma screenshots, not the
   Inspect panel. Correct values in THIS block only — the rest of
   the file reads from it.
========================================= */
:root {
    /* --- Type ---
       TWO families, and the split is deliberate:

         --font-display  HEADINGS. Source Sans Pro. Google Fonts now
                         ships this family as "Source Sans 3" (same
                         typeface, newer release), so both names are
                         listed — the new one is what actually loads,
                         the old one catches anyone who has the
                         original installed locally. Requesting
                         "Source+Sans+Pro" from the css2 API returns
                         nothing, so it cannot be the loaded name.

         --font-body     BODY TEXT. Open Sans.

       Both are enqueued together in functions.php. If you change
       either name here you MUST change it there too, or the family
       silently falls back to Segoe UI and nobody notices until the
       page is on a machine that lacks it. */
    --font-display: 'Source Sans 3', 'Source Sans Pro', 'Segoe UI', Tahoma, sans-serif;
    --font-body: 'Open Sans', 'Segoe UI', Tahoma, sans-serif;

    /* --font-accent  POPPINS. The third family, and the exception that
       proves the rule: in the whole Figma file it is used exactly three
       times, all on the account screens — the "+1" dial code, the
       "Hide" password toggle and the terms sentence. Nothing else on
       the site should reach for this. Loaded at w400 only, since that
       is the only weight the design uses it at. */
    --font-accent: 'Poppins', 'Open Sans', 'Segoe UI', Tahoma, sans-serif;

    /* Read from the Figma file via the API, not off a screenshot.
       The previous values were uniformly ~1.5x too small because they
       were eyeballed at partial zoom — every one of these is now the
       figure the design actually specifies. */
    --fs-hero: 64px;          /* was 52 — Source Sans Pro w700, lh 74 */
    --fs-hero-sub: 24px;      /* Home: Open Sans w400, lh 32.7 */      /* was 18 */
    --fs-card-title: 36px;    /* half-width series card, w700 lh 45.3 */
    --fs-card-title-wide: 40px; /* full-width series card, w700 lh 50.3 */    /* was 36 — w700, lh 40 */
    --fs-card-body: 14px;     /* Open Sans w400, lh 19.1 */
    --fs-section-title: 40px; /* was 32 — w600, lh 24 */
    --fs-btn-lg: 20px;        /* "DOWNLOAD CATALOGUE" - Figma sets 32, reduced by request */
    --fs-btn-sm: 12px;        /* was 13 — w600, lh 15.1 */
    --fs-nav: 16px;           /* was 15 — Source Sans Pro w600, lh 24 */
    --fs-footer-head: 20px;   /* already correct */
    --fs-footer-link: 14px;   /* was 15 — Open Sans w400, lh 24 */

    /* --- Colour ------------------------------------------------
     * Also straight from the file. The theme previously carried 92
     * distinct hex values for what the design defines as a handful —
     * eight near-identical darks, seven near-identical light greys.
     * These are the ones actually used, by frequency across 1191
     * text nodes.
     * ---------------------------------------------------------- */
    --c-ink: #1f1f1f;         /* was #000000 — the dominant ink, x871 */
    --c-text: #1f1f1f;        /* was #333333 — body copy is the same ink */
    --c-muted: #8a8a8a;       /* was #555555 — inactive tabs, secondary labels */
    --c-line: #d9d9d9;        /* was #e8e8e8 */

    /* Two whites, and the difference is deliberate: the PAGE sits on
       #fdfdfd while the header bar is pure #ffffff on top of it. */
    --c-white: #ffffff;
    --c-page: #fdfdfd;

    --c-footer-bg: #1f1f1f;   /* was #1e1e1e */
    --c-footer-link: #fdfdfd; /* was #d6d6d6 */
    --c-footer-tagline: #f5f9f1;

    /* The brand green. CSS previously used a slightly different green
       that does not appear anywhere in the design file. */
    --c-brand: #81af4a;

    /* The wishlist heart, on request — it used to draw in the body ink
       and was indistinguishable from the share icon next to it. Kept in
       the same red family as the error states already in the theme
       (#b42318, #8a2b21) rather than a pure #ff0000, which would be the
       loudest colour on a page whose only other accent is the green. */
    --c-wish: #d92d20;

    /* --- One card shadow, sitewide -----------------------------
     * The Figma file carries FOUR different drop shadows on what is
     * otherwise the same kind of card — y4/blur4 on the homepage FAQ,
     * y10/blur10 on About, y12/blur12 on the blog cards and
     * y14/blur25 on Career and Contact, all at 20% black. That reads
     * as drift rather than intent, so the theme uses ONE value
     * everywhere: the y14/blur25 that appears most often. Change it
     * here and every card on the site follows.
     * ---------------------------------------------------------- */
    --card-shadow: 0 14px 25px rgba(0, 0, 0, 0.2);
    --card-shadow-hover: 0 18px 32px rgba(0, 0, 0, 0.24);

    /* --- Layout --- */
    /* Figma inspect: the multipurpose card is 1372 x 496 on a 1440
       frame, so each gutter is 34px. The cap is raised past 1372 so
       that gutter stays 34px on larger screens instead of the content
       stopping dead and leaving 270px of white either side. 1852 =
       1920 minus two gutters, i.e. edge-to-edge on a common large
       display. Beyond that it holds, so the artwork cannot grow
       without limit on an ultrawide. */
    --container: 1852px;
    /* Header and footer keep the wider 5% inset, matching Figma. */
    --gutter: 5%;
    /* Content sections sit much tighter to the edge in Figma —
       roughly 31px, against the ~76px that 5% produced. Clamped so it
       still breathes on phones and does not run away on wide screens. */
    --section-gutter: clamp(18px, 2.4vw, 34px);
    /* The product grid is now BUTTED — cards meet with no gutter and
       the block runs edge to edge, so both of these are zero. They are
       kept as tokens rather than deleted because the two-column
       breakpoint still reads --grid-gap, and putting a gap back is a
       one-line change here. */
    --grid-gap: 0px;
    --card-radius: 0px;
    --card-border: #000000;
    --card-border-width: 1px;
    /* --- One banner height for every page --------------------------
     * Each screen's Figma frame draws its banner a different height
     * (810 on the home and product pages, 692 on Career and Contact,
     * 620 on About, 628 on Aircare, 548 on the policies, 546 on a blog
     * post). The client wants them identical and filling the window, so
     * every banner is now this token instead: the visible screen, less
     * the sticky header above it.
     *
     * dvh, not vh: on a phone vh is the height with the browser chrome
     * HIDDEN, so a 100vh banner sits partly under the address bar until
     * you scroll. dvh tracks the chrome as it collapses.
     *
     * The subtraction is the header's own min-height, which steps down
     * at the same two breakpoints (80 / 72 / 64) — see .header-container.
     *
     * max(420px, ...) is the floor: a phone held sideways leaves barely
     * 300px of window, which is not enough for a heading, a sub-line and
     * a button. There the banner is allowed to be taller than the window.
     *
     * FULL WINDOW IS A DESKTOP ANSWER. On a phone it fills the screen
     * edge to edge and hides the fact that there is a page underneath,
     * so the two smaller breakpoints below take a share of the window
     * instead — a little over half, the proportion most sites settle on
     * — with a ceiling so a tall phone does not undo it. Both keep the
     * same token, so every page still matches every other.
     * ------------------------------------------------------------- */
    --hero-h: max(420px, calc(100vh - 80px));
    --hero-h: max(420px, calc(100dvh - 80px));
    /* Figma's Nav Bar frame is 1440 x 100, with a 32px gap between
       nav items and a 216 x 31 logo. */
    /* Figma's Nav Bar component is 1440 x 100 and that is what this
       was. Narrowed to 80 on request — a deliberate departure from the
       file. The bar's tallest content is the 31px logo, so 80 still
       leaves ~24px of air above and below it, and the condensed height
       below drops to match.

       This token is not only the bar: scroll-padding-top and the
       drawer max-heights are both calculated from it, so they follow
       automatically. Only screens above 1050 use it — narrower ones
       set 72px and 64px literals further down, and those were already
       compact enough to leave alone. */
    --header-h: 80px;

    /* Carousel card size — fitted to the SCREEN, not fixed.
       Figma drew 448 x 710, which on a 900px-tall laptop filled almost
       the whole window with one card. The HEIGHT follows the viewport:
         - 58% of the screen height, so the heading and a whole card
           sit in view together on a laptop or desktop;
         - never under 360px or over 680px, so an XXL monitor gets a
           generous card and a short laptop still a usable one;
         - never taller than --slide-cap. On a phone that is 96% of the
           screen width, so the card comes out about 60% of the width
           with the next one peeking in. Tablets use a tighter cap
           (section 5): two and a half cards across, not two big ones.
       The WIDTH is the height times --slide-ratio: Figma's tall 0.63 on
       phones and tablets, widened to 0.70 on desktop (section 5). No
       wider than that — the category videos are 9:16, with titles
       filmed near the top and captions near the bottom, and a squarer
       card crops them.
       Section 5 also upgrades vh to svh where supported, so a phone
       address bar sliding away does not resize the cards mid-scroll. */
    --slide-cap: 96vw;
    --slide-ratio: 0.63;
    --slide-h: min(clamp(360px, 58vh, 680px), var(--slide-cap));
    --slide-w: calc(var(--slide-h) * var(--slide-ratio));
    /* Figma leaves a white gutter between slides, not a butted edge.
       Swiper reads this value from main.js, so change it in BOTH
       places if you adjust it. */
    --slide-gap: 16px;

    /* Footer social badge size. Everything else about the badges —
       gap, glyph size, YouTube's proportions — is derived from this. */
    /* Figma: social icons are 24 x 24, drawn as plain glyphs rather
       than the coloured badges this was sized for. */
    --social-size: 24px;

    /* --- Motion --- */
    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    /* Expo-out: fast start, long soft settle. This curve is most of
       why the reveal reads as modern rather than mechanical. */
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* =========================================
   1. RESET & BASE
========================================= */
*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

/* --- The site is a light-mode site, and says so -------------------
 * Every colour on the page is declared, so a browser in dark mode
 * renders it exactly as designed and nothing here is strictly needed
 * for that. Two things still go wrong without this line:
 *
 *   1. Chrome on Android ("Auto dark theme") and Samsung Internet
 *      ("Dark mode for web") REPAINT sites that have not said which
 *      schemes they support. They invert the page's whites to near
 *      black — and the logo, the header marks and the footer artwork
 *      are black files, so they would vanish into it.
 *
 *   2. Native controls the theme does not paint itself — the scrollbar,
 *      a date picker, Chrome's autofill fill on an input — follow the
 *      OS instead, which puts dark widgets on our white forms.
 *
 * `only light` is the strong form: it declares light AND opts the page
 * out of any automatic dark transformation. A real dark theme would
 * replace this with `light dark` and a prefers-color-scheme block.
 * ------------------------------------------------------------- */
:root {
    color-scheme: only light;
}

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

body {
    font-family: var(--font-body);
    color: var(--c-text);
    line-height: 1.6;
    /* #fdfdfd, not pure white — the header bar sits on top of this in
       #ffffff, and the design relies on that small difference. */
    background: var(--c-page);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

img {
    max-width: 100%;
    display: block;
}

a {
    text-decoration: none;
    color: inherit;
}

ul {
    list-style: none;
}

button {
    font: inherit;
}

:focus-visible {
    outline: 2px solid currentColor;
    outline-offset: 3px;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1100;
    padding: 12px 20px;
    background: var(--c-ink);
    color: var(--c-white);
    font-size: 14px;
}

.skip-link:focus {
    left: 0;
}

/* Text for screen readers only — a control whose whole meaning is
   visual, like a colour swatch, needs its name in the page somewhere.
   Moved here from css/product-single.css: the product LISTING draws
   swatches too, and that stylesheet is not loaded there, so every
   colour name was going to render as visible text inside a 24px
   circle. It is a generic utility; it belongs in the base sheet.
   Clipped rather than display:none, which would remove it from the
   accessibility tree along with everything else. */
.screen-reader-text {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* =========================================
   2. HEADER
   Figma: 3-zone bar — logo left, nav optically centred on the
   page, utility icons right. Thin hairline underneath.
========================================= */
/* Figma: the bar is 1440 x 100, fill #ffffff, with the logo starting
   at x=77 and the utility group ending at x=1362 — so 77/78 side
   margins, i.e. 5.35% of the frame rather than the 5% this had.

   NO bottom hairline: the design has none. Separation comes from the
   bar being pure #ffffff against the #fdfdfd page behind it. */
.site-header {
    background-color: var(--c-white);
    padding: 0 clamp(16px, 5.35vw, 77px);
    position: sticky;
    top: 0;
    z-index: 1000;
}

/* 1fr / auto / 1fr keeps the nav centred on the PAGE, not in the
   leftover space between logo and actions. */
.header-container {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 24px;
    max-width: var(--container);
    min-height: var(--header-h);
    margin: 0 auto;
}

/* --- Logo --- */
.logo {
    justify-self: start;
    display: flex;
    align-items: center;
}

/* The Figma lockup is a mark stacked above the wordmark, roughly 3:1.
   Height-driven sizing keeps it aligned with the nav regardless of the
   exported file's dimensions. max-width guards against an export that
   came out with extra whitespace baked in. */
/* Figma: logo-straight_black is 216 x 31 at x=77, y=38. */
.logo-img {
    height: 31px;
    width: auto;
    max-width: 216px;
    object-fit: contain;
    object-position: left center;
}

/* Fallback lockup: mark stacked over a tracked-out wordmark */
.logo-fallback {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    color: var(--c-ink);
}

.logo-mark svg {
    width: 22px;
    height: 22px;
    display: block;
}

.logo-word {
    font-family: var(--font-display);
    font-size: 19px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 4.5px;
    text-transform: uppercase;
    /* letter-spacing adds a trailing gap; pull it back so the
       lockup stays optically centred under the mark */
    margin-right: -4.5px;
}

.logo-word sup {
    font-size: 0.45em;
    top: -0.85em;
    letter-spacing: 0;
}

/* --- Navigation --- */
.main-nav {
    justify-self: center;
}

.main-nav ul {
    display: flex;
    align-items: center;
    gap: 32px;  /* was 50 — Figma's nav Frame has a 32px item gap */
}

/* Figma: Source Sans Pro w600 16 / lh 24 / #1f1f1f.
   The family MUST be declared: with no rule these links inherit the
   body font, which is Open Sans, and the nav silently renders in the
   wrong typeface. */
/* Figma: Source Sans Pro w600 16 / lh 24 / #1f1f1f.
   The family MUST be declared: with no rule these links inherit the
   body font, which is Open Sans, and the nav silently renders in the
   wrong typeface.

   The 11px side padding is not decoration. Figma's nav items are
   boxes 62-74 wide holding 41-49px of text, separated by a 32px gap —
   so the space between one word and the next is 32 + 11 + 11 ≈ 54px,
   not 32. Without the padding the nav sits noticeably tighter than
   the design. */
.main-nav a {
    position: relative;
    display: inline-block;
    padding: 0 11px;
    font-family: var(--font-display);
    font-size: var(--fs-nav);
    font-weight: 600;  /* was 400 */
    color: var(--c-ink);
    white-space: nowrap;
}

/* Current page keeps the underline permanently */
.main-nav a[aria-current="page"]::after {
    transform: scaleX(1);
    transform-origin: left center;
}

/* Underline wipes in from the left, out to the right.
   Inset by the 11px side padding so it tracks the WORD rather than
   the padded box — otherwise it runs 22px wider than the label. */
.main-nav a::after {
    content: '';
    position: absolute;
    left: 11px;
    right: 11px;
    bottom: -5px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right center;
    transition: transform 0.38s var(--ease);
}

.main-nav a:hover::after,
.main-nav a:focus-visible::after {
    transform: scaleX(1);
    transform-origin: left center;
}

/* --- Utility actions --- */
/* Figma spaces these unevenly: cart —24— wishlist —8— Login —8—
   hamburger. The base gap is 8, and the extra 16 between the two
   icons is added below to make 24. */
/* Figma spaces these unevenly — cart —24— wishlist —8— Login — which
   reads as the heart being crowded against the word. One uniform 24px
   gap across all three instead, using the design's icon-to-icon
   figure. */
.header-actions {
    justify-self: end;
    display: flex;
    align-items: center;
    gap: 24px;
}

.icon-link {
    display: inline-flex;
    align-items: center;
    color: var(--c-ink);
    transition: opacity 0.25s var(--ease);
}

.icon-link i {
    font-size: 20px;
    line-height: 1;
}

/* Figma: every utility icon is a 24 x 24 component. Was 21. */
.icon-link svg {
    width: 24px;
    height: 24px;
    display: block;
}

.icon-link:hover {
    opacity: 0.6;
}

/* Same type as the nav links — Source Sans Pro w600 16 / #1f1f1f.
   Without the explicit family this inherits Open Sans. */
.login-link {
    position: relative;
    font-family: var(--font-display);
    font-size: var(--fs-nav);
    font-weight: 600;
    color: var(--c-ink);
    white-space: nowrap;
}

.login-link::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -5px;
    width: 100%;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right center;
    transition: transform 0.38s var(--ease);
}

.login-link:hover::after,
.login-link:focus-visible::after {
    transform: scaleX(1);
    transform-origin: left center;
}

/* =========================================
   2c. MENU PANEL
   -----------------------------------------
   Opened by the hamburger at EVERY width. On desktop it sits beside
   the inline nav and carries what the bar has no room for — the five
   categories, account links and the policy page.

   Figma's "Humberger" frame is 379 x 910, i.e. full-bleed on a phone,
   so the panel is min(100%, 400px): the whole screen on mobile and a
   drawer on desktop.

   Same no-JS contract the cart drawer uses. Without JavaScript the
   hamburger is a plain link to #site-menu, :target reveals the panel
   in normal flow at the foot of the header, and the scrim and close
   control are hidden because there is nothing to overlay. With
   JavaScript it becomes a fixed overlay that slides in.
========================================= */
html:not(.has-js) .site-menu:not(:target) {
    display: none;
}

html:not(.has-js) .site-menu-scrim,
html:not(.has-js) .site-menu-close {
    display: none;
}

.has-js .site-menu {
    position: fixed;
    inset: 0;
    z-index: 1300;
    visibility: hidden;
    pointer-events: none;
}

.has-js .site-menu.is-open {
    visibility: visible;
    pointer-events: auto;
}

.site-menu-scrim {
    position: absolute;
    inset: 0;
    display: block;
    background: rgba(15, 15, 15, 0.42);
    opacity: 0;
    transition: opacity 0.4s var(--ease-out);
}

.has-js .site-menu.is-open .site-menu-scrim {
    opacity: 1;
}

.site-menu-panel {
    position: relative;
    display: flex;
    flex-direction: column;
    /* Figma's rows block is 339 wide inside a 379 panel, so 20px of
       inset on each side. Held as a token because the head, the rows
       and the sub-lists all have to sit on the same column. */
    --menu-inset: 20px;
    /* Figma fills the panel #fdfdfd (Rectangle 99), which is --c-page,
       not the flat white this had. */
    background: var(--c-page);
}

.has-js .site-menu-panel {
    position: absolute;
    top: 0;
    right: 0;
    /* Figma: the panel is 379 wide. Full-bleed below that, since the
       frame is drawn at phone width in the first place. */
    width: min(100%, 379px);
    height: 100%;
    overflow-y: auto;
    overscroll-behavior: contain;
    box-shadow: 0 6px 6px rgba(0, 0, 0, 0.2);
    transform: translateX(100%);
    transition: transform 0.45s var(--ease-out);
}

.has-js .site-menu.is-open .site-menu-panel {
    transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
    .has-js .site-menu-panel,
    .site-menu-scrim { transition: none; }
}

/* --- Panel head --------------------------------------------------
 * Figma: the "Account" title is Source Sans Pro Bold 16 on #1f1f1f,
 * and the close control is a 24 x 24 box holding a 10 x 10 glyph.
 * ------------------------------------------------------------- */
.site-menu-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-shrink: 0;
    padding: 26px var(--menu-inset) 22px;
}

.site-menu-head h2 {
    font-family: var(--font-display);
    font-size: 16px;
    font-weight: 700;
    line-height: 1.3;
    color: var(--c-ink);
}

.site-menu-close {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 24px;
    height: 24px;
    border-radius: 50%;
    color: var(--c-ink);
    transition: background-color 0.25s var(--ease-out);
}

.site-menu-close svg { width: 10px; height: 10px; }

.site-menu-close:hover { background: #f2f2f2; }

.site-menu-body {
    padding: 0 0 32px;
}

/* --- The eight rows ----------------------------------------------
 * Every number here is Figma's, from the "Humberger" frame:
 *
 *     panel            379 wide
 *     rows block       339 x 336, so 20px in from each edge
 *     one row          339 x 28
 *     row gap          16   (336 = 8 rows x 28 + 7 gaps x 16)
 *     icon tile        28 x 28, filled #1f1f1f
 *     tile -> label    16   (the 121-wide inner frame is 28 + 16 + 77)
 *     label            Source Sans Pro SemiBold 12 / #1f1f1f
 *     trailing icon    16 x 16
 *
 * The rows carry NO vertical padding: a row is exactly the height of
 * its tile, and all the air between rows is the list's 16px gap. That
 * is what brings the block back to the designed 336 — it was running
 * at roughly double.
 * ------------------------------------------------------------- */
.site-menu-list {
    display: flex;
    flex-direction: column;
    gap: 16px;
    list-style: none;
    margin: 0;
    padding: 0;
}

/* --- Rows that duplicate the inline nav ------------------------------
 * The hamburger shows at EVERY width, not only small ones — so a row
 * that exists to reach a page the inline nav also links to is needed
 * only while that nav is hidden.
 *
 * 1051px is not arbitrary: .main-nav is hidden at max-width 1050px, so
 * this is exactly the width at which About reappears in the header. The
 * two rules have to move together.
 * ------------------------------------------------------------------ */
@media (min-width: 1051px) {
    .site-menu-item--mobile {
        display: none;
    }
}

.site-menu-row {
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: center;
    gap: 16px;
    width: 100%;
    min-height: 28px;
    /* Horizontal only — see the note above. The inset is the panel's,
       so the hover band runs the full width while the tiles still line
       up on Figma's 20px column. */
    padding: 0 var(--menu-inset);
    font-family: var(--font-display);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--c-ink);
    cursor: pointer;
    transition: background-color 0.2s var(--ease-out);
}

.site-menu-row:hover,
.site-menu-row:focus-visible {
    background: #f5f5f5;
}

/* Figma reports cornerRadius 2 on the tile, but the rendered frame
   shows a distinctly rounded square — the radius sits on the
   component's own background rather than on the instance. 6px matches
   what the design actually looks like; drop it to 2 for the literal
   value. */
.site-menu-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: 6px;
    background: var(--c-ink);
    color: var(--c-white);
}

.site-menu-icon svg {
    width: 15px;
    height: 15px;
}

.site-menu-label {
    min-width: 0;
}

/* Trailing marks. Figma sizes both boxes at 16 x 16. */
.site-menu-chev {
    width: 6px;
    height: 6px;
    margin-right: 4px;
    border-right: 1.6px solid #8a8a8a;
    border-top: 1.6px solid #8a8a8a;
    transform: rotate(45deg);
    transition: transform 0.25s var(--ease-out);
}

/* An SVG, not the border-and-pseudo-element trick the chevron uses.
   That works for a chevron because a chevron IS two borders rotated
   45 degrees — but this arrow needs a corner that stays square while
   the shaft runs diagonally through it, and building that from a
   rotated box left a bent hook instead of an arrowhead. */
.site-menu-arrow {
    display: inline-flex;
    width: 13px;
    height: 13px;
    color: #8a8a8a;
}

.site-menu-arrow svg {
    width: 100%;
    height: 100%;
}

/* --- The two expandable rows ------------------------------------
 * <details>, so they open and close with no script at all — the same
 * reason the product filters use it. The summary IS the row.
 * ------------------------------------------------------------- */
.site-menu-sub summary {
    list-style: none;
}

.site-menu-sub summary::-webkit-details-marker { display: none; }
.site-menu-sub summary::marker { content: ''; }

.site-menu-sub[open] > .site-menu-row .site-menu-chev {
    transform: rotate(135deg);
}

/* Indented to the label's own x — the 28px tile plus the 16px gap —
   so the children line up under the text rather than under the tile. */
.site-menu-sublist {
    list-style: none;
    margin: 12px 0 0;
    padding: 0 0 0 calc(var(--menu-inset) + 44px);
}

.site-menu-sublist li + li {
    margin-top: 10px;
}

.site-menu-sublist a {
    display: block;
    padding-right: var(--menu-inset);
    font-family: var(--font-display);
    font-size: 12px;
    font-weight: 400;
    line-height: 1.2;
    color: #5f5f5f;
    transition: color 0.2s var(--ease-out);
}

.site-menu-sublist a:hover,
.site-menu-sublist a:focus-visible {
    color: var(--c-brand);
}

@media (prefers-reduced-motion: reduce) {
    .site-menu-row,
    .site-menu-chev,
    .site-menu-close,
    .site-menu-sublist a { transition: none; }
}

/* Hamburger — shown at EVERY width now. It used to be `display: none`
   here and switched on only inside the max-width:1050px block; it is
   inline-flex from the start instead, so it sits beside Login on
   desktop as well as on a phone. */
.menu-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    background: none;
    border: none;
    cursor: pointer;
    color: var(--c-ink);
}

.menu-toggle-bars,
.menu-toggle-bars::before,
.menu-toggle-bars::after {
    display: block;
    width: 20px;
    height: 1.6px;
    background: currentColor;
    border-radius: 2px;
    transition: transform 0.3s var(--ease), opacity 0.2s var(--ease);
}

.menu-toggle-bars {
    position: relative;
}

.menu-toggle-bars::before,
.menu-toggle-bars::after {
    content: '';
    position: absolute;
    left: 0;
}

.menu-toggle-bars::before { top: -6px; }
.menu-toggle-bars::after  { top: 6px; }

/* Bars morph to an X while the panel is open */
.menu-toggle[aria-expanded="true"] .menu-toggle-bars {
    background: transparent;
}

.menu-toggle[aria-expanded="true"] .menu-toggle-bars::before {
    top: 0;
    transform: rotate(45deg);
}

.menu-toggle[aria-expanded="true"] .menu-toggle-bars::after {
    top: 0;
    transform: rotate(-45deg);
}

/* --- WordPress admin bar offset ---------------------------------
 * #wpadminbar is position:fixed and occupies the top 32px, so a
 * sticky header pinned to top:0 slides underneath it. WordPress
 * adds .admin-bar to <body> only for logged-in users, so these
 * rules are inert for visitors.
 *
 * Admin bar heights:  >782px = 32px  |  <=782px = 46px
 * Below 600px it switches to position:absolute and scrolls away,
 * so the offset must go back to 0 or a gap opens under it.
 * ------------------------------------------------------------- */
.admin-bar .site-header {
    top: 32px;
}

@media screen and (max-width: 782px) {
    .admin-bar .site-header {
        top: 46px;
    }
}

@media screen and (max-width: 600px) {
    .admin-bar .site-header {
        top: 0;
    }
}

/* Anchor links land below the sticky bar instead of behind it.
   scroll-padding applies to the scroll container (<html>), not
   <body>, so the admin-bar variant is matched with :has(). */
html {
    scroll-padding-top: calc(var(--header-h) + 20px);
}

html:has(body.admin-bar) {
    scroll-padding-top: calc(var(--header-h) + 52px);
}

/* Header condenses and lifts once the page scrolls */
.site-header {
    transition: box-shadow 0.35s var(--ease), background-color 0.35s var(--ease);
}

.site-header .header-container {
    transition: min-height 0.35s var(--ease);
}

.site-header.is-scrolled {
    box-shadow: var(--card-shadow);
    background-color: rgba(255, 255, 255, 0.92);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
}

/* Condensed once the page scrolls. Was 68 against a 100px resting
   bar; now 64 against 80, so the bar still visibly tightens rather
   than the two heights collapsing into each other. */
.site-header.is-scrolled .header-container {
    min-height: 64px;
}

/* =========================================
   2b. MOTION
   --------------------------------------
   Everything here is gated behind .has-js on <html>, which the
   inline snippet in header.php sets. If JS fails the content is
   simply visible — nothing is ever hidden with no way back.
   All of it is switched off under prefers-reduced-motion.
========================================= */
.has-js .reveal {
    /* Per-element lateral offset, set by .stagger below so cards in a
       two-column row arrive from their own side of the grid. */
    --reveal-x: 0px;

    opacity: 0;
    /* The card sits tipped back and slightly below, then stands up
       into place. rotateX off a bottom origin is what gives it depth
       — a flat fade has nowhere to travel from. */
    transform:
        perspective(1300px)
        rotateX(7deg)
        translate3d(var(--reveal-x), 42px, 0)
        scale(0.955);
    transform-origin: center bottom;
    /* Was 14px, which left the page looking out of focus while you
       read it. 5px still reads as a soft resolve but the text is
       legible almost immediately. */
    filter: blur(5px);
    transition:
        opacity 0.75s var(--ease-out),
        transform 1.05s var(--ease-out),
        filter 0.5s var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform, filter;
}

/* Lighter variant for text and the carousel — no tilt, they read
   better arriving flat. */
.has-js .reveal--fade {
    transform: translate3d(0, 20px, 0);
    filter: blur(3px);
}

.has-js .reveal--zoom {
    transform: scale(0.97);
}

.has-js .reveal.is-visible {
    opacity: 1;
    transform: none;
    filter: blur(0);
    will-change: auto;
}

/* --- Stagger + direction ---------------------------------------
 * Grid order is: 1 full / 2 left / 3 right / 4 full / 5 left /
 * 6 right / 7 full. Left-hand cards drift in from the left, right-hand
 * from the right, full-width ones straight up.
 * ------------------------------------------------------------- */
.has-js .stagger > *:nth-child(1) { --reveal-delay: 0ms; }
.has-js .stagger > *:nth-child(2) { --reveal-delay: 60ms;  --reveal-x: -30px; }
.has-js .stagger > *:nth-child(3) { --reveal-delay: 150ms; --reveal-x: 30px; }
.has-js .stagger > *:nth-child(4) { --reveal-delay: 0ms; }
.has-js .stagger > *:nth-child(5) { --reveal-delay: 60ms;  --reveal-x: -30px; }
.has-js .stagger > *:nth-child(6) { --reveal-delay: 150ms; --reveal-x: 30px; }
.has-js .stagger > *:nth-child(7) { --reveal-delay: 0ms; }

/* The FAQ list is a single column — stack it vertically instead */
.has-js .accordion.stagger > *:nth-child(n) { --reveal-x: 0px; }
.has-js .accordion.stagger > *:nth-child(1) { --reveal-delay: 0ms; }
.has-js .accordion.stagger > *:nth-child(2) { --reveal-delay: 70ms; }
.has-js .accordion.stagger > *:nth-child(3) { --reveal-delay: 140ms; }
.has-js .accordion.stagger > *:nth-child(4) { --reveal-delay: 210ms; }
.has-js .accordion.stagger > *:nth-child(5) { --reveal-delay: 280ms; }

/* --- Scroll-linked upgrade -------------------------------------
 * Where supported, the reveal is driven by the element's position in
 * the viewport rather than fired once on a trigger, so it tracks your
 * scrolling in both directions. Others keep the transition above.
 * ------------------------------------------------------------- */
@supports (animation-timeline: view()) {
    @media (prefers-reduced-motion: no-preference) {
        .has-js .reveal {
            opacity: 1;
            transform: none;
            filter: none;
            transition: none;
            animation: amaryllis-reveal linear both;
            animation-timeline: view();
            /* Was `entry 4% cover 36%`, so elements stayed mid-blur
               well into the viewport. Finishing at cover 20% means
               everything is sharp by the time you are reading it. */
            animation-range: entry 8% cover 20%;
        }

        .has-js .reveal--fade {
            animation-name: amaryllis-reveal-flat;
        }
    }
}

@keyframes amaryllis-reveal {
    from {
        opacity: 0;
        transform:
            perspective(1300px)
            rotateX(7deg)
            translate3d(var(--reveal-x, 0px), 42px, 0)
            scale(0.955);
        filter: blur(5px);
    }
    to {
        opacity: 1;
        transform: perspective(1300px) rotateX(0deg) translate3d(0, 0, 0) scale(1);
        filter: blur(0);
    }
}

@keyframes amaryllis-reveal-flat {
    from {
        opacity: 0;
        transform: translate3d(0, 20px, 0);
        filter: blur(3px);
    }
    to {
        opacity: 1;
        transform: none;
        filter: blur(0);
    }
}

/* --- No blur on phones -------------------------------------------
 * The reveal animates `filter: blur()` across whole blocks of text.
 * That is fine on a desktop, where a prose block is wide and short, and
 * wrong on a phone for two compounding reasons:
 *
 *   1. A blurred element is promoted to its own composited layer and
 *      rasterised THROUGH the filter. Mobile GPUs cap the size of that
 *      layer, so a tall block of body copy comes back soft even at
 *      blur(0) — the text never fully resharpens.
 *
 *   2. The scroll-driven range ends at `cover 20%`, and the cover range
 *      is (element height + viewport height) long. Narrow it to a phone
 *      and the same paragraph is two or three times taller while the
 *      viewport is half the size, so 20% of that range is a long scroll:
 *      you are reading the top of the block while it is still resolving.
 *      About Us is the worst case — .about-prose is over 1400px tall.
 *
 * Movement and fade carry the animation perfectly well on their own, so
 * below 900px the blur is simply dropped. Everything else is unchanged.
 * ------------------------------------------------------------- */
@media (max-width: 900px) {
    /* .is-visible has to be listed too. Its resting state is
       `filter: blur(0)`, and a zero-radius blur still promotes the
       element to a filtered layer — the expensive, soft-rasterising
       thing this block exists to avoid. Only `none` removes it. */
    .has-js .reveal,
    .has-js .reveal--fade,
    .has-js .reveal.is-visible {
        filter: none;
        will-change: opacity, transform;
    }

    .has-js .reveal {
        transition:
            opacity 0.75s var(--ease-out),
            transform 1.05s var(--ease-out);
    }

    /* The scroll-driven path animates from a keyframe, so clearing the
       resting filter above is not enough — it needs keyframes that
       never introduce one. */
    @supports (animation-timeline: view()) {
        @media (prefers-reduced-motion: no-preference) {
            .has-js .reveal {
                animation-name: amaryllis-reveal-sharp;
            }

            .has-js .reveal--fade {
                animation-name: amaryllis-reveal-flat-sharp;
            }
        }
    }
}

@keyframes amaryllis-reveal-sharp {
    from {
        opacity: 0;
        transform:
            perspective(1300px)
            rotateX(7deg)
            translate3d(var(--reveal-x, 0px), 42px, 0)
            scale(0.955);
    }
    to {
        opacity: 1;
        transform: perspective(1300px) rotateX(0deg) translate3d(0, 0, 0) scale(1);
    }
}

@keyframes amaryllis-reveal-flat-sharp {
    from {
        opacity: 0;
        transform: translate3d(0, 20px, 0);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* --- Hero entrance: runs on load, not on scroll --- */
@keyframes amaryllis-rise {
    from { opacity: 0; transform: translateY(26px); }
    to   { opacity: 1; transform: none; }
}

.has-js .hero-content > * {
    opacity: 0;
    animation: amaryllis-rise 0.9s var(--ease) forwards;
}

.has-js .hero-content > *:nth-child(1) { animation-delay: 0.15s; }
.has-js .hero-content > *:nth-child(2) { animation-delay: 0.32s; }
.has-js .hero-content > *:nth-child(3) { animation-delay: 0.49s; }

/* Slow drift on the hero photo so the fold is never fully static */
@keyframes amaryllis-drift {
    from { transform: scale(1.06); }
    to   { transform: scale(1); }
}

/* Motion is a nice-to-have, never a requirement */
@media (prefers-reduced-motion: reduce) {
    .has-js .reveal,
    .has-js .reveal.is-visible {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }

    .has-js .hero-content > * {
        opacity: 1;
        animation: none;
    }

    .site-header.is-scrolled .header-container {
        min-height: var(--header-h);
    }
}

/* =========================================
   3. HERO
========================================= */
/* Figma draws the homepage banner 1440 x 810, the tallest of any page.
   Those per-page frame heights are no longer what the site uses: every
   banner takes --hero-h, the window less the header. */
.hero-section {
    position: relative;
    width: 100%;
    /* The window, less the header — the same height every other page's
       banner gets. Figma draws this frame 1440 x 810. */
    height: var(--hero-h);
    display: flex;
    /* Against the LEFT edge, vertically centred (client request). It
       used to sit in the top-left corner; the copy now sits halfway
       down the frame but still starts on the logo's left edge, which
       is what the side clamp matches.

       align-items is the cross axis, so that is the vertical centring;
       justify-content is the main axis, so that is what holds it left.
       Symmetric vertical padding, since nothing is anchored to the top
       any more. */
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    padding: clamp(24px, 3vw, 40px) clamp(16px, 5.35vw, 77px);
    overflow: hidden;
    background-color: #f2f2f2;
}

/* NOTE: the Figma frame contains an "Ellipse 4" that would draw a
   curved white base under this banner — but it is a HIDDEN layer
   (visible: false). It is not part of the design and must not be
   built. The same applies to Ellipse 4 and Ellipse 5 on About Us. */

/* The photo lives on a pseudo-element so it can be scaled
   independently of the overlay and the copy. */
.hero-section::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 0;
    background-image: url('images/hero-bg.webp');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    transform-origin: center;
}

.has-js .hero-section::before {
    animation: amaryllis-drift 2s var(--ease) forwards;
}

/* --- Background video -------------------------------------------
 * Sits in the same layer as the ::before still, and covers it because
 * a real element paints after its parent's ::before at equal z-index.
 * object-fit: cover so it fills the 1440/810 band at any width without
 * distorting, exactly as background-size: cover does for the image.
 * ------------------------------------------------------------- */
.hero-video {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    /* Nothing here is interactive — clicks belong to the copy above. */
    pointer-events: none;
}

/* The still under the film: the first thing painted in the hero, and all a
   visitor sees when the film is skipped (reduced motion, data saver, a slow
   connection, no JavaScript). A real <img> rather than the video's poster, so
   it has a srcset and a phone downloads a phone-sized picture. */
.hero-poster {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
    pointer-events: none;
}

/* The film fades in over the still only once it is really playing - never an
   empty frame while it buffers. js/main.js adds .is-playing. */
.hero-video {
    opacity: 0;
    transition: opacity 0.8s var(--ease-out);
}

.hero-video.is-playing {
    opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
    .hero-video { transition: none; }
}

/* With a video present the still is redundant, and leaving it on would
   download the same picture twice: once as the CSS background and again
   as the video's poster. */
.hero-section.has-video::before {
    display: none;
}

/* A wash over the film so the copy stays readable. It was 0.62, which
   drained the picture — the headline sits in its own green blocks and
   does not need that much help. */
.hero-overlay {
    position: absolute;
    inset: 0;
    background: rgba(255, 255, 255, 0.28);
    z-index: 1;
}

/* A corner block, so it is set to a comfortable measure rather than the
   1012px Figma gives the centred version. */
.hero-content {
    position: relative;
    z-index: 2;
    max-width: 640px;
}

/* Figma: Source Sans Pro w700 64 / lh 80.4 / #000000 — pure black
   here, not the #1f1f1f used elsewhere. The banner ends at 468 and
   the sub-line starts at 488, so 20px below. */
.hero-content h1 {
    font-family: var(--font-display);
    font-size: var(--fs-hero);
    font-weight: 700;
    line-height: 1.26;
    color: #000000;
    margin-bottom: 18px;
    /* Title case, as the design sets it — everything else on the page is
       still capitals, this headline is not. */
    text-transform: none;
}

/* Figma: "Fresh" and "Better" are white on a #81af4a block, not green ink.
   inline-block with its own line-height, because at the headline's 1.26
   the green boxes on the two lines are taller than the gap between them
   and merge into one shape — the design leaves clear air between them. */
/* "Fresh" and "Better" used to sit in green boxes — an inline-block
   with the brand fill behind white type. Both go on request: the words
   are plain black now, the same as the rest of the headline, so the
   line reads as one sentence over the video rather than as four words
   with two of them boxed.

   The span stays in the markup and stays styled, so the emphasis can
   be brought back (or taken somewhere else) from this one rule. */
.hero-content h1 .hero-accent {
    display: inline;
    padding: 0;
    background-color: transparent;
    color: #000000;
}

/* Figma: Open Sans w400 24 / lh 32.7 / #000000. The 899px measure and
   the 82px gap below belong to the centred layout; in the corner the
   line wraps sooner and the button follows closer. */
.hero-content p {
    font-family: var(--font-body);
    font-size: var(--fs-hero-sub);
    color: #000000;
    font-weight: 400;
    line-height: 1.36;
    max-width: 520px;
    margin: 0 0 clamp(24px, 2.4vw, 34px);
}

/* Figma draws this 519 x 60 with a 32px label. The client asked for a
   smaller button, so it is now sized by its own text - about 300 x 50 -
   with the same border, radius and colours. */
.btn-outline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 32px;
    min-height: 50px;
    border: 1px solid #343434;
    background: transparent;
    color: #343434;
    font-family: var(--font-display);
    font-size: var(--fs-btn-lg);
    font-weight: 700;
    line-height: 1.26;
    text-transform: uppercase;
    border-radius: 8px;
    transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}

.btn-outline:hover,
.btn-outline:focus-visible {
    background: #343434;
    color: var(--c-white);
}

/* The hero's second button: same shape, filled. Figma fills it #343434 -
   the same ink as the outline button's border and label. */
.btn-solid {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 12px 32px;
    min-height: 50px;
    border: 1px solid #343434;
    background: #343434;
    color: var(--c-white);
    font-family: var(--font-display);
    font-size: var(--fs-btn-lg);
    font-weight: 700;
    line-height: 1.26;
    text-transform: uppercase;
    border-radius: 8px;
    transition: background-color 0.3s var(--ease), color 0.3s var(--ease);
}

/* Hovering the filled button keeps it filled — it only deepens. Emptying
   it here is what made both buttons look identical mid-hover. */
.btn-solid:hover,
.btn-solid:focus-visible {
    background: var(--c-ink);
    border-color: var(--c-ink);
    color: var(--c-white);
}

/* --- The pair reads as one control ------------------------------
 * Contact Us is the filled one at rest. Hovering Download Catalogue
 * HANDS THE FILL OVER rather than darkening both at once, so exactly
 * one of the two is filled at any moment — the same paired behaviour
 * the product cards used to have.
 * ------------------------------------------------------------- */
.hero-actions:has(.btn-outline:hover) .btn-solid,
.hero-actions:has(.btn-outline:focus-visible) .btn-solid {
    background: transparent;
    border-color: #343434;
    color: #343434;
}

/* Two buttons on one row, wrapping only if a narrow phone forces it. */
.hero-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: clamp(8px, 1.7vw, 24px);
}

/* =========================================
   4. PRODUCT SERIES GRID
========================================= */
.product-series-section {
    /* Full bleed: the cards ARE the section. No page inset, and no
       vertical padding either — the grid meets the hero above it and
       the carousel section below provides its own top spacing. */
    padding: 0;
    background-color: var(--c-white);
    /* Cards drift in 30px from the side during the reveal, which would
       otherwise push the document wider and flash a horizontal
       scrollbar. `clip` rather than `hidden` so no scroll container is
       created and sticky positioning elsewhere is unaffected. */
    overflow-x: clip;
}

.series-container {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: var(--grid-gap);
    /* No max-width. The design runs the artwork edge to edge, so the
       cap that used to hold it at --container would leave white bands
       either side on a wide display. The cards keep their aspect
       ratio, so they grow in proportion rather than distorting. */
    max-width: none;
    margin: 0;
}

/* --- Reveal direction, butted grid ------------------------------
 * With no gutters between cards, the lateral drift that the shared
 * .stagger rules apply would open and close a visible white seam
 * between neighbours as they animate in. Cards rise straight up
 * instead; the delays are kept, so the sequence still reads.
 * ------------------------------------------------------------- */
.has-js .series-container.stagger > *:nth-child(n) { --reveal-x: 0px; }

.series-card {
    /* Figma: a half card is 720 x 528. It was 720 x 496 in the first
       build; the current design is taller because the copy and its
       "Explore More" link now sit at opposite ends of the card rather
       than as one centred stack. */
    --card-ar: 720 / 528;
    /* Used only in the un-stacked phone layout below: the share of the
       artwork that is left once the blank zone the copy sat in is
       cropped away. */
    --art-crop: 720 / 380;

    /* --- Fluid overlay type -------------------------------------
     * The copy sits ON the artwork, so it has to scale WITH the
     * artwork or it drifts out of the clear area as the card narrows.
     * Every value below is the Figma measurement expressed as a
     * percentage of the CARD's own width (cqw), which keeps the
     * composition identical at every size, clamped so it never gets
     * illegibly small or larger than the design.
     *
     * Container units, not viewport units: a two-column card is half
     * the width of a full-width one at the same viewport, so vw would
     * size them identically and only one could be right.
     *
     * Figma, measured on the 720-wide half card: padding 68, bottom
     * padding 50, kicker 12, title 40, body 16, link 16, rule 170 x 2.
     * ----------------------------------------------------------- */
    container-type: inline-size;
    --t-pad:     9.44cqw;
    --t-pad-b:   6.94cqw;
    --t-kicker:  1.67cqw;
    --t-title:   5.56cqw;
    --t-body:    2.22cqw;
    --t-link:    2.22cqw;
    --t-rule:   23.61cqw;

    position: relative;
    aspect-ratio: var(--card-ar);
    overflow: hidden;
    border-radius: var(--card-radius);
    isolation: isolate;
}

.series-card.full-width-card {
    grid-column: 1 / -1;
    /* Figma draws the full-width card 1440 x 496. It is taller here
       because the new artwork for both of these cards puts its product
       in the bottom half of the frame, and Explore More now sits at the
       foot of the card — the extra 90px is the clear band the link
       stands in. It is part of the PICTURE, not of the card: see
       pad_card_art.py for why a painted band could not work. */
    --card-ar: 1440 / 586;
    /* The same share of the top cropped away as before (about 30%),
       for the un-stacked phone layout where the copy sits above the
       artwork rather than on it. */
    --art-crop: 1440 / 410;

    /* The same measurements as a share of a card twice as wide:
       padding-top 52, title 40, body 18, link 16. */
    --t-pad:    3.61cqw;
    --t-title:  2.78cqw;
    --t-body:   1.25cqw;
    --t-link:   1.11cqw;
}

/* Figma draws the Fresh Air card larger than the other full-width one:
   a 54px title over a 16px sub-line, and the block starts higher. Two
   class names, so this outranks the .series-card.full-width-card tokens
   above rather than tying with them. */
.series-card.series-card--lead {
    --t-pad:   2.29cqw;
    --t-title: 3.75cqw;
    --t-body:  1.11cqw;
}

/* The card artwork, filling the card behind the copy. Kept as a real
   <img> rather than a background-image so it keeps its alt text and
   can be lazy-loaded. */
.card-art {
    position: absolute;
    inset: 0;
    z-index: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center;
}

/* --- The copy block ---------------------------------------------
 * Fills the card: the heading group at the top, the link at the foot,
 * both inset by the design's padding. The half cards set their copy
 * against the side their product is NOT on, which is why the alignment
 * is a variant rather than a fixed value.
 * ------------------------------------------------------------- */
.series-content {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    padding: clamp(16px, var(--t-pad), 68px)
             clamp(16px, var(--t-pad), 68px)
             clamp(14px, var(--t-pad-b), 50px);
}

/* Industrial Series: STF stands at the left of its card, so its copy
   runs against the right edge. */
.series-card--right .series-content {
    align-items: flex-end;
    text-align: right;
}

.full-width-card .series-content {
    align-items: center;
    text-align: center;
}

/* Figma: Open Sans w600 12, spaced, uppercase, grey — the series name
   above the product name. */
.series-kicker {
    display: block;
    margin-bottom: clamp(1px, 0.7cqw, 5px);
    font-family: var(--font-body);
    font-size: clamp(9px, var(--t-kicker), 12px);
    font-weight: 600;
    line-height: 1.4;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #6b6b6b;
}

/* Figma: Source Sans Pro w700, 40 on a half card and on Multipurpose,
   54 on Fresh Air, line height 1.25, fill #000000. */
.series-content h2 {
    font-family: var(--font-display);
    font-size: clamp(18px, var(--t-title), 40px);
    font-weight: 700;
    line-height: 1.25;
    color: #000000;
    margin: 0;
    text-transform: uppercase;
}

.series-card--lead .series-content h2 {
    font-size: clamp(20px, var(--t-title), 54px);
}

/* Figma: Open Sans w400 16 / lh 1.4, 18 on the Multipurpose card. The
   line breaks are in the markup, as the design sets them. */
.series-content p {
    font-family: var(--font-body);
    font-size: clamp(11px, var(--t-body), 16px);
    font-weight: 400;
    line-height: 1.4;
    color: #5a5a5a;
    margin: clamp(2px, 0.55cqw, 4px) 0 0;
}

.full-width-card .series-content p {
    font-size: clamp(11px, var(--t-body), 18px);
}

.series-card--lead .series-content p {
    font-size: clamp(11px, var(--t-body), 16px);
}

/* The short green rule under the copy on the half cards. Decoration, so
   it is a span rather than an <hr> and is hidden from screen readers in
   the markup. */
.series-rule {
    display: block;
    width: clamp(64px, var(--t-rule), 170px);
    height: 2px;
    margin-top: clamp(5px, 1.67cqw, 12px);
    background-color: var(--c-brand);
}

/* One link per card now, where the design has an "Explore More" with a
   long thin arrow. `margin-top: auto` pins it to the foot of the card;
   the full-width cards override that below, because there the copy and
   its link are one block at the top and the product fills the rest. */
.series-link {
    display: inline-flex;
    align-items: center;
    gap: clamp(6px, 2.4cqw, 17px);
    margin-top: auto;
    font-family: var(--font-body);
    font-size: clamp(11px, var(--t-link), 16px);
    font-weight: 400;
    line-height: 1.4;
    color: #333333;
    transition: color 0.3s var(--ease);
}

/* The full-width cards used to keep their link in the block at the top
   — copy, then Explore More, then the product filling the rest. On
   request it goes to the FOOT of the card instead, where the four half
   cards already put theirs: the link was landing on the product, and
   with it moved down all six cards read the same way — copy at the top,
   product in the middle, one link along the bottom.

   `margin-top: auto` is the base rule; this override only has to stop
   replacing it. The small top margin is kept as a floor for the case
   where the copy is tall enough to reach the link anyway. */
.full-width-card .series-link {
    margin-top: auto;
    padding-top: clamp(8px, 1.5cqw, 21px);
}

.series-arrow {
    width: clamp(14px, 2.8cqw, 22px);
    height: auto;
    flex: none;
    transition: transform 0.3s var(--ease);
}

.series-link:hover,
.series-link:focus-visible {
    color: var(--c-brand);
}

.series-link:hover .series-arrow,
.series-link:focus-visible .series-arrow {
    transform: translateX(4px);
}

/* --- Card fill --------------------------------------------------
 * On desktop this is only a placeholder while the artwork loads. On
 * phones the card un-stacks and this colour sits directly above the
 * image, so it has to match that image's OWN top edge or you get a
 * visible band change. Sampled per card from the artwork itself.
 * ------------------------------------------------------------- */
/* --- Industrial: the fan and the copy were on top of each other ---
 * card-stf.webp is 2000 x 723 and the half card is 720 x 528, so cover
 * throws away 741px of width and shows the middle. The STF stands dead
 * centre of the file, which put it across the middle of the CARD too —
 * and this card's copy is right-aligned, so the second line of the
 * standfirst and the green rule were both drawn over the fan.
 *
 * Moving the crop window to the right moves the fan to the LEFT, which
 * is the side its card is composed around. Nothing is lost: everything
 * outside the window on either side is the blue sweep.
 * ---------------------------------------------------------------- */
.stf-img { object-position: 74% 50%; }

.bg-light-grey { --card-fill: #f4f4f4; }
.bg-light-blue { --card-fill: #e2f0fb; }

.series-card:has(.multipurpose-img) { --card-fill: #fbfbfc; }
.series-card:has(.fresh-air-img)    { --card-fill: #c4dcfe; }
.series-card:has(.sigma-img)        { --card-fill: #b3dcf2; }
.series-card:has(.stf-img)          { --card-fill: #a8d0fd; }
.series-card:has(.accessories-img)  { --card-fill: #fdfcff; }
.series-card:has(.motion-img)       { --card-fill: #fbfdff; }

/* --- Extending the artwork --------------------------------------
 * Where a layout gives the card more height than the artwork fills,
 * the extra space is painted with the ARTWORK ITSELF rather than a
 * flat colour: same file, drawn at the same horizontal scale and
 * anchored to the top, so its top rows — the blank zone the title
 * sits in — extend upward.
 *
 * A flat --card-fill can never match, because every artwork's top row
 * is a horizontal gradient (Sigma is blue at the left and white at
 * the right). One colour meets it at one x position and is visibly
 * wrong everywhere else, which is the band across these cards.
 * ------------------------------------------------------------- */
/* The 1200px copy, not the full artwork: --art-src is only drawn on phones and
   tablets, stretched 8x tall to paint each card's top edge, where the full
   file is pure download. */
.series-card:has(.multipurpose-img) { --art-src: url('images/sizes/Multipurpose-1200.webp'); }
.series-card:has(.fresh-air-img)    { --art-src: url('images/sizes/fresh-air-1200.webp'); }
.series-card:has(.sigma-img)        { --art-src: url('images/sizes/card-sigma-1200.webp'); }
.series-card:has(.stf-img)          { --art-src: url('images/sizes/card-stf-1200.webp'); }
.series-card:has(.accessories-img)  { --art-src: url('images/sizes/card-accessories-1200.webp'); }
.series-card:has(.motion-img)       { --art-src: url('images/sizes/card-motion-1200.webp'); }

.series-card {
    background-color: var(--card-fill);
    background-repeat: no-repeat;
}

/* No card carries a border. The Multipurpose card used to take a
   black stroke, but with the cards butted together any border draws
   as a hard line down the middle of the block — the updated design
   drops it. */
.series-card {
    border: 0;
}

/* The Fresh Air and Multipurpose artwork used to be scaled 0.6% past
   the card edge, to trim a black stroke baked into an export taken
   from the Figma group rather than the image inside it. The new
   artwork has no stroke — all four edges sample clean — so there is
   nothing left to trim and the scale is gone. */

/* --- No seams between butted cards -------------------------------
 * The cards meet edge to edge with no gutter and the layout is exact:
 * every card abuts its neighbour at 0.000px. The light lines that
 * showed between them were never a gap in the layout.
 *
 * They came from the scroll reveal. It used to sit on each card, and a
 * scroll-linked animation never finishes - it tracks the scroll
 * position for as long as the page is open. Its resting frame is
 * `perspective(1300px) rotateX(0deg) ...` with `filter: blur(0px)`: a
 * 3D transform and a filter, both of which keep an element on its own
 * GPU layer, and filtered layers are rasterised with soft edges. Seven
 * of those butted together at fractional device pixels (a half card is
 * 790.5px wide at 125% display scaling) leaked a light line at every
 * join.
 *
 * Earlier attempts treated the symptom - dropping will-change, then
 * overlapping the cards by a pixel. Neither could work while the
 * animation itself held the layers, and the overlap made things worse:
 * widening the left card of a pair made it 0.69px taller than its
 * neighbour, leaving the right-hand card only 0.31px of overlap onto
 * the row below.
 *
 * The fix is in front-page.php: the reveal now goes on each card's
 * COPY (.series-content), not on the card. The tiles are ordinary
 * boxes painted onto one surface, so there is nothing to seam, and the
 * headings and buttons still animate in as the section scrolls into
 * view.
 * ------------------------------------------------------------- */

/* =========================================
   5. LIFESTYLE CAROUSEL
========================================= */
/* The small viewport height where supported: it ignores the phone
   address bar, so the cards do not change size as it hides and shows.
   (Kept in its own rule because a custom property cannot fall back on
   its own — an unsupported unit would leave the card with no height.) */
@supports (height: 1svh) {
    :root {
        --slide-h: min(clamp(360px, 58svh, 680px), var(--slide-cap));
    }
}

/* Tablets: cap the card at 58% of the screen WIDTH, so about two and a
   half cards show across a portrait tablet instead of two big ones. A
   landscape tablet is already limited by its height and is unaffected.
   From 577px, where the theme's phone layout ends. */
@media (min-width: 577px) and (max-width: 1050px) {
    :root {
        --slide-cap: 58vw;
    }
}

/* Desktop: the same height, a wider card. 0.70 is as wide as the
   category videos allow without clipping their titles — see :root. */
@media (min-width: 1051px) {
    :root {
        --slide-ratio: 0.7;
    }
}

.lifestyle-section {
    padding: 60px 0 40px;
    background-color: var(--c-white);
    overflow: hidden;
}

/* Figma: Source Sans Pro w600 40 / lh 24 / #1f1f1f — weight 600, not
   700, and the section header sits 48px above what follows it. */
.section-title {
    text-align: center;
    font-family: var(--font-display);
    font-size: var(--fs-section-title);
    font-weight: 600;
    color: var(--c-ink);
    line-height: 1.2;
    margin-bottom: 48px;
}

.lifestyle-swiper {
    width: 100%;
    /* Flush to the edge, matching the butted grid above it. The gap
       BETWEEN slides stays — that white gutter is part of the
       carousel design and comes from spaceBetween in main.js. */
    padding-left: 0;
}

/* Linear timing is what makes the autoplay read as a continuous
   drift rather than a series of eased slide steps. */
.lifestyle-swiper .swiper-wrapper {
    transition-timing-function: linear;
}

.lifestyle-swiper .swiper-slide {
    position: relative;
    width: var(--slide-w);
    height: var(--slide-h);
    background: var(--c-white);
    overflow: hidden;
}

/* Keep the normal pointer over the carousel. Swiper's grabCursor is
   off in main.js, but it also sets cursor styles inline on drag, so
   this pins it. */
.lifestyle-swiper,
.lifestyle-swiper .swiper-wrapper,
.lifestyle-swiper .swiper-slide,
.lifestyle-swiper .swiper-slide img {
    cursor: default;
}

/* --- Slide hover --------------------------------------------------
 * A frosted panel rises from the bottom edge carrying the label, its
 * underline wipes in behind it, and the chevron nudges right. The
 * photo stays put — no shrinking frame, no zoom.
 * ---------------------------------------------------------------- */
.ls-media {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

.ls-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Scrim keeps the label readable over any photo, light or dark */
.ls-media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        180deg,
        rgba(12, 14, 18, 0) 45%,
        rgba(12, 14, 18, 0.55) 78%,
        rgba(10, 12, 16, 0.82) 100%
    );
    opacity: 0;
    transition: opacity 0.55s var(--ease-out);
    pointer-events: none;
}

.ls-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    padding: 26px 20px 28px;
    font-family: var(--font-display);
    font-size: 17px;
    font-weight: 600;
    color: var(--c-white);
    transform: translateY(100%);
    opacity: 0;
    transition:
        transform 0.6s var(--ease-out),
        opacity 0.4s var(--ease-out);
    pointer-events: none;
}

/* Hairline that wipes in under the text, just after the panel lands */
.ls-label-text {
    position: relative;
}

.ls-label-text::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -7px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.65s var(--ease-out);
}

/* Chevron, drawn from two borders so it needs no icon font */
.ls-label::after {
    content: '';
    width: 7px;
    height: 7px;
    border-right: 1.6px solid currentColor;
    border-bottom: 1.6px solid currentColor;
    transform: rotate(-45deg) translate(-3px, -3px);
    transition: transform 0.6s var(--ease-out);
}

.lifestyle-swiper .swiper-slide:hover .ls-media::after {
    opacity: 1;
}

.lifestyle-swiper .swiper-slide:hover .ls-label {
    transform: none;
    opacity: 1;
}

.lifestyle-swiper .swiper-slide:hover .ls-label-text::after {
    transform: scaleX(1);
    transition-delay: 0.18s;
}

.lifestyle-swiper .swiper-slide:hover .ls-label::after {
    transform: rotate(-45deg) translate(0, 0);
}

/* --- Category video + link ----------------------------------------
 * Each slide is one product category (inc/category-video.php). Its
 * video fills the frame exactly as the photo did, and the whole slide
 * opens that category. The link is an empty layer over the slide; the
 * label above it is purely visual and ignores the pointer, so a click
 * anywhere — label included — lands on the link.
 * ---------------------------------------------------------------- */
.ls-media video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Which part of the video stays in view when the card is wider than
       the 9:16 video — the desktop card crops about a fifth of its
       height. Set per category in wp-admin ("Video focus"), because the
       videos differ: Inline Fans has its product at the very top of the
       frame, Accessories its captions near the bottom. Centre when none
       is set. Applies to the poster too. */
    object-position: 50% var(--ls-focus, 50%);
}

.ls-link {
    position: absolute;
    inset: 0;
    z-index: 1;
    cursor: pointer;
}

.ls-label {
    z-index: 2;
    pointer-events: none;
}

.ls-link:focus-visible {
    outline: 2px solid var(--c-white);
    outline-offset: -6px;
}

/* A keyboard user gets the same reveal as a mouse */
.lifestyle-swiper .swiper-slide:focus-within .ls-media::after { opacity: 1; }
.lifestyle-swiper .swiper-slide:focus-within .ls-label { transform: none; opacity: 1; }
.lifestyle-swiper .swiper-slide:focus-within .ls-label-text::after { transform: scaleX(1); }
.lifestyle-swiper .swiper-slide:focus-within .ls-label::after { transform: rotate(-45deg) translate(0, 0); }

/* Touch devices have no hover — show the label rather than leave it
   permanently out of reach. */
@media (hover: none) {
    .ls-media::after { opacity: 1; }
    .ls-label { transform: none; opacity: 1; }
    .ls-label-text::after { transform: scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
    .ls-media::after,
    .ls-label,
    .ls-label-text::after,
    .ls-label::after { transition: none; }
}

/* =========================================
   6. FAQ
========================================= */
.faq-section {
    padding: 50px var(--section-gutter) 100px;
    background-color: var(--c-white);
}

/* Was 900px, which was far too narrow. Now close to the grid width
   but pulled in a further 80px each side, so the FAQ reads as its own
   block rather than running edge to edge with the cards. */
.faq-container {
    max-width: calc(var(--container) - 160px);
    margin: 0 auto;
}

.faq-section .section-title {
    margin-bottom: 12px;
}

/* Figma: Open Sans w400 24 / lh 24 / #1f1f1f, across the full 1291.
   The heading block ends at 4388 and the accordion starts at 4420 →
   32px. */
.faq-subtitle {
    text-align: center;
    font-family: var(--font-body);
    color: var(--c-ink);
    font-size: clamp(14px, 1.67vw, 24px);
    font-weight: 400;
    line-height: 1.35;
    margin-bottom: 32px;
}

/* Figma: rows sit at y 4420 / 4518 — 74 tall on a 98 pitch → 24px. */
.accordion {
    display: flex;
    flex-direction: column;
    gap: 24px;
}

/* Kept visually plain, as in the design — the motion does the work,
   not the styling. No accent rail, no colour shift, no text nudge. */
/* Figma: 1280 x 74, radius 12, fill #fdfdfd, DROP_SHADOW y4 blur4
   #000000 at 20%. NO border — the shadow is the only edge, and it is
   a third one again: y4/blur4 here, y10/blur10 on About, y14/blur25
   on Career and Contact. */
.accordion-item {
    position: relative;
    background: var(--c-page);
    border: none;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: var(--card-shadow);
    transition:
        box-shadow 0.5s var(--ease-out),
        border-color 0.5s var(--ease-out);
}

.accordion-item.is-open {
    border-color: var(--c-line);
    box-shadow: var(--card-shadow);
}

.accordion-header {
    width: 100%;
    text-align: left;
    background: transparent;
    border: none;
    /* Figma: the row is 74 tall with its 44-tall content inset 34px
       from the card edge. */
    padding: 15px 34px;
    font-family: var(--font-body);
    font-size: clamp(15px, 1.67vw, 24px);
    font-weight: 700;
    line-height: 1.67;
    color: #5f5f5f;
    cursor: pointer;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 20px;
    transition: color 0.4s var(--ease-out);
}

.accordion-item.is-open .accordion-header {
    color: #111111;
}

/* --- Icon: outline circle, plus turns into a minus -------------
 * Stays as the light outline ring from the design. The vertical bar
 * collapses to nothing while the ring rotates, so it reads as a plus
 * becoming a minus rather than flipping to an x.
 * ------------------------------------------------------------- */
.accordion-icon {
    position: relative;
    flex-shrink: 0;
    width: 24px;
    height: 24px;
    border: 1.4px solid #b8b8b8;
    border-radius: 50%;
    transition:
        transform 0.55s var(--ease-out),
        border-color 0.4s var(--ease-out);
}

.accordion-icon::before,
.accordion-icon::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    background: #4a4a4a;
    border-radius: 2px;
    transition: transform 0.55s var(--ease-out);
}

.accordion-icon::before {
    width: 10px;
    height: 1.5px;
    transform: translate(-50%, -50%);
}

.accordion-icon::after {
    width: 1.5px;
    height: 10px;
    transform: translate(-50%, -50%);
}

.accordion-header:hover .accordion-icon {
    border-color: #8a8a8a;
}

.accordion-item.is-open .accordion-icon {
    transform: rotate(180deg);
    border-color: #6f6f6f;
}

.accordion-item.is-open .accordion-icon::after {
    transform: translate(-50%, -50%) scaleY(0);
}

/* --- Panel ------------------------------------------------------
 * grid-template-rows 0fr -> 1fr animates to the content's real
 * height, so nothing is measured in JS and nothing jumps when the
 * text rewraps.
 * ------------------------------------------------------------- */
.accordion-content {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows 0.5s var(--ease-out);
}

.accordion-item.is-open .accordion-content {
    grid-template-rows: 1fr;
}

.accordion-content > p {
    overflow: hidden;
    min-height: 0;
    padding: 0 26px;
    color: var(--c-muted);
    font-size: 15px;
    line-height: 1.65;
    opacity: 0;
    transform: translateY(-5px);
    transition:
        opacity 0.4s var(--ease-out),
        transform 0.5s var(--ease-out),
        padding-bottom 0.5s var(--ease-out);
}

.accordion-item.is-open .accordion-content > p {
    opacity: 1;
    transform: none;
    padding-bottom: 22px;
    transition-delay: 0.1s;
}

@media (prefers-reduced-motion: reduce) {
    .accordion-item,
    .accordion-icon,
    .accordion-icon::before,
    .accordion-icon::after,
    .accordion-content,
    .accordion-content > p {
        transition: none;
    }
}

/* =========================================
   6b. FALLBACK PAGE SHELL (index.php)
   Keeps archives and single posts from rendering unstyled while
   the rest of the templates are built.
========================================= */
.page-shell {
    max-width: 900px;
    margin: 0 auto;
    padding: 70px var(--gutter) 90px;
}

.page-title {
    font-family: var(--font-display);
    font-size: var(--fs-section-title);
    font-weight: 700;
    color: #111111;
    margin-bottom: 30px;
}

.page-entry {
    padding-bottom: 30px;
    margin-bottom: 30px;
    border-bottom: 1px solid var(--c-line);
}

.page-entry:last-of-type {
    border-bottom: none;
}

.page-entry-title {
    font-family: var(--font-display);
    font-size: 22px;
    font-weight: 600;
    margin-bottom: 10px;
}

.page-entry-content {
    color: var(--c-muted);
    font-size: 15px;
}

.page-pagination {
    margin-top: 20px;
}

/* =========================================
   7. FOOTER
   --------------------------------------
   Figma: five columns on near-black — a wider brand block carrying
   the logo and tagline, then Quick Links, Product Links, Additional
   Feature and Contact. No divider rule anywhere.
========================================= */
/* Figma: the Footer frame is 1440 x 522, fill #1f1f1f, with 43px top
   padding and 70px side padding. Content sits in a 1301-wide block,
   leaving ~37px below it. */
.site-footer {
    background-color: var(--c-footer-bg);
    color: var(--c-footer-link);
    /* Held as ratios with no ceiling (70/1440 = 4.86vw), so the footer
       keeps its proportions on a large display instead of the content
       stranding in the middle.

       The two VERTICAL ratios were trimmed on request — 2.99vw/2.57vw
       down to 2.4vw/2.1vw. The horizontal one is untouched: that is
       what aligns the footer columns with the rest of the page. */
    padding: max(24px, 2.4vw) max(20px, 4.86vw) max(20px, 2.1vw);
}

.footer-container {
    display: grid;
    /* Figma's column widths, used directly as fr ratios. The September
       revision adds a sixth column (Policies) and butts them all
       together: they sit at x = 70 / 474 / 611 / 765 / 951 / 1137 and
       measure 404 / 137 / 154 / 186 / 186 / 248, each starting exactly
       where the last one ends. */
    grid-template-columns: 404fr 137fr 154fr 186fr 186fr 248fr;
    /* Figma's own gutter is 0 — the columns are wider than the text
       inside them, and that slack IS the gutter. Web font metrics are
       not Figma's, though, and "Aditional Feature" and "Return &
       Cancellation" are within ~25px of their column edge, so a real
       gap is kept as insurance against a heading touching its
       neighbour. fr tracks absorb it, so the proportions hold. */
    column-gap: 16px;
    row-gap: 43px;
    /* NO cap. This row and .footer-bottom below were capped at
       different widths — 1301 here, 1852 there — so above 1440 they
       centred on different axes and the logo no longer lined up with
       the social block underneath it. Both now take their width from
       .site-footer's padding, which is the single source. */
    max-width: none;
    margin: 0;
}

/* --- Brand column --- */
.footer-logo {
    display: inline-flex;
    align-items: center;
    margin-bottom: 20px;
    color: var(--c-white);
}

/* Figma: the footer logo is 183 x 26 — a different mark and a
   different size from the header's 216 x 31. */
.footer-logo img {
    height: 26px;
    width: auto;
    max-width: 183px;
    object-fit: contain;
    object-position: left center;
}

/* Fallback lockup. Horizontal here, unlike the header's stacked
   version — the footer gives it the width, and a stacked mark would
   sit oddly above a paragraph. */
.footer-logo-fallback {
    display: inline-flex;
    align-items: center;
    gap: 9px;
}

.footer-logo-mark svg {
    width: 26px;
    height: 26px;
    display: block;
}

.footer-logo-word {
    font-family: var(--font-display);
    font-size: 18px;
    font-weight: 700;
    line-height: 1;
    letter-spacing: 3.5px;
    text-transform: uppercase;
}

.footer-logo-word sup {
    font-size: 0.45em;
    top: -0.85em;
    letter-spacing: 0;
}

/* Figma: Open Sans w400 16 / lh 28 / #f5f9f1 — a pale green-white,
   distinct from the #fdfdfd used by the links. Box is 410 wide. */
.footer-tagline {
    max-width: 410px;
    color: var(--c-footer-tagline);
    font-size: 16px;
    line-height: 1.75;
}

/* --- Link columns --- */
/* Figma: Source Sans Pro w700 20 / lh 24 / #fdfdfd. */
.footer-col h4 {
    font-family: var(--font-display);
    font-size: var(--fs-footer-head);
    font-weight: 700;
    line-height: 1.2;
    color: var(--c-footer-link);
    /* Figma has 40 here — the heading ends at 4713 and the link list
       starts at 4753. Pulled in to 26 as part of narrowing the footer;
       it is the single largest gap in the column and every column
       carries it, so it comes straight off the footer's height. */
    margin-bottom: 26px;
}

/* Figma: link rows are 29 tall on a 41px pitch → a 12px gap. */
.footer-col ul li {
    margin-bottom: 12px;
}

.footer-col ul li:last-child {
    margin-bottom: 0;
}

/* Figma: Open Sans w400 14 / lh 24 / #fdfdfd. */
.footer-col ul a {
    color: var(--c-footer-link);
    font-size: var(--fs-footer-link);
    line-height: 1.71;
    transition: color 0.25s var(--ease);
}

.footer-col ul a:hover {
    color: var(--c-white);
}

/* A policy row whose page has not been written yet. Same type as the
   links around it, dimmed and not clickable, so the column reads as
   the design draws it without promising a destination that 404s.
   inc/privacy.php swaps it for a real <a> the moment a Page with the
   matching slug is published. */
.footer-link-pending {
    display: inline-block;
    color: var(--c-muted);
    font-size: var(--fs-footer-link);
    line-height: 1.71;
    cursor: default;
}

/* --- Contact column --- */
/* Figma: contact rows sit on a 55px pitch with 20px content → 35px. */
.contact-info p {
    color: var(--c-footer-link);
    font-size: var(--fs-footer-link);
    line-height: 1.55;
    /* Figma's 55px pitch put 35 here. The contact column is the
       tallest of the five — three rows on the widest gap in the
       footer — so it is what actually sets the height of the whole
       block, and trimming it is what lets the others come up. */
    margin-bottom: 26px;
    display: flex;
    align-items: flex-start;
    gap: 14px;
}

.contact-info p:last-child {
    margin-bottom: 0;
}

/* Phone, email and map-pin icons, drawn as inline SVG in footer.php.
   They replaced three Font Awesome glyphs, which were loading the whole
   icon library. Same 16px column and white; the shape sits inside its
   24-unit box with a little padding, so a 16px box matches the old
   14px glyph, and 3px down centres it on the first line of text. */
.contact-info .contact-icon {
    flex-shrink: 0;
    width: 16px;
    height: 16px;
    margin-top: 3px;
    color: var(--c-white);
}

.contact-info a {
    color: var(--c-footer-link);
    transition: color 0.25s var(--ease);
}

.contact-info a:hover {
    color: var(--c-white);
}

/* =========================================
   7b. FOOTER BOTTOM
   Three tracks: social block left, copyright centred on the page,
   an empty track right. The empty track is what keeps the copyright
   centred on the PAGE rather than in the leftover space beside the
   social block — same trick as the header nav. It needs no element,
   because both outer tracks are 1fr and only two children are placed.
========================================= */
/* Shares the exact width of .footer-container above, so the social
   block sits on the same left edge as the logo. */
.footer-bottom {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: end;
    gap: 24px;
    max-width: none;
    margin: 0;
    /* Was 52. The largest single gap in the footer after the column
       headings, so the bulk of the narrowing comes from here. */
    padding-top: 34px;
}

/* NO font-family, deliberately — it inherits the body font.
   Figma has this as Open Sans w600 14 / lh 40, even though the
   column headings beside it are Source Sans Pro w700 20. Two
   typefaces on one footer row is what the design actually specifies,
   so "fixing" the inconsistency here would move it away from the file.
   Size and weight below already match. */
.footer-connect h4 {
    font-size: 14px;
    font-weight: 400;
    color: var(--c-footer-link);
    margin-bottom: 14px;
}

/* Figma: the social row is 171 x 24 with a 24px gap — four 24px icons
   (4 x 24 + 3 x 24 = 168). They are plain icons, NOT the coloured
   circular badges this had at 46px. */
.social-icons {
    display: flex;
    align-items: center;
    gap: 24px;
}

/* Each badge keeps its own brand silhouette and proportions, taken
   from the reference: Instagram a rounded square, YouTube a wide
   short rounded rect, Facebook and WhatsApp circles. */
/* Plain 24px glyphs. The circular/squircle badge is gone — the design
   has no filled shape behind these, so border-radius and background
   are dropped and the brand colour moves onto the glyph itself. */
.social-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--social-size);
    height: var(--social-size);
    background: none;
    line-height: 1;
    transition: transform 0.3s var(--ease);
}

/* The inline SVGs carry a viewBox but no width/height, so without
   this they fall back to the SVG default of 300 x 150 and burst out
   of the 24px link. Sizing lives here, in one place. */
.social-btn svg {
    display: block;
    width: 100%;
    height: 100%;
}

.social-btn:hover {
    transform: translateY(-2px);
}

/* NO per-brand colour rules here. Each mark is a multi-coloured inline
   SVG that paints its own fills — Instagram is a gradient, WhatsApp is
   green plus white, YouTube is red plus white. A `color` declaration
   would do nothing, since none of them use currentColor.
   The class names are kept on the links purely as hooks. */

/* --- Legal --- */
.footer-legal {
    text-align: center;
}

.footer-legal p {
    font-size: 13px;
    line-height: 1.6;
    color: var(--c-footer-link);
}

.footer-policy {
    margin-top: 3px;
    font-size: 12px;
}

.footer-policy a {
    color: var(--c-footer-link);
    transition: color 0.25s var(--ease);
}

.footer-policy a:hover {
    color: var(--c-white);
}

/* The legal line sets its own smaller size; a pending row here has to
   follow it rather than the 14px used in the columns above. */
.footer-policy .footer-link-pending {
    font-size: inherit;
    line-height: inherit;
}

.footer-policy span {
    margin: 0 5px;
    color: var(--c-muted);
}

/* =========================================
   8. RESPONSIVE
   Figma supplied desktop frames only, so these are disciplined
   reductions of the same layout rather than designed breakpoints.
========================================= */

/* Nav starts to crowd the logo before it truly breaks */
@media (max-width: 1250px) {
    .main-nav ul { gap: 32px; }
    .header-actions { gap: 18px; }
}

@media (max-width: 1200px) {
    :root {
        --fs-hero: 44px;
        --fs-card-title: 26px;
    }

    /* Brand column gives up a little width before anything wraps.
       Six tracks now — Policies sits between Aditional Feature and
       Contact and needs the same room as its neighbour, since
       "Return & Cancellation" is the longest link in the footer. */
    .footer-container {
        grid-template-columns: 1.6fr 1fr 1fr 1.15fr 1.15fr 1.3fr;
        column-gap: 14px;
        row-gap: 30px;
    }
}

/* --- Nav collapses into the hamburger --- */
@media (max-width: 1050px) {
    /* The hamburger is declared inline-flex in the base rule now and
       shows at every width, so there is nothing to switch on here. */

    /* --- Which side gives way when the row runs out of room -------
     * This was `auto 1fr`, which sizes the LOGO to its content first
     * and leaves the actions whatever is left. The logo is 216px wide,
     * the cart, Login and the hamburger come to about 109, and with the
     * 16px gap that is 341px of content — more than a phone has once
     * the header's own side padding is taken off (335 at 375px wide,
     * 331 at 365). So the actions overflowed their column and the
     * hamburger ended up 7-14px from the screen edge instead of the
     * 17-20px the header pads to.
     *
     * Reversed: the actions are `auto` and never compress, the logo
     * takes what is left and scales down. minmax(0, 1fr) rather than
     * plain 1fr because a grid track's default min-width is auto, which
     * would refuse to shrink below the logo's intrinsic 216px and
     * reintroduce the same overflow.
     * ----------------------------------------------------------- */
    .header-container {
        grid-template-columns: minmax(0, 1fr) auto;
        min-height: 72px;
        gap: 16px;
    }

    /* Two thirds of the window rather than all of it: on a tablet the
       full height is a wall of picture with the page hidden below it. */
    :root {
        --hero-h: clamp(360px, 66vh, 620px);
        --hero-h: clamp(360px, 66dvh, 620px);
    }

    /* stretch, not the default start: the logo has to FILL its cell for
       a percentage width on the image to resolve against something. Left
       as fit-content the image kept its intrinsic width and simply
       overflowed the cell into the cart icon. */
    .logo {
        min-width: 0;
        justify-self: stretch;
    }

    /* Width leads and height follows. A fixed height would pin the
       image's width too (216 x 26/31 = 181px, whatever the screen), so
       it could never give the actions room — which is the entire point
       of the track change above. */
    .logo-img {
        width: 100%;
        height: auto;
        max-width: 216px;
    }

    /* --- The nav simply goes away here ---------------------------
     * It used to become a drop-down panel under the header that the
     * hamburger toggled with an .active class — hence the absolute
     * positioning, the shadow, the stacked rows and the disabled
     * underline that all used to live here.
     *
     * None of that applies now: the hamburger opens #site-menu
     * instead, which is a proper overlay with its own styling in
     * section 2c, and nothing sets .active any more. All that is left
     * to do at this width is hide the inline links.
     * ---------------------------------------------------------- */
    .main-nav {
        display: none;
    }

    /* The fixed 26px height is gone: it pinned the image's width to
       181px at every screen size, so the row could not fit on a
       narrow phone no matter how much the track was allowed to
       shrink. Height now follows the width set above. */
}

@media (max-width: 992px) {
    :root {
        --fs-hero: 38px;
        --fs-hero-sub: 17px;
        --fs-card-title: 24px;
        --fs-card-body: 18px;
        --fs-footer-head: 18px;
    }

    .btn-outline,
    .btn-solid {
        padding: 11px 28px;
        min-height: 46px;
        font-size: 17px;
    }

    /* --- Tablet: one column -------------------------------------
     * Stacked cards run the full width. They used to be capped at
     * their 675px design width and centred, but in a butted,
     * edge-to-edge layout that cap leaves white bands down both
     * sides and breaks the block apart. The aspect ratio holds, so
     * the artwork scales in proportion rather than cropping.
     * ----------------------------------------------------------- */
    .series-container { grid-template-columns: 1fr; }

    .series-card:not(.full-width-card) {
        width: 100%;
        max-width: none;
        margin-inline: 0;
    }

    /* --- Footer: brand spans, links pair up ---------------------
     * Six columns cannot hold here — the headings break onto two
     * lines. The brand block takes the full width and the five link
     * columns sit two-by-two beneath it.
     * ----------------------------------------------------------- */
    .footer-container {
        grid-template-columns: repeat(2, 1fr);
        column-gap: 40px;
        row-gap: 40px;
    }

    .footer-brand {
        grid-column: 1 / -1;
    }

    .footer-tagline {
        max-width: 560px;
    }

    .footer-col h4 { margin-bottom: 22px; }
    .site-footer { padding: 48px var(--gutter) 32px; }

    /* Copyright moves under the social block and both go left —
       centring it against an empty track reads as a mistake once
       there is this little width. */
    .footer-bottom {
        grid-template-columns: 1fr;
        gap: 26px;
        padding-top: 40px;
        text-align: left;
    }

    .footer-legal {
        text-align: left;
    }
}

/* --- Tablet and below: one card per row -------------------------
 * The old two-column tablet case had to invent room above each product
 * for the copy, because the artwork's clear zone was too narrow at half
 * width. The current design does not need it: a card's copy sits beside
 * its product rather than over it, and below 992px each card simply
 * runs the full width at its own 720 x 528 ratio - close to the size
 * the design draws it at, so nothing has to be re-proportioned.
 * ------------------------------------------------------------- */

@media (max-width: 768px) {
    /* Card size needs no override here — the :root formula already
       sizes it from the screen width. The label is scaled to match. */
    .ls-label {
        gap: 10px;
        padding: 18px 14px 20px;
        font-size: 15px;
    }

    .accordion-header { padding: 16px 18px; font-size: 14px; }
    .accordion-content p { padding: 0 18px 18px; font-size: 14px; }
}

@media (max-width: 576px) {
    :root {
        --social-size: 38px;
        --fs-hero: 30px;
        --fs-hero-sub: 15px;
        --fs-card-title: 21px;
        --fs-card-body: 16px;
        --fs-section-title: 26px;
        --fs-footer-link: 14px;
    }

    /* Header: drop the wishlist to keep the bar on one line */
    .header-container { min-height: 64px; }

    /* Phones: a little over half the screen, so the banner reads as a
       banner and the first section below it is visible as you land. The
       320px floor covers a short phone in landscape; the 440px ceiling
       stops a tall one from growing the band back. */
    :root {
        --hero-h: clamp(320px, 56vh, 440px);
        --hero-h: clamp(320px, 56dvh, 440px);
    }
    /* No fixed height here any more. `height: 36px` made the logo box
       WIDER than at any other size — capped at max-width 216, which is
       the single biggest reason the row stopped fitting. It now scales
       with the cell (see the 1050 block above); the cap keeps it from
       growing past its natural cap on a roomier phone.

       160 rather than 216 on request: the mark is legible well below
       its desktop size, and the ~56px given back is what lets the
       wishlist stay in the row (see below). */
    .logo-img { max-width: 160px; }
    .logo-word { font-size: 13px; letter-spacing: 2.5px; margin-right: -2.5px; }
    .logo-mark svg { width: 16px; height: 16px; }
    .header-actions { gap: 12px; }
    .icon-link i { font-size: 18px; }
    .login-link { font-size: 14px; }

    /* The wishlist used to be dropped here to keep the bar on one line.
       It stays on request — it is the only way to reach the list on a
       phone without opening the panel — and sits where it already is in
       source order, between the cart and Login. The smaller logo above
       and the tighter gap here are what pay for it. */
    .icon-link svg { width: 21px; height: 21px; }

    /* The <br> line breaks are desktop-only — collapse them here */
    .hero-content p br,
    .series-content p br { display: none; }

    /* Smaller on a phone than the desktop pair, and tight enough that
       Download Catalogue and Contact Us still share one row at 360px. */
    .btn-outline,
    .btn-solid { padding: 9px 14px; min-height: 38px; font-size: 13px; }

    .product-series-section { padding: 0; }
    .series-content { padding: 0 20px; }

    /* --- Phone: un-stack ----------------------------------------
     * A full-width card is 2.77:1, so at 360px it is only 130px tall
     * — there is no arrangement where readable copy and the product
     * both fit. Below this width the card stops being an overlay:
     * the text sits above and the artwork below at its natural size,
     * uncropped and fully visible.
     * ----------------------------------------------------------- */
    .series-card,
    .series-card.full-width-card {
        aspect-ratio: auto;
        display: flex;
        flex-direction: column;
        padding: 30px 0 0;
    }

    /* Static type again — nothing is overlaid, so it no longer needs
       to track the artwork. The copy also stops hugging one side: a
       phone card is too narrow for right-aligned body text to read
       well, so every card sets its copy from the left and the two
       full-width cards stay centred, as they are on a wide screen. */
    .series-content,
    .series-card--right .series-content {
        position: static;
        align-items: flex-start;
        text-align: left;
        padding: 0 20px;
    }

    .full-width-card .series-content {
        align-items: center;
        text-align: center;
    }

    /* The copy card has no product — its artwork is the wave pattern, and
       at the half card's crop it left a tall empty band under the link.
       A shallow strip of the same file keeps the pattern without it. */
    .series-card--copy { --art-crop: 720 / 200; }

    .series-content h2 { font-size: var(--fs-card-title); }
    .series-content p  { font-size: var(--fs-card-body); }
    .series-kicker { font-size: 11px; margin-bottom: 3px; }
    .series-rule { width: 110px; margin-top: 10px; }

    .series-link {
        margin-top: 16px;
        margin-bottom: 22px;
        font-size: 14px;
        gap: 12px;
    }

    .series-arrow { width: 18px; }

    /* Crop the artwork's blank top away — the copy above it now
       provides that space, so showing it again is what produced the
       large empty band between the buttons and the product. */
    .series-card {
        /* Top edge only — see the note in the tablet block above. */
        background-image: var(--art-src);
        background-size: 100% 800%;
        /* Offset DOWN, not `top`. At 800% one source pixel becomes
           eight on screen, so if a PNG has the Figma stroke baked into
           its very first row that black line gets stretched into a
           band across the top of the card. Starting ~4 source pixels
           in skips any such edge and samples clean gradient. */
        background-position: center -32px;
    }

    .card-art {
        position: static;
        width: 100%;
        height: auto;
        aspect-ratio: var(--art-crop);
        object-fit: cover;
        object-position: center bottom;
        margin-top: 0;

        /* The artwork's top edge fades into the card fill over 70px.
           A mask rather than a colour match, because the artwork's top
           row is itself a horizontal gradient — no single flat colour
           could ever meet it cleanly across the full width. This makes
           the join invisible even if the fill is slightly off. */
        -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 56px);
        mask-image: linear-gradient(to bottom, transparent 0, #000 56px);
    }


    /* --- Footer: two columns, not one ---------------------------
     * On request: Quick Links and Product Links sit SIDE BY SIDE on a
     * phone rather than one under the other. Keeping the 992px pairing
     * rather than collapsing to a single column does that on its own —
     * the two are adjacent in source order, so they land in the same
     * row — and it halves the footer's height into the bargain.
     *
     * minmax(0, 1fr) rather than 1fr: a grid track's default minimum
     * is auto, which would refuse to go below the widest link
     * ("Return & Cancellation" in Policies) and push the row wider
     * than a 360px screen.
     *
     * The brand block already spans both tracks from 992 down. Contact
     * joins it: it is the odd one out of five link columns, and its
     * address wraps to four lines in a half-width track.
     * ----------------------------------------------------------- */
    .footer-container {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        column-gap: 20px;
        row-gap: 32px;
    }

    .contact-info {
        grid-column: 1 / -1;
    }

    .footer-col h4 { margin-bottom: 16px; }
    .footer-col ul li { margin-bottom: 13px; }
    .contact-info p { margin-bottom: 16px; gap: 12px; }

    .site-footer { padding: 40px var(--gutter) 30px; }

    .footer-bottom {
        padding-top: 32px;
        gap: 22px;
    }

    .footer-legal p { font-size: 12px; }
    .footer-policy { font-size: 11px; }
}


/* =========================================================
   APPEND THIS TO THE END OF style.css
   ---------------------------------------------------------
   All new selectors — nothing here overrides an existing rule,
   so it is safe to paste at the bottom of the file. The two
   media queries at the end must stay together with it.
   ========================================================= */

/* =========================================================
   APPEND THIS TO THE END OF style.css
   ---------------------------------------------------------
   All new selectors — nothing here overrides an existing rule,
   so it is safe to paste at the bottom of the file. The two
   media queries at the end must stay together with it.
   ========================================================= */


/* =========================================
   9. CATEGORY DROPDOWN
   -----------------------------------------
   "Category" opens a panel rather than navigating, so the trigger
   is a <button>. It is styled to match the sibling nav links
   exactly, including the underline wipe, so nothing about it reads
   as a different kind of control until you open it.
========================================= */
/* Figma: this is one of the five nav items and carries the same type
   as its siblings — Source Sans Pro w600 16. It was on the BODY font
   at weight 400, so "Category" rendered in Open Sans while Home,
   About, Aircare and Career rendered in Source Sans Pro.

   Its box is 125 wide against 62 for the word, with the label sitting
   31px in from the left — the remaining room on the right is what the
   dropdown caret occupies. */
.nav-dropdown-toggle {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    padding: 0 19px 0 31px;
    background: none;
    border: none;
    cursor: pointer;
    font-family: var(--font-display);
    font-size: var(--fs-nav);
    font-weight: 600;
    color: var(--c-ink);
    white-space: nowrap;
}

/* Same wipe as .main-nav a, and held open while the panel is */
/* Inset to the label, same reasoning as the nav links: the box is
   31px padded on the left and 32px on the right once the caret and
   its gap are counted. */
.nav-dropdown-toggle::after {
    content: '';
    position: absolute;
    left: 31px;
    right: 32px;
    bottom: -5px;
    height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: right center;
    transition: transform 0.38s var(--ease);
}

.nav-dropdown-toggle:hover::after,
.nav-dropdown-toggle:focus-visible::after,
.has-dropdown.is-open .nav-dropdown-toggle::after {
    transform: scaleX(1);
    transform-origin: left center;
}

/* Caret drawn from two borders — no icon font, and it inherits the
   label colour for free. */
.nav-caret {
    width: 6px;
    height: 6px;
    border-right: 1.4px solid currentColor;
    border-bottom: 1.4px solid currentColor;
    transform: translateY(-2px) rotate(45deg);
    transition: transform 0.35s var(--ease);
}

.has-dropdown.is-open .nav-caret {
    transform: translateY(1px) rotate(-135deg);
}

/* --- The panel ---------------------------------------------------
 * position:static on the <li> so the panel is measured against the
 * HEADER, not against the list item — that is what lets it centre on
 * the page and run wider than the word "Category". .site-header is
 * sticky, which counts as positioned, so it is the containing block.
 * ---------------------------------------------------------------- */
.has-dropdown {
    position: static;
}

.mega-menu {
    position: absolute;
    top: calc(100% + 8px);
    left: 50%;
    z-index: 1001;
    /* Figma 'Product dropdown' 314:4970 — the panel is 1256 x 266 with
       a 22px radius and its own y8/blur8 #000000 20% shadow, holding
       ONE ROW of five 156-wide tiles inset 69px, 85px apart. This was
       785px wide with a 3-column grid, so the five categories wrapped
       onto two rows and the panel was 470px too narrow. */
    width: min(1256px, calc(100vw - 40px));
    padding: 0 69px;
    background: var(--c-white);
    border-radius: 22px;
    box-shadow: 0 8px 8px rgba(0, 0, 0, 0.2);
    /* Hidden with visibility rather than display so it can animate,
       and pointer-events so a closed panel cannot be clicked
       through by accident. */
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateX(-50%) translateY(-8px);
    transition:
        opacity 0.3s var(--ease-out),
        transform 0.35s var(--ease-out),
        visibility 0.35s var(--ease-out);
}

.has-dropdown.is-open .mega-menu {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transform: translateX(-50%) translateY(0);
}

/* Five across, one row — Figma has all five categories side by side.
   The panel's own 266px height comes from the tiles, so no fixed
   height here: 111/156 media + 10px gap + the label. */
.mega-grid {
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    gap: 0 85px;
    padding: 64px 0;
}

.mega-item {
    display: block;
    text-align: center;
}

/* Figma: each tile's image is 156 x 111 with a 10px radius. */
.mega-media {
    display: block;
    aspect-ratio: 156 / 111;
    border-radius: 10px;
    overflow: hidden;
    background: #f1f1f1;
}

.mega-media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.55s var(--ease-out);
}

.mega-item:hover .mega-media img,
.mega-item:focus-visible .mega-media img {
    transform: scale(1.06);
}

/* Figma: Source Sans Pro w600 uppercase, 13.55px, 10px under the image.
   Was 12px w700 — the weight was simply wrong.

   On the odd size: this frame's values (13.554 label, 22.025 radius,
   10.166 tile radius, 84.713 gap) are all one factor — 0.8471338 —
   applied to clean integers (16, 26, 12, 100). So the component was
   drawn at 1:1 and scaled down to fit. Un-scaling would put the panel
   at 1483px, which is wider than the 1440 frame and could not sit under
   the nav, whereas 1256 fits neatly inside the nav's 1285px content
   span. So the as-drawn values are the intended ones and everything
   here stays in that scale, including this 13.55. */
.mega-label {
    display: block;
    margin-top: 10px;
    font-family: var(--font-display);
    font-size: 13.55px;
    font-weight: 600;
    line-height: 1.3;
    text-transform: uppercase;
    color: var(--c-ink);
    transition: opacity 0.3s var(--ease);
}

.mega-item:hover .mega-label {
    opacity: 0.6;
}

/* --- Inside the mobile menu -------------------------------------
 * Below 1050 the nav is already an absolutely positioned panel, so
 * a second floating layer inside it would be unusable. The dropdown
 * becomes an inline expanding block instead.
 * ------------------------------------------------------------- */
@media (max-width: 1050px) {
    .nav-dropdown-toggle {
        width: 100%;
        justify-content: space-between;
        padding: 16px var(--gutter);
        border-bottom: 1px solid #f4f4f4;
    }

    /* The sliding underline makes no sense on a stacked row */
    .nav-dropdown-toggle::after {
        display: none;
    }

    .mega-menu {
        position: static;
        width: auto;
        padding: 14px var(--gutter) 20px;
        border-radius: 0;
        box-shadow: none;
        transform: none;
        transition: none;
        /* display, not visibility, so a closed panel takes up no
           height in the stacked menu */
        display: none;
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
    }

    .has-dropdown.is-open .mega-menu {
        display: block;
        transform: none;
    }

    .mega-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 16px 14px;
        /* Resets the 64px the desktop rule uses to centre one row of
           five inside the 266px panel — inside the collapsed menu the
           tiles wrap and that padding is just dead space. */
        padding: 0;
    }

    .mega-label {
        font-size: 11px;
        margin-top: 8px;
    }
}

/* --- Making the collapsed menu scrollable ------------------------
 * .main-nav is absolutely positioned under a STICKY header, so it
 * travels with the header as the page scrolls. Open the category
 * panel inside it and the menu becomes taller than the screen —
 * scrolling the page then moves the content behind it while the menu
 * itself stays put, and the tiles at the bottom can never be
 * reached.
 *
 * Giving the menu its own height limit and its own scrollbar fixes
 * that: the page stops being the thing you scroll, the menu is.
 * dvh rather than vh so the moving browser chrome on a phone does
 * not cut the last row off.
 * ------------------------------------------------------------- */
@media (max-width: 1050px) {
    .main-nav {
        max-height: calc(100vh - var(--header-h) - 12px);
        max-height: calc(100dvh - var(--header-h) - 12px);
        overflow-y: auto;
        /* Stops a flick at the end of the menu from carrying on and
           scrolling the page underneath it. */
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
    }

    /* The admin bar eats 46px at this width for logged-in users */
    .admin-bar .main-nav {
        max-height: calc(100dvh - var(--header-h) - 58px);
    }
}

@media (max-width: 600px) {
    /* Below 600 the admin bar scrolls away with the page, so the
       offset goes back to the plain value. */
    .admin-bar .main-nav {
        max-height: calc(100dvh - var(--header-h) - 12px);
    }
}

@media (prefers-reduced-motion: reduce) {
    .mega-menu,
    .mega-media img,
    .nav-caret,
    .mega-label {
        transition: none;
    }

    .mega-item:hover .mega-media img {
        transform: none;
    }
}

/* =========================================
   POPULAR BLOGS ROW - ARROW BUTTONS
   -----------------------------------------
   Shared by Aircare and the foot of each blog post, where Popular Blogs
   is one row that scrolls sideways. js/blog-scroller.js shows the pair
   only when there is more to see and hides each one at its end.
========================================= */
.blog-scroll-btn {
    position: absolute;
    z-index: 3;
    /* Low on the card photograph, below the text laid over it. */
    top: 45%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: var(--c-page);
    color: var(--c-ink);
    box-shadow: 0 4px 14px rgba(31, 31, 31, 0.18);
    cursor: pointer;
    transform: translateY(-50%);
    transition:
        background-color 0.3s var(--ease-out),
        color 0.3s var(--ease-out),
        opacity 0.3s var(--ease-out),
        visibility 0.3s;
}

.blog-scroll-btn svg {
    width: 20px;
    height: 20px;
}

.blog-scroll-prev { left: -12px; }
.blog-scroll-next { right: -12px; }

.blog-scroll-btn:hover,
.blog-scroll-btn:focus-visible {
    background: var(--c-brand);
    color: var(--c-page);
}

.blog-scroll-btn[hidden] {
    display: none;
}

.blog-scroll-btn:disabled {
    opacity: 0;
    visibility: hidden;
}

@media (max-width: 576px) {
    .blog-scroll-btn { width: 38px; height: 38px; }
    .blog-scroll-prev { left: 6px; }
    .blog-scroll-next { right: 6px; }
}

@media (prefers-reduced-motion: reduce) {
    .blog-scroll-btn { transition: none; }
}

/* =========================================
   BLOG CARD PHOTO TEXT
   -----------------------------------------
   The text over each Popular Blogs card photograph (Aircare, and the foot
   of every post), matched to the Figma row: a small spaced-out green label
   with a short bar, a tall CONDENSED headline on two lines and a small grey
   sentence - one compact block close to the left edge, a little above the
   middle, in the plain part of every photograph. Printed by
   amaryllis_blog_card_banner(); the words are per post in wp-admin.

   Sized in cqw - shares of the CARD's width - so the block keeps the
   design's proportions at every card size. The headline face is Oswald,
   the nearest web font to the condensed type in the design.
========================================= */
.blog-media,
.blog-card-media {
    position: relative;
    container-type: inline-size;
}

/* A soft light wash from the left, just enough to lift the text off the
   photograph without greying the products. */
.blog-media::after,
.blog-card-media::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(
        90deg,
        rgba(255, 255, 255, 0.5) 0%,
        rgba(255, 255, 255, 0.2) 34%,
        rgba(255, 255, 255, 0) 55%
    );
    pointer-events: none;
}

.blog-card-banner {
    position: absolute;
    z-index: 2;
    top: 42%;
    left: 5.5%;
    max-width: 48%;
    transform: translateY(-50%);
    pointer-events: none;
}

.blog-card-banner-label {
    display: block;
    padding-left: 0.8em;
    border-left: 2px solid var(--c-brand);
    font-family: var(--font-body);
    font-size: clamp(8px, 2.1cqw, 11px);
    font-weight: 600;
    line-height: 1.6;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--c-brand);
}

.blog-card-banner-headline {
    display: block;
    margin-top: 0.3em;
    font-family: 'Oswald', 'Arial Narrow', var(--font-display);
    font-size: clamp(13px, 4.2cqw, 22px);
    font-weight: 600;
    line-height: 1.12;
    letter-spacing: 0.01em;
    text-transform: uppercase;
    color: var(--c-ink);
}

.blog-card-banner-text {
    display: block;
    max-width: 30cqw;
    margin-top: 0.55em;
    font-family: var(--font-body);
    font-size: clamp(9px, 2cqw, 11px);
    font-weight: 400;
    line-height: 1.45;
    color: #4f4f4f;
}

/* On a card narrower than about 420px (tablets and phones) the sentence
   could only be set too small to read or across the products, so the
   label and headline carry the card on their own there. */
@container (max-width: 420px) {
    .blog-card-banner-text {
        display: none;
    }
}


/* =========================================
   CATALOGUE DOWNLOAD GATE
   -----------------------------------------
   The form every "Download Catalogue" control opens. Figma: a 443 x 474
   card, corner radius 24, white, with 56 of padding down the sides; the
   fields are flat grey boxes 38 tall with a person mark inside; the
   button is the full content width and dark.

   --cu is one of the design's pixels — exactly 1px at the 1440 the frame
   was drawn at, with a floor so the fields stay usable on a phone.
========================================= */
.catalogue-dialog {
    --cu: clamp(0.9px, 0.0694vw, 1.18px);

    width: min(calc(100vw - 32px), calc(443 * var(--cu)));
    max-width: none;
    max-height: calc(100dvh - 32px);
    padding: 0;
    border: 0;
    background: transparent;
    overflow: visible;
}

/* Centred in the viewport. A browser's own dialog styles do this with
   margin:auto against its own max-width, and overriding that max-width
   above left the box in the top-left corner. */
.catalogue-dialog[open] {
    position: fixed;
    inset: 0;
    margin: auto;
}

.catalogue-dialog::backdrop {
    background: rgba(31, 31, 31, 0.55);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

/* Without JavaScript the dialog never opens, so it is shown in place
   at the foot of the page and the trigger is an anchor to it. */
.catalogue-dialog:not([open]) {
    display: block;
    position: static;
    margin: clamp(32px, 4vw, 64px) auto;
}

.has-js .catalogue-dialog:not([open]) {
    display: none;
}

.catalogue-card {
    position: relative;
    padding: calc(39 * var(--cu)) calc(56 * var(--cu)) calc(42 * var(--cu));
    border-radius: calc(24 * var(--cu));
    background: var(--c-page, #fdfdfd);
    box-shadow: 0 24px 60px rgba(31, 31, 31, 0.22);
    max-height: calc(100dvh - 32px);
    overflow-y: auto;
}

.catalogue-close {
    position: absolute;
    top: calc(14 * var(--cu));
    right: calc(14 * var(--cu));
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 50%;
    color: var(--c-ink);
    cursor: pointer;
    transition: background-color 0.2s var(--ease-out);
}

.catalogue-close svg { width: 18px; height: 18px; }

.catalogue-close:hover,
.catalogue-close:focus-visible { background: rgba(31, 31, 31, 0.08); }

/* Without JS there is nothing to close. */
.catalogue-dialog:not([open]) .catalogue-close { display: none; }

/* Figma: the headline fills the content width in caps. */
.catalogue-title {
    font-family: var(--font-display);
    font-size: calc(31 * var(--cu));
    font-weight: 700;
    line-height: 1.05;
    text-transform: uppercase;
    color: #000000;
}

.catalogue-intro {
    margin-top: calc(24 * var(--cu));
    font-family: var(--font-body);
    font-size: calc(13 * var(--cu));
    font-weight: 400;
    line-height: 1.45;
    color: var(--c-ink);
}

.catalogue-notice {
    margin-top: calc(16 * var(--cu));
    padding: 9px 12px;
    border-radius: 6px;
    font-family: var(--font-body);
    font-size: calc(13 * var(--cu));
    line-height: 1.45;
}

.catalogue-notice--ok    { background: rgba(129, 175, 74, 0.16); color: #3f5c20; }
.catalogue-notice--error { background: rgba(197, 48, 48, 0.12);  color: #8c2b2b; }

/* Off screen, not display:none — a bot should find a field that looks
   real. */
.catalogue-hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

.catalogue-field {
    margin-top: calc(17 * var(--cu));
}

.catalogue-field label {
    display: block;
    margin-bottom: calc(12 * var(--cu));
    font-family: var(--font-body);
    font-size: calc(14 * var(--cu));
    font-weight: 700;
    line-height: 1.2;
    color: #000000;
}

/* Required marker. All three fields are mandatory, so every label takes
   one. A thin space before it rather than a margin, so it never wraps
   onto a line of its own. */
.catalogue-req {
    margin-left: 0.25em;
    color: #d1332e;
    font-weight: 700;
}

/* The person mark sits inside the box, so the box is the positioned
   element and the input sits on top of it. */
.catalogue-input {
    position: relative;
    display: block;
    background: #e3e3e3;
}

.catalogue-icon {
    position: absolute;
    top: 50%;
    left: calc(14 * var(--cu));
    width: calc(17 * var(--cu));
    height: calc(17 * var(--cu));
    margin-top: calc(-8.5 * var(--cu));
    color: #6d6d6d;
    pointer-events: none;
}

.catalogue-field input {
    display: block;
    width: 100%;
    height: calc(38 * var(--cu));
    padding: 0 calc(14 * var(--cu)) 0 calc(44 * var(--cu));
    background: none;
    border: 0;
    border-radius: 0;
    font-family: var(--font-body);
    font-size: calc(13 * var(--cu));
    line-height: calc(38 * var(--cu));
    color: var(--c-ink);
}

.catalogue-field input::placeholder { color: #8d8d8d; }

.catalogue-field input:focus-visible {
    outline: 2px solid var(--c-brand);
    outline-offset: -2px;
}

.catalogue-submit {
    display: block;
    width: 100%;
    min-height: calc(33 * var(--cu));
    margin-top: calc(25 * var(--cu));
    padding: calc(8 * var(--cu)) 16px;
    background: var(--c-ink);
    border: 1px solid var(--c-ink);
    border-radius: 2px;
    color: var(--c-page);
    font-family: var(--font-body);
    font-size: calc(13 * var(--cu));
    font-weight: 700;
    cursor: pointer;
    transition: background-color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}

.catalogue-submit:hover,
.catalogue-submit:focus-visible {
    background: var(--c-brand);
    border-color: var(--c-brand);
}

/* The frame is a desktop one and its 38px fields are mouse sizes.
   Below 1024 the card keeps the layout and takes sizes that can be
   typed into — 16px text, so iOS does not zoom the page on focus. */
@media (max-width: 1024px) {
    .catalogue-dialog { width: min(calc(100vw - 32px), 443px); }

    .catalogue-card { padding: 30px 26px 30px; border-radius: 20px; }

    .catalogue-title { font-size: 26px; }
    .catalogue-intro { margin-top: 12px; font-size: 14px; }
    .catalogue-notice { margin-top: 14px; font-size: 14px; }

    .catalogue-field { margin-top: 16px; }
    .catalogue-field label { margin-bottom: 7px; font-size: 14px; }

    .catalogue-icon { left: 13px; width: 18px; height: 18px; margin-top: -9px; }

    .catalogue-field input {
        height: 46px;
        padding: 0 14px 0 42px;
        font-size: 16px;
        line-height: 46px;
    }

    .catalogue-submit {
        min-height: 46px;
        margin-top: 22px;
        border-radius: 4px;
        font-size: 15px;
    }
}

@media (prefers-reduced-motion: reduce) {
    .catalogue-close,
    .catalogue-submit { transition: none; }
}
