.psg-topbar{height:40px;background:var(--psg-forest);color:#fff;font-size:.8125rem;letter-spacing:.01em}.psg-topbar__inner{height:100%;display:flex;align-items:center;justify-content:space-between;gap:1rem}.psg-topbar__left{display:flex;align-items:center;gap:1rem}.psg-topbar__contact{font-weight:600}.psg-topbar a{color:#fff;text-decoration:none;opacity:.9;transition:opacity .2s var(--psg-ease)}.psg-topbar a:hover{opacity:1}.psg-menu{list-style:none;margin:0;padding:0}.psg-menu--top{display:flex;gap:1rem}.psg-topbar__right .psg-menu--top li:not(:last-child){padding-inline-end:1rem;border-right:1px solid rgba(255,255,255,.35)}.psg-social-links{display:flex;align-items:center;gap:.6rem}.psg-social-links a{display:inline-grid;place-items:center;width:22px;height:22px;border-radius:50%;background:rgba(255,255,255,.18)}.psg-social-links svg{width:12px;height:12px}.site-header{position:sticky;top:0;z-index:30;background:#fff;box-shadow:0 1px 0 var(--psg-line);transition:box-shadow .25s var(--psg-ease)}/* The header's reserved height, published as a token. The homepage hero pulls
   itself up by exactly this much to slide under a transparent header, and that
   number used to be typed out by hand in home.css — so raising the header from
   54px to 56px on mobile left a 2px strip of white header showing above the
   hero. One value, two readers, no drift. */
:root{--psg-header-h:68px}
.site-header__inner{min-height:var(--psg-header-h);display:grid;grid-template-columns:auto 1fr auto;align-items:center;column-gap:var(--psg-space-7)}.site-branding{justify-self:start}.primary-navigation{justify-self:center}.custom-logo{display:block;width:245px;height:auto}.site-logo{display:block;width:200px}.site-logo__img{display:block;width:100%;height:auto}.site-logo--white{display:none}.primary-navigation .psg-menu{display:flex;align-items:center;gap:clamp(1rem,2.2vw,2.75rem)}.primary-navigation a:not(.psg-button){text-decoration:none;color:var(--psg-ink);font-weight:600;font-size:.9375rem;font-size:clamp(.8125rem,.6rem + .5vw,.9375rem);padding-bottom:.3rem;border-bottom:2px solid transparent;transition:border-color .2s var(--psg-ease),color .2s var(--psg-ease)}.primary-navigation a:not(.psg-button):hover{border-bottom-color:var(--psg-forest);color:var(--psg-forest)}.psg-nav-tabs{display:none}.psg-nav-panel[data-psg-nav-panel="account"]{display:none}.psg-nav-account-guest{color:var(--psg-copy);margin:0 0 var(--psg-space-4)}
/*
 * My Account nav (profile header + grouped links) — lives here, not
 * pages.css, because it now also renders inside the header nav drawer's
 * Konto tab (header.php), which exists on every page, not just the ones
 * pages.css loads on. The color !importants on .psg-account-nav's own
 * links are real, confirmed-live fixes, not defensive guessing: this
 * component is nested inside .primary-navigation, and
 * `body.home .primary-navigation a{color:#fff}` (home.css, for the
 * transparent hero header's own inline nav links) was leaking into it —
 * white text on the drawer's own white background, invisible but present
 * (confirmed via getComputedStyle showing rgb(255,255,255), not a layout
 * bug — the items were there all along).
 */
.psg-account-header{display:flex;align-items:center;gap:var(--psg-space-3);padding:var(--psg-space-2) var(--psg-space-2) var(--psg-space-4);margin-bottom:var(--psg-space-2);border-bottom:1px solid var(--psg-line)}.psg-account-header__avatar{border-radius:50%;display:block;flex-shrink:0}.psg-account-header__text{display:flex;flex-direction:column;min-width:0}.psg-account-header__name{font-size:1rem;font-weight:700;color:var(--psg-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.psg-account-header__company{font-size:.8125rem;color:var(--psg-copy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.psg-account-nav{display:flex;flex-direction:column;gap:var(--psg-space-2)}.psg-account-nav__group{margin-bottom:var(--psg-space-2)}.psg-account-nav__group-title{margin:0 0 .4rem;padding:0 var(--psg-space-2);font-size:.6875rem;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:var(--psg-copy)}.psg-account-nav ul{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.15rem}.psg-account-nav a{display:flex;align-items:center;gap:.7rem;padding:.6rem var(--psg-space-2);border-radius:var(--psg-radius-sm);color:var(--psg-copy)!important;text-decoration:none;font-weight:600;font-size:.875rem;transition:background-color .15s var(--psg-ease),color .15s var(--psg-ease)}.psg-account-nav a:hover{background:var(--psg-sand);color:var(--psg-ink)!important}.psg-account-nav li.is-active a{background:var(--psg-forest);color:#fff!important}.psg-account-nav .psg-icon{width:20px;height:20px;flex-shrink:0}.psg-account-nav__group--top{padding-bottom:var(--psg-space-2);margin-bottom:var(--psg-space-2);border-bottom:1px solid var(--psg-line)}.psg-account-nav__group--logout{margin-top:var(--psg-space-2);padding-top:var(--psg-space-3);border-top:1px solid var(--psg-line)}.psg-account-nav__group--logout a{color:var(--psg-error)!important}.psg-account-nav__group--logout a:hover{background:var(--psg-error);color:#fff}.site-header__tools{display:flex;gap:0;align-items:center;position:relative;justify-self:end}.header-account{margin-inline-end:var(--psg-space-4)}.header-search{margin-inline-end:var(--psg-space-4)}.header-account,.header-wishlist{position:relative;display:inline-grid;place-items:center;color:var(--psg-ink);text-decoration:none;transition:color .2s var(--psg-ease)}.header-account:hover,.header-wishlist:hover{color:var(--psg-forest)}.site-header__inner>*,.psg-topbar__inner>*,.psg-topbar__left,.site-footer__panel>*{min-width:0}
.header-cart{position:relative;display:inline-flex;align-items:center;color:var(--psg-ink);text-decoration:none;height:40px;transition:color .2s var(--psg-ease)}.header-cart:hover{color:var(--psg-forest)}
/*
 * Header cart indicator — count-only badge at every width, not just
 * mobile (direct request: make desktop match mobile's own icon+small-
 * corner-badge instead of icon+count+" / "+subtotal price text). This
 * used to be mobile-only (responsive.css's 768px block); same badge
 * values, just unconditional now, and the subtotal/divider spans stay
 * permanently hidden rather than only below 768px. The bottom mobile
 * toolbar's own separate cart badge (responsive.css,
 * .psg-mobile-toolbar__icon-wrap .psg-cart-count) is untouched — it was
 * always its own scoped rule, not this one.
 */
.header-cart .subtotal-divider,.header-cart .psg-cart-subtotal{display:none}
.header-cart .psg-cart-count{position:absolute;top:2px;right:-6px;margin:0;display:inline-grid;place-items:center;min-width:16px;height:16px;padding:0 3px;border-radius:50%;background:var(--psg-forest);color:#fff;font-size:.625rem;font-weight:700;line-height:1}
.header-cart .psg-cart-count[data-count="0"]{display:none}
.menu-toggle{display:none;border:0;background:transparent;padding:.5rem;color:var(--psg-ink)}
/* Both only ever matter inside the mobile nav drawer (see responsive.css) — hidden unconditionally here so neither can render as a stray visible element on desktop, where .primary-navigation stays a plain inline menu and never goes through its is-open/drawer state at all. */
.psg-nav-overlay,.psg-nav-close{display:none;border:0;padding:0}.psg-nav-group--menu,.psg-nav-group__title{display:none}.psg-icon{display:block;width:24px;height:24px;position:relative;color:inherit}.header-search{display:inline-grid;place-items:center;padding:0;border:0;background:none;color:var(--psg-ink);cursor:pointer;transition:color .2s var(--psg-ease)}.header-search:hover{color:var(--psg-forest)}/*
 * The left-hand pair, mobile only. display:none rather than an empty flex box:
 * .site-header__inner is a three-column grid, and even an empty group claims a
 * cell — which shunted the branding, nav and tools one column left and left
 * the tools stack sitting beside the logo instead of at the far right.
 * Everything it contains is mobile-only anyway.
 */
.site-header__lead{display:none;align-items:center;gap:var(--psg-space-4);justify-self:start}
/* One search button per breakpoint — see header.php for why there are two. */
.header-search--lead{display:none}/* Dark ground for the footer proper, the lighter green kept for the call-to-
   action band above it — the reverse of how they started, so the strip that
   asks for something is the lighter of the two. */
.site-footer{margin-top:var(--psg-space-8);padding:0 0 var(--psg-space-4);background:var(--psg-forest-dark);color:#fff}/* The band is the first thing in the footer, so the footer's own top padding would have put a strip of the lighter green above it. That padding moved onto .site-footer__body, which is now what needs it. *//* --psg-forest against the footer's --psg-forest-dark: the same hue one step
   lighter, so the stripe reads as a lift in the footer rather than as a
   different block bolted on top of it. */
.site-footer__cta-band{background:var(--psg-forest);padding-block:var(--psg-space-6)}.site-footer__body{padding-top:var(--psg-space-7)}.site-footer a{color:#fff}
/*
 * Explicit user request: bring back the rounded-card footer treatment
 * this project had earlier (content sitting inside a rounded block,
 * not flush against the section's own flat background). A prior round
 * flattened this entirely after a screenshot showed the live
 * reference's footer as plain flat green with no card at all — that
 * observation stands (no card on the live reference), but the user
 * has since asked for this project's own earlier rounded-block design
 * back regardless, as a deliberate visual choice rather than a
 * parity fix. Kept modest on purpose, unlike the original oversized
 * near-black+gradient version this project moved away from before: a
 * subtle lighter panel on the section's own green, generous rounding,
 * sized to the content via padding rather than a fixed height.
 */
/* z-index:0 (not just position:relative) so this establishes its own real stacking context — the mark's negative z-index below then stays contained inside this card, instead of risking dropping behind some unrelated earlier page element. */
.site-footer__card{position:relative;z-index:0;background:rgba(255,255,255,.08);border-radius:var(--psg-radius-lg);overflow:hidden;box-shadow:none;padding:var(--psg-space-6)}
/*
 * Massive brand mark — webflow.com's own footer treatment (a huge
 * brand-mark icon bleeding off the corner, confirmed by loading their
 * real footer live rather than guessed from memory) adapted to fit a
 * much more compact card than their own spacious dedicated area: same
 * confident, oversized-icon-in-the-corner concept, but at a tempered
 * opacity rather than Webflow's full-strength color, since this footer
 * has nav text sharing the same corner instead of Webflow's own empty
 * page-bottom space. `pointer-events:none` — decorative only, must
 * never intercept a click meant for the real nav links sitting above
 * it in the stacking order.
 */
.site-footer__mark{position:absolute;bottom:-12%;right:-8%;width:clamp(18rem,32vw,26rem);height:auto;opacity:.16;pointer-events:none;z-index:-1}
/* ── Round 194 — footer panel rebuilt as a fluid two-part row ──────────────
 *
 * The previous version reserved the decorative leaf mark's footprint with
 * `padding-right:clamp(18rem,26vw,20rem)` and then centred the nav group in
 * what was left using `margin-inline:auto`. Its comment (removed with it)
 * argued this centred the menus "between the logo and the icon specifically."
 *
 * Measured, it centred them nowhere: `margin-inline:auto` centres inside the
 * CONTENT box, which excludes that padding, so the menus sat a constant 320px
 * left of the card's real centre at every desktop width — navL 171 against
 * navR 491 at 1440px. Worse, the group was `flex:0 0 auto` over fixed
 * `minmax(180px,1fr)` tracks with a 5.5rem gap: a rigid 716px block that could
 * neither shrink nor distribute. As the viewport narrowed it ran out of room,
 * wrapped below the brand and jammed against the left edge (navL 36 at
 * 1150px). That is the reported displacement.
 *
 * Rebuilt as what it actually is — a two-part row. The brand takes a fixed
 * share on the left; the nav group takes ALL remaining width (`flex:1 1 0`)
 * and distributes its columns evenly across it, so the spacing is even by
 * construction at every width instead of being a fixed gap that happens to fit
 * at one. `align-items:center` vertically centres the two against each other
 * inside the rounded card.
 *
 * The leaf mark no longer needs reserving: it is a 16%-opacity watermark
 * sitting behind the content (`z-index:-1`), so menu text over its outer edge
 * stays legible. The small right inset keeps the last column off the card's
 * very edge without pretending to be a layout constraint.
 * ─────────────────────────────────────────────────────────────────────── */
.site-footer__panel{display:flex;flex-direction:row;flex-wrap:nowrap;align-items:center;gap:clamp(2rem,4vw,4.5rem);padding:0 clamp(0rem,2vw,2rem) 0 0;background:transparent}
/* Still a fixed share, not a growing one — the nav group is what absorbs the
   remaining width now, so the brand must not compete for it. */
.site-footer__brand{position:relative;z-index:1;width:auto;flex:0 0 clamp(210px,22%,260px);margin-right:0}
/* Real bundled logo-white.png (header.php's own confirmed asset — has_custom_logo() is false site-wide, see that file's own comment) — swapped in for the plain text this used to fall back to. No invert filter needed, the source file is already white. */
.site-footer__logo img{display:block;width:180px;max-width:100%;height:auto;margin-bottom:var(--psg-space-5)}
.site-footer__address{width:280px;max-width:100%;color:rgba(255,255,255,.85)}.site-footer__address p{margin:0 0 .3rem}.site-footer__address .psg-footer-title{font-size:1rem;font-weight:700;margin:0 0 var(--psg-space-2);color:#fff}/*
 * Real bug from the first pass at this: capping flex-basis at 480px
 * with only a 140px column minimum left no real room for "Dekorpotter &
 * vaser" or "Ofte stilte spørsmål" at this font size — both wrapped to
 * 2 lines, a real visual regression caught on a live screenshot, not
 * assumed fixed from the CSS. Widened the column minimum instead of
 * guessing a bigger fixed number: `flex:0 0 auto` sizes this box by its
 * own grid content (up to `max-width`), so it takes exactly the room 3
 * genuinely single-line columns need — not more (which is what pushed
 * this toward the icon before), not less (which is what caused the
 * wrapping just now).
 */
/* margin-inline:auto — direct feedback that hugging this right against the brand column read as over-adjusted. The icon isn't part of this flex row's own layout (it's absolutely positioned on the card), so centering this box within the *actual* leftover flex space after the brand column is what reads as "evenly spaced, optically balanced against the icon" instead of shoved to one side. */
/* Wider gap + max-width — direct feedback that the 3 columns felt too tight together once they were well-positioned; letting them spread out over more of the available room reads as more considered than 3 lists huddled together. */
/*
 * gap widened again (4.5rem -> 5.5rem) — direct follow-up feedback that
 * it could still spread further. margin-top:3.25rem — direct feedback
 * that the columns should align with "Palmera AS" (the address block's
 * first real text line) rather than the logo image itself, which sits
 * above it; 3.25rem is the logo's own real fixed height (180px wide,
 * 1000:156 real aspect ratio) plus its own margin-bottom
 * (var(--psg-space-5), 1.5rem) — a fixed, exact match rather than a
 * guessed offset, safe to hardcode since the logo's own width is
 * already a fixed 180px, not fluid. Heading/link font-size bumped
 * .9375rem -> 1rem to match the address block's own real text size
 * exactly (confirmed via getComputedStyle: 16px/1rem, inherited
 * body copy size, no separate override anywhere in that block).
 */
/* `minmax(0,1fr)`, not a bare `1fr`: the `auto` minimum of `1fr` refuses to
   shrink below the longest menu label, which is exactly how the old rigid
   block behaved. `min-width:0` does the same job one level up — a flex item's
   automatic minimum size would otherwise re-impose that floor and bring the
   overflow straight back. */
.site-footer__nav-group{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--psg-space-5) clamp(1rem,2.5vw,2.75rem);flex:1 1 0;min-width:0;max-width:none;margin:0}.psg-footer-toggle summary{list-style:none;cursor:default;font-size:1rem;font-weight:700;color:#fff;margin-bottom:var(--psg-space-4)}.psg-footer-toggle summary::-webkit-details-marker{display:none}.psg-footer-toggle>*:not(summary){display:block}.psg-menu--footer{display:grid;gap:.7rem}.psg-menu--footer a{color:rgba(255,255,255,.75);text-decoration:none;font-size:1rem;transition:color .2s var(--psg-ease)}.psg-menu--footer a:hover{color:#fff;text-decoration:underline}.site-footer__legal{display:flex;flex-direction:row;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:1rem;padding-block:var(--psg-space-5);font-size:.875rem;color:rgba(255,255,255,.6)}

/*
 * CTA banner — round 91's footer redesign (see footer.php's own header
 * comment for the full reasoning). Sits above the rounded card, on the
 * section's own flat green: a real, already-used secondary CTA ("Bli
 * forhandler"), the one genuine lead-capture action this B2B site has,
 * not an invented newsletter box.
 */
/* The colour change is the separation now — the hairline and the spacing that   used to stand in for it are gone. */.site-footer__cta{display:flex;align-items:center;justify-content:space-between;gap:var(--psg-space-5)}
.site-footer__cta-copy{max-width:56rem}
.site-footer__cta-eyebrow{display:inline-block;font-size:.8125rem;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:rgba(255,255,255,.7);margin:0 0 var(--psg-space-2)}
.site-footer__cta-copy p{margin:0;color:rgba(255,255,255,.92);font-size:1.0625rem}
.site-footer__cta .psg-button{flex-shrink:0}
/* Real bug caught live on the first desktop screenshot: `.site-footer a{color:#fff}` (a class+tag selector) outranks the plain single-class `.psg-button--white{color:var(--psg-forest)}`, so the button's own text was rendering white-on-white — invisible. This is the first time a `.psg-button--white` has ever sat inside `.site-footer`, so the conflict never existed before. */
.site-footer .psg-button--white{color:var(--psg-forest)}
.site-footer .psg-button--white:hover{color:var(--psg-forest)}
/* Real, already-established contact (get_option('admin_email'), same as the About page's own contact section) — a direct, working contact point, not decoration. */
.site-footer__contact-email{margin-top:var(--psg-space-4)}
.site-footer__contact-email a{font-weight:600}
.site-footer__contact-email a:hover{color:rgba(255,255,255,.8)}
/*
 * Three Elementor-element-ID-specific rules previously here (rounded hero
 * corners on the first Elementor section, an editorial-section text-
 * contrast fix, and its padding override) removed as dead code: the
 * homepage no longer renders any Elementor content at all (front-page.php
 * + inc/homepage.php, a fully self-contained template) — none of these
 * IDs can ever appear in the DOM again.
 */
.psg-slide{position:relative;color:#fff;display:flex;flex-direction:column;background-color:var(--psg-slide-bg-color,#242424);background-image:var(--psg-slide-bg-desktop);background-position:var(--psg-slide-pos-desktop,center center);background-size:var(--psg-slide-size-desktop,cover);background-repeat:no-repeat;padding-block:clamp(3rem,10vw,7rem)}.psg-slide h2{color:#fff}
/*
 * The marquee/feature-strip lives inside .psg-slide in the DOM (confirmed
 * directly in the rendered HTML — it's already nested there, this was
 * never a page-structure/Elementor issue) but with no positioning of its
 * own it just sat wherever normal flow put it inside the slide's own
 * top/bottom padding, reading as a separate floating rectangle rather
 * than a band flush with the hero's own bottom edge (the live
 * reference). margin-top:auto pushes it to the end of the now-flex-
 * column slide; negative side/bottom margins cancel the slide's own
 * padding so it runs edge-to-edge — the parent's overflow:hidden +
 * border-radius above still clips it to match the hero's rounded
 * corners.
 */
.psg-slide>.psg-slide .psg-marquee{margin-top:auto;margin-bottom:calc(-1 * clamp(3rem,10vw,7rem))}
/*
 * The value strip. Its own height is published as a token because the homepage
 * hero sizes itself against the whole top band (topbar + strip) to fill exactly
 * one screen.
 *
 * The gap lives on the GROUP, and each group ends with a trailing separator, so
 * the two groups are identical and translateX(-50%) lands on a repeat. A single
 * row of spans with one shared gap is half a gap short of that, which is what
 * made the strip hitch on every loop.
 */
:root{--psg-marquee-h:38px}
/*
 * On the homepage this sits ON the hero photograph, pulled up over its bottom
 * edge (home.css) so it is inside the first screen without shortening the hero.
 */
.psg-marquee{position:relative;z-index:1;overflow:hidden;background:var(--psg-forest);color:#fff}
.psg-marquee__track{display:flex;width:max-content;gap:0;animation:psg-marquee 60s linear infinite}
/* padding-right matches the gap so the trailing separator is the same
   distance from the next copy's first word as from its own — without it the
   two touched at every copy boundary, the track's gap being 0. It also keeps
   each copy identical, which is what makes the -50% shift land on a repeat. */
.psg-marquee__group{display:flex;align-items:center;gap:2.25rem;padding-right:2.25rem;height:var(--psg-marquee-h)}
/* The eyebrow treatment the rest of the site uses for labels (base.css). */
.psg-marquee__item{white-space:nowrap;font-size:.75rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase}
/* A hairline that stops well short of the band's edges — a divider, not a rule. */
.psg-marquee__sep{flex:0 0 1px;width:1px;height:.9em;background:rgba(255,255,255,.38)}.psg-marquee:hover .psg-marquee__track,.psg-marquee:focus-within .psg-marquee__track{animation-play-state:paused}@keyframes psg-marquee{to{transform:translateX(-50%)}}@media(prefers-reduced-motion:reduce){.psg-marquee__track{animation:none}}.psg-compare{position:relative;overflow:hidden;border-radius:var(--psg-radius-lg);box-shadow:var(--psg-shadow);aspect-ratio:16/9}
.psg-compare__before img,.psg-compare__after img{height:100%;object-fit:cover}.psg-compare__before img,.psg-compare__after img{display:block;width:100%}.psg-compare__after{position:absolute;inset:0}.psg-compare label{position:absolute;inset:0;display:grid;align-items:center}.psg-compare input{width:100%;opacity:.01}.psg-mega-menu__list{list-style:none;margin:0;padding:0;display:grid;gap:.5rem}.psg-products-carousel{position:relative}.psg-products-carousel__scroller{overflow-x:auto;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none}.psg-products-carousel__scroller::-webkit-scrollbar{display:none}.psg-products-carousel__scroller ul.products{display:flex;gap:1.25rem;margin:0;padding:.25rem .25rem 1rem}/*
 * Round 195 — product cards stop growing past a readable size.
 *
 * `.psg-shell` is deliberately uncapped (base.css: a real full-width site, not
 * a boxed one), so "N cards across 100%" meant each card grew without limit:
 * at a 2560px viewport a 4-up row rendered 613px cards. Reported as the cards
 * going "full width universally", which is not what full-width should mean.
 *
 * `min()` around the existing basis caps the card instead of the row. This
 * strip is a snap SCROLLER, not a fixed grid, so a narrower card does not
 * leave a hole — it simply means more products are visible at once, which is
 * the requested behaviour. No breakpoints: the count rises continuously with
 * the viewport, and below ~1400px the computed basis is already under the cap
 * so nothing changes at the sizes that were fine.
 *
 * The cap is 360px, raised from the 320px first shipped — that was reported as
 * having overshot and made the cards too small. One number, `--psg-product-card-max`,
 * which the tablet rule in responsive.css reads too.
 *
 *   1200px -> 273px cards (unchanged, under the cap)
 *   1440px -> 333px, ~3.9 visible   (under the cap, unchanged)
 *   1920px -> 360px, ~4.9 visible   (was 458px, 4 visible)
 *   2560px -> 360px, ~6.9 visible   (was 613px, 4 visible)
 *
 * The tablet/mobile `flex-basis` overrides in responsive.css stay as they are —
 * their computed widths never approach the cap.
 */
.psg-products-carousel__scroller ul.products li.product{flex:0 0 min(calc((100% - (var(--psg-products-per-view) - 1) * 1.25rem) / var(--psg-products-per-view)),var(--psg-product-card-max,360px));scroll-snap-align:start;background:var(--psg-products-bg,var(--psg-paper));border-radius:var(--psg-radius);padding:var(--psg-space-4);transition:box-shadow .2s var(--psg-ease),transform .2s var(--psg-ease)}.psg-products-carousel__scroller ul.products li.product:hover{box-shadow:var(--psg-shadow);transform:translateY(-3px)}
/*
 * Live reference has no add-to-cart/"Les mer" button on homepage
 * product cards at all (only a small icon on hover in the corner) —
 * staging showed a full-width green button on every card. Scoped to
 * .psg-products-carousel specifically (the homepage widget's own
 * wrapper) so the shop/archive grid's real "Legg i handlekurv" buttons
 * — which use the same WooCommerce [products] shortcode markup outside
 * this wrapper — are completely untouched; this only hides the
 * rendered <a class="button"> on the homepage, the underlying
 * shortcode/add-to-cart URLs/AJAX behavior are not modified at all.
 */
.psg-products-carousel__scroller ul.products li.product .button{display:none}.psg-products-carousel__nav{display:flex;justify-content:flex-end;gap:.5rem;margin-top:var(--psg-space-4)}.psg-products-carousel__prev,.psg-products-carousel__next{display:inline-grid;place-items:center;width:44px;height:44px;padding:0;border-radius:50%;border:1px solid var(--psg-line);background:#fff;color:var(--psg-ink);cursor:pointer;transition:background-color .2s var(--psg-ease),border-color .2s var(--psg-ease),color .2s var(--psg-ease)}.psg-products-carousel__prev:hover,.psg-products-carousel__next:hover{background:var(--psg-forest);border-color:var(--psg-forest);color:#fff}.psg-products-carousel__prev:disabled,.psg-products-carousel__next:disabled{opacity:.35;cursor:default}.psg-mobile-toolbar{display:none}
/*
 * Cart drawer used to shrink the whole site into a floating scaled-down
 * card (transform:scale(.86), desktop-only). Direct feedback + a named
 * reference (blumery.co): a real "push" layout reads better and
 * doesn't cover anything — the site's own content column narrows by
 * exactly the drawer's width (a real CSS `margin-right` on the wrapper
 * everything-but-the-drawers lives in, animated, matching how
 * blumery.co's own `.page-wrapper--drawer-open{margin-right:...}`
 * works, confirmed via a direct inspection of their site) instead of
 * shrinking/floating.
 *
 * `--psg-cart-drawer-width` is the one shared source of truth both the
 * cart panel's own width (below, `.psg-cart-drawer`) and this push read
 * from, so they can never drift out of sync at any breakpoint
 * (responsive.css redefines just this one variable at the same width
 * the panel itself already goes full-width at, ≤768px). Real bug found
 * live, not just reasoned about: an initial version of this shared
 * `min(420px,92vw)` value only matched the *generic* `.psg-drawer__panel`
 * fallback width — the real cart drawer has always rendered narrower,
 * `.psg-cart-drawer{width:min(360px,92vw)}` below, its own deliberate
 * size, unrelated to this change and not something to alter — so the
 * push and the panel disagreed by exactly that 60px difference, a real
 * visible gap between the pushed content and the drawer. Fixed by
 * giving this its own dedicated variable matching the cart drawer's
 * actual, already-correct 360px, instead of reusing the unrelated
 * `.psg-drawer__panel` default (still `min(420px,92vw)`, own literal
 * value below, unchanged) which only the account/login drawer — never
 * pushed, this whole mechanism doesn't apply to it — actually uses.
 * Was unconditional (all breakpoints, mobile included) for a while.
 * Real mobile (≤768px) drops that specific part on direct follow-up
 * feedback: pushing the *entire* page by a full 100% of the viewport
 * (the drawer's own mobile width, responsive.css) reads as the whole
 * site sliding away and back, not the drawer sliding in — reported as
 * "weird." Mobile gets no push at all now (its own rule below, or
 * rather the lack of one) — the drawer is already a genuine
 * full-screen panel there, so a plain overlay is correct, not a
 * downgrade back to "covering parts of the page" (there are no parts
 * left uncovered to complain about once the drawer is full-screen).
 * The 769–1024px tablet range keeps this push. Desktop (≥1025px) has
 * moved back to a real visual `transform:scale()` instead — see that
 * rule, further down, kept with the `height:100vh` rule it depends on.
 *
 * That desktop scale was tried once already in this exact spot, hit a
 * real, confirmed transition bug (a `calc()`-driven `transform` got
 * stuck at the identity matrix instead of animating), got reverted back
 * to this push — and *that revert itself* turned out to be based on
 * incomplete evidence: direct follow-up feedback was unambiguous that
 * the push's "site rearranges instead of zooming out" feel was never
 * actually wanted, `getComputedStyle` reads taken right after
 * triggering a transition were confirmed unreliable in this specific
 * environment (the *push itself*, long-shipped and screenshot-proven,
 * read back as `0px` mid-investigation before a screenshot showed it
 * genuinely working), and the scale attempt was never re-verified by
 * screenshot after its `calc()` bug was actually fixed (JS computing a
 * plain number instead) — only re-checked with the same unreliable
 * tool. Rebuilt properly this time and verified by screenshot at every
 * step, not by trusting a computed-style read alone.
 */
/*
 * Round 193: widened 360px → 420px on direct request ("wider overall
 * cartdrawer"), benchmarked against palmera.no's, whose panel measured 500px
 * at a 1920px viewport. The paragraph above warns that 360px was deliberate
 * and not to be altered — that warning was about the era when the push
 * distance and the panel were two independent literals that had silently
 * drifted 60px apart. They are not independent any more: `.psg-cart-drawer`
 * reads this same variable, so changing it here moves both together, which is
 * exactly the property that made the fix above hold. Widening is safe; going
 * back to two separate literals is not.
 */
:root{--psg-cart-drawer-width:min(420px,92vw)}
#psg-site-scale-wrap{transition:margin-right .3s var(--psg-ease)}
@media(min-width:769px) and (max-width:1024px){
	body.psg-cart-scale-open #psg-site-scale-wrap,
	html.psg-cart-restoring #psg-site-scale-wrap{margin-right:var(--psg-cart-drawer-width)}
}
/*
 * Pre-paint state for cart-drawer.js's cross-navigation restore
 * (inc/cart-drawer.php's own inline wp_body_open() script sets this
 * `<html>` class before `#psg-site-scale-wrap` even exists in the DOM,
 * let alone the footer-loaded JS that used to be the only thing capable
 * of applying it) — direct feedback that the drawer should just already
 * be there on a new page if it was open, not visibly snap into place a
 * moment after the page has already painted closed. No transition here
 * on purpose: this has to be the very first rendered state, not
 * something that animates into it. (The actual `margin-right`/
 * `transform` restore rules themselves now live with their real,
 * breakpoint-matched open-state counterparts above/below — this class
 * needs to win at every breakpoint that applies a push or scale at
 * all, so it's included directly in each of those rules rather than
 * declared a second time here.)
 */
html.psg-cart-restoring #psg-cart-drawer[hidden]{display:block}
/*
 * Real bug found live: making the panel visible from the first frame
 * (above) wasn't enough on its own — `.psg-drawer__panel` still
 * carries its own real `animation:psg-drawer-in .25s` unconditionally
 * (below), and a CSS animation plays automatically the moment an
 * element becomes eligible to render it, independent of any class this
 * early mechanism was setting. So the *flash* was gone (round 31) but
 * the slide-in itself still auto-played on every single restore, which
 * is exactly what was reported — direct feedback that nothing at all
 * should happen on a restore, not even its own real entrance
 * animation. `body.psg-drawer-instant` (interactions.js's own later,
 * real open) already neutralizes this same animation for the
 * JS-driven path; this is the identical rule for the pre-paint path,
 * since that path doesn't set that class this early.
 */
html.psg-cart-restoring .psg-drawer__panel{animation:none}
/*
 * Direct correction, with blumery.co inspected again to confirm: the
 * native page scrollbar has to belong to the *narrowing* wrapper, not
 * to `<body>`/the true browser viewport — otherwise the scrollbar stays
 * pinned to the outer edge of the actual window regardless of any
 * margin-right on an inner div (CSS margin never moves who owns
 * document scroll), which means the fixed-position drawer and the
 * native scrollbar end up fighting for the exact same pixels at the
 * true viewport edge — confirmed on blumery.co too: `html`/`body`
 * there are *always* `overflow:hidden` (checked directly, not just
 * when their drawer is open) and `.page-wrapper` itself is the real
 * scrolling element (`height:100dvh;overflow-y:auto`, their own CSS).
 * Moving scroll ownership here the same way means this wrapper's own
 * scrollbar sits at *its* edge — which moves inward with the
 * margin-right above — so the drawer ends up genuinely beyond/outside
 * the scrollbar, never underneath or fighting it, exactly as asked.
 *
 * Desktop-only (matches blumery.co's own `min-width:990px` gate for
 * this specific mechanism, and this project's own existing 1025px
 * desktop threshold elsewhere) — deliberately not touching mobile's
 * scroll ownership, which already has no persistent scrollbar to
 * reposition in the first place and where this exact pattern has
 * real, documented touch-scrolling risk on iOS Safari this project
 * hasn't needed to take on before now.
 *
 * base.css's own `body{overflow-x:hidden}` comment already flags
 * real prior history with this exact class of change breaking
 * position:sticky (the checkout order-summary card) — deliberately
 * verified that card specifically still sticks correctly after this
 * change before calling it done, not just reasoned about.
 *
 * `--psg-vh100` (below and in the cart-scale block further down): a real
 * pixel height set by assets/js/interactions.js from `window.innerHeight`
 * on load/resize/visibility-restore, not a literal `100vh` — direct
 * follow-up to a reported bug (minimizing the browser window then
 * restoring it left a blank area over the lower half of the page) where
 * this element's raw `100vh` may have been left sized for a transient,
 * not-yet-final viewport height from partway through that transition.
 * See interactions.js's own comment for the full reasoning; falls back to
 * literal `100vh` if JS hasn't run yet (SSR/no-JS first paint).
 */
@media(min-width:1025px){
	html,body{height:100%}
	body{overflow:hidden}
	#psg-site-scale-wrap{height:calc(var(--psg-vh100,100vh));overflow-y:auto}
	/*
	 * Cart-open page scale, desktop: direct request for the page to feel
	 * "zoomed out" while the drawer is open — the same layout, same
	 * proportions, just smaller — rather than the product grid/sidebar
	 * rearranging into less space. `transform:scale()` does exactly
	 * that: it shrinks how the whole box *paints*, without changing how
	 * anything inside it *lays out* — the shop grid still computes 4
	 * real columns at their full real width regardless of the drawer,
	 * then the finished result gets shrunk as one piece, so nothing
	 * reflows and nothing looks rearranged.
	 *
	 * `--psg-cart-scale` is set by JS (interactions.js's `initDrawer`,
	 * and this same inline restore script in inc/cart-drawer.php for the
	 * pre-paint path) as a plain resolved number, not computed live here
	 * via `calc()` — confirmed live, not assumed: this element could not
	 * reliably *transition* `transform` when its value was a `calc()`
	 * expression (with or without a custom property in between) — the
	 * transition got stuck at the identity matrix instead of animating.
	 * A concrete number, set just before the class that applies this
	 * rule is added, transitions correctly.
	 *
	 * `height` is the other half of "looks the exact same, just
	 * smaller": `#psg-site-scale-wrap` is already pinned to exactly
	 * `100vh` (the rule directly above) with its own internal scroll, so
	 * real page-taller-than-viewport content already scrolls *inside*
	 * this box, not the true document. `transform` doesn't change a
	 * box's own layout height, only how it paints — so inflating that
	 * height by the *inverse* of the scale before the transform shrinks
	 * it back down means the rendered result still measures exactly
	 * `100vh` (inflated-height × scale = 100vh again), filling the
	 * viewport top-to-bottom instead of visually stopping short of it
	 * and leaving a blank strip. `transform-origin:top left` keeps the
	 * shrunk box's top-left corner exactly where it already was, with
	 * its right edge landing precisely on the drawer's own left edge —
	 * zero gap, zero overlap — because the scale ratio is derived from
	 * the exact same `--psg-cart-drawer-width` the drawer itself is
	 * sized from (JS's own copy of that same `min(360,92vw)` formula).
	 */
	#psg-site-scale-wrap{transform-origin:top left;transition:height .3s var(--psg-ease),transform .3s var(--psg-ease)}
	body.psg-cart-scale-open #psg-site-scale-wrap,
	html.psg-cart-restoring #psg-site-scale-wrap{
		transform:scale(var(--psg-cart-scale,1));
		height:calc(var(--psg-vh100,100vh) / var(--psg-cart-scale,1));
	}
	/*
	 * Real, visually-confirmed bug (not a theory): `.site-header`'s own
	 * `position:sticky` (components.css, above) inside a `transform`-ed
	 * `overflow-y:auto` scroll container is a genuinely broken
	 * combination in this browser — a live screenshot showed the header
	 * tiling itself repeatedly down the entire page instead of rendering
	 * once, not merely failing to scale. Suspending `position:sticky` to
	 * `relative` for the header specifically while the scale is active
	 * fixes that: the header just scrolls with the rest of the page for
	 * the brief time the cart is open (a small, acceptable trade next to
	 * a corrupted page), instead of trying to stick inside a transformed
	 * scroll container at all.
	 */
	body.psg-cart-scale-open .site-header,
	html.psg-cart-restoring .site-header{position:relative}
}
.psg-drawer[hidden]{display:none}.psg-drawer{position:fixed;inset:0;z-index:1000}.psg-drawer--persistent{pointer-events:none}.psg-drawer__overlay{position:absolute;inset:0;border:0;background:rgba(0,0,0,.38);cursor:pointer}.psg-drawer__panel{position:absolute;right:0;top:0;width:min(420px,92vw);height:100%;padding:3rem 2rem;background:#fff;overflow:auto;box-shadow:var(--psg-shadow-lg);animation:psg-drawer-in .25s var(--psg-ease);pointer-events:auto}@keyframes psg-drawer-in{from{transform:translateX(100%)}to{transform:translateX(0)}}@media(prefers-reduced-motion:reduce){.psg-drawer__panel{animation:none}}
/*
 * Direct feedback: reopening the cart drawer on every fresh page load
 * (its own cross-navigation persistence, cart-drawer.js) replayed the
 * full slide-in entrance every time — should feel like the same
 * persistent panel just continuing to exist across navigation, not
 * something that visibly reopens each time. interactions.js's open()
 * adds this class only for that specific restore call (real clicks on
 * the cart icon are unaffected, still animate normally) and removes it
 * again a frame later, so this only ever suppresses the one restore-time
 * transition/animation, never a real user-initiated open/close.
 */
body.psg-drawer-instant #psg-site-scale-wrap{transition:none}
body.psg-drawer-instant .psg-drawer__panel{animation:none}.psg-drawer__close{position:absolute;right:1rem;top:1rem;display:inline-grid;place-items:center;width:36px;height:36px;border:0;border-radius:50%;background:var(--psg-sand);color:var(--psg-ink);cursor:pointer;transition:background-color .2s var(--psg-ease)}.psg-drawer__close:hover{background:var(--psg-line)}

/* Cart drawer — flex column so the footer stays fixed at the bottom regardless of item-list length; the item list scrolls independently. No overlay (see .psg-drawer--persistent above) so the page behind stays scrollable/clickable while it's open. */
.psg-cart-drawer{display:flex;flex-direction:column;padding:0;height:100%;width:var(--psg-cart-drawer-width)}
.psg-cart-drawer__head{flex-shrink:0;display:flex;align-items:center;justify-content:space-between;gap:var(--psg-space-3);padding:var(--psg-space-5) var(--psg-space-6) var(--psg-space-4);border-bottom:1px solid var(--psg-line)}
.psg-cart-drawer__head .psg-cart-drawer__title{margin:0}
.psg-cart-drawer .psg-drawer__close{position:static;flex-shrink:0}
.psg-cart-drawer__scroll{flex:1 1 auto;overflow-y:auto;padding:var(--psg-space-4) var(--psg-space-6);transition:opacity .15s var(--psg-ease)}
[aria-busy="true"] .psg-cart-drawer__scroll{opacity:.5;pointer-events:none}
.psg-cart-drawer__footer{flex-shrink:0;padding:var(--psg-space-4) var(--psg-space-6) var(--psg-space-5);border-top:1px solid var(--psg-line);background:#fff;display:grid;gap:var(--psg-space-3)}.psg-cart-drawer__title{margin-bottom:var(--psg-space-5)}.psg-cart-drawer__empty{color:var(--psg-copy)}.psg-cart-drawer__items{list-style:none;margin:0 0 var(--psg-space-5);padding:0;display:grid;gap:var(--psg-space-4)}.psg-cart-drawer__item{display:grid;grid-template-columns:64px 1fr auto;gap:var(--psg-space-3);align-items:start}.psg-cart-drawer__item-image{display:block;border-radius:var(--psg-radius-sm);overflow:hidden}.psg-cart-drawer__item-image img{display:block;width:64px;height:64px;object-fit:cover}.psg-cart-drawer__item-details{display:flex;flex-direction:column;gap:.35rem;min-width:0}.psg-cart-drawer__item-title{color:var(--psg-ink);font-weight:600;font-size:.9375rem;text-decoration:none}.psg-cart-drawer__item-title:hover{color:var(--psg-forest)}.psg-cart-drawer__item-row{display:flex;align-items:center;justify-content:space-between;gap:var(--psg-space-2)}.psg-cart-drawer__item-price{font-size:.875rem;font-weight:600;color:var(--psg-ink);white-space:nowrap}.psg-qty-stepper{display:inline-flex;align-items:center;border:1px solid var(--psg-line);border-radius:var(--psg-pill);overflow:hidden}.psg-qty-stepper__btn{width:26px;height:26px;display:inline-grid;place-items:center;border:0;background:transparent;padding:0;cursor:pointer;font-size:1rem;line-height:1;color:var(--psg-ink);transition:background-color .2s var(--psg-ease)}.psg-qty-stepper__btn:hover{background:var(--psg-sand)}.psg-qty-stepper__value{min-width:1.5rem;text-align:center;font-size:.8125rem;font-weight:600}.psg-cart-drawer__item-remove{display:inline-grid;place-items:center;width:28px;height:28px;border:0;background:transparent;padding:0;color:var(--psg-copy);cursor:pointer;transition:color .2s var(--psg-ease)}.psg-cart-drawer__item-remove:hover{color:var(--psg-error)}.psg-cart-drawer__subtotal{display:flex;justify-content:space-between;align-items:center;font-size:1.0625rem}.psg-cart-drawer__footer .psg-button{width:100%}
/*
 * Direct request: hide the "(ekskl. mva)" price suffix in the cart
 * drawer specifically, keep it everywhere else (PDP, checkout). Real
 * source, confirmed via a source read of the actual live DOM (an
 * earlier attempt targeted B2BKing's own separate `woocommerce_get_
 * price_suffix` filter/`.woocommerce-price-suffix` class, which turned
 * out not to be what's firing — that feature's own setting is off on
 * this install): it's WooCommerce **core**'s own standard `tax_label`
 * class (`WC_Cart::get_product_subtotal()`/`get_cart_subtotal()`,
 * `includes/class-wc-cart.php` — the exact same real methods `inc/
 * cart-drawer.php` already calls), driven by the real
 * `woocommerce_tax_display_cart` setting ("excl" on this install).
 * `.woocommerce-price-suffix` kept alongside it, harmless, in case
 * B2BKing's own separate suffix setting is ever turned on later.
 * Scoped CSS hide here only, not touching WooCommerce's or B2BKing's
 * own logic at all — still fully active and unchanged on the real cart
 * page, checkout, and PDP.
 */
.psg-cart-drawer .tax_label,.psg-cart-drawer .woocommerce-price-suffix{display:none}
/* Dead rule removed here: `.plantsupply-login-price-link` never matched anything real — the actual guest price-gate link (inc/single-product.php) carries class `psg-price-gate-link`, now properly styled in archive.css (product cards) and single-product.css (product page). */
.b2bking_vat_number_registration_field_container.b2bking_custom_registration_container{display:block!important}
/* Cart-error toast (assets/js/cart-drawer.js's showCartError) — real feedback for a failed stock-limit/network request instead of a silent no-op. */
.psg-cart-toast{position:fixed;left:50%;bottom:var(--psg-space-6);z-index:1100;max-width:min(90vw,26rem);padding:.85rem 1.25rem;border-radius:var(--psg-radius);background:var(--psg-ink);color:#fff;font-size:.875rem;font-weight:600;box-shadow:var(--psg-shadow-lg);text-align:center;transform:translate(-50%,calc(100% + var(--psg-space-6)));opacity:0;transition:transform .25s var(--psg-ease),opacity .25s var(--psg-ease);pointer-events:none}
.psg-cart-toast.is-visible{transform:translate(-50%,0);opacity:1}

/* ── Round 193 — cart drawer product lines, palmera.no layout ─────────────
 *
 * Supersedes round 192, on direct request: match palmera.no's drawer line
 * ("the horizontal layout is perfect") with a smaller price, a smaller remove
 * icon, a wider panel, and — the one thing asked to differ — noticeably less
 * vertical space per line than theirs.
 *
 * Measured on palmera.no live rather than eyeballed: 500px panel, 82px square
 * thumbnail, 12.6px/600 title, 14px/700 price, and a line box running roughly
 * 112–140px tall depending on content. That last number is the one being
 * deliberately undercut here.
 *
 * Two structural moves carry the whole thing:
 *
 * 1. TWO grid tracks, not three. Round 192 gave the remove button its own
 *    third track, which pushed the price's right edge 28px + a gap inboard of
 *    the button's — two ragged right edges down the list. The button now sits
 *    inside the details column on the title's row (see inc/cart-drawer.php and
 *    the mirrored template in assets/js/cart-drawer.js), so × and price share
 *    one flush right edge. This is the specific thing that reads as
 *    "structured" in palmera's version.
 *
 * 2. The THUMBNAIL sets the line height, not the title. Round 192 got uniform
 *    rows by forcing the title into a fixed two-line box, which meant every
 *    single-line name paid for an empty second line — precisely the wasted
 *    vertical space being complained about. Here the details column is a
 *    space-between flex column with the thumbnail's height as its floor: the
 *    title sits at the top, the stepper/price row at the bottom, and the row
 *    is exactly thumbnail-tall whether the name runs to one line or two. Same
 *    uniform rows, none of the padding.
 *
 *    That only holds while the tallest possible content still fits inside
 *    72px: two 14px title lines at 1.4 (39.2px) plus the 26px stepper is
 *    65.2px, so there is ~7px of slack. Growing the title's size or the
 *    stepper past that would start driving the row height off the thumbnail
 *    again and the alignment would quietly degrade.
 *
 * Net line height is 72 + 28 = 100px against palmera's 112–140.
 * ─────────────────────────────────────────────────────────────────────── */

.psg-cart-drawer__items{
	list-style:none;margin:0 0 var(--psg-space-5);padding:0;display:block;
}
.psg-cart-drawer__item{
	display:grid;
	grid-template-columns:72px minmax(0,1fr);
	/* Shorthand, not `column-gap`: the older minified rule earlier in this file
	   still sets a `gap` on this selector, and a bare `column-gap` here would
	   leave its row-gap standing. Inert on a single-row grid today, a trap the
	   moment anyone adds a second row. */
	gap:0 var(--psg-space-4);
	align-items:start;
	/* 14px: 12 reads cramped against the hairline, 16 gives back the vertical
	   space this round exists to reclaim. No token sits at 14. */
	padding:14px 0;
	border-bottom:1px solid var(--psg-line);
}
.psg-cart-drawer__item:first-child{padding-top:0}
/* No trailing rule — the footer's own border already closes the list, and two
   adjacent lines read as a mistake. */
.psg-cart-drawer__item:last-child{border-bottom:0;padding-bottom:0}

.psg-cart-drawer__item-image{display:block;border-radius:var(--psg-radius-sm);overflow:hidden;background:var(--psg-sand)}
.psg-cart-drawer__item-image img{display:block;width:72px;height:72px;object-fit:cover}

.psg-cart-drawer__item-details{
	display:flex;flex-direction:column;justify-content:space-between;
	min-width:0;min-height:72px;
	/* Must be stated, not inherited: the older minified rule earlier in this
	   file leaves `gap:.35rem` on this selector, and on a space-between column
	   a gap is a MINIMUM, so it adds to the content instead of being absorbed
	   by the free space. Measured live, that 5.6px was enough to push the
	   mobile line (whose stepper is a 36px touch target) past the min-height
	   floor, so 2-line names rendered 81px tall against 76px for 1-line ones —
	   the exact uneven-rows problem this layout exists to eliminate. */
	gap:0;
}

/* Title row: name left, remove flush right. */
.psg-cart-drawer__item-head{
	display:flex;align-items:flex-start;justify-content:space-between;
	gap:var(--psg-space-3);
}
.psg-cart-drawer__item-title{
	display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;
	margin:0;color:var(--psg-ink);font-size:.875rem;font-weight:600;line-height:1.4;
	text-decoration:none;
}
.psg-cart-drawer__item-title:hover{color:var(--psg-forest)}

/*
 * Negative offsets, not a smaller box: the button keeps its full hit area while
 * the glyph pulls out to the column's true right edge and up onto the title's
 * first line, so it lands on the same right edge as the price. Shrinking the
 * button instead would have shrunk the tap target with it.
 *
 * The offset is DERIVED from the box size rather than written as a literal,
 * because responsive.css deliberately grows this button to a 34px touch target
 * at ≤768px. A hand-tuned `-5px` (correct for the 24px desktop box) left the
 * glyph 5px inboard of the price on mobile — measured, not theorised — since a
 * bigger box centres the same 13px icon with more padding around it. Expressed
 * as a calc over `--psg-cd-remove`, both breakpoints stay flush automatically,
 * and the mobile override only has to restate the size.
 */
.psg-cart-drawer__item-remove{
	--psg-cd-remove:24px;
	flex-shrink:0;
	width:var(--psg-cd-remove);height:var(--psg-cd-remove);
	margin:-4px calc((var(--psg-cd-remove) - 13px) / -2) 0 0;
	color:var(--psg-copy);
}
.psg-cart-drawer__item-remove:hover{color:var(--psg-ink)}
/* Overrides the width/height/stroke attributes on the inline SVG in both the
   PHP and the JS template at once — CSS beats presentation attributes — so the
   icon's weight is set in one place rather than three. The 13px here is the
   same 13px the offset above divides by; they have to move together. */
.psg-cart-drawer__item-remove .psg-icon{width:13px;height:13px;stroke-width:1.75}

.psg-cart-drawer__item-row{
	display:flex;align-items:center;justify-content:space-between;
	gap:var(--psg-space-3);
}
.psg-cart-drawer__item-price{
	font-size:.8125rem;font-weight:600;color:var(--psg-ink);
	white-space:nowrap;font-variant-numeric:tabular-nums;
}
/*
 * Trimmed to match the smaller price and to hold the 72px budget above —
 * DESKTOP ONLY. Unscoped, this pair outranks responsive.css's `.psg-qty-
 * stepper__btn{width:34px}` on specificity alone (0,2,0 vs 0,1,0) and would
 * have silently shrunk the mobile touch targets back to 24px, which is the
 * opposite of what that rule exists for.
 */
@media(min-width:769px){
	.psg-cart-drawer__item .psg-qty-stepper__btn{width:24px;height:24px;font-size:.875rem}
	.psg-cart-drawer__item .psg-qty-stepper__value{min-width:1.375rem;font-size:.75rem}
}
/*
 * Phones: 30px rather than the 34px responsive.css gives every stepper on the
 * site. Only inside the drawer, where the control sits in a 72px row under a
 * name and a badge and was the heaviest thing in it; the cart page's own
 * steppers keep the larger target.
 */
@media(max-width:768px){
	.psg-cart-drawer__item .psg-qty-stepper__btn{width:30px;height:30px;font-size:1rem}
	.psg-cart-drawer__item .psg-qty-stepper__value{min-width:1.5rem;font-size:.8125rem}
}

/* ── Round 195 — shared hero type scale (home + about) ────────────────────
 *
 * `.psg-home-hero` is used by BOTH the homepage (home.css) and the about page
 * (about.css), which each define it independently — same class, same markup,
 * two separate copies of the type rules that had drifted apart. Reported: the
 * hero elements are far too big on smaller desktops and on phones, out of
 * proportion to the photo behind them.
 *
 * Measured, the title ran 51.8px at a 1024px viewport, 45.6px at 768px and
 * 36.6px at 390px — on a 390px phone that is a headline nearly a tenth of the
 * screen width per character. The old clamp, `1.7rem + 2.4vw`, had a 2.25rem
 * (36px) FLOOR that the vw term never got below, so on small screens the
 * headline simply stopped scaling and sat at its minimum regardless of how
 * little room was left.
 *
 * The scale below lowers the floor and steepens the slope, so it keeps
 * shrinking where it needs to and still opens up on a wide screen:
 *
 *     390px -> 28.0px   (was 36.6)
 *     768px -> 33.7px   (was 45.6)
 *    1024px -> 39.3px   (was 51.8)
 *    1440px -> 48.5px   (was 56-60)
 *    1920px -> 52.0px   (was 60, capped)
 *
 * Defined here rather than in either page file because components.css loads
 * before both, so a page can still override a genuinely page-specific value —
 * but the shared scale is stated once. The font-size declarations that used to
 * live in home.css and about.css were removed with this, otherwise they would
 * simply win by load order and nothing here would apply.
 * ─────────────────────────────────────────────────────────────────────── */
.psg-home-hero__title{font-size:clamp(1.75rem,1.05rem + 2.2vw,3.25rem);line-height:1.12}
.psg-home-hero__lede{font-size:clamp(.9375rem,.85rem + .45vw,1.1875rem);line-height:1.55}
.psg-home-hero__eyebrow{font-size:clamp(.6875rem,.65rem + .15vw,.8125rem)}
/* The actions row shrinks its own breathing room on small screens too — a
   fixed 24px top margin reads as a gap on a 28px headline. */
.psg-home-hero__actions{margin-top:clamp(var(--psg-space-4),2.2vw,var(--psg-space-6))}


/* -- Contact form component (shared) ---------------------------------
 *
 * Moved here from about.css in round 198, unchanged. It stopped being an
 * about-page detail the moment the dedicated contact page (contact.css,
 * inc/contact-page.php) started using the same markup and the same
 * admin_post handler. Duplicating it into contact.css would have left two
 * copies of one component free to drift; components.css loads before both
 * page files, so either can still override a page-specific value.
 * ------------------------------------------------------------------- */
.psg-contact-form__notice{padding:var(--psg-space-3) var(--psg-space-4);border-radius:var(--psg-radius);margin-bottom:var(--psg-space-4);font-size:.9375rem;font-weight:600}
.psg-contact-form__notice--ok{background:rgba(63,122,92,.12);color:var(--psg-forest-dark)}
.psg-contact-form__notice--error{background:rgba(179,38,30,.1);color:var(--psg-error)}
.psg-contact-form{display:flex;flex-direction:column;gap:var(--psg-space-4)}
.psg-contact-form__honeypot{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.psg-contact-form__field{display:flex;flex-direction:column;gap:var(--psg-space-2);font-size:.875rem;font-weight:600;color:var(--psg-ink)}
.psg-contact-form__field input,.psg-contact-form__field textarea{font:inherit;font-weight:400;padding:.85rem 1rem;border-radius:var(--psg-radius);border:1px solid var(--psg-line);background:var(--psg-paper);color:var(--psg-ink);transition:border-color .2s var(--psg-ease),box-shadow .2s var(--psg-ease)}
.psg-contact-form__field input:focus,.psg-contact-form__field textarea:focus{outline:none;border-color:var(--psg-forest);box-shadow:0 0 0 3px rgba(63,122,92,.15)}
.psg-contact-form__field textarea{resize:vertical;min-height:8rem;font-family:inherit}
.psg-contact-form button{align-self:flex-start}

/*
 * Bestillingsvare flag on a cart-drawer line (inc/cart-drawer.php,
 * assets/js/cart-drawer.js) — under the product name, in the site's container
 * grey rather than the product card's leaf green: on a card the badge competes
 * with a photograph for attention, in a cart the line is already the customer's
 * own choice and only has to be readable. The status word carries the weight
 * and the lead time sits back from it.
 *
 * `align-self:flex-start` so the pill is as wide as its words rather than the
 * whole column: it is a label, and a full-width bar would read as a banner.
 * The details column is `justify-content:space-between` with a 72px floor, so
 * on a one-line name this sits in the free space between name and quantity and
 * costs no height at all.
 */
.psg-cart-drawer__item-flag{
	align-self:flex-start;max-width:100%;
	/* 3px over, 6px under, not an even 4.8/4.8: the box gaps were already equal
	   but it did not LOOK centred, because the title's half-leading adds ~2.8px
	   of empty line box above the badge while the stepper's border is its real
	   edge below. The asymmetric margin cancels that, so the two visible gaps
	   measure the same. */
	margin:.1875rem 0 .375rem;padding:.15rem .45rem;border-radius:var(--psg-radius-sm);
	background:var(--psg-sand);color:var(--psg-ink);
	overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
	font-size:.6875rem;font-weight:700;letter-spacing:.01em;line-height:1.4;
}
.psg-cart-drawer__item-flag-lead{color:var(--psg-copy);font-weight:500}

/*
 * The shared hero on phones — one authoritative block, loaded everywhere.
 *
 * `.psg-home-hero` is used by BOTH the front page and the about page, but its
 * mobile rules were written twice, in home.css and in about.css, each loaded on
 * only one of those pages. So a change to the "universal" hero landed on
 * whichever page you happened to test. Caught exactly that way: the same edit
 * fixed the about hero and left the homepage untouched. Both copies are gone;
 * this is the only one.
 *
 * The content is sized to the phone rather than scaled down from the desktop.
 * The two buttons were stretched to the full 355px and stacked, turning a pair
 * of short labels into two full-bleed slabs heavier than the headline itself.
 * Measured, they fit one row easily — about 127px and 120px against 355px — so
 * side by side and sized to their own labels they read as two choices rather
 * than two banners. `flex-wrap` is inherited from the base rule, so a longer
 * label in some future hero still drops to a second row instead of overflowing.
 *
 * The lede at 15.4px sat too close to the 28px headline for either to lead; at
 * 14px with a tighter measure it supports the headline instead of competing.
 */
/*
 * Every selector below is written as `.psg-home-hero X` rather than the bare
 * class. home.css and about.css both still set their own `.psg-home-hero__lede`
 * max-width unconditionally, and both load AFTER this file — at equal
 * specificity the later rule wins, media query or not. Measured: the homepage
 * lede stayed 355px wide while the about page's happened to look right only
 * because its copy is shorter. One extra class settles it.
 */
@media(max-width:640px){
	.psg-home-hero .psg-home-hero__actions{flex-direction:row;justify-content:center;align-items:center;width:100%;gap:var(--psg-space-2)}
	.psg-home-hero .psg-home-hero__actions .psg-button{width:auto;flex:0 1 auto;padding:.6rem 1.1rem;font-size:.875rem;min-height:42px}
	/*
	 * The lede is held to the width of the button row beneath it, so the block
	 * reads as one column rather than a wide paragraph with a narrow pair of
	 * buttons tucked under one end of it. 16rem is a shade over the measured
	 * 252px those two buttons occupy — matching it exactly would make the text
	 * look narrower than the buttons rather than level with them.
	 *
	 * This only works because the copy was shortened to suit: at the old length
	 * this width would have produced a column five lines deep.
	 */
	.psg-home-hero .psg-home-hero__lede{font-size:.875rem;line-height:1.5;max-width:16rem}
	/*
	 * 26ch, not 18: the headline's first line is "Naturtro kunstige planter",
	 * 25 characters, and at 18ch it broke after "kunstige" — which put the
	 * product name across two lines and pushed "til <audience>" onto a third.
	 * Wide enough for that line, still narrow enough to stop the fluid scale
	 * running the headline edge to edge.
	 */
	.psg-home-hero .psg-home-hero__title{max-width:26ch}
}


/*
 * ---------------------------------------------------------------------------
 * Header search — inc/search.php + assets/js/search.js.
 *
 * Rebuilt against a reference the shop owner supplied, measured rather than
 * eyeballed: every number below came off that page's own computed styles at
 * 1920px and at 410px, so desktop and mobile are two points on one design, not
 * two designs.
 *
 * The shape is a full-height sheet, not a dropdown. It is fixed from the
 * header's bottom edge to the bottom of the viewport, opaque white, and it
 * covers the page rather than dimming it — which is why there is no backdrop
 * element here any more. --psg-search-top is the header's real measured bottom,
 * written by search.js on open and on resize; the header differs between
 * breakpoints and between the homepage and everywhere else, so no constant
 * would be right.
 *
 * Measured targets — desktop / mobile:
 *   field          48px/600 centred   /  20px/600 centred
 *   field row      110px tall         /  70px
 *   field inset    100px              /  15px
 *   hint           17.3px #767676     /  14.7px
 *   result card    ~200px wide        /  2 per row
 *   card padding   10px               /  10px
 *   title          14.7px/500         /  14.4px/500
 *   view-all row   52px, uppercase    /  52px, uppercase
 * ---------------------------------------------------------------------------
 */
.psg-search{position:fixed;top:var(--psg-search-top,0px);left:0;right:0;bottom:0;z-index:2;display:flex;flex-direction:column;background:#fff}
/*
 * BOTH of these are required, and neither is redundant. `hidden` is a UA
 * rule (`[hidden]{display:none}`) and loses to ANY author rule that sets
 * display — so the moment the sheet got `display:flex` and the veil got
 * `display:grid`, both stopped honouring their own hidden attribute and
 * rendered over the whole site on every page load. Caught on screen.
 */
.psg-search[hidden]{display:none}

/* ── The field row ─────────────────────────────────────────────────────── */
/* flex-shrink:0 so a long result list cannot squeeze the field. */
.psg-search__form{position:relative;flex:0 0 auto;height:clamp(70px,5.73vw,110px);border-bottom:1px solid var(--psg-line)}
/*
 * The query IS the headline — borderless, centred, and big. Its side padding is
 * what keeps a long query clear of the close button rather than a narrower box.
 */
.psg-search__field{width:100%;height:100%;padding-inline:clamp(.9375rem,5.2vw,100px);border:0;background:none;font:inherit;font-size:clamp(1.25rem,.5rem + 2.2vw,3rem);font-weight:600;line-height:1.1;color:var(--psg-ink);text-align:center;-webkit-appearance:none;appearance:none}
.psg-search__field::placeholder{color:var(--psg-copy);font-weight:600}
/* No outline: the field fills the row, so a ring would draw a box around the
   whole header. Focus is unmistakable — the sheet only opens focused. */
.psg-search__field:focus{outline:none}
.psg-search__field::-webkit-search-cancel-button{display:none}
.psg-search__close{position:absolute;top:50%;right:0;transform:translateY(-50%);display:inline-grid;place-items:center;width:clamp(44px,5.2vw,100px);height:clamp(44px,5.2vw,100px);padding:0;border:0;background:none;color:var(--psg-ink);cursor:pointer;transition:color .2s var(--psg-ease)}
.psg-search__close:hover{color:var(--psg-forest)}

/* ── The body: scrolls, and carries the loading veil ───────────────────── */
/* min-height:0 is what actually lets the results scroll instead of growing the
   flex column past the sheet — a flex item's default min-height is auto. */
.psg-search__body{position:relative;flex:1 1 auto;min-height:0;display:flex}
.psg-search__results{flex:1 1 auto;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:20px clamp(0px,2.6vw,40px) 0}
.psg-search__hint{margin:0;padding-block:var(--psg-space-3);text-align:center;color:var(--psg-copy);font-size:clamp(.9375rem,.85rem + .2vw,1.0625rem)}

/* ── Results ───────────────────────────────────────────────────────────── */
/*
 * auto-fill at 200px, so the row holds as many cards as the sheet can take —
 * nine on a 1920 desktop, matching the reference. The gutter is the cards' own
 * 10px padding rather than a grid gap, which is what makes the images sit 20px
 * apart while the cards themselves tile edge to edge.
 */
.psg-search__list{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:0}
.psg-search-result{display:flex;flex-direction:column;gap:.5rem;padding:10px;border-radius:var(--psg-radius);color:inherit;text-decoration:none;transition:background-color .15s var(--psg-ease)}
.psg-search-result:hover,.psg-search-result:focus-visible{background:var(--psg-sand);color:inherit}
/* 6/7 ≈ the reference's own 180×206 image box, so a row of cards keeps one
   baseline whatever shape the photograph is. */
.psg-search-result__media{display:block;aspect-ratio:6/7;border-radius:var(--psg-radius-sm);overflow:hidden;background:var(--psg-sand)}
.psg-search-result__media img{display:block;width:100%;height:100%;object-fit:cover}
.psg-search-result__body{display:flex;flex-direction:column;gap:.15rem;min-width:0}
/* Deliberately NOT line-clamped: the reference lets titles run to three lines,
   and a truncated plant name ("Kunstig Fikus 150…") is worth less than an even
   row of cards. */
.psg-search-result__title{font-size:.9375rem;font-weight:500;line-height:1.4;color:var(--psg-ink)}
/* The matched words, marked the way the eye already reads a search hit —
   underlined and heavier, not highlighter-yellow. */
.psg-search-result__title mark{background:none;color:inherit;font-weight:700;text-decoration:underline;text-underline-offset:.16em}
.psg-search-result__price{font-size:1rem;font-weight:600;color:var(--psg-ink)}
.psg-search-result__price ins{text-decoration:none;background:none}
.psg-search-result__price del{font-weight:500;color:var(--psg-copy);margin-inline-end:.35em}

/* Full-bleed row across the foot of the results, as on the reference. */
.psg-search__all{display:block;margin-top:20px;padding:1rem 20px;border-top:1px solid var(--psg-line);text-align:center;font-size:clamp(.9375rem,.85rem + .2vw,1.0625rem);font-weight:600;text-transform:uppercase;color:var(--psg-ink);text-decoration:none}
.psg-search__all:hover{color:var(--psg-forest)}

/* The guest price notice, shown only once there is something on screen it could
   be about — data-state is set by search.js as each response lands. */
.psg-search__note{display:none;flex:0 0 auto;margin:0;padding:var(--psg-space-3);text-align:center;font-size:.875rem;border-top:1px solid var(--psg-line)}
.psg-search[data-state="results"] .psg-search__note{display:block}

/* ── Loading ───────────────────────────────────────────────────────────── */
.psg-search__loader{position:absolute;inset:0;display:grid;place-items:center;background:rgba(255,255,255,.8)}
.psg-search__loader[hidden]{display:none}
.psg-search__spinner{width:28px;height:28px;border:2px solid var(--psg-line);border-top-color:var(--psg-forest);border-radius:50%;animation:psg-search-spin .7s linear infinite}
@keyframes psg-search-spin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){.psg-search__spinner{animation-duration:2.4s}}

@media(max-width:768px){
	/* Two per row, edge to edge — the reference's own mobile grid. */
	.psg-search__list{grid-template-columns:repeat(2,minmax(0,1fr))}
	.psg-search-result__title{font-size:.875rem}
	.psg-search-result__price{font-size:.875rem}
	/*
	 * The reference hides its close button entirely on mobile. Kept here, at
	 * touch size: with no Escape key on a phone, the header icon would be the
	 * only way out of a sheet that covers the whole screen, and it is not
	 * obvious that tapping it again closes.
	 */
	.psg-search__close{width:44px;height:44px;right:4px}
	.psg-search__close .psg-icon{width:22px;height:22px}
}

/*
 * ---------------------------------------------------------------------------
 * Scroll lock for every mobile overlay — hamburger nav, cart drawer, search.
 *
 * The whole mechanism now lives on the body, which is where it belongs: see the
 * `overflow-x:clip` note in base.css for why these rules could not work before
 * and can now. Locking the ROOT was the previous attempt; it stopped the page
 * scrolling but changed which element was the scroller mid-interaction, and the
 * search sheet — which is positioned against the sticky header's measured
 * bottom edge — was left pinned to where the header had been.
 *
 * Four classes because the three overlays name their state differently, and the
 * cart drawer is the odd one: it opens "persistent" (interactions.js), so it
 * never adds psg-drawer-open at all — psg-cart-scale-open is its real signal.
 *
 * Below 1025px only. Above it the site already scrolls inside
 * #psg-site-scale-wrap with the body locked anyway, and the cart drawer is
 * meant to push the page aside and leave it usable.
 * ---------------------------------------------------------------------------
 */
@media(max-width:1024px){
	body.psg-nav-open,
	body.psg-drawer-open,
	body.psg-search-open,
	body.psg-cart-scale-open{overflow:hidden}
}

