/*
 * Dermashoppen site stylesheet - the shell's unconditional stylesheet link.
 *
 * WHY THIS FILE EXISTS. shell.js:605 emits
 * <link rel="stylesheet" href="<baseUrl>css/styles.css"> for every page of
 * every tenant, with no condition. Measured 2026-08-25 and again 2026-09-02:
 * that URL is 404 for dermashoppen. Site 10 was created 2026-07-03, before the
 * provisioning flow grew its seed_assets step, so the object was never written.
 *
 * WHAT IT COVERS, MEASURED RATHER THAN GUESSED. The section renderers were
 * inventoried against operakliniken's two stylesheets: of the 112 classes the
 * sections on this site emit, 73 are styled by the renderer that emits them
 * and 5 by a shared /modules/ stylesheet. The remaining set is what a tenant
 * stylesheet has to carry, and it is exactly the site chrome: the nav bar, the
 * mobile menu, the footer, the certification marquee items and the .glass
 * surface. Those are the parts the phase C review saw render unstyled
 * (docs/reports/2026-08-25-wave2-phase-c-dermashoppen.md, T4).
 *
 * IT DOES NOT RESTATE WHAT A SECTION ALREADY OWNS. .nav-cta, .op-cpl__*,
 * .op-prose__*, .op-hi__* and .op-about__* all carry their own CSS inside their
 * renderer. Copying any of them here would create a second definition that
 * drifts the day the renderer changes. The one deliberate overlap is .nav
 * itself: shell.js owns its reveal ANIMATION (opacity and transform, toggled by
 * .nav-scrolled) and this file owns its LAYOUT. Two property sets, no shared
 * property, so neither can contradict the other.
 *
 * .trust-* IS NOT IN THAT LIST ANY MORE, corrected 2026-09-03. The trust
 * section's renderer ships only a max-width:900px block; its desktop layout is
 * an R2 module the section opts into by content key (staged file 080). What
 * this file contributes there is the --trust-* colour tokens the module reads,
 * nothing else. The rendered page was inventoried class by class to settle
 * this rather than read off a comment: seven classes were styled only below
 * 900px, and they were the trust and marquee ones.
 *
 * COLOUR COMES FROM THE THEME ENGINE. Nothing here writes a brand colour. The
 * :root block maps the names the section renderers read onto the palette
 * engine's roles (workers/shared/palette, injected as themeCSS by
 * shell.js:602), with neutral fallbacks for a page rendered before a
 * palette:dermashoppen record exists. Dermashoppen has no brand colour
 * recorded anywhere today - sites.site_data and sites.domain_config are both
 * empty and the snapshot's authored content carries only Divi defaults - so
 * inventing one here would have made a guess into the site's truth.
 * Design type S1: clinical premium monochrome, light, brand-led.
 *
 * ONE TRUTH WITH THE HUB SHEET. The --op-* token block lives in
 * sites/dermashoppen/assets/css/dermashoppen-hub.css and is not repeated here.
 * This file consumes those tokens and adds only the aliases for the
 * unprefixed names (--text, --accent, --surface, --border, --sand-light) that
 * older section CSS reads directly.
 */

/* THE ALIAS VOCABULARY LIVES ONE SCOPE BELOW THE ROLE VOCABULARY, and that
   selector is the whole fix rather than a stylistic choice.

   Three names are in both vocabularies. --surface, --border and --text-muted
   are palette ROLE names that the hub sheet reads
   (--op-bg-surface: var(--surface, #ffffff)) and they are also ALIAS names the
   older section CSS reads. Declared in :root, as they were, each pair pointed
   at the other: --surface read --op-bg-surface which read --surface. A custom
   property in a cycle computes to the guaranteed-invalid value, so every
   var(--surface, ...) on the page fell through to ITS OWN fallback, and the
   platform's fallbacks are another tenant's palette. Measured in the browser
   2026-09-03: --surface and --text-muted computed to the empty string and the
   six product bands painted rgb(250,247,242), the beige in
   content/coupled-content.js.

   The first repair wrote the three as literals here. That stopped the cycle
   and left a worse problem stated in its own comment: a literal in the tenant
   sheet WINS over the palette, because the sheet loads after the palette's
   style block, so the day a palette record existed for this site three of its
   roles would have been ignored. The face would have come half from the
   engine and half from a file.

   A name cannot be both read and written in one scope - any chain between the
   two is circular by construction - so the two vocabularies are separated by
   scope instead of by name. The roles stay at :root, where getPaletteCSS
   writes them and where the hub sheet's --op-* tokens read them. The aliases
   are declared on body, one level down. Custom properties are substituted at
   computed-value time on the element that declares them, so --op-bg-surface
   is resolved at :root against the real role, and body's --surface then reads
   that already-resolved value. No cycle, no literal, and the palette reaches
   every consumer of either vocabulary.

   Every alias moved, not only the three that collided: a name that does not
   collide today collides the day the palette gains a role with that name, and
   one rule about where aliases live is easier to keep true than a list of
   exceptions. */
body {
    --text: var(--op-text-primary);
    --accent: var(--op-accent);
    --accent-strong: var(--op-accent-strong);
    --accent-dark: var(--op-accent-strong);

    --surface: var(--op-bg-surface);
    --surface-soft: var(--op-bg-muted);
    --border: var(--op-border);
    --text-muted: var(--op-text-muted);
    /* Read by hero-intro and about. Named for a colour this site does not
       have; aliased to the muted surface so the name stops meaning sand. */
    --sand-light: var(--op-bg-muted);
    /* THE BEIGE BANDS, found 2026-09-03 and fixed here. shell.js emits a
       baseline alternation - .section reads --sand-light, .section:nth-of-type(even)
       reads --sand - and this site defined only the first of the pair. The
       second therefore fell back to the platform default #e6ddd5, which is
       operakliniken's sand, and every even section on a monochrome site came
       out beige. Enumerated rather than eyeballed: of the 81 custom properties
       the rendered page reads, --sand was the one that changed a visible
       surface. It is aliased to the canvas tone so the alternation stays but
       stays monochrome. */
    --sand: var(--op-bg-canvas);

    /* Read by modules/trust-icons/trust-icons.css, the R2 module that owns the
       trust section's layout and that section 113 opts into. Its own fallbacks
       are built for a dark page (a card background of rgba(255,255,255,0.05)),
       so on this light site the cards would be all but invisible without these.
       The module owns the layout, this file owns the colour: that is the whole
       division, and neither restates the other. */
    /* One line, not a card. The section carries a single statement now, and
       auto-fit gives a single child the whole 1200px track, so a white box
       with a hairline read as an empty button 1200px wide. The module still
       owns the layout; only its two colour tokens say the box is not there. */
    --trust-card-bg: transparent;
    --trust-card-border: transparent;
    --trust-card-radius: var(--op-radius-lg, 12px);
    --trust-icon-color: var(--op-accent);
    --trust-value-size: var(--op-text-xl, 1.25rem);
}

html {
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
}

body {
    margin: 0;
    font-family: var(--op-font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif);
    color: var(--text);
    background: var(--op-bg-canvas);
}

/* ---------------------------------------------------------------------------
 * Nav bar
 *
 * Fixed, because shell.js reveals it on scroll by toggling .nav-scrolled and a
 * static bar would slide out of the document flow when it animates. The bar is
 * opaque rather than translucent: a product grid scrolling under a blurred bar
 * puts arbitrary product photography behind the brand name.
 * ------------------------------------------------------------------------ */

.nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 100;
    display: flex;
    align-items: center;
    gap: var(--op-space-5, 1.5rem);
    padding: var(--op-space-3, 0.75rem) var(--op-space-5, 1.5rem);
    min-height: var(--op-appbar-h, 56px);
    background: var(--op-bg-surface);
    border-bottom: 1px solid var(--op-border);
}

.nav-logo-wrap {
    display: inline-flex;
    align-items: center;
    min-height: var(--op-touch-min, 44px);
    margin-right: auto;
    /* Dermashoppen has no logo asset (sites.domain_config.branding is empty),
       so the nav's two <img> tags fall through to the favicon tier and do not
       resolve. Styling the wrapper as a wordmark makes the images' alt text -
       which is the brand name - the visible mark, so a missing file degrades
       to typography instead of to a broken-image icon. Remove nothing here
       when a logo is uploaded; the img simply takes over. */
    font-family: var(--op-font-display, inherit);
    font-size: var(--op-text-lg, 1.125rem);
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--text);
    text-decoration: none;
}

/* The logo's height is the brand lockup's (tokens.css THE BRAND LOCKUP): the
   basis times the scale of the place it stands in, the narrow one in the
   phone's top row, so the shell byline's inset follows it. */
.nav-logo-img {
    display: block;
    height: calc(var(--op-nav-logo-basis, 28px) * var(--op-nav-logo-scale, 1));
    width: auto;
}

.op-nav-top { --op-nav-logo-scale: var(--op-nav-logo-scale-narrow, 1); }

/* One bar treatment at every scroll position, so only the dark mark is ever
   correct. The white variant exists for tenants whose bar goes transparent
   over a dark hero; this one never does. */
.nav-logo-white { display: none; }
.nav-logo-dark { display: block; }

/* THE WORDMARK THAT FOLDS INTO ITS D (Stanley 2026-11-11). The first-paint
   image and the SVG that replaces it carry the same mark (data-nav-logo-morph,
   modules/nav-logo-morph/nav-logo-morph.js), so this one rule gives both the
   same box and nothing moves when the script takes over. The icon is taller
   than the word and draws over the box's top and bottom, inside the link's
   touch height. On the phone the row rises out of view over its own measured
   height (the shell's --op-shell-flow-header-height, nav.js THE PHONE'S HEAD),
   and the motion runs over that same distance, so it ends as the row leaves. */
.nav-logo-img[data-nav-logo-morph] {
    height: calc(var(--op-nav-logo-basis, 28px) * var(--op-nav-wordmark-scale, 1));
    overflow: visible;
}

.op-nav-top {
    --op-nav-wordmark-scale: var(--op-nav-wordmark-scale-narrow, 1);
    --op-nav-logo-morph-range: var(--op-shell-flow-header-height, 0px);
}

/* The wordmark fallback above only works if the empty image gets out of the
   way. Measured 2026-09-03 in the browser: the shell emits the mark as
   <img src="" alt="Dermashoppen"> because this site has no logo asset, and an
   img with an empty src is a broken image, so the bar showed the broken-image
   glyph next to the alt text on every page. Keyed on the empty src rather than
   on a class, so the day a logo is uploaded the rule stops applying by itself
   and nothing here has to be remembered and removed. */
.nav-logo-img[src=""] { display: none; }

/* No bar clearance here. The site's shell bar is fixed, and the shell's one
   offset (workers/shared/site-shell.js, body padding-top from the measured
   bar) clears it on every page kind. A clearance of this sheet's own, once
   needed for the prose pages and the product list, stood beside it as a second
   one: the terms title sat 100 px under the bar (design pass 1, question 9). */

.op-nav-host { container: op-nav / inline-size; }

.nav-links {
    display: flex;
    align-items: center;
    gap: var(--op-space-5, 1.5rem);
    margin: 0;
    padding: 0;
    list-style: none;
}

.nav-links a {
    display: inline-flex;
    align-items: center;
    min-height: var(--op-touch-min, 44px);
    font-size: var(--op-text-sm, 0.875rem);
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--op-text-secondary);
    text-decoration: none;
    transition: color var(--op-motion-fast, 120ms ease-out);
}

.nav-links a:hover {
    color: var(--text);
}

.nav-hamburger {
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 5px;
    width: var(--op-touch-min, 44px);
    height: var(--op-touch-min, 44px);
    padding: 0;
    background: none;
    border: 0;
    cursor: pointer;
}

.nav-hamburger span {
    display: block;
    width: 22px;
    height: 1.5px;
    margin: 0 auto;
    background: var(--text);
    transition: transform var(--op-motion-fast, 120ms ease-out),
                opacity var(--op-motion-fast, 120ms ease-out);
}

/* ---------------------------------------------------------------------------
 * Mobile menu
 *
 * The shell toggles .open on #mobile-menu. Hidden by max-height rather than by
 * display:none so the transition has something to animate, and with
 * visibility so a closed menu keeps its links out of the tab order.
 * ------------------------------------------------------------------------ */

.mobile-menu {
    position: fixed;
    top: var(--op-appbar-h, 56px);
    left: 0;
    right: 0;
    z-index: 99;
    display: flex;
    flex-direction: column;
    max-height: 0;
    overflow: hidden;
    visibility: hidden;
    background: var(--op-bg-surface);
    border-bottom: 1px solid var(--op-border);
    transition: max-height var(--op-motion-base, 220ms ease),
                visibility var(--op-motion-base, 220ms ease);
}

.mobile-menu.open {
    max-height: 70vh;
    overflow-y: auto;
    visibility: visible;
}

.mobile-link {
    display: flex;
    align-items: center;
    min-height: var(--op-touch-min, 44px);
    padding: var(--op-space-3, 0.75rem) var(--op-space-5, 1.5rem);
    font-size: var(--op-text-base, 1rem);
    color: var(--op-text-secondary);
    text-decoration: none;
    border-bottom: 1px solid var(--op-border);
}

.mobile-link:last-child { border-bottom: 0; }
.mobile-link:hover { background: var(--op-bg-muted); color: var(--text); }

@container op-nav (max-width: 40rem) {
    .nav-links { display: none; }
    .nav-hamburger { display: flex; }
}

@container op-nav (width > 40rem) {
    .mobile-menu { display: none; }
}

/* A menu button that opens nothing is a dead control. sections/nav.js draws
   the desktop list and the mobile menu from ONE list (the nav row's
   content.links, plus mobile_cta), so with no item in that list both are empty
   and the bar is the brand mark alone at every width. Keyed on the menu's own
   links, not on a breakpoint or a site fact: the day the nav row carries items
   the button shows again by itself and nothing here has to be removed.
   Measured 2026-10-08: every Dermashoppen nav row stores "links": []. */
body:not(:has(.mobile-menu a)) .nav-hamburger { display: none; }

/* ---------------------------------------------------------------------------
 * .glass
 *
 * A named surface primitive the trust cards and other sections opt into. On a
 * monochrome light design it is a flat card with a hairline, not a frosted
 * panel: a blur over a white canvas costs a compositing layer and shows
 * nothing.
 * ------------------------------------------------------------------------ */

.glass {
    background: var(--op-bg-surface);
    border: 1px solid var(--op-border);
    border-radius: var(--op-radius-lg, 12px);
    box-shadow: var(--op-shadow-soft);
}

/* ---------------------------------------------------------------------------
 * Certification marquee
 *
 * CORRECTED 2026-09-03. This block used to say the track and its animation were
 * owned by cert-marquee.js and carried only the items. That reading was wrong,
 * and the render proved it: at 1280 wide the marquee came out as four stacked
 * rows of brand names instead of one scrolling band. The renderer's own comment
 * says it plainly - "The base marquee CSS ships from the site bundle
 * (css/styles.css in R2), so this scoped block only carries the mobile
 * divergence" - and its inline style block is a single max-width query. The
 * container, the track and the keyframes are the tenant sheet's, and
 * operakliniken's own styles.css carries exactly these three.
 *
 * The translation is -25%, not operakliniken's -33.333%. The renderer emits the
 * item run FOUR times (cert-marquee.js: "Duplicate 4x for smooth infinite
 * scroll"), so one run is a quarter of the track and a quarter is the only
 * offset that lands on an identical frame. A third of a four-run track jumps
 * once per cycle.
 * ------------------------------------------------------------------------ */

.cert-marquee {
    position: relative;
    width: 100%;
    overflow: hidden;
    padding: var(--op-space-3, 0.75rem) 0;
    background: var(--sand-light);
}

.cert-marquee-track {
    display: flex;
    align-items: center;
    width: max-content;
    white-space: nowrap;
    /* animation-duration is set inline by the renderer from the item count. */
    animation-name: marqueeScroll;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
    will-change: transform;
}

@media (prefers-reduced-motion: reduce) {
    .cert-marquee-track { animation: none; }
}

@keyframes marqueeScroll {
    from { transform: translateX(0); }
    to { transform: translateX(-25%); }
}

.cert-marquee-item {
    display: inline-flex;
    align-items: center;
    white-space: nowrap;
    font-size: var(--op-text-sm, 0.875rem);
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--op-text-muted);
}

.cert-marquee-label {
    /* Centred and margined because the track below it starts hard against the
       viewport edge by design (it scrolls), and a label sharing that edge reads
       as a layout mistake rather than as a heading. */
    margin: 0 0 var(--op-space-2, 0.5rem);
    text-align: center;
    font-size: var(--op-text-xs, 0.75rem);
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--op-text-muted);
}

.cert-marquee-sep {
    display: inline-block;
    width: 4px;
    height: 4px;
    margin: 0 var(--op-space-4, 1rem);
    border-radius: 50%;
    background: var(--op-border-strong);
    /* Decorative: the separator is a bullet between brand names, and a screen
       reader announcing it would read the marquee as a list of dots. */
    vertical-align: middle;
}

/* ---------------------------------------------------------------------------
 * Footer
 *
 * Design pass 2 (the design direction 2026-10-13 block 15, the coordinator's
 * decision 4): the page ends in the brand's own deep sandstone
 * (--op-accent-strong), its words in the on-deep tones mixed from the palette
 * (tokens.css --op-on-deep and --op-on-deep-muted), and its two rows on the
 * page grid every section shares (--op-page-max and the gutters, the wide
 * gutter from the footer's own 40rem container step), so its left edge is
 * the page's.
 * ------------------------------------------------------------------------ */

.footer {
    container: op-footer / inline-size;
    background: var(--op-accent-strong);
    color: var(--op-on-deep, var(--op-text-on-accent));
    border-top: 0;
    padding: var(--op-space-10, 4rem) 0 var(--op-space-5, 1.5rem);
}

/* The columns' minimum is in rem (15rem, 240px at 100 % text), so a larger
   text size gives fewer, wider columns instead of an address wider than its
   column; min() keeps one column inside a footer narrower than that, and a
   link's words may break anywhere as the last resort (round 3: the mail
   address ran 38px past a 1040px page and 3px past a 360px page at 200 %). */
.footer-top {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(15rem, 100%), 1fr));
    gap: var(--op-space-8, 3rem);
    margin: 0 max(var(--op-gutter, 1rem), (100% - var(--op-page-max, 75rem)) / 2);
}

.footer-col {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--op-space-2, 0.5rem);
    min-width: 0;
}

.footer-heading {
    margin: 0 0 var(--op-space-2, 0.5rem);
    font-family: var(--op-font-display, inherit);
    font-size: var(--op-type-micro, 0.75rem);
    font-weight: 600;
    letter-spacing: var(--op-tracking-micro, 0.14em);
    text-transform: uppercase;
    color: var(--op-on-deep, var(--op-text-on-accent));
}

.footer-text {
    margin: 0;
    font-size: var(--op-text-sm, 0.875rem);
    line-height: var(--op-leading-normal, 1.55);
    color: var(--op-on-deep-muted, var(--op-text-on-accent));
}

/* A footer link is a touch target like any other control: the box is the
   touch minimum tall (it was 28px, audit D32), and the hover mark is the
   text's own underline, so it stays under the words however tall the box. */
.footer-link {
    display: inline-flex;
    align-items: center;
    min-height: var(--op-touch-min, 44px);
    max-width: 100%;
    font-size: var(--op-text-sm, 0.875rem);
    overflow-wrap: anywhere;
    color: var(--op-on-deep-muted, var(--op-text-on-accent));
    text-decoration: underline;
    text-decoration-color: transparent;
    text-underline-offset: 0.2em;
    transition: color var(--op-motion-fast, 120ms ease-out),
                text-decoration-color var(--op-motion-fast, 120ms ease-out);
}

.footer-link:hover {
    color: var(--op-on-deep, var(--op-text-on-accent));
    text-decoration-color: currentColor;
}

/* The focus ring on the deep surface (UX plan 2026-10-22, stream W2, audit
   C6). The shell's one focus rule draws in the site's dark accent, which is
   this footer's own background: the ring was there at 1:1 and nobody could
   see it (WCAG 2.4.7, 1.4.11). On this surface it takes the on-deep tone the
   footer's words already use; width and offset stay the shell's. */
.footer :focus-visible {
    outline-color: var(--op-on-deep, var(--op-text-on-accent));
}

.footer-tel {
    font-variant-numeric: tabular-nums;
}

.footer-bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--op-space-3, 0.75rem);
    margin: var(--op-space-8, 3rem) max(var(--op-gutter, 1rem), (100% - var(--op-page-max, 75rem)) / 2) 0;
    padding-top: var(--op-space-5, 1.5rem);
    border-top: 1px solid color-mix(in oklab, var(--op-on-deep, var(--op-text-on-accent)) 18%, transparent);
    font-size: var(--op-text-xs, 0.75rem);
    color: var(--op-on-deep-muted, var(--op-text-on-accent));
}

.footer-bottom p { margin: 0; }

/* The rows' gutter from the footer's own 40rem step, as every section's. */
@container op-footer (min-width: 40rem) {
    .footer-top,
    .footer-bottom { margin-inline: max(var(--op-gutter-wide, 2.5rem), (100% - var(--op-page-max, 75rem)) / 2); }
}

.footer-powered a {
    display: inline-flex;
    align-items: center;
    min-height: var(--op-touch-min, 44px);
    color: inherit;
    text-decoration: underline;
    text-decoration-color: currentColor;
    text-underline-offset: 0.2em;
}

/* ---------------------------------------------------------------------------
 * Footer attributions dialog
 *
 * Injected by injectFooterAttributions from site-scoped truth. It is a native
 * <dialog>, so the open state comes from the element and not from a class.
 * ------------------------------------------------------------------------ */

.footer-attributions-entry {
    margin-top: var(--op-space-3, 0.75rem);
}

.footer-attributions-open {
    /* It is a <button> that has to read as the footer links beside it. */
    padding: 0;
    background: none;
    border: 0;
    border-bottom: 1px solid transparent;
    font: inherit;
    font-size: var(--op-text-sm, 0.875rem);
    color: var(--op-on-deep-muted, var(--op-text-on-accent));
    cursor: pointer;
}

.footer-attributions-open:hover {
    color: var(--op-on-deep, var(--op-text-on-accent));
    border-bottom-color: currentColor;
}

.footer-attributions {
    width: min(560px, calc(100vw - 2rem));
    max-height: min(70vh, 640px);
    padding: var(--op-space-5, 1.5rem);
    border: 1px solid var(--op-border);
    border-radius: var(--op-radius-lg, 12px);
    background: var(--op-bg-surface);
    color: var(--text);
    box-shadow: var(--op-shadow-pop);
}

.footer-attributions::backdrop {
    background: var(--op-scrim, color-mix(in oklab, var(--op-text-primary) 45%, transparent));
}

.footer-attributions-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--op-space-4, 1rem);
    margin-bottom: var(--op-space-4, 1rem);
}

.footer-attributions-title {
    margin: 0;
    font-family: var(--op-font-display, inherit);
    font-size: var(--op-text-lg, 1.125rem);
    font-weight: 600;
}

.footer-attributions-close {
    min-height: var(--op-touch-min, 44px);
    padding: 0 var(--op-space-3, 0.75rem);
    background: none;
    border: 1px solid var(--op-border);
    border-radius: var(--op-radius-md, 6px);
    font: inherit;
    font-size: var(--op-text-sm, 0.875rem);
    color: var(--op-text-secondary);
    cursor: pointer;
}

.footer-attributions-close:hover {
    border-color: var(--op-border-strong);
    color: var(--text);
}

.footer-attributions-list {
    margin: 0;
    padding: 0;
    list-style: none;
    overflow-y: auto;
    font-size: var(--op-text-sm, 0.875rem);
    line-height: var(--op-leading-normal, 1.55);
    color: var(--op-text-muted);
}

.footer-attributions-list li + li {
    margin-top: var(--op-space-3, 0.75rem);
    padding-top: var(--op-space-3, 0.75rem);
    border-top: 1px solid var(--op-border);
}

.footer-attributions-list a { color: inherit; }

/* A narrow footer starts its bottom row at the left; its top space is the
   section step the hub sheet already compresses on a phone (--op-space-10). */
@container op-footer (max-width: 44.99rem) {
    .footer-bottom { justify-content: flex-start; }
}

/* THE TABLET HEADER: between the phone bar and the wide header the brand, the
   links and the routine stand on one row and the search on its own under them,
   two rows where the links' own wrapping made three, and the routine's mark no
   longer stands behind the search's list. The list gives up its box so its items
   share the row with the brand. !important because the nav section prints its
   own rules later in the body.
   2026-11-09 (Stanley: the tablet bar looked loose): since the bar holds the
   logo, the search, Sortiment and three 44 px icon controls, one row fits from
   52rem of bar width (the search keeps at least 15rem), so the two rows stand
   only below that. Row one is the logo with every control pushed to the right
   end, row two the search across the whole bar. The bound is the bar's own
   width (the op-nav container), so a scrollbar is already subtracted. */
@container op-nav (width > 40rem) and (width < 52rem) {
    .op-nav-host nav.nav { flex-wrap: wrap !important; justify-content: flex-start !important; gap: var(--op-space-1, .25rem) var(--op-space-3, .75rem) !important; padding-block: var(--op-space-2, .5rem) !important; }
    .op-nav-host .nav-brand { flex: none !important; width: auto !important; }
    .op-nav-host :is(.nav-brand, nav.nav > .nav-logo-wrap) { margin-inline-end: auto !important; }
    .op-nav-host .nav-links { display: contents !important; }
    .op-nav-host .op-nav-search-entry { order: 99 !important; flex: 1 0 100% !important; width: 100% !important; max-width: none !important; margin-inline-start: 0 !important; }
    .op-nav-host .op-nav-search-entry .op-match__field { max-width: none !important; }
}
/* One row from 52rem: the list may shrink instead of wrapping, so the search
   gives up width (down to 15rem) before any control leaves the row. */
@container op-nav (width >= 52rem) {
    .op-nav-host .nav-links { flex: 1 1 auto !important; min-width: 0 !important; flex-wrap: nowrap !important; }
    .op-nav-host .nav-links .op-nav-search-entry { min-width: 15rem !important; }
}
