/* Minimal, Mud-free base style for merchants on the "plain" layout.
   A starting point — each such merchant can override/replace this. */
:root {
    color-scheme: light dark;      /* UA canvas + form controls may be either */
    --pp-fg: #1a1a1a;
    --pp-bg: #ffffff;
    --pp-accent: #0b6b53;
    --pp-muted: #6b7280;
    --pp-surface: #ffffff;         /* cards, inputs, raised pills */
    --pp-surface-2: #f3f4f6;       /* subtle fills: tag chips, image placeholders */
    --pp-border: #e5e7eb;          /* hairline borders */
    --pp-border-strong: #d1d5db;   /* form-control borders */
    --pp-maxw: 900px;
}

/* Dark palette — two entry points, same values:
   1) AUTO: follow the OS via prefers-color-scheme, UNLESS the visitor pinned "light".
   2) EXPLICIT: the toggle wrote data-theme="dark" on <html>.
   The toggle's tiny head-script (PortalPlainLayout) sets/clears data-theme;
   leaving it unset = "auto", handled by the media query. A merchant can override any
   of these --pp-* variables in its own stylesheet to rebrand either theme. */
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) {
        --pp-fg: #e7e7ea;
        --pp-bg: #121316;
        --pp-accent: #2bb28c;
        --pp-muted: #9aa0aa;
        --pp-surface: #1b1d21;
        --pp-surface-2: #24262c;
        --pp-border: #2c2f35;
        --pp-border-strong: #3b3f47;
    }
}
:root[data-theme="dark"] {
    color-scheme: dark;
    --pp-fg: #e7e7ea;
    --pp-bg: #121316;
    --pp-accent: #2bb28c;
    --pp-muted: #9aa0aa;
    --pp-surface: #1b1d21;
    --pp-surface-2: #24262c;
    --pp-border: #2c2f35;
    --pp-border-strong: #3b3f47;
}
:root[data-theme="light"] { color-scheme: light; }

/* Theme the root canvas too (not just .pp-site) so overscroll / any gap matches. */
html, body {
    background: var(--pp-bg);
    color: var(--pp-fg);
}

/* --- Site width (Merchant.PlainSiteWidth) ---
   One variable governs it, because .pp-inner is the only thing that constrains width. The
   attribute sits on .pp-site rather than :root so a merchant fragment can still override
   --pp-maxw for itself, and because custom properties inherit — everything inside picks it up
   without knowing this setting exists.

   Nothing here affects a phone: .pp-inner is width:100% with padding, and a max-width larger
   than the viewport does not bind. Every setting is full width on a small screen. */
.pp-site[data-pp-width="narrow"]    { --pp-maxw: 720px; }
.pp-site[data-pp-width="wide"]      { --pp-maxw: 1200px; }
.pp-site[data-pp-width="extrawide"] { --pp-maxw: 1440px; }
.pp-site[data-pp-width="full"]      { --pp-maxw: none; }

.pp-site {
    font-family: "Inter", system-ui, -apple-system, sans-serif;
    color: var(--pp-fg);
    background: var(--pp-bg);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    line-height: 1.6;
}

.pp-inner {
    max-width: var(--pp-maxw);
    margin: 0 auto;
    padding: 0 1.25rem;
    width: 100%;
    box-sizing: border-box;
}

.pp-header {
    border-bottom: 2px solid var(--pp-accent);
    padding: 0.9rem 0 0;
    font-weight: 700;
    font-size: 1.35rem;
    letter-spacing: 0.01em;
    /* Anchors the menu's flyouts. Global, not inside a media query -- scoping it to phones
       once put a mega panel 900px down the page on a desktop. */
    position: relative;
}

/* Top row: who this is, and the things a visitor reaches for. */
.pp-header-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}
.pp-header-tools {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex-wrap: wrap;
    font-weight: 400;
    font-size: 1rem;
}
.pp-logo {
    display: block;
    /* Size comes from Merchant.LogoSettings, applied inline -- these are only the bounds that
       keep a badly configured logo from breaking the header on a phone. */
    max-width: min(100%, 15rem);
    height: auto;
    /* A logo drawn for a light background disappears on a dark one. Nothing here can know
       whether this one has a transparent background, so it gets a light plate in dark mode
       rather than being left to vanish. */
}
/* The plate is the fallback for a merchant with ONE logo. A merchant that has supplied a
   dark-background logo (Merchant.LogoDarkBlobId) gets the real thing instead, so the plate must
   not follow it -- hence :not(.pp-logo-dark). */
:root[data-theme="dark"] .pp-logo:not(.pp-logo-dark),
:root:not([data-theme="light"]) .pp-logo:not(.pp-logo-dark) {
    background: #fff;
    border-radius: 6px;
    padding: 0.2rem 0.4rem;
}
/* Carries the same :not(.pp-logo-dark) as the rule above ON PURPOSE. Without it this reset is
   one class less specific and stops winning, and the plate's padding survives into light mode as
   6px of space nothing asked for. Match the selector when you change either one. */
@media (prefers-color-scheme: light) {
    :root:not([data-theme="dark"]) .pp-logo:not(.pp-logo-dark) { background: none; padding: 0; }
}

/* Which of the two logos is showing. Both theme signals have to be covered, and in this order:
     - light (default, and whenever the visitor pinned light) -> the light logo
     - OS dark, unless pinned light                           -> the dark logo
     - pinned dark, whatever the OS says                      -> the dark logo
   The media query excludes [data-theme="light"] itself, so pinning light needs no rule of its
   own; the explicit-dark rules come after it so they win when the OS is light and the visitor
   pinned dark. Those are all four combinations. */
.pp-logo-dark { display: none; }
@media (prefers-color-scheme: dark) {
    :root:not([data-theme="light"]) .pp-logo-light { display: none; }
    :root:not([data-theme="light"]) .pp-logo-dark { display: block; }
}
:root[data-theme="dark"] .pp-logo-light { display: none; }
:root[data-theme="dark"] .pp-logo-dark { display: block; }

/* Bottom row: the menu, with the whole width to itself. */
.pp-header-menu {
    padding-top: 0.6rem;
    padding-bottom: 0.2rem;
    font-weight: 400;
    font-size: 1rem;
}

.pp-main {
    flex: 1 0 auto;
    padding: 2rem 0;
}

.pp-footer {
    border-top: 1px solid var(--pp-border);
    padding: 1.25rem 0;
    color: var(--pp-muted);
    font-size: 0.9rem;
}

/* Columns side by side where there is room, stacked on a phone. auto-fit with a minimum
   rather than a fixed count, so a merchant with only a contact block gets one column filling
   the width instead of an empty gap where the second would have been. */
.pp-footer-inner {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr));
    gap: 1.5rem 2.5rem;
    align-items: start;
}
.pp-footer-col h2 {
    margin: 0 0 0.5rem;
    font-size: 0.95rem;
    font-weight: 700;
    /* The footer text is muted; its headings are not, or the whole block reads as one grey
       smear with no structure. */
    color: var(--pp-fg);
}
.pp-footer-col p {
    margin: 0 0 0.3rem;
    line-height: 1.5;
}
.pp-footer-name { font-weight: 600; }
.pp-footer-col a {
    color: inherit;
    /* Underlined rather than coloured: the accent is the page's "look here", and a footer is
       not where the eye should be pulled. Still unmistakably a link. */
    text-decoration: underline;
    text-underline-offset: 2px;
}
.pp-footer-col a:hover { color: var(--pp-accent); }

/* The copyright line, set apart from the columns above it. */
.pp-footer-legal {
    margin-top: 1.25rem;
    padding-top: 0.9rem;
    border-top: 1px solid var(--pp-border);
    font-size: 0.85rem;
}

/* Blazor error banner: hidden by default; Blazor sets inline display:block when an
   unhandled error or circuit drop occurs. The Mud layouts carry this rule in their
   scoped .razor.css; the plain layout has no scoped css, so it lives here. */
#blazor-error-ui {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    z-index: 1000;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    background: lightyellow;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

/* --- Gallery (gallery-list component) --- */
.pp-gallery {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 1.25rem;
    margin: 1.5rem 0;
}
.pp-card {
    position: relative;
    display: flex;
    flex-direction: column;
    text-decoration: none;
    color: inherit;
    border: 1px solid var(--pp-border);
    border-radius: 12px;
    overflow: hidden;
    background: var(--pp-surface);
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.pp-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 10px 24px rgba(0, 0, 0, 0.10);
    border-color: var(--pp-accent);
}
.pp-card-img {
    width: 100%;
    aspect-ratio: 3 / 2;
    object-fit: cover;
    display: block;
    background: var(--pp-surface-2);
}
.pp-card-title {
    padding: 0.7rem 0.9rem;
    font-weight: 600;
}

/* Fallback for cards without an image: fills the same 3:2 slot. */
.pp-card-noimg {
    aspect-ratio: 3 / 2;
    width: 100%;
    display: flex;
    box-sizing: border-box;
    overflow: hidden;
}
/* a text excerpt of the article, clamped to a few lines */
.pp-card-noimg-text {
    align-items: flex-start;
    padding: 0.85rem 1rem;
    background: var(--pp-surface-2);
    color: var(--pp-muted);
    font-size: 0.9rem;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 5;
    line-clamp: 5;
}
/* the attachment is not a picture: say what it is rather than showing a broken image */
.pp-card-noimg-file {
    align-items: center;
    justify-content: center;
    background: var(--pp-surface-2);
    color: var(--pp-muted);
}
.pp-card-noimg-file .pp-icon { width: 2.6rem; height: 2.6rem; }

/* no text either: a coloured poster with the title's initial */
.pp-card-noimg-poster {
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, var(--pp-accent), #0a4a3a);
}
.pp-card-noimg-poster span {
    font-size: 2.6rem;
    font-weight: 700;
    color: #fff;
    opacity: 0.95;
}
/* no image and no file attachment: a small, honest "image missing" note */
.pp-card-noimg-missing {
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 0.5rem;
    background: var(--pp-surface-2);
    color: var(--pp-muted);
    font-size: 0.85rem;
}

/* --- Shop carousel (PlainCarousel) --- one slide per article, each a link.
   Reuses the article gallery's strip and its script; only the slide content differs.
   Height comes from the CMS (--pp-carousel-h), which is why these slides are sized rather
   than shaped by an aspect ratio. */
.pp-carousel { margin: 0 0 1.5rem; }
.pp-carousel-slide {
    position: relative;
    display: block;
    height: var(--pp-carousel-h, 200px);
    text-decoration: none;
    color: inherit;
    /* Transparent, not the surface fill. Slides are `contain`, so a picture whose shape does
       not match the band leaves fields down the sides — and a grey plate behind them draws a
       hard rectangle around every slide instead of letting the picture sit on the page. The
       "no picture" stand-in keeps its own fill, since there it is the whole slide. */
    background: transparent;
    overflow: hidden;
}
.pp-carousel-slide img {
    display: block;
    width: 100%;
    height: 100%;
    /* contain, so a picture in a fixed-height band is never cropped — the same choice the
       article gallery landed on. */
    object-fit: contain;
}
.pp-carousel-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100%;
    /* The one place the fill still belongs: with no picture there is nothing else to look at,
       and an unfilled slide would be an invisible one. */
    background: var(--pp-surface-2);
    padding: 0 1rem;
    text-align: center;
    font-weight: 600;
    color: var(--pp-muted);
}
.pp-carousel .pp-badges {
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 1;
}

/* --- Feed (PlainFeed) + price (PlainPrice) --- */
.pp-feed { margin: 0 0 2rem; }
.pp-feed-title {
    font-size: 1.35rem;
    margin: 0 0 0.75rem;
}

/* Sits inside the card's <a>, under the title. */
.pp-price {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    padding: 0 0.9rem 0.8rem;
    font-size: 0.95rem;
}
.pp-price-now { font-weight: 700; }
/* A discounted price is called out by colour; the accent is already the theme's
   "look here", and it follows light/dark without a second set of values. */
.pp-price-now.is-discounted { color: var(--pp-accent); }
.pp-price-off {
    font-size: 0.75rem;
    font-weight: 700;
    padding: 0.1rem 0.4rem;
    border-radius: 999px;
    background: var(--pp-accent);
    color: #fff;
}
.pp-price-before {
    font-size: 0.8rem;
    color: var(--pp-muted);
}
/* Deliberately tiny and muted: it qualifies the number, it does not compete with it. Normal
   weight, because .pp-price-now is bold and the marker inherits that otherwise. */
.pp-price-exvat {
    margin-left: 0.25rem;
    font-size: 0.7rem;
    font-weight: 400;
    color: var(--pp-muted);
    white-space: nowrap;
}

/* Stock. Written in by plain-inventory.js after the page has rendered.

   The element ships EMPTY and this reserves its line, so the card is already the right
   height before the number arrives. Letting it size itself measured a 33px jump per card
   and 299px down the page when the fetch landed -- every card in the grid moving at once,
   on every page view. min-height, not a fixed height, so a translated word that wraps is
   not clipped.

   No new palette entries: in stock borrows the accent (already the theme's green, and
   already dark-mode aware), out of stock the muted grey. Colour is not the only signal --
   the words differ too -- so this still reads correctly to anyone who cannot tell the two
   colours apart. */
.pp-stock {
    display: block;
    /* line-height and min-height are the SAME value on purpose: an empty span's reserved
       line must be exactly the line box the text will occupy. Leaving line-height at normal
       reserved 1.2em against a filled 1.6em and left a 5px jump per card. */
    line-height: 1.6;
    min-height: 1.6em;
    padding: 0 0.9rem 0.8rem;
    font-size: 0.8rem;
    font-weight: 600;
}
.pp-stock-in { color: var(--pp-accent); }
.pp-stock-out { color: var(--pp-muted); }

/* On the detail view it stands on its own line above the button, away from the card's
   padding, and sized to sit beside the large price. */
.pp-stock-lg {
    display: block;
    padding: 0 0 0.6rem;
    font-size: 0.95rem;
}



/* The ex/inc VAT switch. A link, so it reads as one — the header has no room for a
   control that needs a label beside it. */
.pp-vat {
    font-size: 0.85rem;
    color: var(--pp-muted);
    text-decoration: none;
    border-bottom: 1px dotted currentColor;
    white-space: nowrap;
}
.pp-vat:hover { color: var(--pp-fg); border-bottom-color: var(--pp-accent); }

/* On the article page the price stands alone rather than inside a card, so it needs no
   card padding and can carry its own weight. */
.pp-price-lg {
    padding: 0 0 1rem;
    font-size: 1.3rem;
}
.pp-price-lg .pp-price-before { font-size: 0.9rem; }

/* --- Cart: indicator (PlainCartIcon) and add button (PlainAddToCart) --- */
.pp-cart {
    position: relative;
    display: inline-flex;
    align-items: center;
    color: inherit;
    text-decoration: none;
}
.pp-cart .pp-icon { width: 1.4rem; height: 1.4rem; }
/* The count rides on the icon rather than beside it, so the header does not reflow when a
   visitor's first item lands in an empty cart. */
.pp-cart-count {
    position: absolute;
    top: -0.35rem;
    right: -0.5rem;
    min-width: 1.05rem;
    padding: 0 0.25rem;
    border-radius: 999px;
    background: var(--pp-accent);
    color: #fff;
    font-size: 0.7rem;
    font-weight: 700;
    line-height: 1.05rem;
    text-align: center;
}
/* hidden is an attribute, and a flex/inline-flex parent would otherwise override it. */
.pp-cart-count[hidden] { display: none; }

.pp-addcart { margin: 0 0 1.25rem; }
.pp-addcart-btn {
    padding: 0.6rem 1.4rem;
    border: 0;
    border-radius: 8px;
    background: var(--pp-accent);
    color: #fff;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.pp-addcart-btn:hover { filter: brightness(1.08); }
.pp-addcart-btn:disabled { opacity: 0.6; cursor: default; }

/* --- Cart page (PlainCartPage) --- */
.pp-cart-table {
    width: 100%;
    border-collapse: collapse;
    margin: 1rem 0 1.5rem;
}
.pp-cart-table th {
    text-align: left;
    font-size: 0.85rem;
    color: var(--pp-muted);
    font-weight: 600;
    padding: 0.4rem 0.5rem;
    border-bottom: 1px solid var(--pp-border);
}
.pp-cart-table td {
    padding: 0.7rem 0.5rem;
    border-bottom: 1px solid var(--pp-border);
    vertical-align: middle;
}
.pp-cart-table tfoot td {
    font-weight: 700;
    border-bottom: 0;
}
.pp-cart-thumb { width: 64px; }
.pp-cart-thumb img {
    width: 56px;
    height: 56px;
    object-fit: contain;
    background: var(--pp-surface-2);
    border-radius: 6px;
    display: block;
}
.pp-cart-no { font-size: 0.8rem; }
.pp-cart-price { text-align: right; white-space: nowrap; }
.pp-cart-remove button {
    border: 0;
    background: none;
    color: var(--pp-muted);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.3rem;
}
.pp-cart-remove button:hover { color: var(--pp-accent); }

/* The quantity stepper: −  n  +. Each button is its own one-button form, so they are laid
   out as a row here rather than inheriting anything from the forms. */
.pp-cart-qty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    white-space: nowrap;
}
.pp-cart-qty form { display: inline-flex; margin: 0; }
.pp-cart-qty button {
    /* A real box rather than bare text: these are hit targets, and on a phone a bare − is
       almost impossible to land on. 2rem is about the smallest that still works with a thumb. */
    width: 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--pp-border-strong);
    border-radius: 6px;
    background: var(--pp-surface);
    color: inherit;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.pp-cart-qty button:hover:not(:disabled) {
    border-color: var(--pp-accent);
    color: var(--pp-accent);
}
.pp-cart-qty button:disabled {
    /* Visibly unavailable rather than merely inert: the minus is disabled at one, and without
       this it looks clickable and simply does nothing. */
    opacity: 0.4;
    cursor: default;
}
/* Fixed width so the row does not shift sideways when 9 becomes 10. */
.pp-cart-qty-n {
    min-width: 1.75rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
.pp-cart-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 1rem;
}
.pp-cart-actions .pp-addcart-btn { text-decoration: none; display: inline-block; }

/* Visible to a screen reader, not on screen -- for a column whose header would otherwise be
   an empty cell. */
.pp-sr {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* A table of lines does not fit a phone. Each row becomes a block, and the headers are
   dropped rather than repeated as labels -- title, quantity and price read fine stacked. */
@media (max-width: 640px) {
    .pp-cart-table thead { display: none; }
    .pp-cart-table, .pp-cart-table tbody, .pp-cart-table tfoot,
    .pp-cart-table tr, .pp-cart-table td { display: block; width: 100%; }
    .pp-cart-table tr {
        display: grid;
        grid-template-columns: 64px 1fr auto;
        align-items: center;
        gap: 0.25rem 0.6rem;
        padding: 0.5rem 0;
        border-bottom: 1px solid var(--pp-border);
    }
    .pp-cart-table td { border-bottom: 0; padding: 0.15rem 0; }
    .pp-cart-price { text-align: left; }
}

/* --- Badges (PlainBadges) --- inline by default; overlaid on a gallery card */
.pp-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}
/* On a gallery card: overlay top-left on the image */
.pp-card .pp-badges {
    position: absolute;
    top: 0.5rem;
    left: 0.5rem;
    z-index: 1;
    max-width: calc(100% - 1rem);
}
/* On the article detail page: sit under the title with some breathing room */
.pp-article > .pp-badges {
    margin: 0 0 1.25rem;
}

/* --- Item groups (PlainItemGroups) --- the groups an article belongs to, as tag links
   at the foot of the detail page (Mud-free equivalent of ShowItemGroups' chips) */
.pp-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin: 1.75rem 0 0;
}
.pp-tag {
    display: inline-block;
    padding: 0.2rem 0.7rem;
    border-radius: 999px;
    font-size: 0.8rem;
    text-decoration: none;
    color: var(--pp-muted);
    background: var(--pp-surface-2);
    border: 1px solid var(--pp-border);
    transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}
.pp-tag:hover {
    color: var(--pp-fg);
    border-color: var(--pp-accent);
    background: var(--pp-surface);
}
.pp-badge {
    display: inline-block;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.45;
    letter-spacing: 0.02em;
    /* defaults; per-badge --bg/--fg override via inline style */
    background: var(--pp-accent);
    color: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.22);
    white-space: nowrap;
}

/* --- Group intro text (ShowGroupPlain: the group's own Text, above the listing) --- */
.pp-group-text { margin: 0 0 2rem; line-height: 1.6; }
.pp-group-text img { max-width: 100%; height: auto; border-radius: 12px; margin: 1rem 0; }

/* --- Article detail (ShowArticlePlain) --- */
.pp-article { margin: 1rem 0 2.5rem; }

/* Pictures beside the text when there is room. Single column by default and widened at the
   breakpoint, rather than the reverse: the stacked order then falls straight out of the
   markup order, so the narrow case — which is most visits — needs no rules at all. */
.pp-article-cols {
    display: grid;
    gap: 1.5rem 2rem;
}
/* 900px is where .pp-inner stops growing (--pp-maxw), so it is the first width at which
   splitting actually gains anything rather than making both columns cramped. */
/* No pictures, no picture column. Without this the grid keeps a 5fr track for a media
   column that rendered nothing — which happens for any article with no images at all, and
   whenever the CMS sets HideMainImage — and the text sits in the right-hand half of the page
   with a hole beside it. Keyed on .pp-figures rather than on the wrapper being :empty,
   because the wrapper always holds whitespace and comments. */
@media (min-width: 900px) {
    .pp-article-cols:not(:has(.pp-figures)) { grid-template-columns: minmax(0, 1fr); }
}

@media (min-width: 900px) {
    .pp-article-cols {
        /* Proportional, not a fixed 380px. A fixed media column meant that widening the site
           gave every extra pixel to the text and none to the pictures — the carousel stayed
           exactly as small on a 1440px site as on a 900px one, which is the opposite of why
           anyone widens a site. Slightly under half, so the text keeps the longer line.
           minmax(0, …) on both so a wide picture cannot push the grid past its track — the
           usual way a grid overflows its container. */
        grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
        align-items: start;
    }
}
/* The gallery already carries the bottom margin that separates it from the body text when
   stacked; side by side that margin is just a gap under the picture. */
@media (min-width: 900px) {
    .pp-article-media .pp-figures { margin-bottom: 0; }
}
/* Stacked, the title comes after the picture and needs air above it. Side by side it is the
   top of its own column and must line up with the picture. */
.pp-article-body > .pp-article-title { margin-top: 1rem; }
@media (min-width: 900px) {
    .pp-article-body > .pp-article-title { margin-top: 0; }
}
.pp-article-title {
    font-size: 1.9rem;
    line-height: 1.2;
    margin: 0 0 1rem;
}
.pp-article .markdown img {
    max-width: 100%;
    height: auto;
    border-radius: 12px;
    margin: 1rem 0;
}
/* --- Article pictures (PlainGallery) ---
   A CSS-only gallery: the strip IS the carousel. scroll-snap makes it settle on one picture
   at a time, which gives swiping on touch and a scrollbar/keyboard on desktop without any
   script, and the thumbnails below are plain anchors to each slide. */
.pp-figures { margin: 0 0 1.5rem; }
/* Holds the strip plus the arrows. The arrows are positioned against THIS rather than the
   strip, because the strip is a scroll container — anything absolutely positioned inside it
   scrolls away with the pictures. */
.pp-figures-viewport { position: relative; }
.pp-figures-strip {
    display: flex;
    gap: 0.75rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    /* Keeps a horizontal flick inside the strip instead of letting it turn into a
       browser back-navigation once the last picture is reached. */
    overscroll-behavior-x: contain;
    border-radius: 12px;
    background: var(--pp-surface-2);
}
.pp-figures-slide {
    /* Basis 100% + shrink 0 makes each picture exactly one "page" of the strip, so a
       snap point always lands on a whole picture rather than between two. */
    flex: 0 0 100%;
    scroll-snap-align: center;
    /* A thumbnail is an anchor, and the browser scrolls EVERY scrollable ancestor to reveal the
       target — the strip horizontally, which is the point, but also the PAGE vertically, which
       yanked the article title off screen on the first click. A vertical scroll margin buys the
       slide enough slack that the page counts as already showing it. Measured on a real article
       (720px viewport, 416px slide), page shift after a thumbnail click:
           0 → 258px    2rem → 226px    10rem → 98px    50vh → 0
       so half a viewport is what actually holds still; smaller values only soften the jump.
       Block only — a horizontal margin would fight the snap alignment. */
    scroll-margin-block: 50vh;
    width: 100%;
    /* Relative to the viewport as well as absolute: on a wide site the slide gets much wider,
       and a fixed 26rem cap would hold the height back so every picture gained nothing but
       letterbox. Still capped, so a tall picture cannot push the text off the screen. */
    max-height: min(60vh, 34rem);
    object-fit: contain;
    /* One picture must not be taller than the next is short — a strip whose height changes
       per slide jumps the page content up and down as you swipe. */
    aspect-ratio: 3 / 2;
    border-radius: 12px;
}
.pp-figures-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    margin-top: 0.6rem;
}
.pp-figures-thumbs a {
    display: block;
    line-height: 0;
    border: 2px solid transparent;
    border-radius: 6px;
    padding: 2px;
}
.pp-figures-thumbs a:hover,
.pp-figures-thumbs a:focus-visible { border-color: var(--pp-accent); }
.pp-figures-thumbs img {
    width: 64px;
    height: 64px;
    object-fit: cover;
    border-radius: 4px;
}

/* One picture needs no strip mechanics. Without this it is forced to the full column width
   and the 3:2 box the snapping layout requires, which UPSCALES a small photo — a 376px-wide
   product shot was being blown up to 860 and looked soft. Let it keep its own size instead,
   centred, with max-width/max-height as the only limits. */
.pp-figures-strip:not(:has(.pp-figures-slide + .pp-figures-slide)) .pp-figures-slide {
    aspect-ratio: auto;
    height: auto;
    flex: 0 1 auto;
    width: auto;
    max-width: 100%;
    margin-inline: auto;
}

@media (prefers-reduced-motion: no-preference) {
    .pp-figures-strip { scroll-behavior: smooth; }
}

/* --- Carousel controls (PlainImageStyle.Carousel, driven by plain-gallery.js) ---
   Everything here is inert without the script: the buttons ship with `hidden`, and the
   script removes it. */
.pp-figures-nav {
    /* Overlaid on the picture, not beside it. Beside was tried: it cost the strip about 90px
       of the 380px media column, and a quarter narrower picture is a steeper price than an
       arrow resting on the letterbox field. Positioned against .pp-figures-viewport rather
       than the strip, because the strip is a scroll container — anything absolute inside it
       scrolls away with the pictures. */
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    border: 1px solid var(--pp-border);
    border-radius: 999px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    cursor: pointer;
    /* Hidden until the pointer is on the picture. The arrows sit ON the image, so out of the
       way is where they belong when nobody is reaching for them.
       Opacity rather than display, so revealing them costs no layout and cannot shift the
       picture; pointer-events follows the opacity so an invisible button is not clickable. */
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s ease, border-color 0.15s ease;
}
.pp-figures-viewport:hover .pp-figures-nav,
/* Keyboard users get no hover. Without this, tabbing to the arrow would move focus to
   something invisible — the classic way a focus ring ends up nowhere. */
.pp-figures-nav:focus-visible {
    opacity: 0.92;
    pointer-events: auto;
}
/* Disabled, never hidden: an arrow that vanishes at the ends shifts the picture sideways
   every time you reach one. It still fades in with the rest, just dimmer. */
.pp-figures-viewport:hover .pp-figures-nav:disabled,
.pp-figures-nav:focus-visible:disabled { opacity: 0.3; }
.pp-figures-nav:disabled { cursor: default; }
.pp-figures-nav:hover:not(:disabled) { border-color: var(--pp-accent); opacity: 1; }

/* A fade is still motion. Someone who asked for less of it gets the arrows immediately. */
@media (prefers-reduced-motion: reduce) {
    .pp-figures-nav { transition: none; }
}
.pp-figures-nav .pp-icon { width: 1.5rem; height: 1.5rem; }

.pp-figures-prev { left: 0.6rem; }
.pp-figures-next { right: 0.6rem; }

.pp-figures-dots {
    display: flex;
    justify-content: center;
    gap: 0.4rem;
    margin-top: 0.6rem;
}
.pp-figures-dot {
    width: 0.55rem;
    height: 0.55rem;
    padding: 0;
    border: 0;
    border-radius: 999px;
    background: var(--pp-border-strong);
    cursor: pointer;
    transition: background 0.15s ease, transform 0.15s ease;
}
.pp-figures-dot.is-current { background: var(--pp-accent); transform: scale(1.25); }

/* The current thumbnail. Only the script can know which one that is, so an unenhanced page
   simply has no highlight rather than a wrong one. */
.pp-figures-thumbs a.is-current { border-color: var(--pp-accent); }

/* A pointer can reach the arrows; a touch screen cannot hover and has swiping instead, so
   the arrows would just cover the picture. Dots and thumbnails still show position. */
@media (hover: none) {
    .pp-figures-nav { display: none; }
}

/* Carousel-only presentation. Scoped to [data-pp-carousel] on purpose — the plain Strip has
   no arrows, so its scrollbar is the only thing telling a mouse user there is more to see,
   and the gap between slides is what shows the next picture peeking in. Take those away only
   where the arrows and dots have replaced them. */
[data-pp-carousel] .pp-figures-strip {
    /* The reserved scrollbar track was 15px of empty grey under every picture. */
    scrollbar-width: none;          /* Firefox */
    -ms-overflow-style: none;       /* old Edge */
    /* Edge-to-edge slides: a gap here shows as a seam sliding past mid-animation. */
    gap: 0;
}
[data-pp-carousel] .pp-figures-strip::-webkit-scrollbar { display: none; }

/* `contain`, NOT `cover` — the whole picture, always. A frame this size will letterbox
   anything whose shape does not match it, and those blank fields down the sides are the
   accepted cost: cover would fill them by cutting the top and bottom off, and on a product
   photo what it cuts is not knowable in advance.

   This was briefly `cover`, to stop the arrows floating on a grey margin beside the picture
   rather than on the picture itself. Showing the whole image won that trade. The arrows do
   sit on the blank fields again for anything narrower than the frame — that is the visible
   consequence, not an oversight.

   The frame stays taller than the Strip's 3:2 so upright pictures keep more of the height
   they need before letterboxing starts. */
/* img-slides only: the shop carousel's slides are anchors with their own height, and
   object-fit/aspect-ratio here would fight it. */
[data-pp-carousel] img.pp-figures-slide {
    object-fit: contain;
    aspect-ratio: 4 / 3;
}

/* A lone picture has nothing to scroll to, so it needs no fixed frame at all — let it size
   to itself rather than sit in blank fields it never had to earn. */
[data-pp-carousel] .pp-figures-strip:not(:has(.pp-figures-slide + .pp-figures-slide)) img.pp-figures-slide {
    aspect-ratio: auto;
}

.pp-muted { color: var(--pp-muted); }
.pp-notfound {
    color: var(--pp-muted);
    padding: 2rem 0;
    text-align: center;
}

/* --- Top menu (PlainMenu) --- */
.pp-header .pp-inner {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    flex-wrap: wrap;
}
.pp-brand {
    font-weight: 700;
    font-size: 1.35rem;
    text-decoration: none;
    color: inherit;
}
.pp-nav {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1.1rem;
    margin-left: auto;
    font-size: 0.98rem;
}
.pp-nav-link {
    text-decoration: none;
    color: var(--pp-muted);
    padding: 0.2rem 0;
    border-bottom: 2px solid transparent;
    transition: color 0.15s ease, border-color 0.15s ease;
}
.pp-nav-link:hover {
    color: var(--pp-fg);
    border-bottom-color: var(--pp-accent);
}

/* --- Contact FAB (ContactFab) ---
   Pure-CSS pop-out: a hidden checkbox toggles the actions and the main-button icon.
   No JS/circuit needed, so it works on the static SSR plain path. */
.pp-fab {
    position: fixed;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 900;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.75rem;
}
.pp-fab-cb {
    /* hidden control that drives the open/closed state */
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
    pointer-events: none;
}
.pp-fab-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: 0.6rem;
    /* collapsed by default */
    opacity: 0;
    transform: translateY(0.75rem) scale(0.96);
    transform-origin: bottom right;
    pointer-events: none;
    transition: opacity 0.2s ease, transform 0.2s ease;
}
.pp-fab-cb:checked ~ .pp-fab-actions {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}
.pp-fab-action {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    text-decoration: none;
    color: inherit;
    background: transparent;
    border: 0;
    cursor: pointer;
}
.pp-fab-action-label {
    background: var(--pp-surface);
    color: var(--pp-fg);
    font-size: 0.88rem;
    font-weight: 600;
    padding: 0.3rem 0.7rem;
    border-radius: 999px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.14);
    white-space: nowrap;
}
.pp-fab-action-ic {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.9rem;
    height: 2.9rem;
    border-radius: 50%;
    background: var(--fab-c, var(--pp-accent));
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.22);
    font-size: 1.3rem;
    line-height: 1;
    flex: none;
    transition: transform 0.15s ease;
}
.pp-fab-action:hover .pp-fab-action-ic { transform: scale(1.08); }

.pp-fab-main {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.6rem;
    height: 3.6rem;
    border-radius: 50%;
    background: var(--pp-accent);
    color: #ffffff;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
    cursor: pointer;
    font-size: 1.5rem;
    line-height: 1;
    user-select: none;
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.pp-fab-main:hover { box-shadow: 0 6px 18px rgba(0, 0, 0, 0.34); }
.pp-fab-cb:checked ~ .pp-fab-main { transform: rotate(90deg); }
.pp-fab-cb:focus-visible ~ .pp-fab-main { outline: 3px solid var(--pp-accent); outline-offset: 3px; }
/* swap the icon glyph on open */
.pp-fab-ic-close { display: none; }
.pp-fab-cb:checked ~ .pp-fab-main .pp-fab-ic-open { display: none; }
.pp-fab-cb:checked ~ .pp-fab-main .pp-fab-ic-close { display: inline; }

@media (max-width: 640px) {
    .pp-fab { right: 1rem; bottom: 1rem; }
    .pp-fab-action-ic { width: 2.7rem; height: 2.7rem; }
}

/* --- Search box (PlainSearchBox) + results (SearchPlain) ---
   A plain GET form (no circuit) that posts ?q= to /search. Sits in the header
   next to the menu, and is reused (prefilled) on the results page. */
.pp-search {
    display: flex;
    align-items: stretch;
    flex: 0 1 260px;
    min-width: 0;
}
.pp-search-input {
    font: inherit;
    font-weight: 400;
    color: var(--pp-fg);
    background: var(--pp-surface);
    border: 1px solid var(--pp-border-strong);
    border-right: 0;
    border-radius: 8px 0 0 8px;
    padding: 0.4rem 0.7rem;
    min-width: 0;
    width: 100%;
}
.pp-search-input:focus {
    outline: none;
    border-color: var(--pp-accent);
}
.pp-search-btn {
    font: inherit;
    border: 1px solid var(--pp-accent);
    background: var(--pp-accent);
    color: #fff;
    border-radius: 0 8px 8px 0;
    padding: 0 0.85rem;
    cursor: pointer;
    line-height: 1;
    transition: filter 0.15s ease;
}
.pp-search-btn:hover { filter: brightness(1.08); }

/* results page (SearchPlain) — reuses .pp-gallery/.pp-card from the gallery */
.pp-search-page > .pp-search { max-width: 480px; flex-basis: auto; margin: 0 0 1.5rem; }
.pp-search-section { margin: 0 0 2rem; }
.pp-search-h {
    font-size: 1.15rem;
    font-weight: 700;
    margin: 0 0 0.75rem;
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
}
.pp-search-count { color: var(--pp-muted); font-size: 0.9rem; font-weight: 400; }

/* group hit = a .pp-tag with a hit-count badge */
.pp-tag-hit { display: inline-flex; align-items: center; gap: 0.45rem; }
.pp-tag-n {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.4em;
    padding: 0 0.4em;
    border-radius: 999px;
    background: var(--pp-accent);
    color: #fff;
    font-size: 0.72rem;
    font-weight: 700;
    line-height: 1.5;
}
.pp-tag-hit:hover .pp-tag-n { filter: brightness(1.08); }

/* --- Plain form (raw-HTML forms embedded in article markdown, e.g. booking) ---
   Static SSR: no circuit, so a real <form> just posts. Experimental; unstyled
   form controls otherwise render inline and cramped, so give them a stacked look. */
.pp-form {
    display: flex;
    flex-direction: column;
    gap: 1rem;
    max-width: 480px;
    margin: 1.5rem 0;
}
.pp-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    font-weight: 600;
    font-size: 0.95rem;
}
.pp-field input,
.pp-field textarea {
    font: inherit;
    font-weight: 400;
    color: var(--pp-fg);
    background: var(--pp-surface);
    border: 1px solid var(--pp-border-strong);
    border-radius: 8px;
    padding: 0.55rem 0.7rem;
    box-sizing: border-box;
    width: 100%;
}
.pp-field input:focus,
.pp-field textarea:focus {
    outline: none;
    border-color: var(--pp-accent);
    box-shadow: 0 0 0 3px rgba(11, 107, 83, 0.15);
}
.pp-field textarea { resize: vertical; }
.pp-btn {
    align-self: flex-start;
    font: inherit;
    font-weight: 600;
    color: #fff;
    background: var(--pp-accent);
    border: 0;
    border-radius: 8px;
    padding: 0.6rem 1.4rem;
    cursor: pointer;
    transition: filter 0.15s ease;
}
.pp-btn:hover { filter: brightness(1.08); }

/* --- Theme toggle (ThemeToggle) --- cycles auto → light → dark.
   Static SSR: driven by the head-script's ppCycleTheme() (no circuit). */
.pp-theme-toggle {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.2rem;
    height: 2.2rem;
    border-radius: 999px;
    border: 1px solid var(--pp-border);
    background: var(--pp-surface);
    color: var(--pp-fg);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.15s ease, background 0.15s ease, transform 0.15s ease;
}
.pp-theme-toggle:hover { border-color: var(--pp-accent); transform: translateY(-1px); }
.pp-theme-toggle:focus-visible { outline: 3px solid var(--pp-accent); outline-offset: 2px; }

/* --- Multi-level menu (PlainMenu / PlainMenuNode) --- a branch with children becomes a
   popover, so the generic [data-pp-panel] rules handle showing and hiding it. */
.pp-nav-item { display: inline-flex; }
.pp-nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font: inherit;
    background: none;
    border: 0;
    border-bottom: 2px solid transparent;
    cursor: pointer;
}
.pp-nav-caret {
    width: 0;
    height: 0;
    border-left: 0.28rem solid transparent;
    border-right: 0.28rem solid transparent;
    border-top: 0.32rem solid currentColor;
    transition: transform 0.15s ease;
}
[data-pp-open] > .pp-nav-toggle .pp-nav-caret { transform: rotate(180deg); }
[data-pp-open] > .pp-nav-toggle { color: var(--pp-fg); border-bottom-color: var(--pp-accent); }

.pp-nav-panel { padding: 0.35rem 0; }
.pp-nav-sub {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    width: 100%;
    padding: 0.45rem 0.9rem;
    text-align: left;
    border-bottom: 0;
    white-space: nowrap;
    text-decoration: none;
    color: var(--pp-fg);
}
.pp-nav-sub:hover { background: var(--pp-surface-2); border-bottom: 0; }
/* the link to the branch's own group page, separated from its children */
.pp-nav-self { font-weight: 600; border-bottom: 1px solid var(--pp-border); }

/* A submenu inside a submenu flies out to the side rather than stacking on top. */
[data-pp-panel] [data-pp-panel] { top: 0; left: 100%; }
[data-pp-panel] .pp-nav-item { display: block; }

@media (max-width: 640px) {
    /* No room to fly out on a phone — nest inline instead, which also keeps it thumb-reachable. */
    [data-pp-panel] [data-pp-panel] {
        position: static;
        border: 0;
        box-shadow: none;
        padding-left: 0.75rem;
        min-width: 0;
    }
}

/* --- Live search overlay (scripts/plain-search.js) --- the panel is a popover, so the
   generic [data-pp-panel] rules above handle position, hiding and the phone takeover. */
.pp-search-results {
    width: min(28rem, 92vw);
    padding: 0.4rem 0;
    font-size: 0.95rem;
    /* Scrolling lives here rather than on every panel: this one has nothing flying out of
       it, so a scroll container is safe. overscroll-behavior stops the page behind scrolling
       once the list hits its end. */
    max-height: min(70vh, 32rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.pp-search-sec + .pp-search-sec { border-top: 1px solid var(--pp-border); }
.pp-search-sec-h {
    margin: 0;
    padding: 0.5rem 0.9rem 0.25rem;
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--pp-muted);
}
.pp-search-row {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.45rem 0.9rem;
    text-decoration: none;
    color: inherit;
}
/* :hover for the mouse, .is-active for arrow-key navigation — same look either way */
.pp-search-row:hover,
.pp-search-row.is-active { background: var(--pp-surface-2); }
.pp-search-row-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pp-search-row-img {
    flex: none;
    width: 3rem;
    height: 2rem;
    object-fit: cover;
    border-radius: 4px;
    background: var(--pp-surface-2);
}
/* Stand-in when the article has no picture: centres the file/no-picture glyph and keeps it
   quiet, so a row without an image does not shout louder than one with. */
.pp-search-row-img-none {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--pp-muted);
}
.pp-search-row-img-none svg { width: 55%; height: 55%; opacity: 0.75; }
.pp-search-empty { margin: 0; padding: 0.9rem; color: var(--pp-muted); }
.pp-search-all {
    display: block;
    padding: 0.55rem 0.9rem;
    border-top: 1px solid var(--pp-border);
    font-weight: 600;
    text-decoration: none;
    color: var(--pp-accent);
}
.pp-search-all:hover, .pp-search-all.is-active { background: var(--pp-surface-2); }

/* On a phone the panel is full-screen, so the rows get more room to breathe. */
@media (max-width: 640px) {
    .pp-search-results { width: auto; font-size: 1rem; }
    .pp-search-row { padding: 0.7rem 1rem; }
    .pp-search-row-img { width: 3.5rem; height: 2.4rem; }
}

/* --- Popover primitive (scripts/plain-popover.js) --- shared by the search overlay and
   the menus. The script toggles [data-pp-open] on the wrapper and the hidden attribute on
   the panel; everything visual lives here. */
[data-pp-popover] {
    position: relative;
}
[data-pp-panel] {
    position: absolute;
    top: calc(100% + 0.4rem);
    left: 0;
    z-index: 800;
    min-width: 12rem;
    /* Deliberately NOT scrollable here. A submenu sits at left:100%, outside its parent's
       box, so any overflow value other than visible turns the parent into a scroll container
       that clips the flyout and grows scrollbars. Panels that need scrolling ask for it
       themselves — see .pp-search-results. */
    background: var(--pp-surface);
    color: var(--pp-fg);
    border: 1px solid var(--pp-border);
    border-radius: 10px;
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
}
/* The panel carries the hidden attribute, so it has to beat any display set above. */
[data-pp-panel][hidden] { display: none; }

/* Anchor a panel to the right edge instead (menus near the end of the header). */
[data-pp-panel][data-pp-align="end"] { left: auto; right: 0; }

/* Opt-in: take over the screen on a phone, the way webshop search does. Position fixed
   rather than locking body scroll -- a scroll lock is what makes these feel broken on iOS,
   and overscroll-behavior above already stops scroll chaining. */
@media (max-width: 640px) {
    [data-pp-panel][data-pp-fullscreen] {
        position: fixed;
        inset: 0;
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1000;
        min-width: 0;
        max-height: none;
        width: auto;
        height: 100dvh;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        padding-top: 0.5rem;
    }
}

/* --- Cookie consent (CookieConsentPlain) --- fixed bottom bar, shown only when the
   merchant has GdpUse and the visitor has not chosen yet. Rendered [hidden]; a script
   reveals it, so the attribute selector must beat the display below. */
.pp-consent {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 950;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 0.75rem 1.25rem;
    padding: 0.9rem 1.25rem;
    background: var(--pp-surface);
    color: var(--pp-fg);
    border-top: 1px solid var(--pp-border);
    box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.18);
    font-size: 0.95rem;
}
.pp-consent[hidden] { display: none; }
.pp-consent-text { max-width: 60ch; }
.pp-consent-actions { display: inline-flex; gap: 0.6rem; flex: none; }
.pp-consent-btn {
    font: inherit;
    font-weight: 600;
    border-radius: 8px;
    padding: 0.4rem 1.1rem;
    cursor: pointer;
    border: 1px solid var(--pp-accent);
    transition: filter 0.15s ease, background 0.15s ease;
}
.pp-consent-yes { background: var(--pp-accent); color: #fff; }
.pp-consent-no { background: transparent; color: var(--pp-fg); border-color: var(--pp-border-strong); }
.pp-consent-btn:hover { filter: brightness(1.08); }
.pp-consent-btn:focus-visible { outline: 3px solid var(--pp-accent); outline-offset: 2px; }

/* keep the FAB clear of the consent bar while it is up */
.pp-consent:not([hidden]) ~ .pp-fab { bottom: 5.5rem; }

/* --- Icons (PlainIcon) --- sized in em so they follow the surrounding text, and filled
   with currentColor so they inherit the theme like any other glyph. */
.pp-icon {
    display: inline-block;
    vertical-align: -0.125em;
    flex: none;
}
/* The FAB and search button size their glyph by font-size, so scale the svg to match. */
.pp-fab-action-ic .pp-icon { width: 1.3rem; height: 1.3rem; color: #fff; }
.pp-search-btn .pp-icon { width: 1.1rem; height: 1.1rem; }

/* Close control for the search overlay. Only needed in the phone takeover: there the panel
   covers the input, there is no Escape key, and every tap lands inside the popover so the
   outside-click dismissal cannot fire — without this there is no way back to the page. */
.pp-search-close { display: none; }
@media (max-width: 640px) {
    .pp-search-close {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        position: sticky;
        top: 0;
        margin-left: auto;
        width: 2.6rem;
        height: 2.6rem;
        font-size: 1.2rem;
        line-height: 1;
        border: 0;
        border-radius: 999px;
        background: var(--pp-surface-2);
        color: var(--pp-fg);
        cursor: pointer;
    }
    .pp-search-close:focus-visible { outline: 3px solid var(--pp-accent); outline-offset: 2px; }
    /* keep it on the right without a wrapper */
    .pp-search-results { display: flex; flex-direction: column; align-items: flex-end; }
    .pp-search-results [data-pp-search-list] { width: 100%; }
}

/* ---------------------------------------------------------------------------
   Phone layout for the popovers. Both fixes below exist because a panel that is
   anchored to its trigger works fine on a wide header and falls off a narrow one.
   --------------------------------------------------------------------------- */
@media (max-width: 640px) {
    /* MENU: the nav sits at the right of the header, so a panel anchored to its item
       opened past the right edge of a 375px screen and was simply cut off. Making the
       item unpositioned moves the anchor to the header, so the panel becomes a
       full-width dropdown under it — which is what a phone wants anyway. */
    .pp-nav-item { position: static; }
    .pp-nav-item > [data-pp-panel] {
        left: 1rem;
        right: 1rem;
        width: auto;
        min-width: 0;
        max-height: 70vh;
        overflow-y: auto;
        overscroll-behavior: contain;
    }
    /* A sub-menu nests inline (rule further up), so it must not re-anchor to the header. */
    .pp-nav-item > [data-pp-panel] [data-pp-panel] { left: auto; right: auto; }

    /* SEARCH: the panel used to cover the whole viewport including the field, so the
       visitor could not see what they were typing. Instead of the panel taking over,
       the FORM does: input and button stay on the first line, the results flow beneath.
       No second input to keep in sync — it is the same field, just repositioned. */
    .pp-search[data-pp-open] {
        position: fixed;
        inset: 0;
        z-index: 1000;
        flex-wrap: wrap;
        align-content: flex-start;
        padding: 0.6rem;
        box-sizing: border-box;
        background: var(--pp-bg);
    }
    /* Pinned to the form's own edges rather than sized by arithmetic: the form is fixed, so
       the page cannot scroll to reach anything below the fold, and any guess at the field's
       height eventually leaves the last result off-screen. `bottom` makes the fit exact. */
    .pp-search[data-pp-open] [data-pp-panel][data-pp-fullscreen] {
        position: absolute;
        top: 4.2rem;
        left: 0.6rem;
        right: 0.6rem;
        bottom: 0.6rem;
        width: auto;
        height: auto;
        max-height: none;
        margin: 0;
        border: 0;
        border-radius: 0;
        box-shadow: none;
        overflow-y: auto;
        overscroll-behavior: contain;
    }
}

/* --- Mega menu (PlainMegaMenu) --- one panel spanning the header showing two levels at once.
   The item is unpositioned so the panel anchors to the header instead of the item, which is
   what lets it span the full width regardless of which item opened it. */
/* The header is the anchor for any panel that must span it — the mega panel at every width,
   and the phone dropdown further down. Without this the panel's containing block is the page,
   and `top: 100%` puts it a screenful below where it belongs. */
.pp-header { position: relative; }
.pp-mega-item { position: static; }

/* Scrim: with a mega panel open, everything BELOW the header is dimmed, so the panel reads
   as the foreground rather than as one more band of a busy page. It starts at the header's
   bottom edge (`top: 100%`) rather than at the panel's — the panel sits 0.4rem lower, and
   dimming that sliver too is what makes the line under the menu bar look deliberate instead
   of leaving a bright hairline above the panel.

   Only the mega menu. A small dropdown covers a fraction of the page and dimming the rest
   for it would be theatrical.

   :has() on the header, not a class toggled from JS: the open state already lives in the
   DOM as [data-pp-open], and a second source of truth is a second thing to get out of step.

   A click on the scrim closes the menu with no extra code -- a pseudo-element's clicks are
   dispatched to its host, the header is outside every popover, and plain-popover.js already
   closes everything on an outside click. */
.pp-header:has(.pp-mega-item[data-pp-open])::after {
    content: "";
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: 100vh;
    /* Under the panel (800) and over the page. */
    z-index: 790;
    background: rgba(0, 0, 0, 0.45);
}

/* An absolutely positioned box hanging 100vh below the header still counts towards the
   page's scroll height. Measured on the cart page: opening the menu grew it by 113px, and on
   a page that exactly fills the viewport that is a scrollbar appearing out of nowhere and
   shoving the layout sideways.

   Clipped only WHILE the menu is open, so nothing about an ordinary page changes. The panel
   cannot be clipped by this: it starts just under the header and is capped at min(80vh,
   40rem), which always lands inside .pp-site's min-height of 100vh. The one sticky element
   in the plain layout lives in the search panel, and a popover closes its siblings, so it
   can never be open at the same time as this. */
.pp-site:has(.pp-mega-item[data-pp-open]) { overflow: clip; }
.pp-mega {
    left: 0;
    right: 0;
    width: auto;
    min-width: 0;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
    max-height: min(80vh, 40rem);
    overflow-y: auto;
    overscroll-behavior: contain;
}
.pp-mega-inner {
    /* the centring .pp-inner would have provided, without inheriting the header's flex row */
    max-width: var(--pp-maxw);
    margin: 0 auto;
    width: 100%;
    box-sizing: border-box;
    padding: 1.25rem 1.25rem 1.5rem;
}

/* the branch's own page — easy to bury behind its children, so it goes first */
.pp-mega-all {
    display: inline-block;
    margin: 0 0 1rem;
    font-weight: 600;
    text-decoration: none;
    color: var(--pp-accent);
}
.pp-mega-all:hover { text-decoration: underline; }

/* Columns rather than a fixed count: the tree decides how many children there are, and
   auto-fill keeps them readable instead of squeezing ten into one row. */
.pp-mega-cols {
    display: grid;
    /* The BLOCK of columns is centred, not the text inside each one.
       Three things make that work together, and removing any one of them breaks it:
         auto-FIT, not auto-fill  -- auto-fill leaves empty tracks standing, and they take up
                                     space, so the filled ones end up pushed off centre;
         a max track size, not 1fr -- 1fr stretches the tracks to consume the whole width, and
                                     there is then nothing left over to centre with;
         justify-content: center   -- what actually places the leftover space either side.
       Each column's own contents stay left-aligned, which is what keeps the headings and the
       sub-links reading as a column rather than as a poster. */
    grid-template-columns: repeat(auto-fit, minmax(190px, 240px));
    justify-content: center;
    gap: 1.5rem 1.75rem;
}
.pp-mega-head {
    display: block;
    text-decoration: none;
    color: inherit;
}
.pp-mega-img {
    display: flex;
    align-items: center;
    justify-content: center;
    aspect-ratio: 3 / 2;
    width: 100%;
    overflow: hidden;
    border-radius: 10px;
    background: var(--pp-surface-2);
    color: var(--pp-muted);
}
.pp-mega-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* The folder stands in for a missing picture in a 3:2 box, so it has room to be read as an
   icon rather than a speck. Doubled from 2.2rem. */
.pp-mega-img .pp-icon { width: 4.4rem; height: 4.4rem; }
.pp-mega-title {
    display: block;
    margin: 0.5rem 0 0.25rem;
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.3;
}
.pp-mega-head:hover .pp-mega-title { color: var(--pp-accent); }

/* the level below: smaller, quieter, stacked under its heading */
.pp-mega-subs { display: flex; flex-direction: column; }
.pp-mega-subs a {
    padding: 0.15rem 0;
    font-size: 0.9rem;
    text-decoration: none;
    color: var(--pp-muted);
}
.pp-mega-subs a:hover { color: var(--pp-fg); }

@media (max-width: 640px) {
    /* The phone rules for .pp-nav-item panels do not apply here (different class), so the
       mega panel needs its own inset — and fewer, wider columns to stay legible. */
    .pp-mega { left: 0.5rem; right: 0.5rem; max-height: 75vh; border-radius: 10px; border-left: 1px solid var(--pp-border); border-right: 1px solid var(--pp-border); }
    .pp-mega-inner { padding: 0.9rem; }
    .pp-mega-cols { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 1rem; }
    .pp-mega-title { font-size: 0.95rem; }
}

/* --- Shipping step (PlainShippingStep) --- */
.pp-ship-zip {
    display: flex;
    align-items: flex-end;
    gap: 0.6rem;
    margin: 1rem 0 1.5rem;
    flex-wrap: wrap;
}
.pp-ship-zip label {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    font-size: 0.9rem;
    color: var(--pp-muted);
}
.pp-ship-zip input {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--pp-border-strong);
    border-radius: 8px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    font: inherit;
    max-width: 8rem;
}
.pp-ship-options,
.pp-ship-points {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}
.pp-ship-option form,
.pp-ship-point form { margin: 0; }
.pp-ship-option button,
.pp-ship-point button {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.8rem 1rem;
    border: 1px solid var(--pp-border);
    border-radius: 8px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.pp-ship-point button { flex-direction: column; align-items: flex-start; gap: 0.15rem; }
.pp-ship-option button:hover,
.pp-ship-point button:hover { border-color: var(--pp-border-strong); }
/* The chosen row is marked by the accent, and reinforced with a check so it does not rely on
   colour alone. */
.pp-ship-option.is-chosen button,
.pp-ship-point.is-chosen button {
    border-color: var(--pp-accent);
    box-shadow: inset 0 0 0 1px var(--pp-accent);
}
.pp-ship-option.is-chosen .pp-ship-title::before,
.pp-ship-point.is-chosen .pp-ship-title::before {
    content: "\2713\00a0";           /* ✓ */
    color: var(--pp-accent);
    font-weight: 700;
}
.pp-ship-title { font-weight: 600; }
.pp-ship-price { white-space: nowrap; }
/* An option the carrier could not price: shown, dimmed, and not clickable. Dimming alone would
   be colour-only, so the button is genuinely disabled and the price cell says why. */
.pp-ship-option.is-unpriced button { opacity: 0.55; cursor: default; }
.pp-ship-option.is-unpriced button:hover { border-color: var(--pp-border); }
.pp-ship-noprice { font-size: 0.85rem; color: var(--pp-muted); white-space: nowrap; }
.pp-ship-failure {
    margin: 0.75rem 0;
    font-size: 0.9rem;
    color: var(--pp-fg);
}
.pp-ship-failure .pp-muted { display: block; font-size: 0.8rem; }
.pp-ship-subtitle { font-size: 1.05rem; margin: 1.25rem 0 0.75rem; }
.pp-ship-total {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding-top: 1rem;
    border-top: 1px solid var(--pp-border);
    font-weight: 600;
}
/* The step's primary action, aligned to the start like the delivery step's Continue. */
.pp-ship-next { margin-top: 1.5rem; }
.pp-ship-next .pp-addcart-btn { text-decoration: none; display: inline-block; }

/* --- Order review (PlainOrderReview) --- */
/* Each block carries one part of the order back-linked to the step that owns it. Framed like the
   delivery summary so the whole checkout reads as one surface. */
.pp-review-block {
    max-width: 32rem;
    margin: 0 0 1.25rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--pp-border);
    border-radius: 12px;
    background: var(--pp-surface);
}
.pp-review-block h2 { margin: 0; font-size: 1.05rem; }
/* Heading on the left, the one "Endre" link on the right, on a single line. */
.pp-review-head {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    margin-bottom: 0.5rem;
}
.pp-review-block .pp-cart-table { margin: 0; }
.pp-review-block .pp-summary-block { margin: 0.25rem 0; line-height: 1.5; }
/* The chosen shipping line: description left, price pushed to the right edge. */
.pp-review-block .pp-summary-block .pp-cart-price { float: right; font-weight: 600; }
.pp-review-qty { text-align: center; white-space: nowrap; }

.pp-review-totals {
    max-width: 32rem;
    margin: 0 0 1.5rem;
}
.pp-review-total-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.35rem 0;
}
/* The amount actually charged: set apart by a rule above it and heavier type. */
.pp-review-grand {
    margin-top: 0.35rem;
    padding-top: 0.6rem;
    border-top: 1px solid var(--pp-border);
    font-weight: 700;
    font-size: 1.1rem;
}
.pp-review-vat { font-size: 0.85rem; padding-top: 0; }
.pp-review-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 32rem;
    margin-bottom: 2rem;
}
.pp-review-actions .pp-tag { border: 1px solid var(--pp-border-strong); }
.pp-review-actions .pp-addcart-btn { text-decoration: none; display: inline-block; margin-left: auto; }

/* Order confirmation (PlainOrderConfirmation): the payment state, marked by more than colour so
   "confirmed" and "pending" are not carried by hue alone. */
.pp-order-status {
    display: inline-block;
    margin: 0.25rem 0 1rem;
    padding: 0.4rem 0.9rem;
    border-radius: 999px;
    font-weight: 600;
}
.pp-order-status.is-paid { background: var(--pp-accent); color: #fff; }
.pp-order-status.is-paid::before { content: "\2713\00a0"; }               /* ✓ */
.pp-order-status.is-pending { background: var(--pp-surface-2); color: var(--pp-muted); }
.pp-order-status.is-pending::before { content: "\2026\00a0"; }            /* … */

/* --- Added-to-cart confirmation (PlainAddedToCartDialog) --- */
.pp-addcart-dialog {
    border: 1px solid var(--pp-border);
    border-radius: 12px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    padding: 1.5rem;
    max-width: 24rem;
    width: calc(100% - 2rem);
}
/* The native backdrop, so the page behind is dimmed without a hand-rolled overlay. */
.pp-addcart-dialog::backdrop {
    background: rgb(0 0 0 / 0.45);
}
.pp-addcart-dialog-close {
    display: flex;
    justify-content: flex-end;
    margin: -0.5rem -0.5rem 0 0;
}
.pp-addcart-dialog-close button {
    border: 0;
    background: none;
    color: var(--pp-muted);
    font-size: 1.4rem;
    line-height: 1;
    padding: 0.25rem 0.5rem;
    cursor: pointer;
}
.pp-addcart-dialog-close button:hover { color: var(--pp-fg); }
.pp-addcart-dialog-title {
    margin: 0 0 0.35rem;
    font-size: 1.15rem;
}
.pp-addcart-dialog-article {
    margin: 0 0 1.1rem;
    color: var(--pp-muted);
}
.pp-addcart-dialog-qty {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    margin-bottom: 1.4rem;
}
.pp-addcart-dialog-qty button {
    width: 2.25rem;
    height: 2.25rem;
    border: 1px solid var(--pp-border-strong);
    border-radius: 8px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    font: inherit;
    font-size: 1.1rem;
    cursor: pointer;
}
.pp-addcart-dialog-qty button:hover:not(:disabled) { border-color: var(--pp-accent); }
.pp-addcart-dialog-qty button:disabled { opacity: 0.45; cursor: default; }
.pp-addcart-dialog-n {
    min-width: 2ch;
    text-align: center;
    font-weight: 600;
    font-variant-numeric: tabular-nums;   /* the number must not shift the buttons as it grows */
}
/* Continue on the left, go-to-cart on the right: the primary action sits where the eye lands
   last, and the two are far enough apart not to be hit by accident. */
.pp-addcart-dialog-actions {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.75rem;
}
.pp-addcart-dialog-actions .pp-tag { cursor: pointer; border: 1px solid var(--pp-border-strong); }
.pp-addcart-dialog-actions .pp-addcart-btn { text-decoration: none; display: inline-block; }

@media (max-width: 420px) {
    .pp-addcart-dialog-actions { flex-direction: column-reverse; align-items: stretch; }
    .pp-addcart-dialog-actions .pp-addcart-btn { text-align: center; }
}

/* --- Checkout forms (PlainDeliveryStep) --- */
.pp-form {
    display: flex;
    flex-direction: column;
    gap: 0.9rem;
    max-width: 32rem;
    margin: 1rem 0 2rem;
}
.pp-form-row {
    display: flex;
    gap: 0.9rem;
}
/* The postcode stays narrow and the city takes the rest, rather than two equal halves. */
.pp-form-row .pp-field:first-child { flex: 0 0 8rem; }
.pp-form-row .pp-field:last-child { flex: 1; }
.pp-field {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
}
.pp-field-label {
    font-size: 0.9rem;
    color: var(--pp-muted);
}
.pp-field input {
    padding: 0.55rem 0.7rem;
    border: 1px solid var(--pp-border-strong);
    border-radius: 8px;
    background: var(--pp-surface);
    color: var(--pp-fg);
    font: inherit;
    width: 100%;
}
.pp-field input:focus-visible {
    outline: 2px solid var(--pp-accent);
    outline-offset: 1px;
}
/* Marked on the field itself as well as by the message, so the error is not carried by colour
   alone for anyone who cannot see it. */
.pp-field input[aria-invalid="true"] { border-color: #d14343; }
.pp-field-err {
    font-size: 0.85rem;
    color: #d14343;
}
.pp-form .pp-addcart-btn { align-self: flex-start; }

@media (max-width: 480px) {
    .pp-form-row { flex-direction: column; }
    .pp-form-row .pp-field:first-child { flex: 1; }
}

/* An address that is already on file is read back, not re-asked. Same width as the form it
   replaces, so the step does not jump between the two states. */
.pp-summary {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem 3rem;
    max-width: 32rem;
    margin: 1rem 0 1.5rem;
    padding: 1rem 1.25rem;
    border: 1px solid var(--pp-border);
    border-radius: 12px;
    background: var(--pp-surface);
}
.pp-summary-block {
    margin: 0;
    line-height: 1.5;
}
.pp-summary-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    max-width: 32rem;
    margin-bottom: 2rem;
}
.pp-summary-actions .pp-tag { border: 1px solid var(--pp-border-strong); }
/* Continue is the primary action here and is a link, so it needs the button's own reset. */
.pp-summary-actions .pp-addcart-btn { text-decoration: none; display: inline-block; }

/* --- Cache admin (PlainCacheList) --- */
.pp-cache-actions {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex-wrap: wrap;
    margin: 1rem 0 1.25rem;
}
.pp-cache-actions form { margin: 0; }
.pp-cache-actions .pp-tag { cursor: pointer; border: 1px solid var(--pp-border-strong); }
/* max-width and min-width:0 both matter: without them the box grows to fit the table instead of
   clipping it, and the whole PAGE scrolls sideways rather than just this one element. */
.pp-cache-scroll {
    overflow-x: auto;
    max-width: 100%;
    min-width: 0;
}
.pp-cache-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 0.9rem;
    /* No min-width: it pushed the table past its scroll box and made the whole PAGE scroll
       sideways. The keys break instead (word-break below), which is fine for a key. */
    table-layout: fixed;
}
.pp-cache-table th,
.pp-cache-table td {
    text-align: left;
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid var(--pp-border);
    vertical-align: top;
}
.pp-cache-table th { color: var(--pp-muted); font-weight: 600; }
/* Keys are long and structured (tenant|type|id); break them rather than letting one row
   stretch the table past the viewport. */
/* The key is what you read to decide what to clear, so it gets the room; the three metadata
   columns need only enough for a timestamp. */
.pp-cache-table th:first-child,
.pp-cache-table td:first-child { width: 46%; }
.pp-cache-table th:last-child,
.pp-cache-table td:last-child { width: 2.5rem; }
.pp-cache-key {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    word-break: break-all;
}
.pp-cache-table form button {
    border: 0;
    background: none;
    color: var(--pp-muted);
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 0.25rem;
}
.pp-cache-table form button:hover { color: #d14343; }

/* Support diagnostics on the shipping step: order ref + line ids, small and unobtrusive. */
.pp-ship-diag { font-size: 0.75rem; margin: 0.25rem 0 1rem; }
.pp-ship-diag ul { margin: 0.25rem 0 0; padding-left: 1.1rem; }
.pp-ship-diag li { line-height: 1.4; }

/* Price/availability inquiry shown on an article that has no price (PlainInquiry). */
.pp-inquiry { margin: 0.75rem 0; }
.pp-inquiry-note { color: var(--pp-muted); margin: 0 0 0.75rem; }
.pp-inquiry-sent {
    margin: 0.75rem 0;
    padding: 0.75rem 1rem;
    background: var(--pp-surface-2);
    border-radius: 0.4rem;
}
.pp-inquiry-form { display: grid; gap: 0.6rem; max-width: 26rem; }
.pp-inquiry-form textarea {
    width: 100%;
    box-sizing: border-box;
    font: inherit;
    padding: 0.5rem 0.6rem;
}
