/* ==========================================================================
   Willis Lane design system
   --------------------------------------------------------------------------
   Loaded AFTER bootstrap and style_v2.css, so it is the last word on type,
   colour and spacing. style_v2.css is the inherited 2.0 stylesheet and is
   still doing real work (grid helpers, legacy page rules); rather than pick
   it apart, this file overrides the handful of global declarations that
   carry the old look — body font, heading font, paragraph colour, the teal
   accent — and then supplies the components the new layout needs.

   Component classes are all `wl-` prefixed. Bootstrap already owns .h2,
   .code, .card, .price and .item, so unprefixed names collide.
   ========================================================================== */

:root {
    /* Ink and surfaces */
    --wl-ink:        #191E1A;
    --wl-bluestone:  #2E463C;
    --wl-stone:      #63675F;
    --wl-chalk:      #F7F5F0;
    /* The alternating section band. Sits between chalk and paper in value, so
       a full-width section reads as a band against the page without the hard
       white edge --wl-paper gives it. Cards inside a plaster band stay
       --wl-paper and gain their contrast from it. */
    --wl-plaster:    #EAE7DE;
    --wl-rule:       #D8D3C6;
    --wl-paper:      #FFFFFF;

    /* Accents */
    --wl-brass:      #8F7238;
    --wl-brass-lit:  #C9A96A;
    --wl-good:       #3F6B4A;
    --wl-low:        #9A5B2B;
    --wl-sale:       #9A3324;

    /* Body copy sits a touch lighter than headings so a page of text does not
       read as one solid block. Still well past AA on chalk and paper. */
    --wl-body-ink:   #4A524B;

    --wl-gutter:     clamp(1rem, 3.5vw, 3rem);
    --wl-maxw:       1440px;

    --wl-display:    "Archivo", system-ui, -apple-system, sans-serif;
    --wl-body:       "IBM Plex Sans", system-ui, -apple-system, sans-serif;
    --wl-mono:       "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}

/* ==========================================================================
   1. Base — overrides of style_v2.css / bootstrap globals
   ========================================================================== */

body {
    font-family: var(--wl-body);
    font-size: 16px;
    line-height: 1.55;
    background-color: var(--wl-chalk);
    color: var(--wl-ink);
    -webkit-font-smoothing: antialiased;
}

/* style_v2.css paints every paragraph #b3b3b3 at weight 300, which is where
   most of the old washed-out feel came from. */
p {
    color: var(--wl-body-ink);
    font-weight: 400;
}

h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 112%;
    letter-spacing: -0.015em;
    line-height: 1.06;
    color: var(--wl-ink);
}

h1, .h1 { font-size: clamp(2rem, 4.5vw, 3.2rem); font-stretch: 118%; letter-spacing: -0.022em; }
h2, .h2 { font-size: clamp(1.6rem, 3.2vw, 2.4rem); font-stretch: 116%; letter-spacing: -0.018em; }
h3, .h3 { font-size: clamp(1.2rem, 2vw, 1.5rem); }
h4, .h4 { font-size: 1.15rem; }
h5, .h5 { font-size: 1rem; }
h6, .h6 { font-size: 0.9rem; }

a { color: inherit; transition: color .16s ease, border-color .16s ease, background .16s ease; }
a:hover { color: var(--wl-brass); }

:focus-visible { outline: 2px solid var(--wl-brass); outline-offset: 2px; }

strong, b { font-weight: 600; }
hr { border-color: var(--wl-rule); }

/* The 2.0 sheet lets .container run to 4000px, which leaves content stranded
   at the edges of a wide monitor. The artifact caps its measure at 1440. */
@media (min-width: 1300px) {
    .container { max-width: var(--wl-maxw); }
}

/* Retire the old teal. It is hard-coded in a lot of inherited markup, so the
   utility classes below give those call sites somewhere to land. */
.wl-accent { color: var(--wl-brass); }
.current   { color: var(--wl-ink); }

/* ==========================================================================
   2. Layout and typographic helpers
   ========================================================================== */

.wl-wrap {
    max-width: var(--wl-maxw);
    margin: 0 auto;
    padding-inline: var(--wl-gutter);
}

/* padding-block, not the padding shorthand: these classes are put on the
   same element as .wl-wrap in several places, and the shorthand resets
   padding-inline to 0 — silently removing the page gutter and running the
   copy into the edge of the screen on narrow viewports. */
.wl-sec { padding-block: clamp(2.8rem, 6vw, 4.6rem); }
/* Consecutive product rows on the signed-in dashboard (Your lists → Buy again
   → Ready to re-order → New for you) get half the top padding, so the stacked
   headings don't sit an entire section-gap apart. Full top padding still
   applies when a wl-sec follows the dashboard or the categories band, so the
   first row after those still breathes off them. */
.wl-sec + .wl-sec { padding-top: clamp(.8rem, 1.5vw, 1.4rem); }
.wl-sec-tight { padding-block: clamp(1.6rem, 3vw, 2.4rem); }

.wl-sec-hd {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    flex-wrap: wrap;
    margin-bottom: 1.6rem;
}

/* Small mono label above a heading. The single most repeated device in the
   design — it is what makes a heading read as a section rather than a shout. */
.wl-eyebrow {
    font-family: var(--wl-body);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--wl-stone);
    margin: 0;
}

.wl-h2 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 116%;
    letter-spacing: -.018em;
    line-height: 1.02;
    font-size: clamp(1.7rem, 3.2vw, 2.6rem);
    margin: .5rem 0 0;
}

.wl-lede {
    margin-top: .8rem;
    max-width: 58ch;
    color: var(--wl-body-ink);
    font-size: 15.5px;
}

/* Stock codes, dimensions, quantities — anything the buyer reads as data
   rather than as prose. */
.wl-code {
    font-family: var(--wl-body);
    font-size: 10.5px;
    letter-spacing: .05em;
    color: var(--wl-stone);
}

.wl-rule-top { border-top: 1px solid var(--wl-rule); }

/* ==========================================================================
   3. Buttons
   ========================================================================== */

.wl-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    font-family: var(--wl-body);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .08em;
    text-transform: uppercase;
    padding: .72rem 1.25rem;
    border: 1px solid var(--wl-ink);
    background: transparent;
    color: var(--wl-ink);
    cursor: pointer;
    white-space: nowrap;
    border-radius: 0;
    transition: background .16s, color .16s, border-color .16s;
}
.wl-btn:hover { background: var(--wl-ink); color: var(--wl-chalk); text-decoration: none; }

.wl-btn-solid { background: var(--wl-ink); color: var(--wl-chalk); }
.wl-btn-solid:hover { background: var(--wl-brass); border-color: var(--wl-brass); color: #fff; }

.wl-btn-quiet { border-color: var(--wl-rule); }
.wl-btn-light { border-color: rgba(255,255,255,.4); color: #fff; }
.wl-btn-light:hover { background: #fff; color: var(--wl-ink); }

.wl-btn-sm { padding: .48rem .78rem; font-size: 11px; }
.wl-btn-block { width: 100%; }

/* Bootstrap buttons appear all over the inherited templates (cart, checkout,
   account). Rather than rewrite every call site, give them the new shape. */
.btn {
    font-family: var(--wl-body);
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: .08em;
    text-transform: uppercase;
    border-radius: 0;
    padding: .72rem 1.25rem;
    transition: background .16s, color .16s, border-color .16s;
}
.btn-primary, .btn-dark, .btn-template {
    background: var(--wl-ink);
    border-color: var(--wl-ink);
    color: var(--wl-chalk);
}
.btn-primary:hover, .btn-dark:hover, .btn-template:hover,
.btn-primary:focus, .btn-dark:focus {
    background: var(--wl-brass);
    border-color: var(--wl-brass);
    color: #fff;
}
.btn-secondary, .btn-outline-dark {
    background: transparent;
    border-color: var(--wl-ink);
    color: var(--wl-ink);
}
.btn-secondary:hover, .btn-outline-dark:hover {
    background: var(--wl-ink);
    border-color: var(--wl-ink);
    color: var(--wl-chalk);
}
.btn-danger { background: var(--wl-sale); border-color: var(--wl-sale); }
.btn-success { background: var(--wl-good); border-color: var(--wl-good); }
.btn-sm { padding: .48rem .78rem; font-size: 11px; }

/* ==========================================================================
   4. Form controls
   ========================================================================== */

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

.form-control,
input[type="text"], input[type="email"], input[type="password"],
input[type="search"], input[type="tel"], input[type="number"],
textarea, select {
    border-radius: 0;
    border: 1px solid var(--wl-rule);
    background: var(--wl-paper);
    color: var(--wl-ink);
    font-size: 14.5px;
    padding: .6rem .75rem;
    height: auto;
}
.form-control:focus,
input:focus, textarea:focus, select:focus {
    border-color: var(--wl-ink);
    box-shadow: none;
    outline: none;
}
::placeholder { color: #9BA1A5; opacity: 1; }

label {
    font-family: var(--wl-body);
    font-size: 10.5px;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--wl-stone);
    margin-bottom: .3rem;
}

/* ==========================================================================
   5. Utility bar
   ========================================================================== */

.wl-util {
    background: var(--wl-bluestone);
    color: #C6C9C4;
    font-family: var(--wl-body);
    font-size: 11.5px;
    letter-spacing: .04em;
}
.wl-util a { color: inherit; }
.wl-util a:hover { color: var(--wl-brass-lit); }
.wl-util-in {
    display: flex;
    /* nowrap: the utility bar must never spill to a second line. Anything
       that would push it over gets removed at breakpoints via the hide-sm
       helper below, rather than being allowed to wrap. */
    flex-wrap: nowrap;
    gap: 1.1rem;
    align-items: center;
    justify-content: space-between;
    /* padding-block only, so the .wl-wrap's inline (gutter) padding stays
       intact. The old `padding: .5rem 0` shorthand was zeroing out the inline
       padding, which is why the dark bar touched the screen edges on mobile. */
    padding-block: .5rem;
}
/* Utility-bar visibility helpers. -hide-sm is the desktop default (visible)
   that drops off on mobile; -only-sm is the mirror (hidden on desktop, shown
   on mobile) used for mobile-only stand-ins like the Contact Us link that
   replaces the missing phone number. */
.wl-util-hide-sm { }
.wl-util-only-sm { display: none; }
/* Below 500px show only the banner text — the nav links (Contact Us, My
   Account, Log out) are hidden to avoid crowding a narrow screen. */
@media (max-width: 499px) {
    .wl-util-in > .wl-util-set:last-child { display: none; }
    .wl-util-in { justify-content: center; }
}
.wl-util-set { display: flex; gap: 1.1rem; flex-wrap: nowrap; align-items: center; white-space: nowrap; }
.wl-pill { background: var(--wl-brass); color: #fff; padding: .14rem .5rem; }

/* ==========================================================================
   6. Header
   ========================================================================== */

/* The dark utility bar and the header stick as one block, so the dark bar
   stays in view instead of scrolling away and leaving the pale header alone.
   Sticking the wrapper rather than each part avoids having to feed the utility
   bar's height in as a `top` offset — it differs between breakpoints, and any
   hard-coded number would drift out of step with it. */
.wl-topbar {
    position: sticky;
    top: 0;
    z-index: 900;
}

.wl-hdr {
    /* Positioning lives on .wl-topbar above. */
    background: var(--wl-chalk);
    border-bottom: 1px solid var(--wl-rule);
    transition: box-shadow .25s ease;
}
.wl-hdr.scrolled { box-shadow: 0 1px 12px rgba(21,24,26,.09); }

.wl-hdr-top {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    gap: clamp(1rem, 3vw, 2.5rem);
    align-items: center;
    /* padding-block only — inline padding comes from .wl-wrap so the logo and
       icons breathe off the viewport edge on mobile. */
    padding-block: .85rem;
}

.wl-mark { display: block; line-height: 1; }
.wl-mark img { display: block; width: 260px; max-width: 100%; height: auto; }
@media (max-width: 1200px) { .wl-mark img { width: 210px; } }

/* The large search box. Deliberately the widest thing in the header — a
   wholesale buyer arrives knowing a stock code far more often than they
   arrive wanting to browse. */
.wl-search { position: relative; display: flex; }
.wl-search input {
    width: 100%;
    padding: .78rem 1rem .78rem 2.6rem;
    border: 1px solid var(--wl-rule);
    background: var(--wl-paper);
    font-size: 14.5px;
}
.wl-search input:focus { border-color: var(--wl-ink); outline: none; }
.wl-search-ico {
    position: absolute;
    left: .95rem;
    top: 50%;
    transform: translateY(-50%);
    color: var(--wl-stone);
    font-size: 14px;
    pointer-events: none;
}

/* Type-ahead panel, anchored to whichever search input is in play. */
.wl-suggest {
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    background: var(--wl-paper);
    border: 1px solid var(--wl-ink);
    box-shadow: 0 12px 28px rgba(21,24,26,.14);
    z-index: 950;
    max-height: 460px;
    overflow-y: auto;
    display: none;
}
.wl-suggest.open { display: block; }
.wl-suggest-item {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .6rem .8rem;
    border-bottom: 1px solid var(--wl-rule);
    color: var(--wl-ink);
}
.wl-suggest-item:last-child { border-bottom: 0; }
.wl-suggest-item:hover, .wl-suggest-item.active { background: var(--wl-plaster); color: var(--wl-ink); }
.wl-suggest-thumb {
    width: 44px; height: 44px; flex: 0 0 44px;
    display: flex; align-items: center; justify-content: center;
    background: var(--wl-paper); border: 1px solid var(--wl-rule);
}
.wl-suggest-thumb img { max-width: 38px; max-height: 38px; object-fit: contain; }
.wl-suggest-info { flex: 1; min-width: 0; }
.wl-suggest-name { font-size: 14px; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.wl-suggest-price { font-family: var(--wl-body); font-size: 13px; font-weight: 600; flex-shrink: 0; }
.wl-suggest-all {
    display: block;
    text-align: center;
    padding: .7rem;
    font-family: var(--wl-body);
    font-size: 11px;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--wl-brass);
    background: var(--wl-plaster);
}
.wl-suggest-empty { padding: 1rem .8rem; font-size: 14px; color: var(--wl-stone); }

.wl-hdr-acts { display: flex; align-items: center; gap: .35rem; }
.wl-icon {
    display: flex; align-items: center; justify-content: center;
    width: 38px; height: 38px;
    color: var(--wl-ink);
    position: relative;
    /* Kill the 300ms tap delay some mobile browsers add on links, and give a
       visible active-state so a tap lands with feedback before the drawer
       animation kicks in. Without this the burger felt unresponsive — users
       tapped it repeatedly wondering if the tap had registered. */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background .1s ease;
}
.wl-icon:hover { background: rgba(21,24,26,.06); color: var(--wl-ink); }
.wl-icon:active { background: var(--wl-ink); color: var(--wl-chalk); }
.wl-icon i { font-size: 17px; }
.wl-cart-badge {
    position: absolute; top: 1px; right: 1px;
    background: var(--wl-brass); color: #fff;
    font-family: var(--wl-body); font-size: 9.5px; font-weight: 600;
    min-width: 17px; height: 17px;
    display: flex; align-items: center; justify-content: center;
    padding: 0 4px; line-height: 1;
}
.wl-cart-badge:empty { display: none; }

/* --- Category nav row ---------------------------------------------------- */

.wl-hdr-nav {
    display: flex;
    gap: 1.5rem;
    padding-block: 0 .8rem;
    font-size: 14px;
    flex-wrap: wrap;
    align-items: center;
}
.wl-nav-item { position: relative; }
.wl-nav-link {
    display: inline-block;
    padding-block: .2rem;
    border-bottom: 1.5px solid transparent;
    color: var(--wl-ink);
}
.wl-nav-item:hover > .wl-nav-link,
.wl-nav-link:hover { border-bottom-color: var(--wl-brass); color: var(--wl-ink); }
/* The count is the point of the row: it tells a buyer how deep a category is
   before they spend a click finding out. */
.wl-nav-link .wl-code { margin-left: .3rem; font-size: 11px; }
.wl-nav-link.is-sale { color: var(--wl-sale); font-weight: 600; }
.wl-nav-link.is-sale:hover { border-bottom-color: var(--wl-sale); }
.wl-nav-link.is-new { color: var(--wl-brass); font-weight: 600; }
/* My Lists nav item — brass accent so it reads as a personal / saved
   affordance (matches the "Save to list" link on product cards). */
.wl-nav-link.is-lists { color: var(--wl-brass); font-weight: 600; }
.wl-nav-link.is-lists:hover { border-bottom-color: var(--wl-brass); }

/* Hover panel of subcategories. Sits flush under the nav row and inherits the
   header's hard-edged, ruled look rather than the old rounded card. */
.wl-nav-drop {
    position: absolute;
    top: 100%;
    left: -.9rem;
    min-width: 250px;
    background: var(--wl-paper);
    border: 1px solid var(--wl-ink);
    box-shadow: 0 14px 30px rgba(21,24,26,.15);
    padding: .5rem 0;
    opacity: 0;
    visibility: hidden;
    transform: translateY(5px);
    transition: opacity .18s, visibility .18s, transform .18s;
    z-index: 940;
}
/* A gap between the link and the panel would drop the hover; this pad bridges
   the nav row's bottom padding. */
.wl-nav-item::after {
    content: '';
    position: absolute;
    top: 100%; left: 0; right: 0;
    height: .8rem;
}
.wl-nav-item:hover .wl-nav-drop {
    opacity: 1;
    visibility: visible;
    transform: translateY(0);
}
.wl-nav-drop a {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    padding: .45rem 1.1rem;
    font-size: 13.5px;
    color: var(--wl-body-ink);
}
.wl-nav-drop a:hover { background: var(--wl-plaster); color: var(--wl-ink); }
.wl-nav-drop .wl-nav-drop-all {
    font-family: var(--wl-body);
    font-size: 10.5px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--wl-brass);
    border-bottom: 1px solid var(--wl-rule);
    padding-bottom: .55rem;
    margin-bottom: .35rem;
}

/* Two-column panel for categories with a long child list. */
.wl-nav-drop-2col { min-width: 480px; }
.wl-nav-drop-2col .wl-nav-drop-list { columns: 2; column-gap: 0; }
.wl-nav-drop-2col .wl-nav-drop-list a { break-inside: avoid; }

/* ==========================================================================
   7. Account dropdown
   ========================================================================== */

.wl-icon-wrap { position: relative; }
.wl-acct-drop {
    position: absolute;
    top: 100%; right: 0;
    min-width: 190px;
    background: var(--wl-paper);
    border: 1px solid var(--wl-ink);
    box-shadow: 0 14px 30px rgba(21,24,26,.15);
    padding: .4rem 0;
    opacity: 0; visibility: hidden; transform: translateY(5px);
    transition: opacity .18s, visibility .18s, transform .18s;
    z-index: 940;
}
.wl-icon-wrap:hover .wl-acct-drop { opacity: 1; visibility: visible; transform: translateY(0); }
.wl-acct-drop a { display: block; padding: .5rem 1.1rem; font-size: 13.5px; color: var(--wl-body-ink); }
.wl-acct-drop a:hover { background: var(--wl-plaster); color: var(--wl-ink); }

/* ==========================================================================
   8. Announcement bar
   ========================================================================== */

.announce-bar {
    position: relative;
    background: var(--wl-ink);
    color: #fff;
    text-align: center;
    padding: .5rem 2.5rem;
    font-family: var(--wl-body);
    font-size: 11.5px;
    letter-spacing: .06em;
}
.announce-bar a { color: inherit; text-decoration: underline; }
.announce-bar .announce-close {
    position: absolute; right: 1rem; top: 50%; transform: translateY(-50%);
    background: none; border: none; color: rgba(255,255,255,.6);
    font-size: 16px; cursor: pointer; padding: 2px 6px;
}
.announce-bar .announce-close:hover { color: #fff; }

/* ==========================================================================
   9. Hero / thesis panel
   ========================================================================== */

/* Copy block above the staged banner: eyebrow, headline, lede and actions on
   the left; the founding date as a single standing credential on the right. */
.wl-hero {
    /* Tighter than the mockup's 2.2–3.6rem — the headline sits above a full
       bleed staged image, so the copy block does not need to breathe as a
       standalone panel would. */
    padding: clamp(1.2rem, 2.2vw, 1.8rem) 0 clamp(1rem, 1.8vw, 1.4rem);
    border-bottom: 1px solid var(--wl-rule);
}
.wl-hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 4rem);
    align-items: center;
}
.wl-hero h1 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 118%;
    letter-spacing: -.02em;
    line-height: 1;
    font-size: clamp(2.1rem, 4.8vw, 3.7rem);
    max-width: 16ch;
    margin: .45rem 0 0;
}
.wl-hero h1 em { font-style: normal; color: var(--wl-brass); }
.wl-hero p { margin-top: 1.1rem; max-width: 52ch; font-size: 16px; color: var(--wl-body-ink); }
.wl-hero-cta { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.5rem; }

.wl-est { border-left: 2px solid var(--wl-brass); padding-left: 1.1rem; }
.wl-est-num {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 122%;
    font-size: clamp(2.6rem, 5vw, 3.6rem);
    line-height: .85;
    letter-spacing: -.02em;
    color: var(--wl-ink);
}
.wl-est-txt { margin-top: .7rem; font-size: 13.5px; color: var(--wl-body-ink); max-width: 32ch; }

/* Staged photograph beneath the editorial hero. Was an inline style on the
   section; it is a named rule now that three other layouts sit alongside it. */
.wl-hero-staged-sec { padding-bottom: clamp(2rem, 4vw, 3rem); }

/* ==========================================================================
   9b. Scheduled hero layouts
   --------------------------------------------------------------------------
   One rule block per LAYOUT value in WEB_HERO. The layout owns its own type,
   spacing and colour so that staff choose a shape rather than a font and a
   hex value — see app/Services/HeroResolver.php.

   Shared: every layout reuses .wl-eyebrow, h1 sizing off .wl-hero h1, and the
   button pair from partials/hero/ctas.blade.php.
   ========================================================================== */

/* Staff-only banner shown above a ?hero=<id> preview, so nobody mistakes a
   preview for what customers are seeing. */
.wl-hero-preview {
    background: var(--wl-low);
    color: #fff;
    font-size: 12px;
    letter-spacing: .04em;
    text-align: center;
    padding: .55rem 1rem;
}

/* ==========================================================================
   Showroom band — copy left, map right
   ========================================================================== */

.wl-showroom { border-top: 1px solid var(--wl-rule); }
.wl-showroom-grid {
    display: grid;
    /* Copy takes the larger share. The map is orientation, not the argument
       for visiting, and the stacked address table below the text makes the
       left column the taller of the two anyway. */
    grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr);
    gap: clamp(1.75rem, 4vw, 3.5rem);
    align-items: center;
}
.wl-showroom-copy .wl-h2 { margin-top: .45rem; max-width: 16ch; }
.wl-showroom-copy .wl-h2 em { font-style: normal; color: var(--wl-brass); }
.wl-showroom-copy p { margin-top: 1rem; max-width: 52ch; }

/* Address, hours and contacts as a plain table: one row each, label in a
   narrow left column and the value beside it. These are looked up rather than
   read, and a stacked list with the labels aligned lets someone find the
   street or the opening time by running an eye down one edge. */
.wl-showroom-facts {
    margin: 1.6rem 0 0;
    border-top: 1px solid var(--wl-rule);
}
.wl-showroom-facts > div {
    margin: 0;
    display: grid;
    /* Label column sized to the longest word rather than a guess, so the
       values line up whatever the labels are changed to in the editor. */
    grid-template-columns: minmax(4.5rem, max-content) minmax(0, 1fr);
    gap: .5rem 1.1rem;
    align-items: baseline;
    padding: .7rem 0;
    border-bottom: 1px solid var(--wl-rule);
}
.wl-showroom-facts dt {
    font-family: var(--wl-body);
    font-size: 10.5px;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--wl-stone);
    margin: 0;
}
.wl-showroom-facts dd {
    margin: 0;
    font-size: 14.5px;
    line-height: 1.45;
    color: var(--wl-ink);
}
.wl-showroom-facts a { border-bottom: 1px solid var(--wl-rule); }
.wl-showroom-facts a:hover { color: var(--wl-brass); border-bottom-color: var(--wl-brass); }

/* "Open in Maps ↗" under an address. Deliberately quieter and smaller than
   the address it belongs to — it is a way through, not the information.

   block, not inline-block: on a wide column an inline link sits up beside the
   street name on the Where row while the Pickups row pushes it below the
   hours, so the two rows disagree about where the link lives. Always on its
   own line means they match at every width. */
.wl-showroom-facts .wl-maplink {
    display: block;
    width: fit-content;
    margin-top: .3rem;
    font-size: 11px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--wl-stone);
    border-bottom-color: var(--wl-brass);
    white-space: nowrap;
}
.wl-showroom-facts .wl-maplink span { margin-left: .25em; }
.wl-showroom-facts .wl-maplink:hover { color: var(--wl-brass); }

/* A qualifying line under a value — pickup hours under the pickup address. */
.wl-showroom-facts .wl-showroom-sub {
    display: block;
    font-size: 13px;
    color: var(--wl-body-ink);
}

.wl-showroom-cta { margin-top: 1.5rem; }

.wl-showroom-map {
    border: 1px solid var(--wl-rule);
    background: var(--wl-plaster);
    /* aspect-ratio alone gives the height: the grid column sets the width and
       the ratio derives the rest, so the iframe — which has no intrinsic size
       — cannot collapse.

       Deliberately no min-height. On an aspect-ratio box a minimum height
       also implies a minimum WIDTH (260px at 3/2 demands 390px), which
       overflows a narrow column and pushes the whole page sideways. */
    aspect-ratio: 4 / 3;
    overflow: hidden;
}
.wl-showroom-map iframe {
    display: block;
    width: 100%;
    height: 100%;
    border: 0;
    /* The embed is a colour photograph of a map against a muted page; taking a
       little saturation out stops it shouting over the copy beside it. */
    filter: saturate(.9);
}

@media (max-width: 860px) {
    .wl-showroom-grid { grid-template-columns: minmax(0, 1fr); }
    /* Map under the copy — the words are what persuade someone to come in. */
    .wl-showroom-map { order: 2; aspect-ratio: 3 / 2; }
    .wl-showroom-copy .wl-h2 { max-width: none; }
}

/* Filled light button, for a hero sitting on a photograph or colour band.
   .wl-btn-light is already the outline-on-dark variant, so this is its
   solid counterpart rather than a replacement. */
.wl-btn-invert { background: var(--wl-chalk); color: var(--wl-ink); border-color: var(--wl-chalk); }
.wl-btn-invert:hover { background: var(--wl-brass-lit); border-color: var(--wl-brass-lit); color: var(--wl-ink); }

/* ---------- split: photo one side, copy the other ---------- */
.wl-hero-split { border-bottom: 1px solid var(--wl-rule); }
.wl-hero-split-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 3.5rem);
    align-items: center;
    /* padding-block, not the padding shorthand: this element also carries
       .wl-wrap, and the shorthand resets padding-inline to 0 — which
       silently removes the page gutter and runs the copy into the edge
       of the screen. */
    padding-block: clamp(1.4rem, 3vw, 2.6rem);
}
/* rtl puts the photograph on the right by reordering, not by swapping the
   markup — the copy stays first in the document for screen readers. */
.wl-hero-split--rtl .wl-hero-split-media { order: 2; }
/* No image yet: the copy runs the full width rather than leaving half the
   row empty, so a hero saved before its photo is ready still looks intended. */
.wl-hero-split.is-textonly .wl-hero-split-grid { grid-template-columns: minmax(0, 1fr); }
.wl-hero-split-media {
    /* No panel behind the photograph and no rule around it: an image narrower
       than the 4:3 frame used to sit in a visible plaster box, which drew a
       border around empty space. Bare, the leftover width is just page. */
    aspect-ratio: 4 / 3;
    overflow: hidden;
    /* Positioning context for the photograph below, which is taken out of flow
       so its height is measured against this frame rather than resolved as a
       percentage inside a flex layout — see the note there. */
    position: relative;
}
/*
   Scale to the frame's HEIGHT and let the width fall where it may, rather than
   object-fit: cover, which crops whichever axis overflows. The top and bottom
   of a hero photograph are where the styling lives — a shelf edge, the top of
   a plant — and cover was silently cutting them off any time the image was
   taller than 4:3.

   Wider than 4:3: overflows and is clipped evenly left and right.
   Taller than 4:3: sits narrower than the frame, showing plaster either side.
   Neither ever loses height.

   max-width:none because a global img { max-width:100% } would otherwise cap
   the width and reintroduce the vertical crop it is meant to prevent.

   Absolutely positioned, NOT a centred flex item. As a flex item with
   width:auto the image sizes to its intrinsic width — 1600px of photograph
   inside a 644px frame — because height:100% does not reliably resolve against
   a height that came from aspect-ratio. Pinning top and bottom gives the
   height directly, with no percentage to resolve, and left:50% + a translate
   centres it whether it is narrower or wider than the frame.
*/
.wl-hero-split-media img {
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    /* An explicit height, not top+bottom: an absolutely positioned replaced
       element with height:auto uses its intrinsic height and simply ignores
       `bottom`, which renders the photograph at full size inside the frame. */
    height: 100%;
    width: auto;
    max-width: none;
    display: block;
}
.wl-hero-split-copy h1 {
    font-family: var(--wl-display);
    font-weight: 700; font-stretch: 118%;
    letter-spacing: -.02em; line-height: 1;
    font-size: clamp(1.9rem, 4.2vw, 3.2rem);
    max-width: 15ch;
    margin: .45rem 0 0;
}
.wl-hero-split-copy h1 em { font-style: normal; color: var(--wl-brass); }
.wl-hero-split-copy p { margin-top: 1rem; max-width: 46ch; font-size: 16px; color: var(--wl-body-ink); }

/* ---------- overlay: copy on a scrim over one full-bleed image ---------- */
.wl-hero-overlay { position: relative; isolation: isolate; }
.wl-hero-overlay-img {
    display: block; width: 100%;
    height: clamp(320px, 46vw, 560px);
    object-fit: cover;
}
/* A gradient rather than a flat panel, so the photograph still reads through
   the top of it. Bottom stop is dark enough that chalk text clears AA over
   any image staff upload. */
.wl-hero-overlay-scrim {
    position: absolute; inset: 0;
    background: linear-gradient(to top, rgba(25,30,26,.88) 0%, rgba(25,30,26,.72) 34%, rgba(25,30,26,.12) 72%, rgba(25,30,26,0) 100%);
}
.wl-hero-overlay-in {
    position: absolute; inset: auto 0 0 0;
    padding-bottom: clamp(1.6rem, 4vw, 3rem);
}
.wl-hero-overlay-copy { max-width: 46ch; }
.wl-hero-overlay-copy .wl-eyebrow { color: var(--wl-brass-lit); }
.wl-hero-overlay-copy h1 {
    font-family: var(--wl-display);
    font-weight: 700; font-stretch: 118%;
    letter-spacing: -.02em; line-height: 1;
    font-size: clamp(1.9rem, 4.4vw, 3.3rem);
    color: var(--wl-chalk);
    margin: .45rem 0 0;
}
.wl-hero-overlay-copy h1 em { font-style: normal; color: var(--wl-brass-lit); }
.wl-hero-overlay-copy p { margin-top: .9rem; font-size: 16px; color: rgba(247,245,240,.86); }

/* ---------- poster: a solid band, type only ---------- */
.wl-hero-poster--verdigris { background: var(--wl-bluestone); --poster-ink: var(--wl-chalk); --poster-accent: var(--wl-brass-lit); }
.wl-hero-poster--ink       { background: var(--wl-ink);       --poster-ink: var(--wl-chalk); --poster-accent: var(--wl-brass-lit); }
/* The lit brass, not --wl-brass: chalk on the darker brass is 4.2:1, which
   fails AA for the body line under the headline. Lit brass takes ink text
   instead and clears it comfortably. */
.wl-hero-poster--brass     { background: var(--wl-brass-lit); --poster-ink: var(--wl-ink);   --poster-accent: var(--wl-ink); }
.wl-hero-poster-in {
    /* padding-block, not the padding shorthand: this element also carries
       .wl-wrap, and the shorthand resets padding-inline to 0 — which
       silently removes the page gutter and runs the copy into the edge
       of the screen. */
    padding-block: clamp(2.2rem, 5.5vw, 4.2rem);
    text-align: center;
}
.wl-hero-poster-in .wl-eyebrow { color: var(--poster-accent); }
.wl-hero-poster-in h1 {
    font-family: var(--wl-display);
    font-weight: 700; font-stretch: 120%;
    letter-spacing: -.022em; line-height: .98;
    font-size: clamp(2.1rem, 5.4vw, 4rem);
    color: var(--poster-ink);
    max-width: 20ch; margin: .5rem auto 0;
}
.wl-hero-poster-in h1 em { font-style: normal; color: var(--poster-accent); }
.wl-hero-poster-in p {
    margin: .9rem auto 0; max-width: 52ch; font-size: 16px;
    color: var(--poster-ink); opacity: .86;
}
.wl-hero-poster-in .wl-hero-cta { justify-content: center; }

/* ---------- mosaic: copy beside a tall shot and two squares ---------- */
.wl-hero-mosaic { border-bottom: 1px solid var(--wl-rule); }
.wl-hero-mosaic-grid {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 7fr);
    gap: clamp(1.5rem, 4vw, 3.5rem);
    align-items: center;
    /* padding-block, not the padding shorthand: this element also carries
       .wl-wrap, and the shorthand resets padding-inline to 0 — which
       silently removes the page gutter and runs the copy into the edge
       of the screen. */
    padding-block: clamp(1.4rem, 3vw, 2.6rem);
}
.wl-hero-mosaic-copy h1 {
    font-family: var(--wl-display);
    font-weight: 700; font-stretch: 118%;
    letter-spacing: -.02em; line-height: 1;
    font-size: clamp(1.9rem, 4.2vw, 3.2rem);
    max-width: 14ch;
    margin: .45rem 0 0;
}
.wl-hero-mosaic-copy h1 em { font-style: normal; color: var(--wl-brass); }
.wl-hero-mosaic-copy p { margin-top: 1rem; max-width: 44ch; font-size: 16px; color: var(--wl-body-ink); }

/* One tall cell on the left spanning both rows, two squares stacked right. */
.wl-hero-mosaic-media {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 1fr 1fr;
    gap: clamp(.6rem, 1.2vw, 1rem);
}
.wl-hero-mosaic-fig {
    margin: 0; overflow: hidden;
    background: var(--wl-plaster);
    border: 1px solid var(--wl-rule);
}
.wl-hero-mosaic-fig img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The squares set their own height; the tall shot takes whatever the two of
   them add up to. No aspect-ratio on it deliberately — giving it one makes it
   shorter than the column beside it and the two columns end on different
   lines, which is exactly the ragged edge the layout exists to avoid. */
.wl-hero-mosaic-fig--1 { grid-row: span 2; }
.wl-hero-mosaic-fig--2,
.wl-hero-mosaic-fig--3 { aspect-ratio: 1 / 1; }

/* Two images: both run full height side by side rather than leaving a hole
   where the third would have been. Here they do need a ratio, because there
   is no stack of squares to take the height from. */
.wl-hero-mosaic-media.is-two .wl-hero-mosaic-fig--1,
.wl-hero-mosaic-media.is-two .wl-hero-mosaic-fig--2 { grid-row: span 2; aspect-ratio: 3 / 4.35; }

/* The quiet line under the hero buttons: "Already a stockist? Log in for
   pricing". Sized and tracked like an eyebrow so it reads as a footnote to
   the actions rather than a third one. The link carries a brass underline —
   a rule rather than a colour change, so it stays legible on the dark
   layouts where a brass text colour would not. */
/* p.wl-hero-note, not .wl-hero-note: each layout sets `font-size:16px` on
   plain paragraphs via selectors like `.wl-hero p`, which out-specify a lone
   class and would render this footnote at full body size. Matching their
   specificity and sitting later in the file settles it. */
p.wl-hero-note {
    margin-top: 1.25rem;
    font-size: 12px;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--wl-stone);
    /* The poster layout fades its paragraphs; this one manages its own
       contrast and must not be dimmed on top of it. */
    opacity: 1;
    max-width: none;
}
.wl-hero-note a {
    margin-left: .45rem;
    font-weight: 500;
    letter-spacing: .13em;
    color: var(--wl-ink);
    border-bottom: 1px solid var(--wl-brass);
    padding-bottom: 3px;
    white-space: nowrap;
}
.wl-hero-note a:hover { color: var(--wl-brass); }

/* On a photograph or a colour band the same line has to read light. */
.wl-hero-overlay-copy .wl-hero-note,
.wl-hero-poster-in .wl-hero-note { color: rgba(247,245,240,.72); }
.wl-hero-overlay-copy .wl-hero-note a,
.wl-hero-poster-in .wl-hero-note a { color: var(--wl-chalk); border-bottom-color: var(--wl-brass-lit); }
.wl-hero-overlay-copy .wl-hero-note a:hover,
.wl-hero-poster-in .wl-hero-note a:hover { color: var(--wl-brass-lit); }
/* The brass poster band is light, so its note keeps ink text. */
.wl-hero-poster--brass .wl-hero-note { color: rgba(25,30,26,.7); }
.wl-hero-poster--brass .wl-hero-note a { color: var(--wl-ink); border-bottom-color: var(--wl-ink); }
.wl-hero-poster-in .wl-hero-note { text-align: center; }


@media (max-width: 860px) {
    .wl-hero-split-grid { grid-template-columns: minmax(0, 1fr); }
    /* Photograph back above the copy on a phone whichever side it was on. */
    .wl-hero-split--rtl .wl-hero-split-media { order: 0; }
    .wl-hero-overlay-img { height: clamp(300px, 78vw, 420px); }
    .wl-hero-mosaic-grid { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   Signed-in dashboard band
   --------------------------------------------------------------------------
   Sits where the guest hero would. A greeting, then up to three status
   panels (draft cart / open order / last invoiced), then the personalised
   product rows below use the standard wl-sec + wl-grid pattern.
   ========================================================================== */

.wl-dash {
    padding: clamp(1.4rem, 2.5vw, 2rem) 0 clamp(1.2rem, 2vw, 1.6rem);
    border-bottom: 1px solid var(--wl-rule);
}
.wl-dash-name {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 118%;
    letter-spacing: -.02em;
    font-size: clamp(1.6rem, 3vw, 2.4rem);
    line-height: 1;
    margin: .35rem 0 0;
    color: var(--wl-ink);
}
.wl-dash-panels {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 1px;
    margin-top: 1.4rem;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
}
.wl-dash-panel {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: 1.1rem 1.2rem;
    background: var(--wl-paper);
    color: var(--wl-ink);
    text-decoration: none;
    transition: background .16s;
}
.wl-dash-panel:hover { background: var(--wl-plaster); color: var(--wl-ink); text-decoration: none; }
.wl-dash-fig {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 116%;
    font-size: 1.75rem;
    line-height: 1.05;
    letter-spacing: -.015em;
    margin: 0;
}
.wl-dash-fig span {
    font-family: var(--wl-body);
    font-weight: 400;
    font-size: .95rem;
    color: var(--wl-stone);
    margin-left: .3rem;
    letter-spacing: 0;
}
.wl-dash-sub {
    font-family: var(--wl-body);
    font-size: 11px;
    letter-spacing: .05em;
    color: var(--wl-stone);
    margin: 0;
}

@media (max-width: 640px) {
    /* One column, still hairline-separated. */
    .wl-dash-panels { grid-template-columns: 1fr; }
}

/* ==========================================================================
   Cart lists (/lists, /lists/{id}, shared view, picker)
   ========================================================================== */

/* Index page — create form + grid of existing lists. */
.wl-lists-create {
    margin-top: 1.4rem;
    padding: 1.1rem 1.2rem;
    border: 1px solid var(--wl-rule);
    background: var(--wl-paper);
}
.wl-lists-create label {
    display: block;
    margin-bottom: .55rem;
}
.wl-lists-create-row {
    display: flex;
    gap: .5rem;
    flex-wrap: wrap;
}
.wl-lists-create-row input {
    flex: 1;
    min-width: 240px;
}
.wl-lists-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
    gap: 1px;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
    margin-top: 2rem;
}
.wl-lists-card {
    background: var(--wl-paper);
    padding: 1.1rem 1.2rem;
    color: var(--wl-ink);
    text-decoration: none;
    display: block;
    transition: background .16s;
}
.wl-lists-card:hover { background: var(--wl-plaster); color: var(--wl-ink); text-decoration: none; }
.wl-lists-card h3 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 112%;
    font-size: 1.15rem;
    margin: .3rem 0 0;
    color: var(--wl-ink);
}
.wl-lists-card:hover h3 { color: var(--wl-brass); }

/* Show page — actions row + line items. */
.wl-list-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .55rem;
    align-items: flex-start;
    margin-top: 1.4rem;
    padding-bottom: 1.4rem;
    border-bottom: 1px solid var(--wl-rule);
}
/* <details> replaces would-be modals for rename / email / share URL.
   Server-rendered, no JS required, and the browser gives us open/close for free. */
.wl-list-detail summary { list-style: none; cursor: pointer; }
.wl-list-detail summary::-webkit-details-marker { display: none; }
.wl-list-detail[open] summary { background: var(--wl-ink); color: var(--wl-chalk); }
.wl-list-email-form,
.wl-list-share-panel {
    margin-top: .6rem;
    padding: 1rem 1.1rem;
    background: var(--wl-paper);
    border: 1px solid var(--wl-rule);
    max-width: 460px;
}
.wl-list-email-form label {
    display: block;
    margin-top: .6rem;
    margin-bottom: .35rem;
}
.wl-list-email-form label:first-child { margin-top: 0; }
.wl-list-email-form input,
.wl-list-email-form textarea {
    width: 100%;
    margin-bottom: .35rem;
}
.wl-list-email-form button { margin-top: .5rem; }
/* The two send buttons wrap rather than shrink — "Email without prices" is a
   long label, and a squeezed pair reads as one control. */
.wl-list-email-btns { display: flex; gap: .5rem; flex-wrap: wrap; }

/* "Back to My Account" above the heading on every account sub-page. Quiet —
   it is a way out, not the point of the page — and left-aligned even where the
   heading below it is centred, so it reads as navigation rather than a title. */
.wl-backlink { margin: 0 0 .9rem; text-align: left; }
.wl-backlink a {
    font-family: var(--wl-body);
    font-size: 11px;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--wl-stone);
    border-bottom: 1px solid var(--wl-rule);
    padding-bottom: 2px;
}
.wl-backlink a:hover { color: var(--wl-brass); border-bottom-color: var(--wl-brass); }
.wl-backlink span { margin-right: .35em; }
.wl-list-share-url {
    width: 100%;
    font-family: var(--wl-body);
    font-size: 12px;
    padding: .5rem .6rem;
    background: var(--wl-plaster);
}

.wl-list-items {
    margin-top: 1.6rem;
    display: grid;
    gap: 1px;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
}
.wl-list-item {
    background: var(--wl-paper);
    display: grid;
    grid-template-columns: 80px minmax(0, 1fr) auto auto;
    gap: 1rem;
    align-items: center;
    padding: .8rem 1rem;
}
.wl-list-item-thumb {
    width: 80px; height: 80px;
    display: flex; align-items: center; justify-content: center;
    background: var(--wl-paper); border: 1px solid var(--wl-rule);
}
.wl-list-item-thumb img { max-width: 68px; max-height: 68px; object-fit: contain; }
.wl-list-item-body h3 {
    font-family: var(--wl-body);
    font-size: 14.5px;
    font-weight: 500;
    margin: .15rem 0 0;
}
.wl-list-item-body h3 a { color: var(--wl-ink); text-decoration: none; }
.wl-list-item-body h3 a:hover { color: var(--wl-brass); }
.wl-list-item-qty { display: flex; gap: .4rem; align-items: center; }
.wl-list-item-qty label { margin: 0; }
.wl-list-item-qty input { width: 74px; }
.wl-list-item-remove {
    font-family: var(--wl-body);
    font-size: 10.5px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--wl-stone);
}
.wl-list-item-remove:hover { color: var(--wl-sale); }

@media (max-width: 640px) {
    .wl-list-item {
        grid-template-columns: 60px 1fr;
        grid-template-areas: "thumb body" "qty qty" "remove remove";
        row-gap: .6rem;
    }
    .wl-list-item-thumb { grid-area: thumb; width: 60px; height: 60px; }
    .wl-list-item-body { grid-area: body; }
    .wl-list-item-qty { grid-area: qty; }
    .wl-list-item-remove { grid-area: remove; }
}

/* Picker — Save-to-list from a product card. */
.wl-list-picker-existing {
    display: grid;
    gap: 1px;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
    margin-top: .9rem;
}
.wl-list-picker-btn {
    background: var(--wl-paper);
    border: none;
    padding: .9rem 1.1rem;
    text-align: left;
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 1rem;
    cursor: pointer;
    color: var(--wl-ink);
    font: inherit;
    transition: background .16s;
}
.wl-list-picker-btn:hover { background: var(--wl-plaster); }
.wl-list-picker-name { font-weight: 500; }
.wl-list-picker-new {
    margin-top: .9rem;
    padding: 1rem 1.1rem;
    background: var(--wl-paper);
    border: 1px solid var(--wl-rule);
}
.wl-list-picker-new label { display: block; margin-bottom: .4rem; }
.wl-list-picker-new input { width: 100%; margin-bottom: .55rem; }

/* "Save to list" link inside a product card foot. */
.pc-savelist { color: var(--wl-brass); cursor: pointer; }
.pc-savelist:hover { color: var(--wl-ink); }

/* ==========================================================================
   Save-to-list modal
   --------------------------------------------------------------------------
   Opens over the current page when a customer clicks "Save to list" on any
   product card or details page. Kept native — no external library, no jQuery
   dependency, no aria-heavy dialog polyfill. The picker page still works
   with JS off, so the modal is progressive enhancement rather than a
   requirement.
   ========================================================================== */

.wl-modal {
    position: fixed; inset: 0;
    z-index: 9998;
    display: flex; align-items: center; justify-content: center;
    padding: 1rem;
}
.wl-modal[hidden] { display: none; }
.wl-modal-backdrop {
    position: absolute; inset: 0;
    background: rgba(21,24,26,.45);
    cursor: pointer;
}
.wl-modal-panel {
    position: relative;
    background: var(--wl-paper);
    border: 1px solid var(--wl-ink);
    box-shadow: 0 20px 50px rgba(21,24,26,.25);
    max-width: 480px;
    width: 100%;
    padding: 1.6rem 1.6rem 1.4rem;
    max-height: calc(100vh - 2rem);
    overflow-y: auto;
}
.wl-modal-close {
    position: absolute; top: .55rem; right: .75rem;
    background: none; border: none;
    font-size: 24px; color: var(--wl-stone);
    line-height: 1; padding: .25rem .45rem;
    cursor: pointer;
}
.wl-modal-close:hover { color: var(--wl-ink); }
.wl-modal-title {
    font-family: var(--wl-display);
    font-weight: 700; font-stretch: 116%;
    font-size: 1.4rem; line-height: 1.15;
    letter-spacing: -.015em;
    margin: .3rem 0 0;
    color: var(--wl-ink);
}
.wl-modal-loading { padding: 1.2rem 0; }
.wl-modal-status {
    margin-top: 1rem;
    padding: .55rem .75rem;
    font-family: var(--wl-body);
    font-size: 11.5px;
    letter-spacing: .04em;
    border: 1px solid var(--wl-rule);
}
.wl-modal-status.is-ok    { border-color: var(--wl-good); color: #2E5236; background: #F1F5F1; }
.wl-modal-status.is-error { border-color: var(--wl-sale); color: #7A281C; background: #F8F0EE; }

.wl-modal-lists {
    display: grid; gap: 1px;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
    margin-top: 1rem;
}
.wl-modal-list-btn {
    background: var(--wl-paper);
    border: none;
    padding: .8rem 1rem;
    text-align: left;
    display: flex; justify-content: space-between; align-items: center;
    gap: 1rem;
    font: inherit; color: var(--wl-ink);
    cursor: pointer;
    transition: background .16s;
}
.wl-modal-list-btn:hover:not(:disabled) { background: var(--wl-plaster); }
.wl-modal-list-btn:disabled { opacity: .6; cursor: default; }
/* Recently-added mark so the customer sees which list just took the item. */
.wl-modal-list-btn.is-added {
    background: #F1F5F1;
    box-shadow: inset 3px 0 0 var(--wl-good);
}
.wl-modal-list-name { font-weight: 500; }

.wl-modal-new { margin-top: 1.2rem; }
.wl-modal-new input {
    padding: .55rem .7rem;
    border: 1px solid var(--wl-rule);
    background: var(--wl-paper);
    font-family: var(--wl-body); font-size: 14px;
}
.wl-modal-new input:focus { border-color: var(--wl-ink); outline: none; }

/* "View {list} →" link slotted in right under the button that just took
   the item. Sits inside the .wl-modal-lists grid as its own row, so it
   inherits the hairline separators and reads as a nested action. */
.wl-modal-list-view {
    display: block;
    padding: .55rem 1rem .65rem;
    background: #F1F5F1;
    box-shadow: inset 3px 0 0 var(--wl-good);
    font-family: var(--wl-body);
    font-size: 11px;
    letter-spacing: .06em;
    color: var(--wl-good);
    text-decoration: none;
}
.wl-modal-list-view:hover { background: #E4EEE5; color: var(--wl-good); text-decoration: none; }

/* Post-add "Continue shopping" row. Replaces the new-list form so the
   customer's primary next action is obvious after they've saved. */
.wl-modal-done { margin-top: 1.2rem; }
.wl-modal-done .wl-btn { width: 100%; }

/* Facts strip. Four columns divided by hairline verticals, sitting directly
   below the staged image as a plain statement of what the wholesale offer is
   before any product grids. First column's <dt> aligns flush with the wl-wrap
   gutter, so it sits under the hero headline and above the first product card.
   No horizontal rules top or bottom: the paper panel already reads as a band
   against the chalk page, and the section and the wrap each used to draw their
   own hairline, which stacked into one heavy grey line. Only the verticals
   between the columns are drawn. */
/* Band colour is picked in the intranet (facts.background) and applied inline,
   along with the type and rule colours the homepage derives from it — an
   arbitrary colour cannot have a fixed pair of text colours sitting on it.
   This declaration is only what shows if that value is ever missing. */
.wl-facts { background: var(--wl-bluestone); }
.wl-facts-in {
    display: grid;
    /* One equal column per claim, however many there are.
     *
     * Was repeat(auto-fit, minmax(210px, 1fr)), which sounds adaptive but
     * makes as many columns as will fit rather than as many as there are
     * items: at 1280px that is five for four claims, so the strip stopped
     * four-fifths of the way across with an empty column on the end.
     * auto-flow column asks for exactly one track per claim, which also keeps
     * a three-claim strip full-width when one is left blank in the editor. */
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
    /* margin: 0 auto (not 0) so the wrap stays centred at wide viewports the
       way every other .wl-wrap does — otherwise on a monitor past 1440 the
       facts flush-left while the hero and the product grid stay centred, and
       the first fact reads as inset. */
    margin: 0 auto;
}
.wl-fact {
    padding: 1.3rem 1.4rem;
    border-left: 1px solid var(--wl-rule);
    margin: 0;
}
/* The first column pulls its left padding to zero so its label lines up with
   the wl-wrap gutter, and the last does the same on the right so the row is
   balanced against the wrap edges rather than sitting inset from both. */
.wl-fact:first-child {
    padding-left: 0;
    border-left: 0;
}
.wl-fact:last-child {
    padding-right: 0;
}
/* The term is the loud half of the pair — "Since 1984", "In stock" — and the
   description supports it underneath. That inverts the small-label/large-value
   pattern used elsewhere on the page, deliberately: these read as four short
   claims, not four labelled data points. */
.wl-fact dt {
    font-family: var(--wl-display);
    font-size: clamp(1.3rem, 2.4vw, 1.85rem);
    font-weight: 600;
    font-stretch: 110%;
    letter-spacing: -.012em;
    line-height: 1.06;
    color: var(--wl-ink);
    margin: 0;
}
.wl-fact dd {
    margin: .5rem 0 0;
    font-size: 12.5px;
    line-height: 1.45;
    letter-spacing: .02em;
    max-width: 30ch;
    color: var(--wl-body-ink);
}
/* The claims strip below ~900px.
 *
 * These rules used to assume the grid had collapsed to one column, but
 * auto-fit keeps making two right down to about 440px. The result was the
 * first claim sitting a padding-height above the one beside it, and no gap at
 * all between the two columns. Pinning the column count per breakpoint means
 * the layout and the rules that dress it can no longer disagree.
 */
@media (max-width: 900px) {
    /* Back to row flow: the base rule lays claims out in one row of columns. */
    .wl-facts-in { grid-auto-flow: row; grid-template-columns: repeat(2, minmax(0, 1fr)); }

    /* Two columns: the left one is flush with the gutter, the right one is
       inset off its dividing rule. Rows are separated by a hairline. */
    .wl-fact { padding: 1.1rem 0 1.1rem 1.2rem; border-left: 1px solid var(--wl-rule); border-top: 0; }
    .wl-fact:nth-child(odd)  { padding-left: 0; border-left: 0; }
    .wl-fact:nth-child(n+3)  { border-top: 1px solid var(--wl-rule); }
    .wl-fact:first-child, .wl-fact:last-child { padding-right: 0; }
    /* first-child no longer gets padding-top:0 — its neighbour has padding,
       and zeroing one of a pair is what knocked them out of line. */
}

@media (max-width: 560px) {
    /* Genuinely one column now, so the stacked treatment is correct. */
    .wl-facts-in { grid-auto-flow: row; grid-template-columns: minmax(0, 1fr); }
    .wl-fact { border-left: 0; border-top: 1px solid var(--wl-rule); padding-left: 0; padding-right: 0; }
    /* No rule above the first claim — there is nothing to divide it from — but
       it keeps its top padding. Zeroing that left it jammed against the top
       edge of the plaster band, which has no padding of its own. */
    .wl-fact:first-child { border-top: 0; }
}

/* Staged room photograph beneath the thesis. */
.wl-staged {
    position: relative;
    border: 1px solid var(--wl-rule);
    background: #DFDBCE;
    aspect-ratio: 21 / 9;
    min-height: 280px;
    overflow: hidden;
}
.wl-staged img { width: 100%; height: 100%; object-fit: cover; display: block; }
.wl-staged-ph {
    position: absolute; inset: 0;
    display: grid; place-items: center;
    font-family: var(--wl-body);
    font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
    color: rgba(21,24,26,.34);
    text-align: center; padding: 1rem;
}

/* ==========================================================================
   10. Category bar
   ========================================================================== */

.wl-catbar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
    gap: 1px;
    background: var(--wl-rule);
    border: 1px solid var(--wl-rule);
    margin-bottom: 1.4rem;
}
.wl-catbar a {
    background: var(--wl-paper);
    padding: .95rem 1rem;
    color: var(--wl-ink);
    transition: background .16s, color .16s;
}
.wl-catbar a:hover { background: var(--wl-bluestone); color: var(--wl-chalk); }
.wl-catbar a:hover .wl-code { color: var(--wl-brass-lit); }
.wl-catbar h3 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 110%;
    font-size: 1.1rem;
    margin: .25rem 0 0;
    color: inherit;
}

/* Ranges / categories band. A dark green section with its heading stacked
   above a grid of cards. The cards stay white, so the band is what gives them
   their contrast — no border or rule of its own is needed to separate it from
   the chalk page above and below. */
.wl-coll {
    background: var(--wl-bluestone);
}
.wl-coll-head { padding-block: clamp(2.4rem, 5vw, 3.6rem) 0; }
.wl-coll-head .wl-h2 { margin-top: .45rem; max-width: 24ch; }
.wl-coll-head p.wl-lede { margin-top: .7rem; max-width: 56ch; font-size: 15px; }
.wl-coll-foot { padding-bottom: clamp(2.4rem, 5vw, 3.8rem); }
/* The heading colours are applied inline by the homepage, derived from the
   chosen band colour (App\Helpers\BandColour) — they cannot be fixed here
   when the background is free. */

.wl-coll-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    /* The 1px gaps used to be filled by a rule colour behind the grid. On the
       dark band the band itself shows through them instead, which separates
       the white cards more cleanly than a light hairline would. */
    gap: 1px;
    margin-top: 1.4rem;
}
.wl-coll-card {
    background: var(--wl-paper);
    padding: 1.4rem;
    display: flex;
    flex-direction: column;
    color: var(--wl-ink);
}
.wl-coll-card h3 {
    font-family: var(--wl-display);
    font-weight: 700;
    font-stretch: 112%;
    font-size: 1.25rem;
    letter-spacing: -.01em;
    margin: .15rem 0 0;
}
.wl-coll-media {
    margin-top: .9rem;
    border: 1px solid var(--wl-rule);
    overflow: hidden;
}
.wl-coll-media img {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    transition: transform .5s ease;
}
.wl-coll-card:hover .wl-coll-media img { transform: scale(1.04); }
.wl-coll-card:hover h3 { color: var(--wl-brass); }
.wl-coll-spec {
    display: flex; flex-wrap: wrap; gap: 1.1rem;
    margin-top: .85rem;
    font-family: var(--wl-body); font-size: 11px; color: var(--wl-stone);
}
.wl-coll-spec b { color: var(--wl-ink); font-weight: 500; }
/* The button is a span inside the card's own anchor, so it has to take its
   hover state from the card rather than from itself. */
.wl-coll-card .wl-btn { margin-top: auto; align-self: flex-start; }
.wl-coll-card:hover .wl-btn { background: var(--wl-ink); color: var(--wl-chalk); }
.wl-coll-card .wl-coll-media + .wl-btn { margin-top: 1rem; }

/* ==========================================================================
   11. Product card
   ========================================================================== */

.wl-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
    /* Chalk gaps on both axes so cards breathe apart rather than reading as
       a solid slab. Column gap is smaller than row gap so the grid still
       feels like a grid — bigger horizontal air would break the columns
       into visually isolated cards. */
    column-gap: .75rem;
    row-gap: 1.5rem;
    background: transparent;
    border: 0;
}

.product-card {
    background: var(--wl-paper);
    /* Each card carries its own full border now. Column-gap on the grid
       provides the horizontal breathing room so no more negative-margin
       overlap trick is needed. */
    border: 1px solid var(--wl-rule);
    display: flex;
    flex-direction: column;
    /* The inherited card floated at a fixed 350px. The grid owns width now. */
    float: none;
    width: auto;
    margin: 0;
    position: relative;
    /* Native browser lazy-render for off-screen cards. On a sale page with
       500+ items the browser was laying out and painting every card at load
       time; content-visibility:auto lets it skip both for anything outside
       the viewport (and near-viewport) and reclaim on scroll. contain-
       intrinsic-size gives the layout engine a placeholder box so the scroll
       bar doesn't jump when cards resolve. */
    content-visibility: auto;
    contain-intrinsic-size: 320px 460px;
}
.product-card .thumb-wrap {
    width: auto;
    aspect-ratio: 1 / 1;
    height: auto;
    background: var(--wl-paper);
    display: flex; justify-content: center; align-items: center;
    position: relative;
    overflow: hidden;
}
.product-card .thumb-wrap > a { display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; }
.product-card .thumb-wrap img { max-width: 86%; max-height: 86%; width: auto; height: auto; object-fit: contain; }

.pc-badge {
    position: absolute; top: .5rem; left: .5rem; z-index: 2;
    font-family: var(--wl-body);
    font-size: 9px; font-weight: 500; letter-spacing: .1em; text-transform: uppercase;
    padding: .18rem .42rem; border-radius: 0;
    background: var(--wl-ink); color: var(--wl-chalk);
}
.pc-badge-pre  { background: var(--wl-bluestone); }
.pc-badge-new  { background: var(--wl-brass); }
.pc-badge-sale { background: var(--wl-sale); }

.product-card .summary {
    padding: .8rem .85rem .95rem;
    display: flex; flex-direction: column; flex: 1;
    text-align: left;
}
.product-card .product-code {
    display: block;
    font-family: var(--wl-body);
    font-size: 10.5px; letter-spacing: .05em;
    color: var(--wl-stone);
    opacity: 1;
    margin-bottom: .15rem;
    line-height: 1.3;
    /* Reserve two lines. Codes appended with a category name wrap to two
       lines on one card and not the next; without a floor here the title
       and everything below it slide down on the wrapping card, which is
       what threw the "Log in for pricing" row out of alignment. */
    min-height: 2.6em;
}
.product-card .product-title {
    font-family: var(--wl-body);
    font-size: 14px; font-weight: 500;
    color: var(--wl-ink);
    line-height: 1.28;
    letter-spacing: 0;
    min-height: 2.56em;
    margin: 0 0 .1rem;
}
.product-card a:hover .product-title { color: var(--wl-brass); }

/* Dimensions / carton, in the mono spec idiom. */
.pc-spec {
    margin-top: .6rem;
    border-top: 1px solid var(--wl-rule);
    font-family: var(--wl-body);
    font-size: 10.5px;
    color: var(--wl-stone);
}
.pc-spec div {
    display: flex; justify-content: space-between; gap: .5rem;
    padding: .22rem 0;
    border-bottom: 1px dotted var(--wl-rule);
    /* Reserve two lines. A dimension string like "57.5cm × 57.5cm × 78cm"
       will wrap to a second line inside a narrow card while a shorter one
       ("58cm × 55cm × 78cm") stays on one — that inconsistency pushed the
       Log-in / More rows out of alignment across cards on the same row. */
    min-height: 2.6em;
    align-items: flex-start;
}
.pc-spec div:last-child { border-bottom: 0; }
.pc-spec b { color: var(--wl-ink); font-weight: 500; text-align: right; }

.pc-price-row { margin-top: .7rem; display: flex; align-items: baseline; gap: .4rem; flex-wrap: wrap; }
.product-card .current {
    font-family: var(--wl-display);
    font-size: 1.35rem; font-weight: 700; font-stretch: 110%;
    letter-spacing: -.01em;
    color: var(--wl-ink);
}
.product-card .current.is-sale { color: var(--wl-sale); }
.pc-was { min-height: 1.25em; font-family: var(--wl-body); font-size: 11px; color: var(--wl-stone); }
.pc-was .original { text-decoration: line-through; }
.pc-off { color: var(--wl-sale); margin-left: .3rem; }
.pc-gst { font-family: var(--wl-body); font-size: 10px; color: var(--wl-stone); }

.pc-volume { font-family: var(--wl-body); font-size: 10.5px; color: var(--wl-good); margin-top: .3rem; }
/* Sits BELOW the Add form so cards with a low-stock warning don't push the
   button out of alignment with cards next to them that don't have one. */
.pc-lowstock { font-family: var(--wl-body); font-size: 10.5px; color: var(--wl-low); margin-top: .5rem; }

/* No margin-top:auto here — .pc-foot below already bottom-anchors, and two
   auto margins in the same flex column split the space rather than one taking
   all of it. The button's Y position is whatever the natural flow of the
   preceding rows (title, spec, was, price) makes it, which is now consistent
   because those rows all have reserved heights. */
.pc-add { display: flex; gap: .4rem; padding-top: .7rem; }
.pc-qty {
    flex: 0 0 auto; width: 68px;
    padding: .42rem .4rem;
    border: 1px solid var(--wl-rule); border-radius: 0;
    font-family: var(--wl-body); font-size: 12px;
    background: var(--wl-paper);
}
.pc-add-btn {
    flex: 1;
    padding: .45rem .6rem;
    background: var(--wl-ink); color: var(--wl-chalk);
    border: 1px solid var(--wl-ink); border-radius: 0;
    font-family: var(--wl-body); font-size: 10.5px;
    letter-spacing: .08em; text-transform: uppercase;
    cursor: pointer;
    transition: background .16s, border-color .16s;
}
.pc-add-btn:hover { background: var(--wl-brass); border-color: var(--wl-brass); color: #fff; }

/* Bottom-anchored footer holding the login gate and the "more in category"
   link. margin-top:auto pushes it to the bottom of the summary column, so
   every card in a row ends its footer at the same Y regardless of how much
   spec, price or stock content sits above. Fixed line-heights make sure the
   gate and more-link occupy the same vertical slot even when one of them is
   absent — a placeholder <span> is emitted in place of a missing more-link. */
.pc-foot {
    margin-top: auto;
    padding-top: .7rem;
    border-top: 1px solid var(--wl-rule);
    display: flex;
    flex-direction: column;
    gap: .55rem;
}
.pc-login-note { line-height: 1.2; min-height: 1.2em; }
.pc-login-note a,
.wl-gate {
    font-family: var(--wl-body);
    font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--wl-brass);
}
.pc-login-note a:hover, .wl-gate:hover { color: var(--wl-ink); }

.pc-more {
    display: block;
    line-height: 1.2;
    min-height: 1.2em;
    font-family: var(--wl-body);
    font-size: 10px;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--wl-stone);
    /* One line, ellipsised on overflow — "More Chairs, Stools and Ottomans"
       wraps to two lines in a narrow card and threw the footer height out of
       sync with cards whose category name fits. Full text is on hover title. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
}
.pc-more:hover { color: var(--wl-brass); }
.pc-more-placeholder { visibility: hidden; }

/* Card carousel, retuned to the square grid cell. */
.card-carousel { width: 100%; height: 100%; aspect-ratio: 1 / 1; }
.cc-arrow {
    background: rgba(255,255,255,.9);
    border: 1px solid var(--wl-rule);
    border-radius: 0;
    width: 26px; height: 26px;
    color: var(--wl-ink);
    box-shadow: none;
}
.cc-arrow:hover { background: #fff; border-color: var(--wl-ink); }
.cc-dot { border-radius: 0; width: 5px; height: 5px; background: rgba(21,24,26,.22); }
.cc-dot.active { background: var(--wl-ink); }

/* ==========================================================================
   12. Tables — invoices, orders, cart, credits
   ========================================================================== */

.table {
    background: var(--wl-paper);
    color: var(--wl-ink);
    font-size: 14px;
    margin-bottom: 0;
}
.table thead th {
    font-family: var(--wl-body);
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: .11em;
    text-transform: uppercase;
    color: var(--wl-stone);
    background: var(--wl-plaster);
    border-bottom: 1px solid var(--wl-rule);
    border-top: 0;
    padding: .7rem .75rem;
    vertical-align: middle;
}
.table td { border-top: 1px solid var(--wl-rule); padding: .7rem .75rem; vertical-align: middle; }
.table-striped tbody tr:nth-of-type(odd) { background: rgba(234,231,222,.5); }
.table-hover tbody tr:hover { background: var(--wl-plaster); }

/* ==========================================================================
   13. Alerts and pagination
   ========================================================================== */

.alert {
    border-radius: 0;
    border: 1px solid var(--wl-rule);
    background: var(--wl-paper);
    color: var(--wl-ink);
    font-size: 14px;
    padding: .8rem 1rem;
}
.alert-success { border-color: var(--wl-good); color: #2E5236; background: #F1F5F1; }
.alert-danger  { border-color: var(--wl-sale); color: #7A281C; background: #F8F0EE; }
.alert-warning { border-color: var(--wl-low);  color: #7A4720; background: #FAF4EE; }
.alert-info    { border-color: var(--wl-brass); color: #6B551F; background: #FAF7F0; }

.page-link {
    border-radius: 0 !important;
    border-color: var(--wl-rule);
    color: var(--wl-ink);
    font-family: var(--wl-body);
    font-size: 12px;
}
.page-item.active .page-link { background: var(--wl-ink); border-color: var(--wl-ink); color: var(--wl-chalk); }

/* ==========================================================================
   14. Footer
   ========================================================================== */

footer.main-footer { background: var(--wl-bluestone); }
footer.main-footer .main-block {
    background: var(--wl-bluestone);
    color: rgba(247,245,240,.72);
    padding: clamp(2.6rem, 5vw, 4rem) 0;
}
footer.main-footer .main-block h5 {
    font-family: var(--wl-body);
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--wl-brass-lit);
    margin: 0 0 1.1rem;
}
footer.main-footer .info p { font-size: 14px; line-height: 1.75; color: rgba(247,245,240,.72); }
footer.main-footer .info a, footer.main-footer .site-links a { color: rgba(247,245,240,.82); font-size: 14px; }
footer.main-footer .info a:hover, footer.main-footer .site-links a:hover { color: var(--wl-brass-lit); }
footer.main-footer .site-links li { margin-bottom: .3rem; }
footer.main-footer .footer-btn {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .55rem 1rem;
    border: 1px solid rgba(255,255,255,.28); border-radius: 0;
    color: rgba(247,245,240,.9);
    font-family: var(--wl-body); font-size: 10.5px;
    letter-spacing: .09em; text-transform: uppercase;
    background: transparent;
}
footer.main-footer .footer-btn:hover { background: #fff; color: var(--wl-ink); border-color: #fff; }
footer.main-footer .footer-user {
    width: 100%; margin-bottom: .3rem;
    font-family: var(--wl-body); font-size: 10.5px;
    letter-spacing: .09em; text-transform: uppercase;
    color: rgba(247,245,240,.5);
}
footer.main-footer .footer-brand {
    font-family: var(--wl-display); font-weight: 700; font-stretch: 118%;
    font-size: 1.5rem; letter-spacing: -.015em;
    color: var(--wl-chalk); margin-bottom: .2rem;
}

/* ==========================================================================
   15. Mobile drawer
   --------------------------------------------------------------------------
   There is no separate mobile header any more: the one header reflows via
   grid-template-areas, the nav row hides, and the hamburger opens this
   drawer. The full-screen search overlay is gone with it — the header's own
   search field goes full width on a narrow screen, so a second search UI had
   nothing left to do.
   ========================================================================== */

.wl-burger { display: none; }
@media (max-width: 960px) {
    .wl-burger { display: flex; }
    .wl-hdr-nav { display: none; }
}

.mob-overlay {
    display: none;
    position: fixed; inset: 0;
    background: rgba(21,24,26,.45);
    z-index: 9990;
}
.mob-overlay.open { display: block; }

.mob-drawer {
    /* Slides in from the right, matching the side the burger sits on — a
       thumb reaching the burger lands next to the drawer that opens, not
       across the screen from it. */
    position: fixed; top: 0; right: 0;
    width: 320px; max-width: 85vw; height: 100%;
    z-index: 9991; overflow-y: auto;
    transform: translateX(100%);
    /* Faster slide (was .3s) so the drawer feels immediate after the tap.
       ease-out lands quickly then decelerates — reads as "responsive". */
    transition: transform .2s ease-out;
    will-change: transform;
}
.mob-drawer.open { transform: translateX(0); }
/* Loading state after the customer taps a nav link. Dims the drawer's
   contents so it's obviously frozen, and overlays a slim brass progress
   bar at the top that reads as "we're on it". Cleared when the new page
   renders (the whole layout replaces this drawer node). */
.mob-drawer.is-loading > * { opacity: .35; transition: opacity .12s ease; pointer-events: none; }
.mob-drawer.is-loading::before {
    content: '';
    position: sticky; top: 0; left: 0;
    display: block; height: 3px; width: 40%;
    background: var(--wl-brass);
    animation: wl-drawer-progress 1.2s ease-in-out infinite;
    z-index: 2;
    opacity: 1;
}
@keyframes wl-drawer-progress {
    0%   { margin-left: -40%; }
    100% { margin-left: 100%; }
}

.mob-drawer-header {
    display: flex; justify-content: space-between; align-items: center;
    padding: 1.1rem 1.25rem;
}
.mob-close {
    background: none; border: none;
    font-size: 26px; color: var(--wl-stone);
    cursor: pointer; line-height: 1; padding: 0 .25rem;
}
.mob-close:hover { color: var(--wl-ink); }

.mob-search { padding: 1rem 1.25rem .5rem; }
.mob-search-wrap { display: flex; align-items: center; gap: .6rem; padding: .6rem .85rem; }
.mob-search-wrap input {
    flex: 1; border: none; background: transparent; outline: none;
    font-size: 14px; color: var(--wl-ink); padding: 0;
}

.mob-nav { padding: .5rem 0 2rem; }
.mob-section-label { padding: .9rem 1.25rem .4rem; }
.mob-link {
    display: flex; align-items: center; gap: .65rem;
    padding: .8rem 1.25rem;
    text-decoration: none;
}
.mob-link-icon { width: 18px; text-align: center; font-size: 14px; }
.mob-divider { height: 1px; margin: .5rem 1.25rem; }

.mob-accordion-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: .7rem 1.25rem; cursor: pointer;
}
.mob-accordion-header a, .mob-accordion-header span { text-decoration: none; flex: 1; }
.mob-chevron { font-size: 11px; color: var(--wl-stone); transition: transform .25s; }
.mob-accordion-header.open .mob-chevron { transform: rotate(180deg); }
.mob-accordion-body { display: none; padding: 0 1.25rem .5rem 2.25rem; }
.mob-accordion-body.open { display: block; }
.mob-accordion-body a { display: block; padding: .55rem 0; text-decoration: none; }
.mob-accordion-body a:last-child { border-bottom: none; }

.mob-drawer { background: var(--wl-paper); }
.mob-drawer-header { border-bottom: 1px solid var(--wl-rule); }
.mob-section-label {
    font-family: var(--wl-body);
    font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase;
    color: var(--wl-stone); font-weight: 500;
}
.mob-link { font-size: 16px; color: var(--wl-ink); touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.mob-link:hover { background: var(--wl-plaster); color: var(--wl-ink); }
/* Strong active-state so tapping a drawer option registers visually before
   the destination page loads — the previous silence made the tap feel lost. */
.mob-link:active,
.mob-accordion-body a:active {
    background: var(--wl-ink);
    color: var(--wl-chalk);
}
.mob-link:active .mob-link-icon { color: var(--wl-chalk); }
.mob-link-icon { color: var(--wl-stone); }
.mob-divider { background: var(--wl-rule); }
.mob-accordion-header a, .mob-accordion-header span { font-size: 16px; color: var(--wl-ink); }
/* The category count in the mobile drawer inherits the row's 16px ink style
   otherwise, which reads as part of the name. Restore the mono/stone look and
   push it away from the name so it's visibly a count. */
.mob-accordion-header .wl-code,
.mob-link .wl-code {
    display: inline-block;
    margin-left: .5rem;
    font-family: var(--wl-body);
    font-size: 11px;
    font-weight: 400;
    letter-spacing: .06em;
    color: var(--wl-stone);
    vertical-align: middle;
}
.mob-accordion-body a { font-size: 14.5px; color: var(--wl-body-ink); border-bottom: 1px solid var(--wl-rule); }
.mob-accordion-body a:hover { color: var(--wl-brass); }
.mob-search-wrap {
    background: var(--wl-plaster);
    border: 1px solid var(--wl-rule);
    border-radius: 0;
}
.mob-search-wrap i { color: var(--wl-stone); }

/* ==========================================================================
   16. Responsive
   ========================================================================== */

@media (max-width: 1100px) {
    .wl-hdr-nav { gap: 1.1rem; font-size: 13.5px; }
}

@media (max-width: 960px) {
    .wl-hero-grid { grid-template-columns: 1fr; align-items: start; }
    .wl-hero h1 { max-width: 18ch; }
    /* The logo column must be allowed to shrink. With two `auto` columns the
       grid is sized by the logo's intrinsic width, and the full-width search
       row below inherits that total — which is how the header ended up wider
       than the phone it was on. */
    .wl-hdr-top {
        grid-template-columns: minmax(0, 1fr) auto;
        grid-template-areas: "mark acts" "search search";
        column-gap: .6rem;
        row-gap: .7rem;
    }
    .wl-mark { grid-area: mark; min-width: 0; }
    /* No fixed width here — the img's `width:260px; max-width:100%` default
       means it grows to fill whatever the grid column allocates. At 960px
       the column has plenty of room; on a narrow phone the auto-sized acts
       column shrinks it naturally. Hardcoding it under-sized the logo on
       every mobile width. */
    .wl-search { grid-area: search; min-width: 0; }
    .wl-search input { min-width: 0; }
    .wl-hdr-acts { grid-area: acts; }
    /* min-height has to go with the ratio change: 280px against 4/3 sets a
       373px floor on the width, which is wider than the phone. */
    .wl-staged { aspect-ratio: 4 / 3; min-height: 0; }
    .wl-nav-drop-2col { min-width: 300px; }
    .wl-nav-drop-2col .wl-nav-drop-list { columns: 1; }
}

@media (max-width: 640px) {
    .wl-grid { grid-template-columns: repeat(auto-fill, minmax(155px, 1fr)); }
    .wl-est-num { font-size: 2.4rem; }
    .wl-util-in { justify-content: flex-start; gap: .6rem; }
}

@media (max-width: 460px) {
    /* Logo sizing removed — see 960 breakpoint. The default max-width:100%
       lets the logo fill the whole space the grid column allocates, which is
       twice what the hardcoded 150px was giving it. */
    .wl-hdr-acts { gap: 0; }
    .wl-icon { width: 34px; }
}

@media (prefers-reduced-motion: reduce) {
    * { transition: none !important; animation: none !important; }
}
