/* ==========================================================================
   Storeroomrack.com — design system
   Brand colours sampled from the logo:
     orange  #F7913C   cyan  #38B0DF   grey  #959392   pale  #C4D8E5
   Breakpoints:  mobile < 768   |   tablet 768–1023   |   desktop >= 1024
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
    /* Brand */
    --brand-orange:      #f7913c;
    --brand-orange-dark: #e07620;
    --brand-orange-soft: #fef2e7;
    --brand-cyan:        #38b0df;
    --brand-cyan-dark:   #1f8fbb;
    --brand-cyan-soft:   #eaf6fc;
    --brand-pale:        #c4d8e5;
    --brand-grey:        #959392;

    /* Neutrals */
    --ink:          #1c2126;
    --ink-soft:     #4a545e;
    --ink-faint:    #737f8a;
    --line:         #e2e8ed;
    --line-strong:  #cbd5dd;
    --surface:      #ffffff;
    --surface-alt:  #f6f9fb;
    --surface-deep: #1c2126;

    /* Status */
    --ok:    #2f8f5b;
    --warn:  #b06a12;
    --error: #c0392b;

    /* Typography */
    --font-sans: "Raleway", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
                 "Helvetica Neue", Arial, sans-serif;

    /* Mobile-first type scale (overridden per breakpoint in section 9) */
    --fs-xs:   0.75rem;
    --fs-sm:   0.875rem;
    --fs-base: 1rem;
    --fs-md:   1.0625rem;
    --fs-lg:   1.1875rem;
    --fs-xl:   1.375rem;
    --fs-2xl:  1.625rem;
    --fs-3xl:  1.9375rem;
    --fs-4xl:  2.25rem;

    --lh-tight: 1.2;
    --lh-snug:  1.35;
    --lh-body:  1.65;

    /* Spacing */
    --sp-1:  0.25rem;
    --sp-2:  0.5rem;
    --sp-3:  0.75rem;
    --sp-4:  1rem;
    --sp-5:  1.5rem;
    --sp-6:  2rem;
    --sp-7:  2.75rem;
    --sp-8:  3.5rem;
    --sp-9:  4.5rem;

    /* Section rhythm — grows with viewport */
    --section-y: 2.5rem;
    --gutter:    1rem;
    --container: 1200px;

    /* Shape */
    --radius-sm: 4px;
    --radius:    8px;
    --radius-lg: 14px;
    --radius-pill: 999px;

    --shadow-sm: 0 1px 2px rgba(28, 33, 38, .06), 0 1px 3px rgba(28, 33, 38, .08);
    --shadow:    0 2px 6px rgba(28, 33, 38, .07), 0 8px 20px rgba(28, 33, 38, .07);
    --shadow-lg: 0 12px 32px rgba(28, 33, 38, .13);

    --header-h: 62px;
}

/* --------------------------------------------------------------------------
   2. Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--header-h) + 1rem);
}

body {
    margin: 0;
    font-family: var(--font-sans);
    font-size: var(--fs-base);
    line-height: var(--lh-body);
    color: var(--ink);
    background: var(--surface);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6 {
    margin: 0 0 var(--sp-4);
    font-weight: 700;
    line-height: var(--lh-tight);
    color: var(--ink);
    text-wrap: balance;
}

h1 { font-size: var(--fs-4xl); letter-spacing: -0.02em; }
h2 { font-size: var(--fs-3xl); letter-spacing: -0.015em; }
h3 { font-size: var(--fs-xl); }
h4 { font-size: var(--fs-lg); }

p  { margin: 0 0 var(--sp-4); text-wrap: pretty; }

a {
    color: var(--brand-cyan-dark);
    text-decoration-thickness: 1px;
    text-underline-offset: 2px;
}
a:hover { color: var(--brand-orange-dark); }

ul, ol { margin: 0 0 var(--sp-4); padding-left: 1.35em; }
li { margin-bottom: var(--sp-2); }

/* Every content image renders whole — never cropped — and scales down on
   smaller devices while keeping its aspect ratio. */
img, picture, video, svg {
    max-width: 100%;
    height: auto;
    display: block;
}
img { object-fit: contain; }

figure { margin: 0 0 var(--sp-5); }
figcaption {
    margin-top: var(--sp-2);
    font-size: var(--fs-sm);
    color: var(--ink-faint);
}

hr {
    border: 0;
    border-top: 1px solid var(--line);
    margin: var(--sp-6) 0;
}

table { border-collapse: collapse; width: 100%; }

button, input, select, textarea { font: inherit; color: inherit; }

:focus-visible {
    outline: 3px solid var(--brand-cyan);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

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

/* --------------------------------------------------------------------------
   3. Layout
   -------------------------------------------------------------------------- */
.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
}
.container--narrow { max-width: 780px; }
.container--wide   { max-width: 1400px; }

.section { padding-block: var(--section-y); }
.section--alt  { background: var(--surface-alt); }
.section--tint { background: var(--brand-cyan-soft); }
.section--deep { background: var(--surface-deep); color: #dfe6ec; }
.section--deep h1, .section--deep h2, .section--deep h3 { color: #fff; }

.stack > * + * { margin-top: var(--sp-4); }

.grid { display: grid; gap: var(--sp-5); }
.grid--2 { grid-template-columns: 1fr; }
.grid--3 { grid-template-columns: 1fr; }
.grid--4 { grid-template-columns: repeat(2, 1fr); }

.split {
    display: grid;
    gap: var(--sp-5);
    grid-template-columns: 1fr;
    align-items: center;
}

.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 999;
    padding: var(--sp-3) var(--sp-4);
    background: var(--brand-orange);
    color: #fff;
    font-weight: 600;
}
.skip-link:focus { left: 0; }

/* --------------------------------------------------------------------------
   4. Header & navigation
   -------------------------------------------------------------------------- */
.site-header {
    position: sticky;
    top: 0;
    z-index: 100;
    background: rgba(255, 255, 255, .97);
    backdrop-filter: saturate(180%) blur(8px);
    border-bottom: 1px solid var(--line);
}

.site-header__bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-4);
    min-height: var(--header-h);

    /* The drawer is a third child of this bar. Without wrapping it stays on the
       same row as the logo and the hamburger and gets flex-shrunk to about
       148px of the 343px available — a cramped column of wrapped labels — while
       squeezing the hamburger down to 27px. Wrapping lets the drawer take a row
       of its own underneath. */
    flex-wrap: wrap;
}

/* Neither of these may be shrunk to make room for anything else. */
.site-logo,
.nav-toggle { flex: 0 0 auto; }

.site-logo { flex: 0 0 auto; line-height: 0; }
.site-logo img { width: auto; height: 34px; }

.nav-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    cursor: pointer;
}
.nav-toggle__box {
    position: relative;
    width: 20px;
    height: 2px;
    background: var(--ink);
    transition: background .2s;
}
.nav-toggle__box::before,
.nav-toggle__box::after {
    content: "";
    position: absolute;
    left: 0;
    width: 20px;
    height: 2px;
    background: var(--ink);
    transition: transform .2s;
}
.nav-toggle__box::before { top: -6px; }
.nav-toggle__box::after  { top: 6px; }

.nav-toggle[aria-expanded="true"] .nav-toggle__box { background: transparent; }
.nav-toggle[aria-expanded="true"] .nav-toggle__box::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__box::after  { transform: translateY(-6px) rotate(-45deg); }

.site-nav {
    display: none;
    padding-block: var(--sp-3) var(--sp-5);
    border-top: 1px solid var(--line);
}
/* The open drawer holds 24 links across five groups, which is around 1,375px
   tall — far more than a phone viewport. It lives inside a position:sticky
   header, and a sticky box taller than the viewport cannot be scrolled to:
   everything past the fold was simply unreachable, including Contact, Get a
   Free Quote and Basket.

   So the drawer scrolls on its own instead. dvh is used because mobile browsers
   shrink the visual viewport when the address bar shows, and vh does not follow
   that; the vh line above it is the fallback for browsers without dvh.

   overscroll-behavior stops a flick at the end of the list from scrolling the
   page underneath it. */
.site-nav.is-open {
    display: block;

    /* Own row, full width — see the flex-wrap note on .site-header__bar. */
    flex: 0 0 100%;
    width: 100%;
    max-height: calc(100vh - var(--header-h) - 1px);
    max-height: calc(100dvh - var(--header-h) - 1px);
    overflow-y: auto;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
}

.site-nav__list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.site-nav__link {
    display: block;
    padding: var(--sp-3) var(--sp-2);
    border-radius: var(--radius);
    color: var(--ink);
    font-weight: 600;
    text-decoration: none;
}
.site-nav__link:hover,
.site-nav__link:focus-visible { background: var(--surface-alt); color: var(--brand-cyan-dark); }
.site-nav__link[aria-current="page"] { color: var(--brand-orange-dark); }

.site-nav__cta { margin-top: var(--sp-3); }

/* --------------------------------------------------------------------------
   Basket — an icon rather than the word "Basket"

   A square button, so the cart glyph is not stranded in a pill sized for text.
   Sizing notes are on the rule itself.
   -------------------------------------------------------------------------- */
.btn--icon,
.btn--sm.btn--icon {
    position: relative;
    padding-inline: var(--sp-3);

    /* Square-ish, and a full 44px in both axes. .btn--sm drops to 38px from
       tablet up, but this is the checkout entry point and it is already the
       smallest control in the header — so it keeps the touch minimum at every
       width. The compound selector is needed to beat that later override. */
    min-width: 44px;
    min-height: 44px;
}

.site-cart__icon { display: block; }

/* The count is decorative — the real figure is in the link's aria-label, so a
   screen reader hears "Basket, 3 items" rather than an unexplained number. */
.site-cart__count {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    border-radius: var(--radius-pill);
    background: var(--brand-orange);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
}

/* Two buttons side by side in the footer's first column, wrapping on a narrow
   phone rather than overflowing. */
.site-footer__cta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}

/* Dropdown — collapses to a plain nested list on mobile */
.has-sub > .site-nav__link { display: flex; justify-content: space-between; align-items: center; }
.site-nav__sub {
    list-style: none;
    margin: 0 0 var(--sp-2);
    padding-left: var(--sp-4);
    border-left: 2px solid var(--line);
}
.site-nav__sub a {
    display: block;
    padding: var(--sp-2);
    color: var(--ink-soft);
    font-size: var(--fs-sm);
    font-weight: 500;
    text-decoration: none;
}
.site-nav__sub a:hover { color: var(--brand-cyan-dark); }

/* --------------------------------------------------------------------------
   5. Buttons
   -------------------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    padding: 0.75rem 1.4rem;
    min-height: 46px;
    border: 2px solid transparent;
    border-radius: var(--radius);
    font-size: var(--fs-base);
    font-weight: 700;
    line-height: 1.2;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    transition: background .18s, border-color .18s, color .18s, transform .18s;
}
.btn:active { transform: translateY(1px); }

.btn--primary {
    background: var(--brand-orange);
    border-color: var(--brand-orange);
    color: #fff;
}
.btn--primary:hover { background: var(--brand-orange-dark); border-color: var(--brand-orange-dark); color: #fff; }

.btn--secondary {
    background: var(--brand-cyan);
    border-color: var(--brand-cyan);
    color: #fff;
}
.btn--secondary:hover { background: var(--brand-cyan-dark); border-color: var(--brand-cyan-dark); color: #fff; }

.btn--outline {
    background: transparent;
    border-color: var(--line-strong);
    color: var(--ink);
}
.btn--outline:hover { border-color: var(--brand-cyan); color: var(--brand-cyan-dark); }

.btn--block { display: flex; width: 100%; }

/* Small buttons stay a 44px tap target on touch-sized viewports; they only
   shrink from tablet up, where a pointer is doing the aiming. */
.btn--sm { padding: 0.5rem 0.9rem; min-height: 44px; font-size: var(--fs-sm); }

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
}

/* --------------------------------------------------------------------------
   6. Cards & components
   -------------------------------------------------------------------------- */
.card {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    overflow: hidden;
    transition: box-shadow .2s, transform .2s, border-color .2s;
}
.card:hover { box-shadow: var(--shadow); border-color: var(--line-strong); transform: translateY(-2px); }

.card__media {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--sp-4);
    background: var(--surface-alt);
    /* Fixed box, contained image: the whole product stays visible on every
       device instead of being cropped to fill. */
    aspect-ratio: 4 / 3;
}
.card__media img { max-height: 100%; width: auto; margin-inline: auto; }

.card__body { padding: var(--sp-4); flex: 1 1 auto; display: flex; flex-direction: column; }
.card__title { font-size: var(--fs-md); margin-bottom: var(--sp-2); }
.card__title a { color: inherit; text-decoration: none; }
.card__title a:hover { color: var(--brand-cyan-dark); }
.card__meta { font-size: var(--fs-sm); color: var(--ink-faint); margin-bottom: var(--sp-3); }
.card__price { font-size: var(--fs-lg); font-weight: 800; color: var(--brand-orange-dark); margin-bottom: var(--sp-3); }
.card__footer { margin-top: auto; padding-top: var(--sp-3); }

.badge {
    display: inline-block;
    padding: 0.2em 0.65em;
    border-radius: var(--radius-pill);
    font-size: var(--fs-xs);
    font-weight: 700;
    letter-spacing: .02em;
    text-transform: uppercase;
}
.badge--stock    { background: #e6f4ec; color: var(--ok); }
.badge--low      { background: #fdf1e0; color: var(--warn); }
.badge--out      { background: #fbeae8; color: var(--error); }
.badge--new      { background: var(--brand-cyan-soft); color: var(--brand-cyan-dark); }

.eyebrow {
    display: block;
    margin-bottom: var(--sp-2);
    font-size: var(--fs-sm);
    font-weight: 700;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--brand-cyan-dark);
}

.lede { font-size: var(--fs-lg); color: var(--ink-soft); }

.note {
    padding: var(--sp-4);
    border-left: 4px solid var(--brand-cyan);
    border-radius: 0 var(--radius) var(--radius) 0;
    background: var(--brand-cyan-soft);
}
.note--warn { border-left-color: var(--brand-orange); background: var(--brand-orange-soft); }
.note :last-child { margin-bottom: 0; }

/* Breadcrumbs */
.breadcrumbs {
    padding-block: var(--sp-3);
    font-size: var(--fs-sm);
    color: var(--ink-faint);
}
.breadcrumbs ol {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-2);
}
.breadcrumbs li { margin: 0; display: flex; gap: var(--sp-2); align-items: center; }
.breadcrumbs li + li::before { content: "›"; color: var(--line-strong); }
.breadcrumbs a { color: var(--ink-soft); text-decoration: none; }
.breadcrumbs a:hover { color: var(--brand-cyan-dark); text-decoration: underline; }

/* Responsive table wrapper — wide specs tables scroll rather than
   forcing the page to scroll sideways. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: var(--sp-5); }
.table-scroll table { min-width: 480px; }
.table-scroll th, .table-scroll td {
    padding: var(--sp-3);
    border-bottom: 1px solid var(--line);
    text-align: left;
    font-size: var(--fs-sm);
}
.table-scroll th { background: var(--surface-alt); font-weight: 700; white-space: nowrap; }

/* ---------------------------------------------------------------------------
   Phone-width tables: stack each row as a card
   ---------------------------------------------------------------------------
   A five-column price table in a 343px window showed about 60% of itself and
   had to be swiped sideways. Worse, the narrow "Suits" column wrapped to six
   or seven lines while every other cell stayed on one, so a row stood ~600px
   tall with the size marooned in the middle of it.

   Below 768px each row becomes a small card: the size as its heading, then one
   label/value line per column. No sideways scrolling, nothing cut off, and the
   row height follows the content.

   Scoped to max-width on purpose. The stylesheet is mobile-first everywhere
   else, but the desktop table is right as it is and this must not be able to
   reach it. `.is-stacked` is added by initTables() in site.js once it has
   stamped data-label onto every cell; without JavaScript the class never
   arrives and the old scrolling table stands, which is still readable.
--------------------------------------------------------------------------- */
@media (max-width: 767.98px) {
    .table-scroll.is-stacked { overflow-x: visible; }

    .table-scroll.is-stacked table { min-width: 0; }

    /* The headings are repeated onto every cell, so the row is now noise for
       a screen reader as much as for the eye. */
    .table-scroll.is-stacked thead { display: none; }

    .table-scroll.is-stacked tbody { display: block; }

    .table-scroll.is-stacked tbody tr {
        display: block;
        margin-bottom: var(--sp-4);
        padding: var(--sp-4);
        border: 1px solid var(--line);
        border-radius: var(--radius-lg);
        background: var(--surface);
    }

    .table-scroll.is-stacked tbody tr:last-child { margin-bottom: 0; }

    /* The first cell is the row header — the size — and reads as the card
       title rather than as another label/value pair. */
    .table-scroll.is-stacked tbody tr > :first-child {
        display: block;
        padding: 0 0 var(--sp-3);
        margin-bottom: var(--sp-3);
        border-bottom: 1px solid var(--line);
        font-size: var(--fs-md);
        font-weight: 700;
        color: var(--ink);
        background: none;
        white-space: normal;
    }

    .table-scroll.is-stacked tbody tr > :first-child::before { display: none; }

    .table-scroll.is-stacked tbody td {
        display: flex;
        gap: var(--sp-4);
        align-items: baseline;
        justify-content: space-between;
        padding: var(--sp-2) 0;
        border: 0;
        text-align: right;
    }

    .table-scroll.is-stacked tbody td::before {
        content: attr(data-label);
        flex: 0 1 auto;
        text-align: left;
        font-weight: 700;
        color: var(--ink-faint);
    }

    /* A long prose column — "Suits" — reads better on its own line than
       squeezed against its label. */
    .table-scroll.is-stacked tbody td[data-wrap] {
        display: block;
        text-align: left;
    }

    .table-scroll.is-stacked tbody td[data-wrap]::before {
        display: block;
        margin-bottom: 2px;
    }
}

/* ---------------------------------------------------------------------------
   Customer logo marquee
   ---------------------------------------------------------------------------
   Replaces jQuery + Slick + Bootstrap JS from the Joomla site with two
   keyframes. Each row holds its logos twice and slides exactly one copy's
   width, so the restart lands on an identical frame and the loop is seamless.

   The duplicate copy is aria-hidden in the markup; here it is also what makes
   the -50% work, so the two must stay in step.
--------------------------------------------------------------------------- */
.logo-band { overflow: hidden; }

.logo-marquee {
    overflow: hidden;
    margin-top: var(--sp-6);
    /* Fade the two edges so logos enter and leave rather than being chopped. */
    -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
            mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}

.logo-marquee + .logo-marquee { margin-top: var(--sp-4); }

.logo-marquee__track {
    display: flex;
    align-items: center;
    gap: var(--sp-7);
    width: max-content;
    margin: 0;
    padding: 0;
    list-style: none;
    animation: logo-scroll var(--logo-seconds, 40s) linear infinite;
    animation-direction: var(--logo-direction, normal);
}

/* Hovering lets someone actually look at a logo they recognised. */
.logo-marquee:hover .logo-marquee__track,
.logo-marquee:focus-within .logo-marquee__track {
    animation-play-state: paused;
}

.logo-marquee__item {
    flex: 0 0 auto;
    width: 108px;
    display: flex;
    align-items: center;
    justify-content: center;
}

.logo-marquee__item img {
    width: 100%;
    height: auto;
    max-height: 72px;
    object-fit: contain;
}

@keyframes logo-scroll {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(-50%, 0, 0); }
}

/*
 * No animation means the track must not stay as one long strip: everything past
 * the first few logos would be unreachable behind `overflow: hidden`. It
 * becomes a centred, wrapping row instead, and the duplicate copy is dropped so
 * nothing appears twice.
 */
@media (prefers-reduced-motion: reduce) {
    .logo-marquee { -webkit-mask-image: none; mask-image: none; }

    .logo-marquee__track {
        animation: none;
        width: auto;
        flex-wrap: wrap;
        justify-content: center;
        gap: var(--sp-5) var(--sp-6);
    }

    .logo-marquee__item[aria-hidden="true"] { display: none; }
}

/* Responsive embeds (Google Maps, YouTube) */
.embed {
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    border-radius: var(--radius-lg);
    overflow: hidden;
    background: var(--surface-alt);
}
.embed iframe {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
}
.embed--map { aspect-ratio: 4 / 3; }

/* A video dropped into an article body needs its own breathing room,
   because .prose spacing applies to paragraphs rather than to this. */
.embed--video { margin: var(--sp-6) 0; }

/* --------------------------------------------------------------------------
   7. FAQ  (every page carries five, preceded by a separator)
   -------------------------------------------------------------------------- */
.faq-separator {
    margin-block: var(--sp-7) var(--sp-6);
    border: 0;
    height: 1px;
    background: linear-gradient(
        to right,
        transparent,
        var(--brand-pale) 15%,
        var(--brand-cyan) 50%,
        var(--brand-pale) 85%,
        transparent
    );
}

.faq { margin-bottom: var(--sp-6); }
.faq__heading { margin-bottom: var(--sp-5); }

.faq__item {
    border: 1px solid var(--line);
    border-radius: var(--radius);
    margin-bottom: var(--sp-3);
    background: var(--surface);
    overflow: hidden;
}
.faq__item[open] { border-color: var(--brand-cyan); }

.faq__q {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-4);
    font-weight: 700;
    font-size: var(--fs-md);
    cursor: pointer;
    list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
    content: "+";
    flex: 0 0 auto;
    font-size: 1.4em;
    font-weight: 400;
    line-height: 1;
    color: var(--brand-cyan-dark);
    transition: transform .2s;
}
.faq__item[open] .faq__q::after { content: "−"; }
.faq__q:hover { background: var(--surface-alt); }

.faq__a {
    padding: 0 var(--sp-4) var(--sp-4);
    color: var(--ink-soft);
}
.faq__a :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   7b. Reviews
   -------------------------------------------------------------------------- */
.rating-summary {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3) var(--sp-5);
    padding: var(--sp-5);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-alt);
    margin-bottom: var(--sp-6);
}
.rating-summary__score {
    font-size: var(--fs-3xl);
    font-weight: 800;
    line-height: 1;
    color: var(--brand-orange-dark);
}
.rating-summary__meta { color: var(--ink-soft); font-size: var(--fs-sm); }

/* Star row. Rendered as text so it stays legible with images blocked. */
.stars {
    display: inline-flex;
    gap: 1px;
    color: var(--brand-orange);
    font-size: var(--fs-md);
    line-height: 1;
    letter-spacing: .05em;
}
.stars--muted { color: var(--line-strong); }

/* --------------------------------------------------------------------------
   Product variant picker — frame height and shelf count
   -------------------------------------------------------------------------- */
.variant-picker {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--sp-3);
    margin-bottom: var(--sp-4);
    width: 100%;
}

.variant-field { display: flex; flex-direction: column; gap: 4px; }

.variant-field label {
    font-size: var(--fs-sm);
    font-weight: 600;
    color: var(--ink);
}

.variant-field select {
    width: 100%;
    min-height: 46px;
    padding: 0.55rem 0.75rem;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    font: inherit;
    color: var(--ink);
}

.variant-field select:focus-visible {
    outline: 2px solid var(--brand-cyan);
    outline-offset: 1px;
}

/*
 * A product with one frame height has nothing to choose: the storeroom and
 * warehouse racks are each sold at a single height, and only the shelf count
 * varies. The select stays in the DOM because the picker script reads its
 * value to look up the variant; it just has no business being on screen.
 *
 * This must stay BELOW `.variant-field { display: flex }` — both are a single
 * class, so source order is the only thing deciding it.
 */
.variant-field--fixed {
    display: none;
}

.review-grid {
    display: grid;
    gap: var(--sp-4);
    grid-template-columns: 1fr;
}

/* The summary sentence quotes how many cards are on the page, so its number
   has to follow the same rule the grid does. */
.reviews-count-wide { display: none; }
.reviews-count-narrow { display: inline; }

/* Phones and tablets get 16 of the 24; desktop gets all of them. Hidden rather
   than not rendered, because the server cannot know the viewport — and the
   images are lazy, so the cards below the cut cost nothing to download. */
.review-grid > .review:nth-child(n + 17) { display: none; }

.review {
    display: flex;
    flex-direction: column;
    padding: var(--sp-5);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);

    /* Every card exactly the same height, so the rows line up however long the
       review is. min-height alone was not enough: a grid row stretches to its
       tallest card, so rows still differed from one another.

       The figure is set to fit the worst case — a two-line name plus seven
       clamped lines plus the toggle. Expanding a card releases the height,
       which is the whole point of the toggle. */
    height: 360px;
}

.review.is-expanded { height: auto; }
.review__head {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    margin-bottom: var(--sp-3);
}

/* Initials avatar — avoids a third-party image request per review. */
/* The reviewer's actual Google profile picture, downloaded to our own server
   by app/database/fetch-review-avatars.php. Same circle as the initials
   fallback so a card looks right either way. */
.review__avatar--photo {
    padding: 0;
    background: var(--surface-alt);
    object-fit: cover;
}

.review__avatar {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: 50%;
    background: var(--brand-cyan-soft);
    color: var(--brand-cyan-dark);
    font-weight: 800;
    font-size: var(--fs-sm);
    letter-spacing: .02em;
}
.review__who { min-width: 0; }
.review__name {
    font-weight: 700;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
.review__date { font-size: var(--fs-xs); color: var(--ink-faint); }
.review__body { margin: 0; color: var(--ink-soft); overflow-wrap: anywhere; }
.review__body p:last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   Long reviews — clamp, with a Read more toggle

   The clamp is applied by site.js (.is-clamped), not by default, so that with
   JavaScript off the full review is still readable rather than cut off with no
   way to open it. The toggle button starts hidden and is only revealed for the
   cards whose text actually overflows, so a two-line review never gets a
   pointless "Read more".
   -------------------------------------------------------------------------- */
.review__body.is-clamped p {
    display: -webkit-box;
    -webkit-line-clamp: 7;
    line-clamp: 7;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.review__more {
    align-self: flex-start;
    margin-top: var(--sp-2);
    padding: var(--sp-2) 0;
    border: 0;
    background: none;
    color: var(--brand-cyan-dark);
    font: inherit;
    font-size: var(--fs-sm);
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
    min-height: 44px;
}

.review__more:hover,
.review__more:focus-visible { color: var(--brand-orange-dark); }

/* Cards whose review fits still reserve the toggle's space, so every card in a
   row ends up the same height. Invisible, unfocusable, and out of the
   accessibility tree — it is spacing, not a control. */
.review__more.is-placeholder {
    visibility: hidden;
    pointer-events: none;
}

/* Pushes the toggle to the bottom of the card so the buttons line up across a
   row even when the clamped text ends at different points. */
.review__body { flex: 1 1 auto; }

/* --------------------------------------------------------------------------
   7b2. Product gallery
   -------------------------------------------------------------------------- */
.product-gallery__main {
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    aspect-ratio: 4 / 3;
    margin-bottom: var(--sp-3);
}
/* Product photography is never cropped — the whole rack stays visible. */
.product-gallery__main img {
    max-height: 100%;
    width: auto;
    object-fit: contain;
    margin-inline: auto;
}

.product-gallery__thumbs {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--sp-2);
}
.product-gallery__thumb {
    display: grid;
    place-items: center;
    padding: var(--sp-2);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    background: var(--surface-alt);
    aspect-ratio: 4 / 3;
    overflow: hidden;
}
.product-gallery__thumb:hover,
.product-gallery__thumb:focus-visible { border-color: var(--brand-cyan); }
.product-gallery__thumb img {
    max-height: 100%;
    width: auto;
    object-fit: contain;
}

/* --------------------------------------------------------------------------
   7b3. Embedded quotation / order forms
   The standalone form CSS styles `.form-container h1`. Embedded in a page
   that already has its own H1, that heading is an H2 — this keeps it looking
   identical without touching the form's own stylesheet.
   -------------------------------------------------------------------------- */
.form-container .form-title {
    font-size: 24px;
    font-weight: 700;
    margin-bottom: 32px;
    color: #1a1a2e;
    line-height: 1.25;
}

/* Headline figure in a trust-bar tile. Deliberately not a heading: it is a
   label, and marking it up as one would skip a heading level. */
.stat-line {
    margin-bottom: var(--sp-2);
    font-size: var(--fs-xl);
    font-weight: 700;
    line-height: var(--lh-tight);
    color: var(--ink);
}

/* --------------------------------------------------------------------------
   7b4a. Public form controls
   The admin has its own copy in admin.css; these are for pages the public
   sees, so they must live here — admin.css is never loaded on the front end.
   -------------------------------------------------------------------------- */
.form-field { margin-bottom: var(--sp-4); }
.form-field > label {
    display: block;
    margin-bottom: var(--sp-1);
    font-weight: 700;
    font-size: var(--fs-sm);
}
.form-field input[type="text"],
.form-field input[type="email"],
.form-field input[type="tel"],
.form-field input[type="number"],
.form-field input[type="date"],
.form-field input[type="search"],
.form-field select,
.form-field textarea {
    width: 100%;
    padding: 0.65rem 0.75rem;
    /* 44px minimum so every control is a comfortable tap target. */
    min-height: 46px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    font-size: var(--fs-base);
    font-family: inherit;
}
.form-field textarea { min-height: 110px; resize: vertical; }
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus { border-color: var(--brand-cyan); }
.form-field small { display: block; margin-top: var(--sp-1); color: var(--ink-faint); font-size: var(--fs-xs); }

.form-field--check label {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 44px;
    font-weight: 500;
    font-size: var(--fs-base);
    cursor: pointer;
}
.form-field--check input { width: 20px; height: 20px; flex: 0 0 auto; }

.form-row { display: grid; gap: var(--sp-3); grid-template-columns: 1fr; }

/* Inline status messages */
.flash {
    padding: var(--sp-4);
    margin-bottom: var(--sp-5);
    border-radius: var(--radius);
    border-left: 4px solid;
    font-size: var(--fs-sm);
}
.flash--ok    { background: #e9f6ee; border-color: var(--ok);    color: #1d5e3b; }
.flash--error { background: #fbeae8; border-color: var(--error); color: #8c2a1f; }
.flash :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   7b4. Basket and checkout
   -------------------------------------------------------------------------- */
.cart-table { font-size: var(--fs-sm); }
.cart-table th,
.cart-table td {
    padding: var(--sp-3);
    border-bottom: 1px solid var(--line);
    text-align: left;
    vertical-align: middle;
}
.cart-table thead th {
    background: var(--surface-alt);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--ink-faint);
    white-space: nowrap;
}

.cart-line { display: flex; align-items: center; gap: var(--sp-3); }
.cart-line img {
    flex: 0 0 auto;
    width: 64px;
    height: 48px;
    object-fit: contain;
    background: var(--surface-alt);
    border-radius: var(--radius-sm);
}
.cart-line small { display: block; color: var(--ink-faint); font-weight: 400; }

.cart-qty { display: flex; gap: var(--sp-2); align-items: center; margin: 0; }
.cart-qty input {
    width: 4.5rem;
    padding: 0.45rem 0.5rem;
    min-height: 40px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    text-align: center;
}

.cart-summary {
    padding: var(--sp-5);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface-alt);
    position: sticky;
    top: calc(var(--header-h) + var(--sp-4));
}
.cart-summary h2 { font-size: var(--fs-lg); }

.cart-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: var(--sp-3);
    padding-top: var(--sp-4);
    margin-bottom: var(--sp-4);
    border-top: 2px solid var(--brand-cyan);
    font-size: var(--fs-lg);
}
.cart-total strong { font-size: var(--fs-xl); color: var(--brand-orange-dark); }

.checkout-lines { list-style: none; margin: 0 0 var(--sp-4); padding: 0; }
.checkout-lines li {
    display: flex;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--line);
    font-size: var(--fs-sm);
    margin: 0;
}
.checkout-lines small { color: var(--ink-faint); }

.checkout-block {
    padding: var(--sp-5);
    margin-bottom: var(--sp-5);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    background: var(--surface);
}
.checkout-block h2 { font-size: var(--fs-lg); }

.add-to-cart {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-3);
    align-items: center;
    margin-bottom: var(--sp-4);
}
.add-to-cart input[type="number"] {
    width: 5rem;
    padding: 0.65rem 0.5rem;
    min-height: 46px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    text-align: center;
    font-size: var(--fs-base);
}

/* --------------------------------------------------------------------------
   7b5. Click-to-play animations
   A poster frame stands in for a heavy animated GIF until the reader asks.
   -------------------------------------------------------------------------- */
.gif-player { margin: var(--sp-5) 0; }

.gif-player__link {
    position: relative;
    display: block;
    max-width: 820px;
    margin-inline: auto;
    border-radius: var(--radius);
    overflow: hidden;
    line-height: 0;
}
.gif-player__link img { width: 100%; height: auto; }

.gif-player__play {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    background: rgba(28, 33, 38, .42);
    color: #fff;
    font-size: var(--fs-base);
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    line-height: 1;
    transition: background .2s;
}
.gif-player__link:hover .gif-player__play,
.gif-player__link:focus-visible .gif-player__play { background: rgba(28, 33, 38, .58); }

.gif-player__link.is-loading { cursor: progress; }
.gif-player__link.is-playing { cursor: default; }

.gif-player figcaption {
    max-width: 820px;
    margin-inline: auto;
    text-align: center;
}

/* --------------------------------------------------------------------------
   7c. Step-by-step guides
   -------------------------------------------------------------------------- */
.steps {
    list-style: none;
    margin: 0 0 var(--sp-6);
    padding: 0;
    counter-reset: step;
}
.steps > li {
    position: relative;
    margin-bottom: var(--sp-5);
    padding-left: 3.25rem;
    counter-increment: step;
}
.steps > li::before {
    content: counter(step);
    position: absolute;
    left: 0;
    top: 0;
    display: grid;
    place-items: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: 50%;
    background: var(--brand-orange);
    color: #fff;
    font-weight: 800;
    font-size: var(--fs-base);
    line-height: 1;
}
.steps h3 { margin-bottom: var(--sp-2); }
.steps > li > :last-child { margin-bottom: 0; }

/* --------------------------------------------------------------------------
   7d. Long-form prose (policies, guides)
   -------------------------------------------------------------------------- */
.prose > h2 {
    margin-top: var(--sp-7);
    padding-top: var(--sp-5);
    border-top: 1px solid var(--line);
}
.prose > h2:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
.prose > h3 { margin-top: var(--sp-5); }
.prose li { margin-bottom: var(--sp-2); }

.meta-line {
    margin-bottom: var(--sp-6);
    font-size: var(--fs-sm);
    color: var(--ink-faint);
}

/* Blog post body — migrated Joomla markup, so it has to be forgiving.
   Images sit on their own line at full width, never cropped. */
.post-body img {
    width: 100%;
    height: auto;
    max-width: 820px;
    margin: var(--sp-5) auto;
    border-radius: var(--radius);
    background: var(--surface-alt);
}
.post-body p { margin-bottom: var(--sp-4); }
.post-body h2 { font-size: var(--fs-xl); }
.post-body h3 { font-size: var(--fs-lg); }
.post-body ul,
.post-body ol { margin-bottom: var(--sp-5); }
.prose blockquote,
.post-body blockquote {
    margin: var(--sp-5) 0;
    padding: var(--sp-4) var(--sp-5);
    border-left: 4px solid var(--brand-cyan);
    background: var(--brand-cyan-soft);
    border-radius: 0 var(--radius) var(--radius) 0;
}
.post-body blockquote :last-child { margin-bottom: 0; }
/* Tables inside migrated copy have no wrapper, so make them scroll safely. */
.post-body table {
    display: block;
    overflow-x: auto;
    margin-bottom: var(--sp-5);
    font-size: var(--fs-sm);
}
.post-body th,
.post-body td {
    padding: var(--sp-3);
    border-bottom: 1px solid var(--line);
    text-align: left;
}
.post-body th { background: var(--surface-alt); font-weight: 700; }

/* Definition-style key/value blocks — contact details, spec summaries */
.info-list { margin: 0 0 var(--sp-5); }
.info-list dt {
    font-weight: 700;
    color: var(--ink);
    margin-top: var(--sp-4);
}
.info-list dt:first-child { margin-top: 0; }
.info-list dd { margin: var(--sp-1) 0 0; color: var(--ink-soft); }

/* --------------------------------------------------------------------------
   8. Footer
   -------------------------------------------------------------------------- */
.site-footer {
    margin-top: var(--sp-8);
    padding-block: var(--sp-7) var(--sp-5);
    background: var(--surface-deep);
    color: #b9c4cd;
    font-size: var(--fs-sm);
}
.site-footer a { color: #dfe6ec; text-decoration: none; }
.site-footer a:hover { color: var(--brand-orange); text-decoration: underline; }

.site-footer__grid {
    display: grid;
    gap: var(--sp-6);
    grid-template-columns: 1fr;
}
.site-footer h2, .site-footer h3 {
    color: #fff;
    font-size: var(--fs-base);
    letter-spacing: .04em;
    text-transform: uppercase;
    margin-bottom: var(--sp-3);
}
.site-footer ul { list-style: none; margin: 0; padding: 0; }
.site-footer li { margin-bottom: var(--sp-2); }

.site-footer__logo img { height: 38px; width: auto; margin-bottom: var(--sp-3); }

/* Accreditation logos.

   All three are dark artwork drawn for a white background — bizSAFE is grey and
   red, vendors@gov is navy, GeBIZ is blue. On the dark footer they would be
   close to illegible, so each sits on its own white tile. That is also how these
   marks are meant to be reproduced.

   The three files have very different proportions (433x212, 317x66, 127x85), so
   the tile fixes the HEIGHT and lets the width follow. Anything else would make
   the wide, short vendors@gov logo tower over the small GeBIZ one. */
.site-footer__accred {
    margin-top: var(--sp-6);
    padding-top: var(--sp-5);
    border-top: 1px solid rgba(255, 255, 255, .12);
}

.site-footer__accred-label {
    display: block;
    font-size: var(--fs-xs);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: #8d99a3;
    margin-bottom: var(--sp-3);
}

.site-footer__accred-list {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-3);
    margin: 0;
    padding: 0;
    list-style: none;
}

.site-footer__accred-list li {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border-radius: var(--radius-sm);
    padding: var(--sp-2) var(--sp-3);
    min-height: 56px;
}

.site-footer__accred-list img {
    display: block;
    height: 34px;
    width: auto;
    max-width: 100%;
    object-fit: contain;
}

.site-footer__bottom {
    margin-top: var(--sp-6);
    padding-top: var(--sp-4);
    border-top: 1px solid rgba(255, 255, 255, .12);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    font-size: var(--fs-xs);
    color: #8d99a3;
}

/* About Us and the legal pages sit here rather than in the Policies column.
   Four links do not fit one line on a narrow phone, so let them wrap, and give
   each one vertical padding so the tap target stays comfortable when they do. */
.site-footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1) var(--sp-2);
    align-items: center;
}

.site-footer__links a {
    display: inline-block;
    padding-block: var(--sp-1);
}

/* --------------------------------------------------------------------------
   9. Breakpoints — tablet
   -------------------------------------------------------------------------- */
/* ── Home hero banner ────────────────────────────────────────────────────────
   Full-bleed photo with the copy on a dark panel over it.

   object-fit: cover is used here and nowhere else. The site-wide default is
   contain, so that a customer always sees the whole rack in a product photo —
   this is a decorative banner, not product photography, and a banner that
   letterboxes itself looks broken. The rule is deliberately scoped to this
   block so it cannot leak.

   The supplied photo is 2.540:1, which is 154px tall at phone width. Far too
   short to hold a headline, so the section sets its own min-height per
   breakpoint and the photo covers it. The interesting part of the frame (the
   aisle and the window) sits right of centre, so object-position keeps that in
   view while the copy sits over the darker left side.
   ------------------------------------------------------------------------- */

.hero-banner {
    position: relative;
    isolation: isolate;
    display: flex;
    align-items: center;
    overflow: hidden;
    min-height: 30rem;
    padding-block: var(--sp-7);
    background: var(--surface-deep);
}

.hero-banner__media {
    position: absolute;
    inset: 0;
    z-index: -2;
}

.hero-banner__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: 68% center;
}

/* Mobile: the copy sits over the picture, so the wash has to carry the whole
   width. Still a gradient rather than a flat panel, so the photo reads as a
   photo instead of a grey block. */
.hero-banner::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: linear-gradient(180deg,
        rgba(16, 20, 24, 0.86) 0%,
        rgba(16, 20, 24, 0.78) 45%,
        rgba(16, 20, 24, 0.62) 100%);
}

.hero-banner__inner { max-width: 34rem; }

.hero-banner .eyebrow { color: var(--brand-pale); }

.hero-banner__title {
    margin: 0 0 var(--sp-4);
    color: #fff;
    font-size: var(--fs-4xl);
    font-weight: 800;
    line-height: 1.08;
    letter-spacing: -0.02em;
}

.hero-banner__title span {
    display: block;
    color: var(--brand-orange);
}

.hero-banner__lede {
    margin: 0 0 var(--sp-5);
    color: #dbe3ea;
    font-size: var(--fs-md);
    line-height: var(--lh-snug);
}

.hero-banner .btn-row { margin-bottom: var(--sp-6); }

/* The phone button is an outline button on a dark ground, so it needs its own
   colours — the stock .btn--outline is drawn for a white page. */
.hero-banner__call {
    border-color: rgba(255, 255, 255, 0.45);
    color: #fff;
}

.hero-banner__call:hover,
.hero-banner__call:focus-visible {
    border-color: #fff;
    background: rgba(255, 255, 255, 0.1);
    color: #fff;
}

.hero-banner__call svg,
.hero-banner__cta svg {
    flex: none;
    width: 1.125rem;
    height: 1.125rem;
}

/* ── The four segments ─────────────────────────────────────────────────────
   Links, not decoration. Each one is the shortest route to the page a visitor
   in that segment actually wants, with the segment name as the anchor text. */

.hero-segments {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--sp-4);
    margin: 0;
    padding: var(--sp-5) 0 0;
    border-top: 1px solid rgba(255, 255, 255, 0.18);
    list-style: none;
}

.hero-segments a {
    display: block;
    color: #fff;
    text-decoration: none;
}

.hero-segments a:hover .hero-segments__name,
.hero-segments a:focus-visible .hero-segments__name { color: var(--brand-orange); }

.hero-segments svg {
    display: block;
    width: 1.5rem;
    height: 1.5rem;
    margin-bottom: var(--sp-2);
    color: #fff;
}

.hero-segments__name {
    display: block;
    font-size: var(--fs-sm);
    font-weight: 700;
    line-height: 1.25;
    transition: color 0.15s ease;
}

.hero-segments__note {
    display: block;
    margin-top: 0.15rem;
    color: #aebbc6;
    font-size: var(--fs-xs);
    line-height: 1.3;
}


@media (min-width: 768px) {
    :root {
        --fs-xs:   0.8125rem;
        --fs-sm:   0.9375rem;
        --fs-base: 1.0625rem;
        --fs-md:   1.125rem;
        --fs-lg:   1.3125rem;
        --fs-xl:   1.5rem;
        --fs-2xl:  1.875rem;
        --fs-3xl:  2.25rem;
        --fs-4xl:  2.75rem;

        --section-y: 3.5rem;
        --gutter:    1.75rem;
        --header-h:  70px;
    }

    .grid--2 { grid-template-columns: repeat(2, 1fr); }
    .grid--3 { grid-template-columns: repeat(2, 1fr); }
    .grid--4 { grid-template-columns: repeat(3, 1fr); }

    .split { grid-template-columns: 1fr 1fr; gap: var(--sp-6); }

    .site-logo img { height: 40px; }

    .site-footer__grid { grid-template-columns: repeat(2, 1fr); }
    .site-footer__bottom { flex-direction: row; justify-content: space-between; align-items: center; }

    .btn-row .btn { width: auto; }
    .btn--sm { min-height: 38px; }

    .review-grid { grid-template-columns: repeat(2, 1fr); }

    /* The teaser is exactly three cards, so it goes 3-up from tablet rather
       than leaving one stranded on a second row. The full grid on
       /customer-reviews keeps 2-up here — it has 16 cards and they would be
       too narrow at three. */
    .review-grid--teaser { grid-template-columns: repeat(3, 1fr); }

    /* Three cards across a tablet leaves each one about 222px wide, narrow
       enough that a name like "Peng Choong Koh" wraps to two lines and pushes
       the card past its fixed height. One line less of review text absorbs
       that, rather than letting the text clip. */
    .review-grid--teaser .review__body.is-clamped p {
        -webkit-line-clamp: 6;
        line-clamp: 6;
    }
    .steps > li { padding-left: 3.75rem; }
    .form-row { grid-template-columns: repeat(3, 1fr); }

    .hero-banner { min-height: 32rem; }

    .hero-banner::after {
        background: linear-gradient(100deg,
            rgba(16, 20, 24, 0.93) 0%,
            rgba(16, 20, 24, 0.88) 45%,
            rgba(16, 20, 24, 0.70) 72%,
            rgba(16, 20, 24, 0.48) 100%);
    }
    .hero-banner__title { font-size: 2.75rem; }

    .hero-segments {
        grid-template-columns: repeat(4, minmax(0, 1fr));
        gap: var(--sp-5);
    }

}

/* --------------------------------------------------------------------------
   10. Breakpoints — desktop
   -------------------------------------------------------------------------- */
@media (min-width: 1024px) {
    :root {
        --fs-xs:   0.8125rem;
        --fs-sm:   0.9375rem;
        --fs-base: 1.0625rem;
        --fs-md:   1.1875rem;
        --fs-lg:   1.375rem;
        --fs-xl:   1.625rem;
        --fs-2xl:  2rem;
        --fs-3xl:  2.5rem;
        --fs-4xl:  3.25rem;

        --section-y: 4.5rem;
        --gutter:    2rem;
        --header-h:  78px;
    }

    .grid--3 { grid-template-columns: repeat(3, 1fr); }
    .grid--4 { grid-template-columns: repeat(4, 1fr); }

    .split { gap: var(--sp-8); }
    .split--wide-left  { grid-template-columns: 1.25fr 1fr; }
    .split--wide-right { grid-template-columns: 1fr 1.25fr; }

    .site-logo img { height: 46px; }

    /* Horizontal nav replaces the drawer */
    .nav-toggle { display: none; }

    .site-nav,
    .site-nav.is-open {
        display: block;
        padding: 0;
        border-top: 0;

        /* Back into the header row alongside the logo. */
        flex: 0 1 auto;
        width: auto;

        /* The drawer's scroll cage must not follow the nav into the horizontal
           bar, or the dropdowns would be clipped by their own parent. */
        max-height: none;
        overflow: visible;
    }
    .site-nav__list {
        flex-direction: row;
        align-items: center;
        gap: var(--sp-1);
    }
    .site-nav__link { padding: var(--sp-2) var(--sp-3); white-space: nowrap; }
    .site-nav__cta { margin-top: 0; margin-left: var(--sp-3); }

    .has-sub { position: relative; }
    .has-sub > .site-nav__link::after {
        content: "▾";
        margin-left: .35em;
        font-size: .7em;
        color: var(--ink-faint);
    }
    .site-nav__sub {
        position: absolute;
        top: 100%;
        left: 0;
        min-width: 230px;
        margin: 0;
        padding: var(--sp-2);
        border: 1px solid var(--line);
        border-left: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        box-shadow: var(--shadow);
        opacity: 0;
        visibility: hidden;
        transform: translateY(-6px);
        transition: opacity .16s, transform .16s, visibility .16s;
    }
    .has-sub:hover > .site-nav__sub,
    .has-sub:focus-within > .site-nav__sub {
        opacity: 1;
        visibility: visible;
        transform: translateY(0);
    }
    .site-nav__sub a { border-radius: var(--radius-sm); }
    .site-nav__sub a:hover { background: var(--surface-alt); }

    .site-footer__grid { grid-template-columns: 1.4fr 1fr 1fr 1fr; }

    .table-scroll table { min-width: 0; }

    .logo-marquee__item { width: 150px; }
    .logo-marquee__item img { max-height: 96px; }

    .review-grid { grid-template-columns: repeat(3, 1fr); }
    .review-grid > .review:nth-child(n + 17) { display: flex; }

    /* Cards are wide enough again for the full seven lines. */
    .review-grid--teaser .review__body.is-clamped p {
        -webkit-line-clamp: 7;
        line-clamp: 7;
    }
    .reviews-count-wide { display: inline; }
    .reviews-count-narrow { display: none; }
    .hero-banner {
        min-height: 36rem;
        padding-block: var(--sp-8);
    }

    .hero-banner__media img { object-position: center; }

    /* From here the copy has its own side of the frame, so the wash can fall
       away to the right and let the photograph through. */
    .hero-banner::after {
        background: linear-gradient(90deg,
            rgba(16, 20, 24, 0.95) 0%,
            rgba(16, 20, 24, 0.88) 32%,
            rgba(16, 20, 24, 0.55) 55%,
            rgba(16, 20, 24, 0.12) 78%,
            rgba(16, 20, 24, 0.04) 100%);
    }

    /* The copy column and the segment row are different widths on purpose.
       Headline and lede stay narrow enough to read in two or three lines; the
       four segments get the full panel so their labels sit on one line each
       instead of wrapping into stacks of two words. */
    .hero-banner__inner { max-width: 48rem; }

    .hero-banner__title {
        /* Sized so each of the two lines holds on one line of its own.
           Larger than this and "Boltless Storage Racks" breaks after the
           first word, which reads as four ragged lines rather than a
           headline. */
        max-width: 42rem;
        font-size: 3rem;
    }

    .hero-banner__lede {
        max-width: 34rem;
        font-size: var(--fs-lg);
    }

    .hero-segments { gap: var(--sp-6); }

    /* Dividers between the four, as in the reference. */
    .hero-segments li + li {
        padding-left: var(--sp-6);
        border-left: 1px solid rgba(255, 255, 255, 0.18);
    }

}


/* --------------------------------------------------------------------------
   11. Utilities
   -------------------------------------------------------------------------- */
.visually-hidden {
    position: absolute !important;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.text-center { text-align: center; }
.mb-0 { margin-bottom: 0; }
.mt-5 { margin-top: var(--sp-5); }
.mt-6 { margin-top: var(--sp-6); }

.eml-link { cursor: pointer; }

@media print {
    .site-header, .site-footer, .nav-toggle, .btn { display: none !important; }
    body { color: #000; }
}
