/*
 * Dermashoppen hub - design tokens and layout primitives.
 *
 * WHY THIS FILE EXISTS. renderer.js derives the hub stylesheet name from the
 * tenant slug (hubStylesheetFor, renderer.js:123), so every site that is not
 * operakliniken has always linked a file nobody had authored. Measured
 * 2026-08-25 and again 2026-09-02: 404 for dermashoppen, 200 and 21 585 bytes
 * for operakliniken. The consequence is not cosmetic - .op-hub carries the
 * reset, the type scale and the token block that the other stylesheet and the
 * sections both read, so without it a page renders with browser defaults.
 *
 * WHERE IT IS SERVED FROM, AND WHY NOT FROM THE WORKER BUNDLE.
 * operakliniken's equivalent is imported into the Worker and handed out of
 * BUNDLED_ASSETS under a hardcoded per-tenant key (index.js:96). Adding a
 * second literal there would put a per-tenant fact in shared code, which is
 * the second-truth pattern this project treats as a bug even while it works.
 * This file is served instead by the derivation that already exists:
 * handleAsset resolves a site-local request to sites/<slug>/<path>
 * (index.js:1098), so sites/dermashoppen/assets/css/dermashoppen-hub.css IS
 * the object behind /assets/css/dermashoppen-hub.css. No code knows this file
 * by name; the slug produces it.
 *
 * COLOUR IS NOT WRITTEN DOWN HERE. Every colour token below resolves through
 * the palette engine (workers/shared/palette, injected as themeCSS ahead of
 * this file by shell.js:602). The shared fallback tokens are FALLBACKS for a page rendered
 * before a palette:dermashoppen record exists, and they are deliberately
 * neutral: design type S1 is "clinical premium monochrome, light, brand-led",
 * so with no brand colour recorded the site reads as clean monochrome rather
 * than as somebody's guess at Dermashoppen's brand. The day the palette record
 * is written through the portal wizard, this file needs no edit.
 *
 * Custom properties resolve at use time, so the aliasing works regardless of
 * whether the palette block precedes or follows this file in the cascade.
 *
 * Token NAMES are a platform contract, not a local choice: the section
 * renderers read --op-text-primary, --op-border, --op-space-*, --op-text-*,
 * --op-flow-pad and --op-touch-min directly (measured across
 * workers/site-router/src and modules/). Renaming one here would silently
 * unstyle a section, so the set matches the contract even where this design
 * uses a token only through another token.
 */

:root {
    /* Theme roles precede the saved recipe. Shared fallback tokens retain
       the previous monochrome colours when neither owner supplies a role.
       Soft and focus tints are mixed from that selected accent. */
    --op-accent: var(--theme-primary, var(--cta-primary-bg, var(--site-fallback-ink)));
    --op-accent-strong: var(--theme-secondary, var(--cta-primary-hover, var(--site-fallback-ink-strong)));
    --op-accent-soft: color-mix(in srgb, var(--op-accent) 7%, transparent);
    --op-accent-ring: color-mix(in srgb, var(--op-accent) 30%, transparent);
    --op-accent-on-dark: var(--hero-accent, var(--site-fallback-paper));

    /* -------------------------------------------------------------- Surface
     * Light, high-key, low-chroma. The canvas is a hair off white so a white
     * product card reads as a raised object rather than as a hole.
     */
    /* The canvas is the warm porcelain mixed from the palette (tokens.css
       --op-porcelain: the accent at 4% in the surface; design pass 2, the
       design direction 2026-10-13 section 3.1), not the palette's cool grey;
       the palette's own canvas stays the fallback. */
    --op-bg-canvas: var(--op-porcelain, var(--bg-canvas, var(--theme-background, var(--site-fallback-canvas))));
    --op-bg-surface: var(--surface, var(--site-fallback-paper));
    --op-bg-muted: var(--surface-muted, var(--site-fallback-surface-muted));
    --op-bg-strong: var(--surface-inverse, var(--site-fallback-ink));

    /* ----------------------------------------------------------------- Text
     * The muted neutral was #6c737d, which measures 4.79:1 on pure white and
     * 4.46:1 on this site's canvas. Summary text on a product card is normal
     * body text, so 4.46 is below AA, and the failure appears only once the
     * canvas stops being pure white. Darkened to a neutral that clears AA on
     * the canvas the site actually paints: 5.59:1 on #f6f7f8, 5.99:1 on white.
     */
    --op-text-primary: var(--text-primary, var(--theme-text, var(--site-fallback-ink)));
    --op-text-secondary: var(--text-secondary, var(--site-fallback-text-secondary));
    --op-text-muted: var(--text-muted, var(--site-fallback-text-muted));
    --op-text-on-accent: var(--cta-primary-text, var(--site-fallback-paper));
    --op-text-on-dark: var(--text-inverse, var(--site-fallback-canvas));

    /* -------------------------------------------------------------- Borders
     * Hairlines, not frames. A clinical surface separates by edge, not by box.
     */
    --op-border: var(--border, var(--site-fallback-border));
    --op-border-strong: var(--border-strong, var(--site-fallback-border-strong));
    --op-border-focus: var(--border-focus, var(--op-accent));

    /* ---------------------------------------------------------------- Status */
    --op-ok: var(--success, var(--site-status-success));
    --op-warn: var(--warning, var(--site-status-warning));
    --op-err: var(--danger, var(--site-status-error));

    /* ------------------------------------------------------------ Typography
     * System stack only. A third-party font CDN would put a render-blocking
     * dependency on a customer's page for a draft nobody has approved yet.
     */
    --op-font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --op-font-display: var(--op-font-sans);
    --op-font-mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace;

    /* One step up the scale and one step calmer, which is the whole
       typographic brief: larger than a shop, quieter than a shop. Body goes
       from 16px to 17px, every heading step follows from the same 1.25 ratio
       it always used, and the line heights open so a heading and its lead do
       not read as one block. The ratio is unchanged on purpose - retuning it
       would have been a second type system rather than this one, louder. */
    --op-text-xs: 0.8125rem;
    --op-text-sm: 0.9375rem;
    --op-text-base: 1.0625rem;
    --op-text-lg: 1.1875rem;
    --op-text-xl: 1.375rem;
    --op-text-2xl: 1.625rem;
    --op-text-3xl: 2rem;
    --op-text-4xl: 2.5rem;
    --op-text-5xl: 3.25rem;

    --op-leading-tight: 1.24;
    --op-leading-snug: 1.4;
    --op-leading-normal: 1.65;
    --op-leading-relaxed: 1.8;

    /* -------------------------------------------------------------- Spacing */
    --op-space-1: 0.25rem;
    --op-space-2: 0.5rem;
    --op-space-3: 0.75rem;
    --op-space-4: 1rem;
    --op-space-5: 1.5rem;
    --op-space-6: 2rem;
    --op-space-7: 2.5rem;
    --op-space-8: 3rem;
    --op-space-10: 4rem;
    --op-space-12: 5rem;
    --op-space-16: 7rem;

    /* ---------------------------------------------------------------- Radii
     * Smaller than the clinic's. A pharmacy shelf reads as precise, and a
     * 16px corner on a product card reads as soft goods.
     */
    --op-radius-sm: 3px;
    --op-radius-md: 6px;
    --op-radius-lg: 12px;
    --op-radius-pill: 999px;

    /* -------------------------------------------------------------- Shadows
     * Nearly flat. Depth comes from the hairline, not from a drop shadow.
     */
    --op-shadow-soft: var(--site-fallback-shadow-soft);
    --op-shadow-card: var(--site-fallback-shadow-card);
    --op-shadow-pop: var(--site-fallback-shadow-pop);

    /* --------------------------------------------------------------- Layout */
    --op-max-w-content: 68ch;
    --op-max-w-page: 1180px;
    --op-max-w-wide: 1400px;

    /* --------------------------------------------------------------- Motion */
    --op-motion-fast: 120ms ease-out;
    --op-motion-base: 220ms cubic-bezier(0.2, 0.6, 0.2, 1);
    --op-motion-slow: 420ms cubic-bezier(0.2, 0.6, 0.2, 1);

    --op-focus-ring: 0 0 0 3px var(--op-accent-ring);

    /* App shell tokens, per docs/rules/mobile-app-shell-standard.md. Read by
       the shell recipe, so they are part of the contract even on a site that
       does not use the compact composition yet. */
    --op-appbar-h: 56px;
    --op-flowbar-h: 56px;
    --op-flow-pad: 12px;
    --op-touch-min: 44px;

    /* The vector lockup (2572 x 572: head mark, DERMASHOPPEN and BY
       OPERAKLINIKEN underneath) carries its own byline. 56 px tall on
       desktop is 252 px wide and leaves the search field its room at a
       1024 px viewport; 48.48 px is 218 px on the phone's own logo row. */
    --op-nav-logo-scale: calc(56 / 28);
    --op-nav-logo-scale-narrow: calc(48.48 / 28);

    /* The wordmark alone (assets/figures/brand/dermashoppen-wordmark.svg,
       2026-11-11), the logo that folds into its D on scroll. Its height is
       the one the approved animation gives it: there the nav is a 76-unit
       frame and the word stands 22.834 units tall in it, and that frame is
       drawn at the lockup's height above, 56 px on desktop (the word 16.8 px
       tall, 222.6 px wide) and 48.48 px on the phone's row (14.6 px, 192.7
       px). The icon it becomes is 50 units of the same frame. */
    --op-nav-wordmark-scale: calc(22.834 / 76 * 56 / 28);
    --op-nav-wordmark-scale-narrow: calc(22.834 / 76 * 48.48 / 28);
    --op-nav-byline-rise: .15;
}

/* Mobile rhythm: the block-scale tokens compress so a narrow viewport does
   not inherit desktop-scale dead bands between sections. Component-scale
   tokens (space-1 to space-8) stay fixed, matching the platform rule the
   operakliniken sheet documents. Breakpoint matches MOBILE_MAX in
   workers/site-router/src/templates/breakpoints.js. */
@media (max-width: 719.98px) {
    :root {
        --op-space-10: 3rem;
        --op-space-12: 3.5rem;
        --op-space-16: 4rem;
    }
}

/* ---------------------------------------------------------------------------
 * Reset and typography, scoped to .op-hub.
 *
 * Scoped rather than global on purpose: .op-hub is the wrapper the shell puts
 * around hub content (shell.js), so scoping keeps this file from reaching any
 * other surface that happens to load it.
 * ------------------------------------------------------------------------ */

.op-hub *,
.op-hub *::before,
.op-hub *::after {
    box-sizing: border-box;
}

.op-hub {
    font-family: var(--op-font-sans);
    font-size: var(--op-text-base);
    line-height: var(--op-leading-normal);
    color: var(--op-text-primary);
    background: var(--op-bg-canvas);
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

.op-hub h1,
.op-hub h2,
.op-hub h3,
.op-hub h4 {
    font-family: var(--op-font-display);
    /* 500, not 700. Weight is the clinical register's main lever: a product
       catalogue that shouts reads as discount retail. */
    font-weight: 500;
    line-height: var(--op-leading-tight);
    letter-spacing: -0.015em;
    color: var(--op-text-primary);
    margin: 0 0 var(--op-space-4) 0;
}

.op-hub h1 { font-size: var(--op-text-4xl); }
.op-hub h2 { font-size: var(--op-text-3xl); }
.op-hub h3 { font-size: var(--op-text-2xl); }
.op-hub h4 { font-size: var(--op-text-xl); }

.op-hub p {
    margin: 0 0 var(--op-space-4) 0;
    max-width: var(--op-max-w-content);
}

.op-hub a {
    color: var(--link, var(--op-text-primary));
    text-decoration-color: var(--op-border-strong);
    text-underline-offset: 0.18em;
}

.op-hub a:hover {
    color: var(--link-hover, var(--op-accent-strong));
    text-decoration-color: currentColor;
}

.op-hub img {
    max-width: 100%;
    height: auto;
}

/* One focus treatment for the whole hub. Keyboard-only, so a pointer user
   never sees a ring on click but a keyboard user never loses their place. */
.op-hub :focus-visible {
    outline: 2px solid var(--op-border-focus);
    outline-offset: 2px;
    box-shadow: var(--op-focus-ring);
    border-radius: var(--op-radius-sm);
}

@media (prefers-reduced-motion: reduce) {
    .op-hub *,
    .op-hub *::before,
    .op-hub *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
}

/* THE PLATE MARBLE (Stanley 2026-10-21): a quiet marble behind every product
   picture, drawn in black and white alpha only so the palette keeps the
   plate's colour (tokens.css --op-plate-texture). */
:root {
    --op-plate-texture: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%20400%20500'%20preserveAspectRatio%3D'xMidYMid%20slice'%3E%3Cfilter%20id%3D'c'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.003%200.011'%20numOctaves%3D'3'%20seed%3D'24'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%200%200%200%200%200%200%200%200%200%200%200%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200.4%201'%2F%3E%3C%2FfeComponentTransfer%3E%3C%2Ffilter%3E%3Cfilter%20id%3D'v'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.0024%200.008'%20numOctaves%3D'5'%20seed%3D'21'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%200%200%200%200%200%200%200%200%200%200%200%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200%200%200%200%200%200%200.5%201%200.5%200%200%200%200.4%200.8%200.4%200%200%200%200%200%200%200%200'%2F%3E%3C%2FfeComponentTransfer%3E%3CfeGaussianBlur%20stdDeviation%3D'1.2'%2F%3E%3C%2Ffilter%3E%3Cfilter%20id%3D'h'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.0024%200.008'%20numOctaves%3D'5'%20seed%3D'21'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%201%200%200%200%200%201%200%200%200%200%201%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200%200%200%200%200%200.7%200%200%200.7%200%200%200%200.5%200%200.5%200%200%200%200%200%200%200%200'%2F%3E%3C%2FfeComponentTransfer%3E%3CfeGaussianBlur%20stdDeviation%3D'1.7999999999999998'%2F%3E%3C%2Ffilter%3E%3Cg%20transform%3D'rotate(-24%20200%20250)'%3E%3Crect%20x%3D'-300'%20y%3D'-300'%20width%3D'1000'%20height%3D'1100'%20filter%3D'url(%23c)'%20opacity%3D'0.03'%2F%3E%3Crect%20x%3D'-300'%20y%3D'-300'%20width%3D'1000'%20height%3D'1100'%20filter%3D'url(%23h)'%20opacity%3D'0.25'%2F%3E%3Crect%20x%3D'-300'%20y%3D'-300'%20width%3D'1000'%20height%3D'1100'%20filter%3D'url(%23v)'%20opacity%3D'0.05'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
}

/* THE SEARCH BAR (Stanley 2026-10-21): the phone's search bar at the top of
   every page stands on the site's brown with the discreet dark marble of the
   approved proposal, and its field is white. */
:root {
  --op-searchbar-surface: var(--op-accent, #574e3a);
  --op-searchbar-texture: url("data:image/svg+xml,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20width%3D'390'%20height%3D'120'%20preserveAspectRatio%3D'xMidYMid%20slice'%3E%3Cfilter%20id%3D'c'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.004%200.014'%20numOctaves%3D'3'%20seed%3D'9'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%200.10%200%200%200%200%200.075%200%200%200%200%200.04%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200.3%200.7%201'%2F%3E%3C%2FfeComponentTransfer%3E%3C%2Ffilter%3E%3Cfilter%20id%3D'v'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.003%200.01'%20numOctaves%3D'5'%20seed%3D'8'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%200.10%200%200%200%200%200.075%200%200%200%200%200.04%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200%200%200%200%200%200%200.5%201%200.5%200%200%200%200.4%200.8%200.4%200%200%200%200%200%200%200%200'%2F%3E%3C%2FfeComponentTransfer%3E%3CfeGaussianBlur%20stdDeviation%3D'0.6'%2F%3E%3C%2Ffilter%3E%3Cfilter%20id%3D'h'%20x%3D'-50%25'%20y%3D'-50%25'%20width%3D'200%25'%20height%3D'200%25'%3E%3CfeTurbulence%20type%3D'fractalNoise'%20baseFrequency%3D'0.003%200.01'%20numOctaves%3D'5'%20seed%3D'8'%2F%3E%3CfeColorMatrix%20type%3D'matrix'%20values%3D'0%200%200%200%200.92%200%200%200%200%200.86%200%200%200%200%200.74%201%200%200%200%200'%2F%3E%3CfeComponentTransfer%3E%3CfeFuncA%20type%3D'table'%20tableValues%3D'0%200%200%200%200%200%200%200.6%200%200%200.6%200%200%200%200.4%200%200.4%200%200%200%200%200%200%200%200'%2F%3E%3C%2FfeComponentTransfer%3E%3CfeGaussianBlur%20stdDeviation%3D'0.84'%2F%3E%3C%2Ffilter%3E%3Cg%20transform%3D'rotate(-20%20195%2060)'%3E%3Crect%20x%3D'-200'%20y%3D'-200'%20width%3D'790'%20height%3D'520'%20filter%3D'url(%23c)'%20opacity%3D'0.4'%2F%3E%3Crect%20x%3D'-200'%20y%3D'-200'%20width%3D'790'%20height%3D'520'%20filter%3D'url(%23h)'%20opacity%3D'0.22'%2F%3E%3Crect%20x%3D'-200'%20y%3D'-200'%20width%3D'790'%20height%3D'520'%20filter%3D'url(%23v)'%20opacity%3D'1'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
  --op-searchbar-field: #ffffff;
  --op-searchbar-field-shadow: 0 1px 2px rgb(0 0 0 / .18);
}

/* THE BUTTON ROLES (Stanley 2026-11-02). Three roles, one table, this shop only:
   the sections are shared with other sites and keep their own looks there.
     buy     the clinic green: only what puts a priced product in the routine or
             the cart, or goes to the checkout;
     lead    the ink: the main step that is not a purchase (the guide, a filter,
             a consent, a chosen tab);
     second  the neutral surface with an edge: every choice beside or under one
             of those.
   !important because each section prints its own rules later in the body, at
   whatever weight it needs; these three blocks are the one place that overrides
   them. The tokens below also repoint the shared door tokens that read the green,
   so the guide's next door and the phone bar's routine hub leave it too. */
:root {
  --op-btn-buy-bg: var(--role-brand);
  --op-btn-buy-bg-hover: var(--role-brand-deep);
  --op-btn-buy-on: var(--role-brand-on);
  --op-btn-lead-bg: var(--role-ink);
  --op-btn-lead-bg-hover: color-mix(in oklab, var(--role-ink) 84%, #ffffff);
  --op-btn-lead-on: #ffffff;
  --op-btn-second-bg: #ffffff;
  --op-btn-second-bg-hover: #f9f8f6;
  --op-btn-second-edge: #dfdedb;
  --op-btn-second-on: var(--role-ink);
  --op-btn-size: .82rem;
  --op-btn-tracking: .06em;
  --op-btn-touch: 44px;
  --accent-dark: var(--op-accent);
  --op-guide-door-bg: var(--op-btn-lead-bg);
  --op-guide-door-bg-hover: var(--op-btn-lead-bg-hover);
  --op-guide-door-on: var(--op-btn-lead-on);
  --op-appbar-brand: var(--op-btn-lead-bg);
  --op-shelf-buy-surface: var(--op-btn-buy-bg);
  --op-shelf-buy-texture: none;
  --op-shelf-buy-ink: var(--op-btn-buy-on);
  /* The bar's routine button stands in the bar: raised, it covered the text
     passing above it, and the same door is a tab already. */
  --op-appbar-hub-size: 2rem;
  --op-appbar-hub-rise: -.25rem;
  --op-appbar-hub-ring: 0px;
  --op-appbar-hub-mark: 1.375rem;
}

:is(
  .op-bt .op-bt__routine,
  .op-shelf .op-shelf__series-all,
  .op-guide-local .op-guide-local-take-door:not([data-wizard-take-done]),
  .op-shelf .op-shelf__cta[data-shelf-buy-all],
  .op-shelf .op-shelf__cta[data-shelf-draft-take],
  .op-product .op-product__actions a.op-product__buy,
  .op-pcard__doors a.op-pcard__door--buy,
  .op-checkout .op-checkout__pay,
  .op-rcheck .op-rcheck__add
) {
  border-color: var(--op-btn-buy-bg) !important;
  background: var(--op-btn-buy-bg) !important;
  color: var(--op-btn-buy-on) !important;
  box-shadow: none !important;
}

:is(
  .op-book a.op-book__cta,
  .op-needs .op-needs__guide-cta,
  .op-guide-local [data-wizard-controls] [data-wizard-next],
  .op-match-refine .op-match-refine__show,
  .th-cdlg .th-cdlg__btn--yes,
  .th-cdlg .th-cdlg__btn--choice[data-consent="yes"],
  .op-shelf .op-shelf__guide-start .op-shelf__cta,
  .op-checkout .op-checkout__guide,
  .op-match-refine .op-match-refine__chip[aria-pressed="true"]
) {
  border-color: var(--op-btn-lead-bg) !important;
  background: var(--op-btn-lead-bg) !important;
  color: var(--op-btn-lead-on) !important;
  box-shadow: none !important;
}

:is(
  .op-book a.op-book__secondary,
  .op-shelf .op-shelf__cta[data-shelf-browse-more],
  .op-shelf .op-rp__quiet,
  .op-shelf .op-rp__outline,
  .op-guide-local [data-wizard-edit],
  .op-guide-local [data-wizard-series-choice],
  .op-guide-local [data-wizard-save],
  .op-guide-local [data-wizard-clear],
  .op-guide-local .op-guide-local-take-door[data-wizard-take-done],
  .th-cdlg .th-cdlg__btn--no,
  .th-cdlg .th-cdlg__btn--choice[data-consent="no"],
  .op-product .op-product__actions .op-product__routine,
  .op-plist__more,
  .op-plist__refine,
  .op-rcheck .op-rcheck__skip,
  .op-rcheck .op-rcheck__addon-add
) {
  border: 1px solid var(--op-btn-second-edge) !important;
  background: var(--op-btn-second-bg) !important;
  color: var(--op-btn-second-on) !important;
  box-shadow: none !important;
  text-decoration: none !important;
}

/* The shape of every button in the three roles: one size, one case, one target. */
:is(
  .op-bt .op-bt__routine,
  .op-shelf .op-shelf__series-all,
  .op-guide-local .op-guide-local-take-door,
  .op-shelf .op-shelf__cta[data-shelf-buy-all],
  .op-shelf .op-shelf__cta[data-shelf-draft-take],
  .op-product .op-product__actions a.op-product__buy,
  .op-pcard__doors a.op-pcard__door--buy,
  .op-checkout .op-checkout__pay,
  .op-checkout .op-checkout__guide,
  .op-book a.op-book__cta,
  .op-needs .op-needs__guide-cta,
  .op-guide-local [data-wizard-controls] [data-wizard-next],
  .op-match-refine .op-match-refine__show,
  .th-cdlg .th-cdlg__btn--yes,
  .th-cdlg .th-cdlg__btn--no,
  .th-cdlg .th-cdlg__btn--choice,
  .op-shelf .op-shelf__guide-start .op-shelf__cta,
  .op-book a.op-book__secondary,
  .op-shelf .op-shelf__cta[data-shelf-browse-more],
  .op-shelf .op-rp__quiet,
  .op-shelf .op-rp__outline,
  .op-guide-local [data-wizard-edit],
  .op-guide-local [data-wizard-series-choice],
  .op-guide-local [data-wizard-save],
  .op-guide-local [data-wizard-clear],
  .op-product .op-product__actions .op-product__routine,
  .op-plist__more,
  .op-plist__refine,
  .op-rcheck .op-rcheck__add,
  .op-rcheck .op-rcheck__skip,
  .op-rcheck .op-rcheck__addon-add
) {
  min-block-size: var(--op-btn-touch) !important;
  font-size: var(--op-btn-size) !important;
  font-weight: 600 !important;
  letter-spacing: var(--op-btn-tracking) !important;
  text-transform: uppercase !important;
}

/* The buy row on a phone: the two words and the heart must share the width of
   the screen, so the words may take two lines rather than push the row wider. */
@media (max-width: 40rem) {
  .op-product .op-product__actions > :is(a.op-product__buy, .op-product__routine) {
    min-inline-size: 0 !important;
    padding-inline: var(--op-space-3, 12px) !important;
    white-space: normal !important;
  }
  .op-product .op-product__actions > .op-product__heart {
    flex: none;
  }
}

/* The trust row under the hero holds its points on one line from the tablet up,
   where two lines cost the last one its place on the first screen. Measured with
   the site's font: 721 px of points; narrower they keep wrapping. */
@media (min-width: 48rem) {
  .op-book .op-book__facts {
    flex-wrap: nowrap !important;
    gap: var(--op-space-5, 1.5rem) !important;
    white-space: nowrap;
  }
}

/* The quiet words become buttons here, so they need the room of one. A series
   choice that carries its description is a card, not a pill: it keeps the
   guide's card radius and padding, so lines of text never run into round ends. */
.op-shelf .op-rp__quiet,
.op-guide-local [data-wizard-edit],
.op-guide-local [data-wizard-series-choice]:not(:has(.op-wizard-series-detail)),
.op-guide-local [data-wizard-save],
.op-guide-local [data-wizard-clear] {
  padding-inline: var(--op-space-3, 12px) !important;
  border-radius: var(--op-radius-pill, 999px) !important;
}

@media (hover: hover) {
  :is(.op-bt .op-bt__routine, .op-shelf .op-shelf__series-all, .op-guide-local .op-guide-local-take-door:not([data-wizard-take-done]), .op-shelf .op-shelf__cta[data-shelf-buy-all], .op-shelf .op-shelf__cta[data-shelf-draft-take], .op-product .op-product__actions a.op-product__buy, .op-pcard__doors a.op-pcard__door--buy, .op-checkout .op-checkout__pay, .op-rcheck .op-rcheck__add):hover {
    border-color: var(--op-btn-buy-bg-hover) !important;
    background: var(--op-btn-buy-bg-hover) !important;
  }
  :is(.op-book a.op-book__cta, .op-needs .op-needs__guide-cta, .op-guide-local [data-wizard-controls] [data-wizard-next], .op-match-refine .op-match-refine__show, .th-cdlg .th-cdlg__btn--yes, .th-cdlg .th-cdlg__btn--choice[data-consent="yes"], .op-shelf .op-shelf__guide-start .op-shelf__cta):hover {
    border-color: var(--op-btn-lead-bg-hover) !important;
    background: var(--op-btn-lead-bg-hover) !important;
  }
  :is(.op-book a.op-book__secondary, .op-shelf .op-shelf__cta[data-shelf-browse-more], .op-shelf .op-rp__quiet, .op-shelf .op-rp__outline, .op-guide-local [data-wizard-edit],
  .op-guide-local [data-wizard-series-choice], .op-guide-local [data-wizard-save], .op-guide-local [data-wizard-clear], .th-cdlg .th-cdlg__btn--no, .th-cdlg .th-cdlg__btn--choice[data-consent="no"], .op-product .op-product__actions .op-product__routine, .op-plist__more, .op-plist__refine, .op-rcheck .op-rcheck__skip, .op-rcheck .op-rcheck__addon-add):hover {
    background: var(--op-btn-second-bg-hover) !important;
  }
}

/* THE PACKAGE SAVINGS LINE (product-card.js renderKitSaving, Stanley
   2026-11-09): a kit beside buying its members separately. A calm line in
   the secondary ink and a small pill in the brand's light pair; no strike,
   no sale colour. On a card it closes the price row on a line of its own. */
.op-pcard__saving,
.op-product__saving {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--op-space-1) var(--op-space-2);
    margin: 0;
    font-size: var(--op-text-xs);
    font-weight: 400;
    line-height: var(--op-leading-snug, 1.35);
    color: var(--op-text-secondary);
    font-variant-numeric: tabular-nums;
}
.op-pcard__saving {
    flex-basis: 100%;
    order: 1;
}
/* Card B keeps its price and add mark on one line (nowrap); a row that
   carries the savings line wraps so the line takes a line of its own. */
body .op-pcard[data-media-actions] .op-pcard__price-row:has(> .op-pcard__saving) {
    flex-wrap: wrap;
}
.op-product__saving {
    margin-block-start: var(--op-space-1);
}
.op-pcard__saving-pill,
.op-product__saving-pill {
    display: inline-flex;
    align-items: center;
    padding: .125rem var(--op-space-2);
    border-radius: var(--op-radius-pill);
    background: var(--role-brand-light);
    color: var(--role-brand-light-on);
    font-weight: 600;
    white-space: nowrap;
}

/* SOFT MOTION (Stanley 2026-11-09). Durations and the curve are the
   tokens.css motion tokens; only opacity and transform move, a fold's height
   being the one exception, and nothing moves unless the visitor's system
   allows it. Everything here is Dermashoppen's: this sheet loads on no other
   site. */
@property --op-rp-ship-at {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 0%;
}

@media (prefers-reduced-motion: no-preference) {
    /* A product picture still loading when the page starts arrives with a
       fade (media-gallery.js installImageArrival marks it); its plate or
       stage holds the box, so nothing shifts. */
    img[data-img-arrive] {
        transition: opacity var(--op-motion-image) var(--op-motion-ease-out), transform var(--op-motion-image) var(--op-motion-ease-out);
    }
    img[data-img-arrive="wait"] {
        opacity: 0;
    }

    /* Folds open and close by height where the browser can animate to auto
       (::details-content with interpolate-size); elsewhere they stay instant. */
    @supports (interpolate-size: allow-keywords) and selector(::details-content) {
        body :is(.op-product__fold, .op-product-band__inci, .op-product__details, .op-hf__item, .op-lib__item) {
            interpolate-size: allow-keywords;
        }
        body :is(.op-product__fold, .op-product-band__inci, .op-product__details, .op-hf__item, .op-lib__item)::details-content {
            block-size: 0;
            overflow-y: clip;
            transition: block-size var(--op-motion-panel) var(--op-motion-ease-out), content-visibility var(--op-motion-panel) allow-discrete;
        }
        body :is(.op-product__fold, .op-product-band__inci, .op-product__details, .op-hf__item, .op-lib__item)[open]::details-content {
            block-size: auto;
        }
    }

    /* Panels and drawers: one curve and one duration in and out. The routine
       panel's drawer and sheet, the filter sheet with its backdrop, the menu
       panel and the search's result groups. */
    body .op-shelf__drawer {
        transition: grid-template-rows var(--op-motion-panel) var(--op-motion-ease-out), visibility var(--op-motion-panel) var(--op-motion-ease-out);
    }
    body .op-shelf[data-shelf-navigation-ready] {
        transition: transform var(--op-motion-panel) var(--op-motion-ease-out), visibility 0s linear var(--op-motion-panel);
    }
    body .op-shelf[data-shelf-navigation-ready][data-shelf-state="open"] {
        transition-delay: 0s;
    }
    body .op-match-refine[open] {
        animation-duration: var(--op-motion-panel);
        animation-timing-function: var(--op-motion-ease-out);
    }
    body .op-match-refine {
        transition: opacity var(--op-motion-panel) var(--op-motion-ease-out), transform var(--op-motion-panel) var(--op-motion-ease-out), overlay var(--op-motion-panel) allow-discrete, display var(--op-motion-panel) allow-discrete;
    }
    body .op-match-refine:not([open]) {
        opacity: 0;
        transform: translateY(var(--op-motion-shift));
    }
    body .op-match-refine::backdrop {
        transition: opacity var(--op-motion-panel) var(--op-motion-ease-out), overlay var(--op-motion-panel) allow-discrete, display var(--op-motion-panel) allow-discrete;
    }
    body .op-match-refine:not([open])::backdrop {
        opacity: 0;
    }
    @starting-style {
        body .op-match-refine[open]::backdrop {
            opacity: 0;
        }
    }
    body .op-nav-menu[data-nav-menu-panel] {
        transition: opacity var(--op-motion-panel) var(--op-motion-ease-out), transform var(--op-motion-panel) var(--op-motion-ease-out), display var(--op-motion-panel) allow-discrete;
    }
    body .op-nav-menu[data-nav-menu-panel][hidden] {
        opacity: 0;
        transform: translateY(var(--op-motion-shift));
    }
    @starting-style {
        body .op-nav-menu[data-nav-menu-panel]:not([hidden]) {
            opacity: 0;
            transform: translateY(var(--op-motion-shift));
        }
    }
    html body .op-nav-scrim {
        transition-duration: var(--op-motion-panel);
        transition-timing-function: var(--op-motion-ease-out);
    }
    body .op-match__group {
        transition: opacity var(--op-motion-feedback) var(--op-motion-ease-out);
    }
    @starting-style {
        body .op-match__group:not([hidden]) {
            opacity: 0;
        }
    }

    /* The product page's routine door confirms with its own word and check
       ("I rutinen"): they fade in when pressed. The heart answers a press. */
    body .op-product__routine[aria-pressed="true"] > .op-product__routine-word--on {
        animation: op-ds-confirm var(--op-motion-feedback) var(--op-motion-ease-out) both;
    }
    body .op-product__routine[aria-pressed="true"]::before {
        animation: op-ds-fade var(--op-motion-feedback) var(--op-motion-ease-out) both;
    }
    body .op-product__heart[aria-pressed="true"] {
        animation: op-ds-press var(--op-motion-feedback) var(--op-motion-ease-out);
    }

    /* The free shipping bar moves to its new share instead of jumping. */
    body .op-rp__ship-bar {
        transition: --op-rp-ship-at var(--op-motion-progress) var(--op-motion-ease-out);
    }

    @keyframes op-ds-confirm {
        from { opacity: 0; transform: translateY(.125rem); }
    }
    @keyframes op-ds-fade {
        from { opacity: 0; }
    }
    @keyframes op-ds-press {
        50% { transform: scale(.94); }
    }
}

/* A card answers a desktop pointer only: its picture grows a hair, the
   shadow step stays; the lift is gone, and a touch leaves nothing raised. */
body .op-pcard:is(:hover, :focus-within) {
    transform: none;
}
@media (hover: none) {
    body .op-pcard:is(:hover, :focus-within) {
        box-shadow: var(--op-shadow-elev-1, var(--op-shadow-card, none));
    }
}
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    body .op-pcard[data-media-actions]:hover .op-pcard__img {
        transform: scale(var(--op-hover-zoom));
    }
}
/* Keyboard focus on the product's link answers like the pointer does. */
@media (prefers-reduced-motion: no-preference) {
    body .op-pcard[data-media-actions]:has(.op-pcard__name a:focus-visible) .op-pcard__img {
        transform: scale(var(--op-hover-zoom));
    }
}
